🥵

LLMに優しいクローラーライブラリーcrawl4aiを使ってみた

2024/10/22に公開

はじめに

クローラーライブラリーといえば、scrapyやbeautifulsoup、シンプルなrequestsなどがあります。
いずれも老舗のライブラリーで、LLMにあげるまで何らかの型変換を行う必要がありました。
本記事では、LLMに優しいクローラーライブラリーcrawl4aiを使ってみたいと思います。

crawl4ai主な機能

以下はcrawl4aiの主な機能です:

  • オープンソース
  • パフォーマンスが優秀
  • LLMに優しい出力形式(JSON、クリーンなHTML、マークダウン)をサポートする
  • 複数のURLを同時にクロールできる
  • すべてのメディアタグ(画像、音声、動画)を抽出できる
  • すべての外部リンクと内部リンクを抽出できる
  • ページのスクリーンショット取得可能
  • クロール前に複数のカスタムJavaScriptを実行可能
  • JsonCssExtractionStrategyを使用してLLMなしで構造化された出力を生成できる
  • プロキシの適応

インストール

pip3 install crawl4ai
playwright install

使い方

まずは公式のサンプルでAWSニュースをクローリングしてみます。

import asyncio
from crawl4ai import AsyncWebCrawler

async def main():
    async with AsyncWebCrawler(verbose=True) as crawler:
        result = await crawler.arun(url="https://aws.amazon.com/jp/about-aws/whats-new/2024/10/aws-security-hub-new-security-controls/")
        print(result.markdown)

if __name__ == "__main__":
    asyncio.run(main())
出力結果
[LOG] 🌤️  Warming up the AsyncWebCrawler
[LOG] 🌞 AsyncWebCrawler is ready to crawl
[LOG] 🚀 Content extracted for https://aws.amazon.com/jp/about-aws/whats-new/2024/10/aws-security-hub-new-security-controls/, success: True, time taken: 0.02 seconds
[LOG] 🚀 Extraction done for https://aws.amazon.com/jp/about-aws/whats-new/2024/10/aws-security-hub-new-security-controls/, time taken: 0.02 seconds.
## Cookie の設定を選択する

当社は、当社のサイトおよびサービスを提供するために必要な必須 Cookie および類似のツールを使用しています。当社は、パフォーマンス Cookie
を使用して匿名の統計情報を収集することで、お客様が当社のサイトをどのように利用しているかを把握し、改善に役立てています。必須 Cookie
は無効化できませんが、[カスタマイズ] または [拒否] をクリックしてパフォーマンス Cookie を拒否することはできます。  
  
お客様が同意した場合、AWS および承認された第三者は、Cookie
を使用して便利なサイト機能を提供したり、お客様の選択を記憶したり、関連する広告を含む関連コンテンツを表示したりします。すべての必須ではない Cookie
を受け入れるか拒否するには、[受け入れる] または [拒否] をクリックしてください。より詳細な選択を行うには、[カスタマイズ] をクリックしてください。

受け入れる拒否カスタマイズ

## Cookie の設定をカスタマイズする

当社は、以下の目的で Cookie および同様のツール (以下総称して「Cookie」) を使用いたします。

### Essential

Essential Cookie
は、当社のサイトおよびサービスを提供するために必要であり、無効にすることはできません。通常、プライバシー設定の選択、サインイン、フォームへの入力など、サイトでのアクションに応じてのみ設定されます。

### Performance

Performance Cookie
は、お客様によるサイトの操作方法に関する匿名の統計を提供するため、サイトのエクスペリエンスとパフォーマンスを向上させることができます。承認された第三者は、当社に代わって分析を行う場合がありますが、データを独自の目的で使用することはできません。

Performance カテゴリを許可する

許可

### Functional

Functional Cookie
は、有用なサイト機能の提供、ユーザーの嗜好の記憶、関連コンテンツの表示に役立ちます。承認された第三者は、特定のサイト機能を提供するためにこれらのクッキーを設定する場合があります。これらのクッキーを許可しない場合、サービスの一部またはすべてが適切に機能しない可能性があります。

Functional カテゴリを許可する

許可

### Advertising

Advertising Cookie
は、当社の広告パートナーによって当社のサイトを通じて設定され、関連するマーケティングコンテンツの配信に役立ちます。これらの Cookie
を許可しないと、広告の関連性が低くなります。

Advertising カテゴリを許可する

許可

