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
相关产品推荐
相关产品推荐

