You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Playwright结合Crawlee爬取报错:Target page...已关闭

问题:Playwright + Crawlee 爬取时出现"Target page, context or browser has been closed"报错

在结合Playwright与Crawlee进行网页爬取和数据分析时,调用locator.count()方法持续触发"Target page, context or browser has been closed"错误,报错发生在自定义的对比函数中:

const myComparisonFunction: MyAnalyzerComparator<Frame | Page> = async (element, data) => {
  if(!data.length) return false
  
  // 生成校验所有数据值的XPath表达式
  const xpathChecks = data.map((d) => `contains(., "${d.value}")`).join(' and ')
  const locator = element.locator(`xpath=//tr[${xpathChecks}]`);

  // 统计匹配行数
  const matchingRowsCount = await locator.count()
  return matchingRowsCount > 0
}

相关的Crawlee爬虫配置类如下:

import { PlaywrightCrawler } from 'crawlee';
import { Page } from 'playwright';

export class PlaywrightCrawleePageResolver extends CrawleePromiseResolver<PlaywrightCrawler, Page> {
  constructor() {
    super((resolveUrl) => new PlaywrightCrawler({
      keepAlive: true,
      maxRequestsPerCrawl: 10,
      async requestHandler({ page, request, log }) {
        log.info(`Processing ${request.url} ...`);
        resolveUrl(request.url, page);
      },
    }));
  }
}

分析逻辑的调用方式:

const playwrightCrawleePageResolver = new PlaywrightCrawleePageResolver()

const analyzeElementProximity: MyStrategyType<Frame | Page> = async ({ element, rules, dataItems }) => {
  const analyzer = new MyAnalyzerClass(rules, dataItems, myComparisonFunction);
  await analyzer.analyze(element);
  return analyzer.detectedIds;
}

已排查步骤:

  • 调试确认报错确实发生在locator.count()调用时
  • 验证Playwright locator及count()方法使用无误
  • 搜索相关问题未找到匹配场景
  • 检查竞态条件未发现页面提前关闭的代码证据
  • 确认Crawlee与Playwright集成的异步处理逻辑正确

预期目标:爬取完成后可通过Playwright locator正常分析HTML内容,避免页面/上下文关闭的报错。


原因分析与解决方案

核心原因

报错本质是页面/浏览器上下文在分析逻辑执行前已被Crawlee回收。即使设置了keepAlive: true,Crawlee的requestHandler执行完毕后,会默认进入页面资源回收流程;而你的分析逻辑是在requestHandler外部异步执行的,此时页面已经被关闭,导致后续的locator操作失败。

具体解决方案

1. 将分析逻辑移至requestHandler内部执行

不要将page对象传递到requestHandler外部处理,直接在handler内完成所有分析操作,确保页面在整个流程中保持活跃:

async requestHandler({ page, request, log }) {
  log.info(`Processing ${request.url} ...`);
  // 直接在handler内执行分析
  const detectedIds = await analyzeElementProximity({
    element: page,
    rules: /* 你的规则定义 */,
    dataItems: /* 待匹配的数据项 */
  });
  // 处理分析结果,例如存入Crawlee数据集
  await Dataset.pushData({ url: request.url, detectedIds });
}

2. 调整Crawlee配置,延长页面生命周期(需外部处理的场景)

如果必须在requestHandler外部处理页面,需修改配置避免页面被过早回收,并通过Promise确保分析完成后再结束handler:

new PlaywrightCrawler({
  keepAlive: true,
  maxRequestsPerCrawl: 10,
  browserPoolOptions: {
    // 延长浏览器闲置回收时间,单位:秒
    retireBrowserAfterIdleSecs: 300,
  },
  autoscaledPoolOptions: {
    // 控制并发数,避免资源竞争导致页面被强制回收
    maxConcurrency: 1,
  },
  async requestHandler({ page, request, log }) {
    log.info(`Processing ${request.url} ...`);
    // 使用Promise等待外部分析逻辑完成
    await new Promise((resolve) => {
      resolveUrl(request.url, page, resolve);
    });
  },
});

同时修改外部分析逻辑,完成后通知handler结束:

const analyzeElementProximity: MyStrategyType<Frame | Page> = async ({ element, rules, dataItems, resolve }) => {
  const analyzer = new MyAnalyzerClass(rules, dataItems, myComparisonFunction);
  await analyzer.analyze(element);
  resolve(); // 通知requestHandler可以结束,释放页面资源
  return analyzer.detectedIds;
}

3. 排查主动销毁页面的逻辑

检查代码中是否存在主动调用page.close()、browserContext.close()或browser.close()的逻辑,确保这些操作仅在所有分析完成后执行。


内容的提问来源于stack exchange,提问作者Wojciech Król

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 02:57:53