AsyncPlaywrightCrawlerStrategy 使用教程(crawl4ai)

适用版本:crawl4ai==0.7.8

AsyncPlaywrightCrawlerStrategy 是 crawl4ai 默认的“抓取策略(Crawler Strategy)”之一:底层使用 Playwright 启动/连接浏览器,执行真实页面导航、渲染与 JS,然后把最终 HTML 返回给 crawl4ai 的后处理流程(抽取 Markdown、截图/PDF、提取链接等)。

如果你遇到以下场景,优先考虑使用它:

  • 站点是 SPA/SSR 混合、内容需要 JS 执行后才能出现
  • 需要登录态、Cookie、localStorage 等浏览器状态
  • 需要做滚动加载(lazy load / infinite scroll)
  • 需要截图、导出 PDF、抓取 mhtml、采集网络请求/控制台日志

0. crawl4ai 架构速览:Strategy 在哪里

在 crawl4ai 中,你通常会用 AsyncWebCrawler 作为入口:

  • AsyncWebCrawler:负责生命周期(start/close)、调度并发(arun_many)、缓存、HTML 后处理(markdown/抽取)。
  • CrawlerRunConfig:一次抓取任务的配置(等待策略、截图、滚动、抓取网络等)。
  • AsyncPlaywrightCrawlerStrategy:负责“怎么把 URL 变成 HTML”(也就是浏览器导航、等待、执行 JS、拿 page.content())。
  • BrowserConfig:浏览器/上下文级别配置(是否 headless、代理、UA、stealth、是否 text_mode 等)。

你可以:

  1. 直接用 AsyncWebCrawler(config=BrowserConfig(...)) —— 默认就会用 AsyncPlaywrightCrawlerStrategy
  2. 或显式传入:AsyncWebCrawler(crawler_strategy=AsyncPlaywrightCrawlerStrategy(...))

1. 环境准备(Playwright 必做)

1.1 安装 Python 包

pip install "crawl4ai==0.7.8" playwright

1.2 安装浏览器运行时

二选一即可:

# crawl4ai 提供的初始化命令(本仓库 crawl.py 也提到)
crawl4ai-setup

或直接用 Playwright:

playwright install chromium

建议:优先 chromium,兼容性最好。


2. 最小示例:用默认 AsyncPlaywrightCrawlerStrategy 抓一页

AsyncWebCrawler 默认就是 AsyncPlaywrightCrawlerStrategy,因此你只要提供 BrowserConfig + CrawlerRunConfig 即可:

import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig


async def main():
    browser_cfg = BrowserConfig(
        headless=True,
        enable_stealth=True,      # 可选:启用 stealth(基于 playwright_stealth)
        light_mode=True,          # 可选:轻量模式(少加载资源,通常更快)
    )

    run_cfg = CrawlerRunConfig(
        wait_until="domcontentloaded",  # 常用:domcontentloaded / load
        page_timeout=60_000,            # 单页超时(毫秒)
        verbose=False,
    )

    async with AsyncWebCrawler(config=browser_cfg) as crawler:
        result = await crawler.arun("https://example.com", config=run_cfg)
        # result 是 CrawlResultContainer,可像 result.success/result.markdown 这样直接用(会代理到第一个结果)
        print("success:", result.success)
        print("url:", result.url)
        print("html_len:", len(result.html or ""))
        print("md_len:", len(result.markdown or ""))


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

3. BrowserConfig:浏览器/上下文级别配置(非常重要)

BrowserConfig 会影响浏览器如何启动、context 如何创建(代理、UA、headers、下载等)。这些配置通常在一个 crawler 生命周期内复用。

3.1 常用字段速查

下面只列最常用、最影响效果/性能的字段(更多字段可见 crawl4ai/async_configs.py 的注释):

  • headless:是否无头(默认 True)。调试时可设 False。
  • browser_typechromium / firefox / webkit(默认 chromium)。
  • channel / chrome_channel:chromium 发行通道(如 chrome/msedge/chromium),仅对 chromium 有效。
  • proxy_config:代理配置(推荐用这个,proxy 字段已不推荐)。
  • headers:全局请求头(对整个 context 生效)。
  • cookies:启动后注入 cookies(全局)。
  • storage_state:加载 Playwright storage_state(登录态/本地存储)。
  • use_persistent_context + user_data_dir:使用持久化 profile(适合“长期登录态”)。
  • enable_stealth:启用 stealth(非 undetected 模式下生效)。
  • light_mode:轻量模式(减少加载资源/特性,通常更快、更省内存)。
  • text_mode:文本模式(会拦截大量静态资源/图片等,适合只要正文的场景)。
  • accept_downloads + downloads_path:允许并保存下载文件。