一部の種類の Cookie をブロックすると、サイトの操作に影響する可能性があります。Cookie の設定は、このサイトのフッターにある [Cookie
preferences] をクリックすることで、いつでも変更できます。当社および承認された第三者が Cookie
をどのように使用しているかについては、「AWS Cookie Notice」をお読みください。

キャンセル設定を保存

## Cookie の設定を保存できません

Cookie の設定を保存できなかったため、現時点では不可欠な Cookie のみを保存します。  
  
Cookie の設定を変更する場合は、AWS
コンソールのフッターにあるリンクを使用して後でもう一度お試しください。問題が解決しない場合は、サポートにお問い合わせください。

閉じる

メインコンテンツに移動

アマゾン ウェブ サービスのホームページに戻るには、ここをクリック

AWS について お問い合わせ サポート  日本語  アカウント

サインイン

AWS アカウントを作成

  * 製品
  * ソリューション
  * 料金
  * ドキュメント
  * 学ぶ
  * パートナーネットワーク
  * AWS Marketplace
  * カスタマーサポート
  * イベント
  * その他 

閉じる

  * عربي
  * Bahasa Indonesia
  * Deutsch
  * English
  * Español
  * Français
  * Italiano
  * Português

  * Tiếng Việt
  * Türkçe
  * Ρусский
  * ไทย
  * 한국어
  * 中文 (简体)
  * 中文 (繁體)

閉じる

  * 自分のプロフィール
  * AWS Builder ID からサインアウト
  * AWS マネジメントコンソール
  * アカウント設定
  * 請求情報とコスト管理
  * セキュリティ認証情報
  * AWS Personal Health Dashboard

閉じる

  * サポートセンター
  * エキスパートによるサポート
  * ナレッジセンター
  * AWS サポートの概要
  * AWS re:Post

アマゾン ウェブ サービスのホームページに戻るには、ここをクリック

無料で開始する

お問い合わせ

  * 製品 
  * ソリューション 
  * 料金 
  * AWS 入門 
  * 開始方法 
  * ドキュメント 
  * トレーニングと認定 
  * デベロッパーセンター 
  * お客様の導入事例 
  * パートナー 
  * AWS Marketplace 
  * サポート 
  * AWS re:Post 
  * コンソールにログイン 
  * モバイルアプリをダウンロード 

# AWS Security Hub が 7 つの新しいセキュリティコントロールをリリース

投稿日: 2024年10月4日

AWS Security Hub は 7 つの新しいセキュリティコントロールをリリースしました。これにより、提供されるコントロールの総数は 430
に増えました。Security Hub は、Amazon Simple Storage Service (S3) マルチリージョンアクセスポイントや
Amazon Managed Streaming for Apache Kafka (MSK) Connect
など、新しいリソースタイプのコントロールをサポートするようになりました。Security Hub は、Amazon GuardDuty EKS
Runtime Monitoring の新たなコントロールもリリースしました。最近リリースされたコントロールと利用できる AWS
リージョンの全リストについては、Security Hub ユーザーガイドをご覧ください。
新しいコントロールを使用するには、そのコントロールが属する標準を有効にしてください。その後、Security Hub
はセキュリティ体制の評価を開始し、関連するセキュリティコントロールについてリソースを監視します。一元構成を使用すると、1
回の操作ですべての組織アカウントとリンクされたリージョンを対象に実行できます。既に該当の標準を使用していて、Security Hub
で新しいコントロールが自動で有効になるように設定している場合、これらの新しいコントロールは追加のアクションなしで実行されます。
使用を開始するには、以下のリストのリソースをご確認ください。

  * Security Hub の機能の詳細については、AWS Security Hub ユーザーガイドをご覧ください
  * Security Hub の新機能や新コントロールに関する通知を受け取るには、Security Hub の SNS トピックをサブスクライブしてください
  * AWS 無料利用枠で Security Hub を 30 日間無料でお試しいただけます。

コンソールにサインイン

###  AWS について学ぶ

  * AWS とは
  * クラウドコンピューティングとは
  * AWS のインクルージョン、ダイバーシティ、および公平性
  * DevOps とは
  * コンテナとは
  * データレイクとは
  * AWS クラウドセキュリティ
  * 最新情報
  * ブログ
  * Press Releases

###  AWS のリソース

  * 開始方法
  * トレーニングと認定
  * AWS ソリューションライブラリ
  * アーキテクチャセンター
  * 製品と技術上のよくある質問
  * アナリストレポート
  * AWS パートナー

###  AWS を利用するデベロッパー

  * デベロッパーセンター
  * SDK とツール
  * AWS での .NET
  * AWS での Python
  * AWS での Java
  * AWS での PHP
  * AWS での JavaScript
  * builders.flash (AWS 公式ウェブマガジン)

