ScrapeGraphAI + Scrapeless: CloudブラウザにSmartScraperGraphをポイントする
Advanced Data Extraction Specialist
TL;DR:
- ScrapeGraphAIは、セレクタの代わりにプロンプトを使用して抽出しますが、下層のフェッチは、あなた自身のマシンで起動された普通のPlaywrightブラウザです。
pip install scrapegraphaiは、Playwrightクライアントを提供し、ブラウザバイナリを提供しないため、ストックローダーは新しいインストール時にplaywright install chromiumを実行するまで失敗します。ChromiumLoaderはgetattr(self, f"ascrape_{self.backend}")を使ってフェッチメソッドを選択するため、backendは固定リストから選ぶのではなくメソッドの名前を指定します — これがカスタムフェッチバックエンドが依存する部分です。- 短いサブクラスの
ascrape_scrapelessメソッドは、wss://CDP接続を介してScrapeless Scraping Browserを通じてフェッチし、ローカルにブラウザをインストールする必要はありません。 FetchNodeはChromiumLoaderを名前でインポートするため、fetch_node.ChromiumLoaderを再バインドすることが実際にグラフがあなたのサブクラスを使用する原因です。それをスキップすると、正しく見えるローダーは静かに無視されます。- パイプライン全体はローカルモデルで実行されます:
SmartScraperGraphはollama/qwen2.5:0.5bとともに構造化された著者と引用のペアを返し、クラウドモデルキーは不要です。 - Scrapeless無料プランから始めて、フェッチをあなたのラップトップから移動させてください。
Scrapeless Scraping Browserは、立ち上げるのではなく接続するクラウドブラウザです。これは、安全なWebSocket CDPエンドポイントでリッスンしており、ScrapeGraphAIのフェッチレイヤーが裏でPlaywrightを利用しているため、Playwrightが自身が起動していないブラウザに接続できるという点で重要です。
ScrapeGraphAIは、人々が話すパイプラインの一部です: 望むことを文で説明すると、ノードのグラフがページを構造化されたJSONに変換します, CSSセレクタは一切使用しません。誰も話さない部分は最初のノードです。どのモデルも何かを見る前に、FetchNodeはHTMLを生成しなければならず、それを行うためにスクリプトが実行されている任意のマシンでChromiumを起動します。検索結果の最初のページにあるすべてのチュートリアルは、そのノードをまったく同じ方法で構成し、プロキシ辞書を使用し、そこで停止します。
このガイドはフェッチレイヤーに焦点を当てています: それがどこに存在し、実際の拡張ポイントは何であり、クラウドブラウザを介してそれをルーティングする方法です。以下のすべてのブロックは、ライブで実行され、抽出を含みます。
Where ScrapeGraphAI's Fetch Layer Actually Lives
FetchNodeはほぼすべてのグラフのエントリノードであり、いくつかの分岐の1つで終了します。ノード設定でbrowser_base、scrape_do、またはplasmateを設定した場合、scrapegraphai/docloaders/内のベンダーローダーに渡されます。そうでない場合は、デフォルトパスであるChromiumLoaderに落ち着き、ほとんど全員が使用している道です。
そのフォールバックは二つの理由から重要です。ベンダーのフェッチバックエンドは、これを発明しているのではなく、すでにこのコードベースで確立された形状を持っています — browser_base.pyとscrape_do.pyはレポに同梱されています。ChromiumLoaderは自分自身のフェッチメソッドを名前で選択します:
python
scraping_fn = getattr(self, f"ascrape_{self.backend}")
backendは固定列挙体に対して検証されません。それは属性ルックアップへの文字列補間であるため、ascrape_<something>と呼ばれる任意のメソッドは、backendを<something>に設定することによって到達可能になります。それがそのつなぎ目です。
デフォルトのascrape_playwrightはp.chromium.launch(...)を呼び出し、ローカルでブラウザプロセスを開始します。それをすでに実行中のブラウザへのWebSocket接続に交換するのは、一つの呼び出しの変更です。
Prerequisites
- Python 3.10以上。
- ダッシュボードからのScrapeless APIキー、
SCRAPELESS_KEYとしてエクスポート。 - クラウドモデルキーが不要で、抽出ステップを追う場合には、ローカルで実行中のOllamaとプルされたモデル。
- クラウドパスではローカルブラウザバイナリは必要ありません。ストックローダーを実行したい場合にのみ必要です。
Install
bash
pip install "scrapegraphai==2.1.6" "langchain-ollama==1.1.0"
Playwrightは依存関係として到着しますが、そのブラウザは到着しません。この区別が、ほとんどの人が直面する最初の失敗を引き起こします。
bash
export SCRAPELESS_KEY="your_api_key_here"
What a Fresh Install Actually Fetches
ストックローダーをインストール直後に実行すると、ページには全く到達しません。
python
from scrapegraphai.docloaders import ChromiumLoader
try:
docs = ChromiumLoader(["https://quotes.toscrape.com/"], headless=True).load()
print("chars:", len(docs[0].page_content))
except Exception as exc:
print(f"{type(exc).__name__}: {str(exc).split(' at /')[0]}")
text
RuntimeError: Failed to scrape after 1 attempts: BrowserType.launch: Executable doesn't exist
Playwrightクライアントはインストールされています; 起動したいChromiumはインストールされていません。playwright install chromiumがそれを修正し、グラフを実行するすべてのマシンに数百メガバイトのコストがかかります — CIイメージ、コンテナ、各開発者のラップトップ。クラウドパスではそれを完全にスキップします; なぜなら、彼女が駆動するブラウザはすでに他のどこかで実行中だからです。
Point the Loader at a Cloud Browser
ChromiumLoaderをサブクラス化し、希望するバックエンドの名前を付けたメソッドを追加し、super().__init__が実行された後にself.backendを再バインドします。
python
import os
from scrapegraphai.docloaders import ChromiumLoader
CDP = (
"wss://browser.scrapeless.com/api/v2/browser"
f"?token={os.environ['SCRAPELESS_KEY']}&sessionTTL=180&proxyCountry=ANY"
)
class ScrapelessLoader(ChromiumLoader):
"""Fetch through a remote CDP browser instead of launching one locally."""
def __init__(self, urls, **kwargs):
kwargs.pop("backend", None)
super().__init__(urls, backend="playwright", **kwargs)
self.backend = "scrapeless"
async def ascrape_scrapeless(self, url: str, browser_name: str = "chromium") -> str:
from playwright.async_api import async_playwright
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(CDP)
page = await browser.new_page()
await page.goto(url, wait_until=self.load_state)
html = await page.content()
await browser.close()
return html
loader = ScrapelessLoader(["https://quotes.toscrape.com/"])
print("dispatches to:", getattr(loader, f"ascrape_{loader.backend}").__name__)
docs = loader.load()
print("chars:", len(docs[0].page_content))
print("Einstein present:", "Einstein" in docs[0].page_content)
二つの詳細が作業を行います。super().__init__ は backend="playwright" で呼び出されます。なぜなら、コンストラクターがその文字列に対してインポートチェックを実行するからです。playwright は解決される名前です; self.backend は再割り当てされ、その後 lazy_load でディスパッチが ascrape_scrapeless を見つけます。browser_name をデフォルト付きでシグネチャに保持してください — ディスパッチャはそのメソッドを URL のみで呼び出します。
text
dispatches to: ascrape_scrapeless
chars: 10968
Einstein present: True
ページは、このマシンに存在しなかったブラウザから完全にレンダリングされた状態で返されます。proxyCountry は、特定の国からリクエストをエグレスする必要がある場合は二文字のコードを受け入れ、sessionTTL はリモートセッションが開いている時間の長さを制限します。
グラフを利用する
サブクラスを自分でインスタンス化することで取得が機能することが証明されますが、グラフは自力ではそれを認識しません。FetchNode は from ..docloaders import ChromiumLoader を実行し、その後その名前を直接呼び出すので、グラフが使用するクラスはノードのモジュールネームスペースでバインドされるものです。グラフを構築する前に再バインドしてください。
python
import scrapegraphai.nodes.fetch_node as fetch_node
fetch_node.ChromiumLoader = ScrapelessLoader
これは、上記のいずれかが効果を持つかどうかを決定するステップです。正しく書かれたサブクラスですが、決してバインドされないものは、実行され、成功し、ローカルブラウザを通じて静かに全体を取得するグラフを生成します。
置き換えが同じコンストラクターシグネチャを維持するため、FetchNode が既に通過するすべてのもの — headless、storage_state、および loader_kwargs に入れたもの — はそのまま到着し続けます。
自分のマシンから取得を移動する準備はできていますか? 無料のScrapelessアカウントを作成する そして最初のグラフをそれに指し示してください。
ローカルモデルで全体のグラフを実行する
ローダーがバインドされると、SmartScraperGraph は通常どおりに動作します。llm ブロックをOllamaに指し示すことで、全体のパイプラインが有料APIから外れ、取得レイヤーの反復が安価になります。
python
import os
import scrapegraphai.nodes.fetch_node as fetch_node
from scrapegraphai.docloaders import ChromiumLoader
from scrapegraphai.graphs import SmartScraperGraph
CDP = (
"wss://browser.scrapeless.com/api/v2/browser"
f"?token={os.environ['SCRAPELESS_KEY']}&sessionTTL=180&proxyCountry=ANY"
)
class ScrapelessLoader(ChromiumLoader):
def __init__(self, urls, **kwargs):
kwargs.pop("backend", None)
super().__init__(urls, backend="playwright", **kwargs)
self.backend = "scrapeless"
async def ascrape_scrapeless(self, url: str, browser_name: str = "chromium") -> str:
from playwright.async_api import async_playwright
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(CDP)
page = await browser.new_page()
await page.goto(url, wait_until=self.load_state)
html = await page.content()
await browser.close()
return html
fetch_node.ChromiumLoader = ScrapelessLoader
graph = SmartScraperGraph(
prompt="List the quote authors on this page.",
source="https://quotes.toscrape.com/",
config={
"llm": {
"model": "ollama/qwen2.5:0.5b",
"temperature": 0,
"format": "json",
"model_tokens": 4096,
},
"verbose": False,
"headless": True,
},
)
result = graph.run()
print("keys:", sorted(result))
print("authors:", [item["author"] for item in result["content"]][:4])
text
keys: ['content']
authors: ['Albert Einstein', 'J.K. Rowling', 'Jane Austen', 'Marilyn Monroe']
著者は正確に戻り、構造は正しいです。このモデルからの引用テキストはあまり信頼性が高くなく — 0.5Bパラメータモデルは長いリスト上で文字列をクリップし、マージします — が、取得レイヤーは全ページを配信し、モデルが制限因子となります。model キーをより大きなローカルモデルまたはホスト型のものに移動すると、同じグラフが同じローダーを通じてクリーンなテキストを生成します。
構造化された出力が全てがここでのポイントです:グラフは パースされたHTMLドキュメント を読み取ります。これはリモートブラウザがレンダリングしたものであり、プレーンなHTTPクライアントが受け取った生のマークアップではありません。format を json に設定すると、モデルに対して JSONインターチェンジフォーマット に準拠した出力を要求します。これにより、結果が直接サブスクリプト可能になり、パースする必要のある文字列にはなりません。
結論
ScrapeGraphAIの取得レイヤーは、チュートリアルが示唆するよりもはるかに構成可能であり、構成ポイントは loader_kwargs ではありません — それは backend 文字列で、メソッド名に解決します。ascrape_scrapeless メソッドを持つ一つのサブクラスは、取得をクラウドブラウザに移動させ、fetch_node.ChromiumLoader の再バインディングがグラフにそれを使用させます。
結果が変わっていないように見える場合は、最初に再バインディングを確認してください。決してバインドされないサブクラスは静かに失敗し、症状は以前と同じように正確に機能するグラフです。前述の中間ステップの独自のローダーを確認すると、取得問題とモデル問題を一回の実行で分離します。
ブラウザ制御が標準として向かう先については、 WebDriver BiDi仕様 を追跡する価値があります。PlaywrightおよびScraping Browserガイド では接続自体についてより深く説明しており、計画の詳細は Scrapeless料金ページ にあり、セッションパラメータは Scrapelessドキュメント にあります。
FAQ
Q: クラウドパスを使用するためにブラウザをインストールする必要がありますか?
いいえ。pip install scrapegraphai はPlaywrightクライアントを提供し、connect_over_cdp が必要とするのはそれだけです。なぜなら、ドライブされるブラウザはすでにリモートで実行中だからです。通常のローカルローダーを実行したい場合のみ playwright install chromium が必要です。
Q: なぜ私のカスタムローダーがグラフによって無視されるのですか?
ほとんど常に fetch_node.ChromiumLoader が再绑定されていなかったためです。 FetchNode は名前でクラスをインポートし、その名前を呼び出すため、サブクラス化だけでは何も変わりません — ノードは元のクラスを構築し続けます。グラフを構築する前に、モジュール上の属性を再绑定してください。
Q: サブクラスの代わりに loader_kwargs を使用できますか?
プロキシやブラウザ起動オプションの場合、はい — loader_kwargs は ChromiumLoader に直接流れます。ただし、fetch をリモートブラウザにリダイレクトすることはできません。なぜなら、スタックメソッドが chromium.launch() を呼び出し、常にローカルプロセスを起動するからです。宛先を変更するとメソッドも変更され、つまりサブクラスが必要になります。
Q: SmartScraperGraph 以外のグラフでも機能しますか?
はい。再绑定はノードレベルで行われ、FetchNode は他のグラフタイプのエントリノードでもあるため、URL を取得するすべてのグラフは属性がバインドされると同じローダーを通過します。
Q: sessionTTL は何を制御しますか?
リモートブラウザのセッションがオープンのままでいる時間(秒)。単一の fetch にかかる時間よりも快適な範囲で設定してください。セッションは接続が終了するかウィンドウが期限切れになると閉じられます。どちらが先でも構いません。
Q: Fetch の間にクッキーやログインセッションを保持できますか?
storage_state は FetchNode によってすでに渡され、サブクラスコンストラクターに変更なしで到達するため、標準の Playwright ストレージステートファイルは機能します。リモートブラウザの起動時ではなく、リモートブラウザのコンテキストを作成する際にそれを適用してください。なぜなら、リモートブラウザはあなたのコードによって起動されていないからです。
Q: 0.5B のローカルモデルは、本物の抽出に十分ですか?
シンプルな形状の短いページの場合、上記のように使用可能な構造を生成します。長いページやネストされたスキーマでは劣化し、テキストがクリップされ、フィールドがマージされます。小さなローカルモデルは、fetch レイヤーを安価に反復するための方法と見なし、その後本番実行用に model キーを切り替えてください。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



