ScrapeGraphAI + Scrapeless: 将 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方法通过 Scrapeless Scraping Browser 通过wss://CDP 连接进行提取,并且不需要在本地安装浏览器。 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 是几乎每个图形的入口节点,并且它以少数几条分支结束。如果您在节点配置中设置 browser_base、scrape_do 或 plasmate,它将移交给 scrapegraphai/docloaders/ 中的供应商加载器。否则,它将通过 ChromiumLoader,这是默认路径,也是几乎所有人使用的路径。
该回退对于两个原因很重要。供应商提取后端在该代码库中已经是一个既定的形状,而不是您所发明的东西 — browser_base.py 和 scrape_do.py 在 repo 中提供。而且 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 浏览器指南 更深入地涵盖了连接本身,计划详细信息在Scrapeless 定价页面上,会议参数在Scrapeless 文档中。
常见问题
问:我是否需要安装浏览器才能使用云路径?
不需要。pip install scrapegraphai 提供 Playwright 客户端,这就是 connect_over_cdp 所需的,因为被驱动的浏览器已经在远程运行。如果你还想运行股票本地加载器,则只需要 playwright install chromium。
问:为什么我的自定义加载器会被图表忽略?
几乎总是因为 fetch_node.ChromiumLoader 从未被重新绑定。 FetchNode 通过名称导入类并调用该名称,因此仅仅子类化并不会改变任何东西——节点保持构造原始类。在构建图形之前在模块上重新绑定属性。
问:我可以使用 loader_kwargs 替代子类吗?
对于代理和浏览器启动选项,可以——loader_kwargs 直接流入 ChromiumLoader。不过,它无法将获取重定向到远程浏览器,因为股票方法调用 chromium.launch(),这总是启动本地进程。更改目的地意味着更改方法,这意味着需要一个子类。
问:这适用于除 SmartScraperGraph 之外的图形吗?
是的。重新绑定发生在节点级别,FetchNode 也是其他图形类型的入口节点,因此任何获取 URL 的图形在属性绑定后都会通过同一个加载器。
问:sessionTTL 控制什么?
远程浏览器会话保持打开的时间,单位为秒。设置在单次获取所需时间之上;会话在连接结束或窗口过期时关闭,以先到者为准。
问:我可以在多次获取之间保留 cookies 或已登录的会话吗?
storage_state 已经被 FetchNode 传递,并且原封不动地到达子类构造函数,因此标准的 Playwright 存储状态文件可以正常工作。在远程浏览器上创建上下文时应用它,而不是在启动时,因为远程浏览器并不是由你的代码启动的。
问:0.5B 的本地模型足以进行真实提取吗?
对于形状简单的短页面,它会生成可用的结构,如上所述。较长页面和嵌套模式是它退化的地方——文本被截断,字段被合并。将小型本地模型视为低成本迭代获取层的方式,然后将 model 密钥切换到生产运行。
在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。