###  ヘルプ

  * お問い合わせ
  * 専門家によるサポートを受ける
  * サポートチケットを申請する
  * AWS re:Post
  * ナレッジセンター
  * AWS サポートの概要
  * 法務関連
  * AWS の採用情報

AWS アカウントを無料で作成

Amazon は男女雇用機会均等法を順守しています。
人種、出身国、性別、性的指向、障がい、年齢、その他の属性によって差別することなく、平等に採用選考の機会を提供しています。

  * 言語
  * عربي
  * Bahasa Indonesia
  * Deutsch
  * English
  * Español
  * Français
  * Italiano
  * Português
  * Tiếng Việt
  * Türkçe
  * Ρусский
  * ไทย
  * 日本語
  * 한국어
  * 中文 (简体)
  * 中文 (繁體)

  * プライバシー
  * |
  * サイト規約
  * |
  * Cookie の詳細設定 
  * |
  * © 2023, Amazon Web Services, Inc. or its affiliates.All rights reserved.

####  Internet Explorer のサポートの終了

了承しました

AWS support for Internet Explorer は 07/31/2022
に終了します。サポートされているブラウザは、Chrome、Firefox、Edge、Safari です。 詳細はこちら »

了承しました


実行時間: 0.35797595977783203秒

基本markdownで出力されているので、LLMに送信してもそのまま使えますね。
スピードも速いです。
MarkdownをJSONに変換したり、APIサーバーに取り込むこともできます。

import time
import asyncio
import json
from crawl4ai import AsyncWebCrawler

def markdown_to_json(markdown):
    return {
        "content": markdown,
        "type": "markdown"
    }