3.2 代理(proxy_config)

from crawl4ai import BrowserConfig, ProxyConfig

browser_cfg = BrowserConfig(
    proxy_config=ProxyConfig.from_string("http://user:pass@host:port"),
)

说明:

  • proxy_config 会在浏览器 context 层生效(更可靠)。
  • 如果你同时设置了 proxyproxy_config,crawl4ai 会优先用 proxy_config

3.3 反爬:Stealth vs Undetected

在 crawl4ai 里常见有两种路线:

  1. Stealth(推荐先试)
    BrowserConfig(enable_stealth=True)BrowserManager 会使用 StealthAdapter(基于 playwright_stealth)对 page 做补丁。

  2. Undetected(更强但依赖更多)
    初始化 AsyncPlaywrightCrawlerStrategy(browser_adapter=UndetectedAdapter())
    BrowserManager 会切到 patchright 的 playwright 实现(更偏“反检测”)。

示例(Undetected):

import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
from crawl4ai.async_crawler_strategy import AsyncPlaywrightCrawlerStrategy
from crawl4ai.browser_adapter import UndetectedAdapter


async def main():
    browser_cfg = BrowserConfig(headless=True)  # undetected 仍可 headless,但部分站点更偏好 headful
    strategy = AsyncPlaywrightCrawlerStrategy(browser_config=browser_cfg, browser_adapter=UndetectedAdapter())

    run_cfg = CrawlerRunConfig(verbose=False)
    async with AsyncWebCrawler(crawler_strategy=strategy) as crawler:
        result = await crawler.arun("https://example.com", config=run_cfg)
        print(result.success, result.url)


asyncio.run(main())

经验建议:先用 enable_stealth=True,不行再上 UndetectedAdapter(),并配合代理/合理 UA/等待策略。

3.4 连接已有 Chrome(CDP 模式)

当你希望复用一个已启动的浏览器(例如你自己打开 Chrome 登录后,再让爬虫接管),可以用 CDP:

  1. 启动 Chrome(示例):
chrome --remote-debugging-port=9222
  1. 让 crawl4ai 连接:
from crawl4ai import BrowserConfig

browser_cfg = BrowserConfig(
    cdp_url="http://localhost:9222",  # 注意:0.7.8 会访问 {cdp_url}/json/version 做探活
    use_managed_browser=True,
    headless=False,                  # 通常你本地 Chrome 是 headful
)

4. CrawlerRunConfig:一次抓取任务的配置(与 Playwright 强相关)

CrawlerRunConfig 是“每次请求”的配置,最影响页面等待、滚动、截图、网络抓取等行为。

下面按“你最常调的参数”分类说明(只列 Playwright 相关的核心项)。

4.1 导航与等待

  • wait_until:传给 Playwright page.goto(..., wait_until=...)
    常见值:"domcontentloaded""load"(有些版本 Playwright 也支持 "networkidle",但对长连接站点不友好)。
  • page_timeout:传给 page.goto(..., timeout=...) 的超时(毫秒)。
  • wait_for:二段等待(goto 完成后再等)
    • 支持:
      • css:...:按 CSS selector 等待出现
      • js:...:按 JS 表达式等待返回 true
      • 不写前缀:策略内部会“先当 JS 试一下,不行就当 CSS”
  • wait_for_timeoutwait_for 的超时,缺省则回退到 page_timeout
  • delay_before_return_html:在拿 HTML 前 sleep 一下(适合等待最后一波异步渲染)。

示例:等某个主内容节点出现:

run_cfg = CrawlerRunConfig(
    wait_until="domcontentloaded",
    wait_for="css:main article",     # 或 "css:#content"
    wait_for_timeout=20_000,
)

4.2 js_only(容易踩坑)

AsyncPlaywrightCrawlerStrategy 的实现里:

  • js_only=False(默认)时:会正常执行 page.goto(url, ...)
  • js_only=True 时:会跳过 goto(不会导航到 URL)

除非你非常清楚自己在做什么(例如你在 hook 里自行导航/复用 page),否则请保持 js_only=False

4.3 滚动与懒加载

  • scan_full_page=True:会执行“整页滚动”以触发懒加载(lazy load)。
    • scroll_delay:每次滚动的等待(秒)。
    • max_scroll_steps:最大滚动次数(防止无限滚动页面卡死)。
  • virtual_scroll_config:处理“虚拟列表”场景(DOM 会复用/替换),常见于信息流。
    • 结构:VirtualScrollConfig(container_selector, scroll_count, scroll_by, wait_after_scroll)

示例:整页滚动 + 限制最多滚 30 次:

run_cfg = CrawlerRunConfig(
    scan_full_page=True,
    scroll_delay=0.2,
    max_scroll_steps=30,
)

示例:虚拟列表滚动(信息流容器):

from crawl4ai.async_configs import VirtualScrollConfig
from crawl4ai import CrawlerRunConfig

run_cfg = CrawlerRunConfig(
    virtual_scroll_config=VirtualScrollConfig(
        container_selector="div[role='feed']",
        scroll_count=12,
        scroll_by="container_height",  # 也可以是 "page_height" 或数字像素
        wait_after_scroll=0.6,
    )
)

4.4 执行自定义 JS(js_code)

js_code 可以是字符串或字符串列表,策略会执行并把结果写到 AsyncCrawlResponse.js_execution_result,最终在 CrawlResult.js_execution_result 里可取到。

典型用途:

  • 点击“加载更多”
  • 关闭弹窗
  • 展开折叠内容

示例(点击“加载更多”按钮 5 次):

run_cfg = CrawlerRunConfig(
    js_code="""
() => {
  const btn = document.querySelector('button.load-more');
  if (btn) btn.click();
  return {clicked: Boolean(btn)};
}
""",
    scan_full_page=True,
    max_scroll_steps=10,
    wait_for="css:article",  # 等正文出现
)

建议:js_code 往往需要配合 delay_before_return_html / wait_for,否则点击后内容还没渲染完就取 HTML 了。

4.5 只抓某些 DOM(css_selector)

如果你只关心页面某些区域,可以设置 css_selector(支持逗号分隔多个 selector)。策略会执行:

Array.from(document.querySelectorAll(selector)).map(el => el.outerHTML).join('')

最后把结果包在一个 <div class='crawl4ai-result'>...</div> 返回。

示例:只抓 main + article

run_cfg = CrawlerRunConfig(css_selector="main, article")

4.6 iframe 与遮罩

  • process_iframes=True:会尝试处理 iframe(把 iframe 内容“合并/展开”进结果,具体行为取决于策略实现)。
  • remove_overlay_elements=True:会尝试移除 overlay(弹窗/遮罩层),减少“Cookie 同意框遮住正文”的情况。

4.7 截图 / PDF / MHTML

策略支持三种“导出型”产物:

  • screenshot=True:截图(可配 screenshot_wait_for / screenshot_height_threshold
  • pdf=True:导出 PDF(export_pdf
  • capture_mhtml=True:抓 mhtml

示例(截图 + PDF):

run_cfg = CrawlerRunConfig(
    screenshot=True,
    screenshot_wait_for=1.0,
    pdf=True,
)

4.8 捕获网络请求/控制台(调试神器)

  • capture_network_requests=True:记录 request/response/failed 等事件(注意:response body 可能很大)
  • capture_console_messages=True:记录 console 输出(在排查站点脚本错误时很有用)

示例:

run_cfg = CrawlerRunConfig(
    capture_network_requests=True,
    capture_console_messages=True,
)

抓取后可在结果里读取:

  • result.network_requests
  • result.console_messages

5. 直接使用 AsyncPlaywrightCrawlerStrategy(需要 hooks/自定义行为时)

如果你需要:

  • goto 前后插入逻辑(改 headers、注入脚本、设置路由)
  • 或做更细的 Playwright 操作

可以显式创建 strategy 并设置 hooks。

5.1 可用 hook 列表

AsyncPlaywrightCrawlerStrategy 内置 hooks:

  • on_browser_created:browser 创建完成(参数:browser, context, **kwargs)
  • on_page_context_created:拿到 page/context 后(参数:page, context=…, config=…)
  • on_user_agent_updated
  • on_execution_started / on_execution_ended
  • before_goto / after_goto
  • before_retrieve_html:准备拿 HTML 前
  • before_return_html:HTML 已拿到、返回前(可修改 HTML)

5.2 示例:在 page 创建后注入路由(拦截图片)

如果你只是想“更快”,优先用 BrowserConfig(text_mode=True);route 更适合做更细粒度控制。

import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
from crawl4ai.async_crawler_strategy import AsyncPlaywrightCrawlerStrategy


async def on_page_created(page, *, context, config, **kwargs):
    # 例:拦截图片请求
    await page.route("**/*.{png,jpg,jpeg,gif,webp,svg}", lambda route: route.abort())


async def main():
    browser_cfg = BrowserConfig(headless=True, enable_stealth=True)
    strategy = AsyncPlaywrightCrawlerStrategy(browser_config=browser_cfg)
    strategy.set_hook("on_page_context_created", on_page_created)

    run_cfg = CrawlerRunConfig(wait_until="domcontentloaded", verbose=False)
    async with AsyncWebCrawler(crawler_strategy=strategy) as crawler:
        result = await crawler.arun("https://example.com", config=run_cfg)
        print(result.success, result.url, len(result.html or \"\"))


asyncio.run(main())

6. 和 BFSDeepCrawlStrategy 配合(深度爬取)

深度爬取的关键点是:把 deep_crawl_strategy 放进 CrawlerRunConfig

import asyncio
from crawl4ai import AsyncWebCrawler, BrowserConfig, CrawlerRunConfig
from crawl4ai import FilterChain, URLPatternFilter
from crawl4ai.deep_crawling import BFSDeepCrawlStrategy


async def main():
    browser_cfg = BrowserConfig(headless=True, enable_stealth=True)

    filter_chain = FilterChain([
        URLPatternFilter(["https://example.com/**"]),
    ])
    deep = BFSDeepCrawlStrategy(max_depth=2, max_pages=50, filter_chain=filter_chain)

    run_cfg = CrawlerRunConfig(
        deep_crawl_strategy=deep,
        stream=True,  # 深度爬取推荐 stream,边抓边处理
        wait_until="domcontentloaded",
        verbose=False,
    )

    async with AsyncWebCrawler(config=browser_cfg) as crawler:
        async for r in await crawler.arun("https://example.com", config=run_cfg):
            depth = (r.metadata or {}).get("depth")
            parent = (r.metadata or {}).get("parent_url")
            print(r.success, depth, r.url, "<-", parent)


asyncio.run(main())

7. 性能与稳定性建议(实战经验)

  1. 不要盲目用 wait_until="networkidle"
    很多站点有长连接/埋点/轮询,会导致网络永远不 idle;更推荐 domcontentloaded + wait_for(css:...)

  2. 对滚动/信息流页面一定加 max_scroll_steps
    防止无限滚动导致一页耗时失控。

  3. 正文抓取优先 text/light mode

    • light_mode=True:更像“少加载一些资源”
    • text_mode=True:更激进,会 route abort 多种静态资源(速度明显提升,但站点可能缺少某些依赖导致渲染异常)
  4. 并发要克制
    Playwright 每开一个 page/context 都是实打实的资源;arun_many 并发过大很容易触发 Target closed / 内存飙升。

  5. 需要登录态时优先 storage_state 或 persistent context

    • 临时:storage_state="state.json"(可复用一次登录态)
    • 长期:use_persistent_context=True + user_data_dir=...

8. 常见问题排查

8.1 页面空白/只返回 about:blank

优先检查 CrawlerRunConfig(js_only=True) 是否误开;在该策略实现中 js_only=True 会跳过 page.goto()

8.2 下载文件时报 net::ERR_ABORTED

如果你启用了下载(BrowserConfig(accept_downloads=True)),导航可能会被浏览器中止(这是下载触发时常见行为)。策略里对这种情况做了兼容:会把它当作“可能在下载”并继续流程。
你可以从结果里取 downloaded_files

8.3 超时(Timeout)

排查顺序:

  1. 降低 wait_until 的严格程度(先用 domcontentloaded
  2. wait_for="css:..." 精准等待关键节点
  3. 增大 page_timeout / wait_for_timeout
  4. 对滚动页面设置 max_scroll_steps,避免卡在滚动

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