async def main():
    async with AsyncWebCrawler(verbose=True) as crawler:
        result = await crawler.arun(url="https://aws.amazon.com/jp/about-aws/whats-new/2024/10/aws-security-hub-new-security-controls/")
        json_result = markdown_to_json(result.markdown)
        print(json.dumps(json_result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
    start_time = time.time()
    asyncio.run(main())
    end_time = time.time()
    print(f"実行時間: {end_time - start_time}秒")
出力結果
[LOG] 🌤️  Warming up the AsyncWebCrawler
[LOG] 🌞 AsyncWebCrawler is ready to crawl
[LOG] 🚀 Content extracted for https://aws.amazon.com/jp/about-aws/whats-new/2024/10/aws-security-hub-new-security-controls/, success: True, time taken: 0.02 seconds
[LOG] 🚀 Extraction done for https://aws.amazon.com/jp/about-aws/whats-new/2024/10/aws-security-hub-new-security-controls/, time taken: 0.02 seconds.
{
  "content": "## Cookie の設定を選択する\n\n当社は、当社のサイトおよびサービスを提供するために必要な必須 Cookie および類似のツールを使用しています。当社は、パフォーマンス Cookie\nを使用して匿名の統計情報を収集することで、お客様が当社のサイトをどのように利用しているかを把握し、改善に役立てています。必須 Cookie\nは無効化できませんが、[カスタマイズ] または [拒否] をクリックしてパフォーマンス Cookie を拒否することはできます。  \n  \nお客様が同意した場合、AWS および承認された第三者は、Cookie\nを使用して便利なサイト機能を提供したり、お客様の選択を記憶したり、関連する広告を含む関連コンテンツを表示したりします。すべての必須ではない Cookie\nを受け入れるか拒否するには、[受け入れる] または [拒否] をクリックしてください。より詳細な選択を行うには、[カスタマイズ] をクリックしてください。\n\n受け入れる拒否カスタマイズ\n\n## Cookie の設定をカスタマイズする\n\n当社は、以下の目的で Cookie および同様のツール (以下総称して「Cookie」) を使用いたします。\n\n### Essential\n\nEssential Cookie\nは、当社のサイトおよびサービスを提供するために必要であり、無効にすることはできません。通常、プライバシー設定の選択、サインイン、フォームへの入力など、サイトでのアクションに応じてのみ設定されます。\n\n### Performance\n\nPerformance Cookie\nは、お客様によるサイトの操作方法に関する匿名の統計を提供するため、サイトのエクスペリエンスとパフォーマンスを向上させることができます。承認された第三者は、当社に代わって分析を行う場合がありますが、データを独自の目的で使用することはできません。\n\nPerformance カテゴリを許可する\n\n許可\n\n### Functional\n\nFunctional Cookie\nは、有用なサイト機能の提供、ユーザーの嗜好の記憶、関連コンテンツの表示に役立ちます。承認された第三者は、特定のサイト機能を提供するためにこれらのクッキーを設定する場合があります。これらのクッキーを許可しない場合、サービスの一部またはすべてが適切に機能しない可能性があります。\n\nFunctional カテゴリを許可する\n\n許可\n\n### Advertising\n\nAdvertising Cookie\nは、当社の広告パートナーによって当社のサイトを通じて設定され、関連するマーケティングコンテンツの配信に役立ちます。これらの Cookie\nを許可しないと、広告の関連性が低くなります。\n\nAdvertising カテゴリを許可する\n\n許可\n\n一部の種類の Cookie をブロックすると、サイトの操作に影響する可能性があります。Cookie の設定は、このサイトのフッターにある [Cookie\npreferences] をクリックすることで、いつでも変更できます。当社および承認された第三者が Cookie\nをどのように使用しているかについては、「AWS Cookie Notice」をお読みください。\n\nキャンセル設定を保存\n\n## Cookie の設定を保存できません\n\nCookie の設定を保存できなかったため、現時点では不可欠な Cookie のみを保存します。  \n  \nCookie の設定を変更する場合は、AWS\nコンソールのフッターにあるリンクを使用して後でもう一度お試しください。問題が解決しない場合は、サポートにお問い合わせください。\n\n閉じる\n\nメインコンテンツに移動\n\nアマゾン ウェブ サービスのホームページに戻るには、ここをクリック\n\nAWS について お問い合わせ サポート  日本語  アカウント\n\nサインイン\n\nAWS アカウントを作成\n\n  * 製品\n  * ソリューション\n  * 料金\n  * ドキュメント\n  * 学ぶ\n  * パートナーネットワーク\n  * AWS Marketplace\n  * カスタマーサポート\n  * イベント\n  * その他 \n\n閉じる\n\n  * عربي\n  * Bahasa Indonesia\n  * Deutsch\n  * English\n  * Español\n  * Français\n  * Italiano\n  * Português\n\n  * Tiếng Việt\n  * Türkçe\n  * Ρусский\n  * ไทย\n  * 한국어\n  * 中文 (简体)\n  * 中文 (繁體)\n\n閉じる\n\n  * 自分のプロフィール\n  * AWS Builder ID からサインアウト\n  * AWS マネジメントコンソール\n  * アカウント設定\n  * 請求情報とコスト管理\n  * セキュリティ認証情報\n  * AWS Personal Health Dashboard\n\n閉じる\n\n  * サポートセンター\n  * エキスパートによるサポート\n  * ナレッジセンター\n  * AWS サポートの概要\n  * AWS re:Post\n\nアマゾン ウェブ サービスのホームページに戻るには、ここをクリック\n\n無料で開始する\n\nお問い合わせ\n\n  * 製品 \n  * ソリューション \n  * 料金 \n  * AWS 入門 \n  * 開始方法 \n  * ドキュメント \n  * トレーニングと認定 \n  * デベロッパーセンター \n  * お客様の導入事例 \n  * パートナー \n  * AWS Marketplace \n  * サポート \n  * AWS re:Post \n  * コンソールにログイン \n  * モバイルアプリをダウンロード \n\n# AWS Security Hub が 7 つの新しいセキュリティコントロールをリリース\n\n投稿日: 2024年10月4日\n\nAWS Security Hub は 7 つの新しいセキュリティコントロールをリリースしました。これにより、提供されるコントロールの総数は 430\nに増えました。Security Hub は、Amazon Simple Storage Service (S3) マルチリージョンアクセスポイントや\nAmazon Managed Streaming for Apache Kafka (MSK) Connect\nなど、新しいリソースタイプのコントロールをサポートするようになりました。Security Hub は、Amazon GuardDuty EKS\nRuntime Monitoring の新たなコントロールもリリースしました。最近リリースされたコントロールと利用できる AWS\nリージョンの全リストについては、Security Hub ユーザーガイドをご覧ください。\n新しいコントロールを使用するには、そのコントロールが属する標準を有効にしてください。その後、Security Hub\nはセキュリティ体制の評価を開始し、関連するセキュリティコントロールについてリソースを監視します。一元構成を使用すると、1\n回の操作ですべての組織アカウントとリンクされたリージョンを対象に実行できます。既に該当の標準を使用していて、Security Hub\nで新しいコントロールが自動で有効になるように設定している場合、これらの新しいコントロールは追加のアクションなしで実行されます。\n使用を開始するには、以下のリストのリソースをご確認ください。\n\n  * Security Hub の機能の詳細については、AWS Security Hub ユーザーガイドをご覧ください\n  * Security Hub の新機能や新コントロールに関する通知を受け取るには、Security Hub の SNS トピックをサブスクライブしてください\n  * AWS 無料利用枠で Security Hub を 30 日間無料でお試しいただけます。\n\nコンソールにサインイン\n\n###  AWS について学ぶ\n\n  * AWS とは\n  * クラウドコンピューティングとは\n  * AWS のインクルージョン、ダイバーシティ、および公平性\n  * DevOps とは\n  * コンテナとは\n  * データレイクとは\n  * AWS クラウドセキュリティ\n  * 最新情報\n  * ブログ\n  * Press Releases\n\n###  AWS のリソース\n\n  * 開始方法\n  * トレーニングと認定\n  * AWS ソリューションライブラリ\n  * アーキテクチャセンター\n  * 製品と技術上のよくある質問\n  * アナリストレポート\n  * AWS パートナー\n\n###  AWS を利用するデベロッパー\n\n  * デベロッパーセンター\n  * SDK とツール\n  * AWS での .NET\n  * AWS での Python\n  * AWS での Java\n  * AWS での PHP\n  * AWS での JavaScript\n  * builders.flash (AWS 公式ウェブマガジン)\n\n###  ヘルプ\n\n  * お問い合わせ\n  * 専門家によるサポートを受ける\n  * サポートチケットを申請する\n  * AWS re:Post\n  * ナレッジセンター\n  * AWS サポートの概要\n  * 法務関連\n  * AWS の採用情報\n\nAWS アカウントを無料で作成\n\nAmazon は男女雇用機会均等法を順守しています。\n人種、出身国、性別、性的指向、障がい、年齢、その他の属性によって差別することなく、平等に採用選考の機会を提供しています。\n\n  * 言語\n  * عربي\n  * Bahasa Indonesia\n  * Deutsch\n  * English\n  * Español\n  * Français\n  * Italiano\n  * Português\n  * Tiếng Việt\n  * Türkçe\n  * Ρусский\n  * ไทย\n  * 日本語\n  * 한국어\n  * 中文 (简体)\n  * 中文 (繁體)\n\n  * プライバシー\n  * |\n  * サイト規約\n  * |\n  * Cookie の詳細設定 \n  * |\n  * © 2023, Amazon Web Services, Inc. or its affiliates.All rights reserved.\n\n####  Internet Explorer のサポートの終了\n\n了承しました\n\nAWS support for Internet Explorer は 07/31/2022\nに終了します。サポートされているブラウザは、Chrome、Firefox、Edge、Safari です。 詳細はこちら »\n\n了承しました\n\n",
  "type": "markdown"
}
実行時間: 0.26685094833374023秒

高度な機能と使用例

Proxyの適応

Proxyを使用する場合は、以下のようにします。

import asyncio
from crawl4ai import AsyncWebCrawler

async def main():
    async with AsyncWebCrawler(verbose=True, proxy="http://your-proxy-url:port") as crawler:
        result = await crawler.arun(url="https://aws.amazon.com/jp/about-aws/whats-new/2024/10/aws-security-hub-new-security-controls/")
        print(result.markdown)

if __name__ == "__main__":
    asyncio.run(main())

構造化データの抽出

crawl4aiは、CSSセレクタを使用して構造化データを抽出する機能も提供しています:

import time
import asyncio
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
import json

async def main():
    schema = {
        "name": "News Articles",
        "baseSelector": ".wide-tease-item__info-wrapper",
        "fields": [
            {
                "name": "title",
                "selector": "h2",
                "type": "text",
            },
            {
                "name": "summary",
                "selector": "div.wide-tease-item__description",
                "type": "text",
            }
        ],
    }

    async with AsyncWebCrawler(verbose=True) as crawler:
        result = await crawler.arun(
            url="https://www.nbcnews.com/business",
            bypass_cache=True,
            extraction_strategy=JsonCssExtractionStrategy(schema, verbose=True)
        )
        extracted_data = json.loads(result.extracted_content)
        print(extracted_data)

if __name__ == "__main__":
    start_time = time.time()
    asyncio.run(main())
    end_time = time.time()
    print(f"実行時間: {end_time - start_time}秒")
出力結果
[LOG] 🌤️  Warming up the AsyncWebCrawler
[LOG] 🌞 AsyncWebCrawler is ready to crawl
[LOG] 🕸️ Crawling https://www.nbcnews.com/business using AsyncPlaywrightCrawlerStrategy...
[LOG] ✅ Crawled https://www.nbcnews.com/business successfully!
[LOG] 🚀 Crawling done for https://www.nbcnews.com/business, success: True, time taken: 0.66 seconds
[LOG] 🚀 Content extracted for https://www.nbcnews.com/business, success: True, time taken: 0.04 seconds
[LOG] 🔥 Extracting semantic blocks for https://www.nbcnews.com/business, Strategy: AsyncWebCrawler
[LOG] 🚀 Extraction done for https://www.nbcnews.com/business, time taken: 0.06 seconds.
[{'title': 'Consumer', 'summary': 'Many victims facing catastrophic losses due to water damage “are just walking away.”'}, {'title': 'News', 'summary': 'Whitewater-trained crews in Tennessee and North Carolina have spent the last two weeks pivoting from recreation to emergency response in the wreckage of two deadly hurricanes.'}, {'title': 'Business News', 'summary': 'The job and cost cuts are the most dramatic moves to date from CEO\xa0Kelly Ortberg, who is just over two months into his tenure in the top job.'}, {'title': 'Personal Finance', 'summary': 'The shift in the way the next generations give, as well as the causes they favor, is likely to remake the charitable landscape.'}, {'title': 'Consumer', 'summary': 'The latest Social Security adjustments come amid growing concerns about the\xa0program’s solvency.'}, {'title': 'Economy', 'summary': "The latest data indicates that though prices aren't receding from their heights of a few years ago, the rate at which they are increasing is pulling back."}, {'title': 'Markets', 'summary': 'A strong start to the third-quarter earnings season provided a lift to stocks.'}, {'title': 'Culture & Trends', 'summary': '“This sort of thing can easily be prevented by hiring the so-called ‘DEI’ candidate,” one X user wrote.'}, {'title': 'World', 'summary': 'Four had been detained in China’s Zhengzhou, home to a major Foxconn plant assembling Apple’s iPhones, on suspicion of the equivalent of “breach of trust,” Taiwan’s Mainland Affairs Council said.'}, {'title': '2024 Election', 'summary': "The former president didn't name any companies in his address to the Detroit Economic Club."}, {'title': 'Business News', 'summary': 'As part of the deal, TD Bank is accepting limits on its growth, the\xa0Office of the Comptroller of the Currency\xa0announced Thursday.'}]
実行時間: 0.9827871322631836秒

動的コンテンツの複数ページクローリング

crawl4aiは、動的に変化するコンテンツを持つ複数のページをクロールする高度な機能も提供しています。
以下は、GitHubのコミット履歴を複数ページにわたってクロールする例です。
この例では、JavaScriptを実行してページネーションを行い、新しいコンテンツが読み込まれるのを待ってから次のページをクロールしています。

import time
import asyncio
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import JsonCssExtractionStrategy
import json

async def main():
    schema = {
        "name": "News Articles",
        "baseSelector": ".wide-tease-item__info-wrapper",
        "fields": [
            {
                "name": "title",
                "selector": "h2",
                "type": "text",
            },
            {
                "name": "summary",
                "selector": "div.wide-tease-item__description",
                "type": "text",
            }
        ],
    }

    async with AsyncWebCrawler(verbose=True) as crawler:
        async with AsyncWebCrawler(verbose=True) as crawler:
            url = "https://github.com/microsoft/TypeScript/commits/main"
            session_id = "typescript_commits_session"
            all_commits = []

            js_next_page = """
            const commits = document.querySelectorAll('li.Box-sc-g0xbh4-0 h4');
            if (commits.length > 0) {
                window.firstCommit = commits[0].textContent.trim();
            }
            const button = document.querySelector('a[data-testid="pagination-next-button"]');
            if (button) button.click();
            """

            wait_for = """() => {
            const commits = document.querySelectorAll('li.Box-sc-g0xbh4-0 h4');
            if (commits.length === 0) return false;
            const firstCommit = commits[0].textContent.trim();
            return firstCommit !== window.firstCommit;
            }"""

            schema = {
                "name": "Commit Extractor",
                "baseSelector": "li.Box-sc-g0xbh4-0",
                "fields": [
                    {
                        "name": "title",
                        "selector": "h4.markdown-title",
                        "type": "text",
                        "transform": "strip",
                    },
                ],
            }

            extraction_strategy = JsonCssExtractionStrategy(schema, verbose=True)

            for page in range(3):  # 3ページをクロール
                result = await crawler.arun(
                    url=url,
                    session_id=session_id,
                    css_selector="li.Box-sc-g0xbh4-0",
                    extraction_strategy=extraction_strategy,
                    js_code=js_next_page if page > 0 else None,
                    wait_for=wait_for if page > 0 else None,
                    js_only=page > 0,
                    bypass_cache=True,
                    headless=False,
                )
                assert result.success, f"Failed to crawl page {page + 1}"
                commits = json.loads(result.extracted_content)
                all_commits.extend(commits)
                print(f"Page {page + 1}: Found {len(commits)} commits")

            await crawler.crawler_strategy.kill_session(session_id)
            print(f"Successfully crawled {len(all_commits)} commits across 3 pages")

if __name__ == "__main__":
    start_time = time.time()
    asyncio.run(main())
    end_time = time.time()
    print(f"実行時間: {end_time - start_time}秒")
出力結果
[LOG] 🌤️  Warming up the AsyncWebCrawler
[LOG] 🌞 AsyncWebCrawler is ready to crawl
[LOG] 🌤️  Warming up the AsyncWebCrawler
[LOG] 🌞 AsyncWebCrawler is ready to crawl
[LOG] 🕸️ Crawling https://github.com/microsoft/TypeScript/commits/main using AsyncPlaywrightCrawlerStrategy...
[LOG] ✅ Crawled https://github.com/microsoft/TypeScript/commits/main successfully!
[LOG] 🚀 Crawling done for https://github.com/microsoft/TypeScript/commits/main, success: True, time taken: 1.46 seconds
[LOG] 🚀 Content extracted for https://github.com/microsoft/TypeScript/commits/main, success: True, time taken: 0.07 seconds
[LOG] 🔥 Extracting semantic blocks for https://github.com/microsoft/TypeScript/commits/main, Strategy: AsyncWebCrawler
[LOG] 🚀 Extraction done for https://github.com/microsoft/TypeScript/commits/main, time taken: 0.11 seconds.
Page 1: Found 35 commits
[LOG] 🕸️ Crawling https://github.com/microsoft/TypeScript/commits/main using AsyncPlaywrightCrawlerStrategy...
[LOG] ✅ Crawled https://github.com/microsoft/TypeScript/commits/main successfully!
[LOG] 🚀 Crawling done for https://github.com/microsoft/TypeScript/commits/main, success: True, time taken: 1.82 seconds
[LOG] 🚀 Content extracted for https://github.com/microsoft/TypeScript/commits/main, success: True, time taken: 0.08 seconds
[LOG] 🔥 Extracting semantic blocks for https://github.com/microsoft/TypeScript/commits/main, Strategy: AsyncWebCrawler
[LOG] 🚀 Extraction done for https://github.com/microsoft/TypeScript/commits/main, time taken: 0.13 seconds.
Page 2: Found 35 commits
[LOG] 🕸️ Crawling https://github.com/microsoft/TypeScript/commits/main using AsyncPlaywrightCrawlerStrategy...
[LOG] ✅ Crawled https://github.com/microsoft/TypeScript/commits/main successfully!
[LOG] 🚀 Crawling done for https://github.com/microsoft/TypeScript/commits/main, success: True, time taken: 0.64 seconds
[LOG] 🚀 Content extracted for https://github.com/microsoft/TypeScript/commits/main, success: True, time taken: 0.06 seconds
[LOG] 🔥 Extracting semantic blocks for https://github.com/microsoft/TypeScript/commits/main, Strategy: AsyncWebCrawler
[LOG] 🚀 Extraction done for https://github.com/microsoft/TypeScript/commits/main, time taken: 0.11 seconds.
Page 3: Found 35 commits
Successfully crawled 105 commits across 3 pages
実行時間: 4.919917821884155秒

LLMによる高度なデータ抽出

crawl4aiは、LLMを使用して構造化データや関連コンテンツをWebページから抽出可能です。
これでPromptとして与えることで、柔軟なデータ抽出が可能です。
下記例では、OpenAIの価格ページから構造化データ(モデル名と料金)を抽出しています。

import os
import json
import asyncio
import time
from crawl4ai import AsyncWebCrawler
from crawl4ai.extraction_strategy import LLMExtractionStrategy
from pydantic import BaseModel, Field

class OpenAIModelFee(BaseModel):
    model_name: str = Field(..., description="Name of the OpenAI model.")
    input_fee: str = Field(..., description="Fee for input token for the OpenAI model.")
    output_fee: str = Field(..., description="Fee for output token for the OpenAI model.")

async def extract_openai_fees():
    url = 'https://openai.com/api/pricing/'

    async with AsyncWebCrawler(verbose=True) as crawler:
        result = await crawler.arun(
            url=url,
            word_count_threshold=1,
            extraction_strategy=LLMExtractionStrategy(
                provider="openai/gpt-4o",
                api_token=os.getenv('OPENAI_API_KEY'),
                schema=OpenAIModelFee.model_json_schema(),
                extraction_type="schema",
                instruction="From the crawled content, extract all mentioned model names along with their "
                            "fees for input and output tokens. Make sure not to miss anything in the entire content. "
                            'One extracted model JSON format should look like this: '
                            '{ "model_name": "GPT-4", "input_fee": "US$10.00 / 1M tokens", "output_fee": "US$30.00 / 1M tokens" }'
            ),
            bypass_cache=True,
        )

    model_fees = json.loads(result.extracted_content)
    print(f"Number of models extracted: {len(model_fees)}")

    with open("./openai_fees.json", "w", encoding="utf-8") as f:
        json.dump(model_fees, f, indent=2)

if __name__ == "__main__":
    start_time = time.time()
    asyncio.run(extract_openai_fees())
    end_time = time.time()
    print(f"実行時間: {end_time - start_time}秒")
出力結果
[LOG] 🌤️  Warming up the AsyncWebCrawler
[LOG] 🌞 AsyncWebCrawler is ready to crawl
[LOG] 🕸️ Crawling https://openai.com/api/pricing/ using AsyncPlaywrightCrawlerStrategy...
[LOG] ✅ Crawled https://openai.com/api/pricing/ successfully!
[LOG] 🚀 Crawling done for https://openai.com/api/pricing/, success: True, time taken: 0.84 seconds
[LOG] 🚀 Content extracted for https://openai.com/api/pricing/, success: True, time taken: 0.04 seconds
[LOG] 🔥 Extracting semantic blocks for https://openai.com/api/pricing/, Strategy: AsyncWebCrawler
[LOG] Call LLM for https://openai.com/api/pricing/ - block index: 0
[LOG] Call LLM for https://openai.com/api/pricing/ - block index: 1
[LOG] Call LLM for https://openai.com/api/pricing/ - block index: 2
[LOG] Call LLM for https://openai.com/api/pricing/ - block index: 3
[LOG] Extracted 7 blocks from URL: https://openai.com/api/pricing/ block index: 2
[LOG] Call LLM for https://openai.com/api/pricing/ - block index: 4
/Users/dang/.pyenv/versions/3.12.2/lib/python3.12/site-packages/pydantic/main.py:347: UserWarning: Pydantic serializer warnings:
  Expected `PromptTokensDetails` but got `dict` - serialized value may not be as expected
  return self.__pydantic_serializer__.to_python(
[LOG] Extracted 0 blocks from URL: https://openai.com/api/pricing/ block index: 4
[LOG] Call LLM for https://openai.com/api/pricing/ - block index: 5
[LOG] Extracted 7 blocks from URL: https://openai.com/api/pricing/ block index: 1
[LOG] Extracted 5 blocks from URL: https://openai.com/api/pricing/ block index: 0
[LOG] Extracted 1 blocks from URL: https://openai.com/api/pricing/ block index: 5
[LOG] Extracted 16 blocks from URL: https://openai.com/api/pricing/ block index: 3
[LOG] 🚀 Extraction done for https://openai.com/api/pricing/, time taken: 9.50 seconds.
Number of models extracted: 36
実行時間: 10.728801012039185秒
抽出されたデータ
[
  {
    "model_name": "gpt-4o-2024-08-06",
    "input_fee": "$3.750 / 1M input tokens",
    "output_fee": "$15.000 / 1M output tokens",
    "error": false
  },
  {
    "model_name": "gpt-4o-mini-2024-07-18",
    "input_fee": "$0.300 / 1M input tokens",
    "output_fee": "$1.200 / 1M output tokens",
    "error": false
  },
  {
    "model_name": "gpt-3.5-turbo",
    "input_fee": "$3.000 / 1M input tokens",
    "output_fee": "$6.000 / 1M output tokens",
    "error": false
  },
  ...
]

まとめ

以上で、crawl4aiの使用方法について紹介しました。
これだけの機能を持っているのに、オープンソースで有料ツールに負けない性能があってなかなかありがたいです。
ひたすらrequestsを書いたり、BeautifulSoupに頼ったりする時代もう終わったかという感想でした。

株式会社 SIVA

Discussion