
Crawlee BasicCrawler 能力全景从配置参数到源码实现的分层解析【免费下载链接】crawleeCrawlee—A web scraping and browser automation library for Node.js to build reliable crawlers. In JavaScript and TypeScript. Extract data for AI, LLMs, RAG, or GPTs. Download HTML, PDF, JPG, PNG, and other files from websites. Works with Puppeteer, Playwright, Cheerio, JSDOM, and raw HTTP. Both headful and headless mode. With proxy rotation.项目地址: https://gitcode.com/GitHub_Trending/cr/crawleeBasicCrawler是 Crawlee 中负责“抓取调度”的底层核心它不负责页面解析或浏览器渲染而是把静态 URL 列表、动态请求队列乃至 sitemap 统一抽象为请求来源并以并发受控的方式逐个派发requestHandler。本文以crawlee/basic包的 CHANGELOG.md 为主线串联其演进历史与核心源码梳理BasicCrawler的完整能力清单帮助读者理解每个选项背后的实现原理并掌握停止、暂停、批量入队、robots.txt 遵守等关键操作的正确姿势。一、BasicCrawler 是什么定位与适用场景BasicCrawler是 Crawlee 所有爬虫的最底层抽象位于 basic-crawler/src/index.ts其对外导出由crawlee/core的公共类型与 internals/basic-crawler.ts 中的类实现共同构成。官方示例 docs/examples/basic_crawler.mdx 将其定位为“最底层的示例”——它把每个 URL 抽象成一个Request对象通过requestHandler回调执行用户逻辑例如用sendRequest下载 HTML 存入 Dataset而把页面解析、浏览器控制等留给了更高层的CheerioCrawler、PuppeteerCrawler、PlaywrightCrawler。在 Changelog 中BasicCrawler的演进始终围绕几个核心主题请求来源的多样化RequestList、RequestQueue、sitemap、Tandem 组合、请求调度与重试策略、防封锁代理、会话、robots.txt、以及运行生命周期控制。下面按这些主题展开。二、请求来源从 requestList/requestQueue 到 requestManager2.1 传统双入口requestList 与 requestQueue最初版本的BasicCrawler通过两个构造选项提供请求来源requestList静态 URL 列表RequestList适合确定性的批量抓取requestQueue动态请求队列RequestQueue支持爬取过程中递归入队新 URL。Changelog 中多次修复围绕这一组合展开例如 3.5.7 版本“add warning when we detect use of RL and RQ, but RQ is not provided explicitly”检测到同时使用两种来源时给出警告以及 3.15.0 引入的TandemRequestProvider——它把只读的RequestList与可写的RequestQueue组合成一个串联管理器先消费列表中的 URL再自动把它们入队保证同一 URL 不会被重复抓取。2.2 新一代统一入口requestManager从源码看这两个传统选项目前已被标记为deprecated/** * deprecated Use the requestManager option instead. ... */ requestList?: IRequestLoader; /** * deprecated Use the requestManager option instead. ... */ requestQueue?: RequestQueue; requestManager?: IRequestManager;构造函数中会把两者“折叠”进统一的requestManager两者同时给出时组合成RequestManagerTandem。RequestQueue本身就是请求管理器可以直接传入只读来源RequestList、SitemapRequestLoader则通过requestLoader.toTandem(requestQueue)组合。当什么也没传时爬虫会在首次需要时打开默认的RequestQueue——注意实现细节第一个爬虫实例使用默认队列null标识后续实例通过唯一别名__default_id__各自获得独立队列以避免冲突见openOwnedRequestQueue()。关于 sitemap3.11.0 引入了“Sitemap-based request list implementation”也就是SitemapRequestLoader它让爬虫可以直接从sitemap.xml生成请求列表相关用法可参考 docs/guides/request_loaders.mdx。2.3 批量入队addRequests 与 addRequestsBatched3.9.2 修复了“dont call notify in addRequests()”3.13.9 起addRequests开始接受 (Async)Iterable。crawler.addRequests()内部走RequestQueue.addRequestsBatched()的批量逻辑测试 test/batch-add-requests.test.ts 验证了其行为默认每批 1000 条返回{ addedRequests, waitForAllRequestsToBeAdded }——首批返回即解析剩余批次在后台继续入队只有显式传入waitForAllRequestsToBeAdded: true才会一次性全部入队。关键选项来自addRequestsOptionsSchema选项说明forefront是否插入队列前端优先处理batchSize每批入队数量默认 1000waitBetweenBatchesMillis批次间等待时间waitForAllRequestsToBeAdded是否等待全部入队完成maxNewRequests/limit本次入队的请求上限include/excludeglob 或正则 URL 过滤模式strategy入队策略All/SameHostname/SameDomain/SameOrigin此处默认AlltransformRequestFunction入队前改写请求对象userData/label为整批请求统一附加数据与路由标签include/exclude与strategy是“与”关系URL 必须同时满足两者这与 crawlee-python 的行为保持一致。入队时会同步执行maxRequestsPerCrawl预算计算#calculateEnqueuedRequestLimit避免入队超过爬虫实际处理能力的链接数量。三、请求处理、重试与错误处理3.1 requestHandler 与超时requestHandler是每个 URL 的处理逻辑默认超时requestHandlerTimeoutSecs 60秒见optionsShape与构造函数的60_000毫秒兜底。源码中runRequestHandler()用addTimeoutToPromise包裹用户回调超时抛出带请求 ID 的TimeoutError。3.4.2 修复了“limit internalTimeoutMillis in addition to requestHandlerTimeoutMillis”的问题引入internalTimeoutMillis作为整个请求生命周期的兜底超时它至少是requestHandlerTimeoutMillis * 2且不小于 300 秒也可通过配置internalTimeoutMillis覆盖。该超时通过raceWithTimeoutrequest-timeout.ts实现——它特意使用“裸定时器”而非嵌套的addTimeoutToPromise帧避免内部超时触发时把请求处理中的AbortController一并取消、导致随后的错误处理无法执行。请求各阶段导航、导航钩子、requestHandler有自己的独立超时context.extendTimeout(secs)可同时向后推延这三个预算。3.2 重试机制maxRequestRetries 与错误分类maxRequestRetries默认 3。重试判定逻辑canRequestBeRetried()遵循以下规则request.noRetry true或错误为NonRetryableError不重试错误为RetryRequestError用户显式要求重试强制重试忽略次数否则比较request.maxRetries ?? maxRequestRetries与request.retryCount。3.3.3 引入了Request.maxRetries允许对单个请求覆盖全局重试次数。错误处理有三个层级对应 Changelog 中的演进errorHandler每次重试前的钩子3.5.1 起会话轮换次数超限也会触发failedRequestHandlerfailedRequestHandler所有重试耗尽后调用SessionError/ 会话相关错误错误处理过程中若判定为会话问题会调用session.retire()。Changelog 3.11.5 修复“trigger errorHandler for session errors”确保会话类错误也会走errorHandler。请求成功后会session.markGood()失败则markBad()降低会话信誉分。3.3 阻塞检测与重试blockedStatusCodes / retryOnBlockedblockedStatusCodes默认[401, 403, 429]命中即抛SessionError并轮换会话retryOnBlocked默认false开启后自动尝试绕过 Cloudflare Bot Management 与 Google Search 的限流检测。3.7.0 增加了“log cause with retryOnBlocked”3.4.2 则让其在探测到被封锁页面时自动重试HTTP 状态码处理默认 500视为错误可用ignoreHttpErrorStatusCodes豁免、additionalHttpErrorStatusCodes追加见isErrorStatusCode()。此外429 响应会被recordDomainRateLimit()记录到请求管理器由ThrottlingRequestManager按Retry-After头与指数退避策略延迟重试并抛出RequestThrottledError而非记失败。四、并发控制与任务循环4.1 并发快捷选项BasicCrawler提供四个并发快捷参数选项默认说明minConcurrency自动伸缩下限设置过低会拖慢爬虫过高可能耗尽内存/CPUmaxConcurrency自动伸缩上限与 min 一起决定并发区间initialConcurrency取minConcurrency启动时的初始并发度maxRequestsPerMinuteInfinity每分钟最大请求数须为 1的整数这些快捷选项最终折叠进爬虫自建的ConcurrencySystemcreateDefaultConcurrencySystem实现“有足够空闲 CPU/内存才派发新任务”的自动伸缩。若同时显式传入concurrencySystem与上述快捷选项构造函数会直接抛错因为两者互相排斥。4.2 任务循环与 keepAlive爬虫的核心循环由CrawlerRuncrawler-run.ts承载它封装AutoscaledPool与runTaskFunction、isTaskReadyFunction、isFinishedFunction三个谓词。keepAlive: true会强制isFinishedFunction恒为false使爬虫在队列清空后仍保持运行等待新请求此时必须通过stop()或teardown()退出。taskLoopOptions允许用户覆盖 ready/finished 谓词但任务本身取请求、跑管道归爬虫所有、不可覆写。3.13.3 修复了“respect autoscaledPoolOptions.isTaskReadyFunction option”确保用户自定义的 ready 谓词真正生效。五、会话、代理与 robots.txt 遵守5.1 会话与代理爬虫自带SessionPool可通过sessionPool选项注入共享实例注入的池生命周期由调用方负责爬虫不会销毁它。同时传入sessionPool与proxyConfiguration时proxyConfiguration会被忽略并给出警告——代理应配置在会话池上如addSession({ proxyInfo })。3.9.0 为代理配置引入了tieredProxyUrls分层代理 URL3.5.0 支持“retire session on proxy error”。5.2 respectRobotsTxtFile从布尔到自定义 userAgent这一功能的演进在 Changelog 中非常清晰3.13.1重命名RobotsFile为RobotsTxtFile3.13.1新增respectRobotsTxtFile爬虫选项3.15.3支持自定义userAgent3.17.0修复enqueueLinks中自定义 userAgent 不生效的问题。当前选项签名respectRobotsTxtFile?: boolean | { userAgent?: string };开启后爬虫在请求派发前调用isAllowedBasedOnRobotsTxtFile()检查对应源的 robots.txt按 origin 缓存于LruCache容量 1000不合法则跳过并触发onSkippedRequestreason 为robotsTxt若 robots.txt 声明了Crawl-delay该延迟会通过applyCrawlDelay()交给请求管理器执行。enqueueLinks/addRequests入队时同样会过滤被 robots.txt 禁止的 URL。默认 userAgent 为*可通过{ userAgent: MyBot }指定。5.3 onSkippedRequest感知被跳过的请求3.13.2 引入onSkippedRequest回调当前覆盖四类跳过原因robots.txt 不允许robotsTxt不匹配enqueueLinks的 include/exclude 过滤filters重定向后不再匹配入队策略redirect达到maxRequestsPerCrawl上限limit。爬虫还会用logOnce保证“达到 maxRequestsPerCrawl / maxCrawlDepth 上限”这类提示只打印一次对应 3.16.0 的“maxCrawlDepth warning is logged only once”。六、爬取深度与总请求上限6.1 maxRequestsPerCrawlmaxRequestsPerCrawl是防止死循环的关键保护达到上限后isTaskReadyFunction返回false不再派发新请求正在进行的请求允许完成3.17.0 修复了它可能导致请求被意外丢弃的问题。入队侧同步受限#calculateEnqueuedRequestLimit()会用“上限 − 已处理数 − 待处理数”计算出剩余预算作为addRequestsBatched的maxNewRequests。6.2 maxCrawlDepth3.14.0 新增maxCrawlDepth选项。语义为0只处理初始请求跳过所有入队的新链接1处理初始请求及初始请求处理器中入队的链接未设置不限制深度。深度由context.addRequests/context.enqueueLinks通过addCrawlDepthRequestGenerator注入crawlDepth request.crawlDepth 1入队时检查crawlDepth maxCrawlDepth即跳过。3.15.0 修复了自定义transformRequestFunction场景下深度校验不生效的问题。七、数据输出与状态管理7.1 数据集操作BasicCrawler内置了默认数据集助手3.5.3 的“default dataset helpers”pushData()、getData()、getDataset()以及exportData()3.6.0 引入。exportData(path, format)支持json与csv两种格式格式可从路径后缀自动推断3.15.0 为其加入collectAllKeys选项导出 CSV 时汇总所有记录的全部键而非仅首条记录的键。3.15.0 还修复了空数据集下exportData报错的问题。7.2 状态持久化useState 与 iduseState()基于 KeyValueStore 的getAutoSavedValue实现键为CRAWLEE_STATE。多个爬虫实例共用该键会互相覆盖因此 3.18.0 版本配套了id选项指定id后状态键变为CRAWLEE_STATE_id每个实例状态隔离且跨重启持久化多个无id实例同时调用useState()会收到警告。该id同时也用于状态消息事件与队列别名隔离。7.3 状态消息与统计setStatusMessage(message, options)按level默认DEBUG记录日志并通过EventType.STATUS_MESSAGE事件广播3.3.0 引入3.3.0 将实现从存储层移入 Crawlee3.10.4 起不再 await 该调用避免拖慢爬虫statusMessageLoggingInterval默认 10 秒3.3.3 曾固定为 5 秒并以 debug 级别输出statusMessageCallback3.5.0 起可自定义状态消息内容回调需显式调用crawler.setStatusMessage()默认消息包含成功数/总数、失败数及期望并发度3.10.3 加入 desired concurrencystatistics可注入自定义统计实例3.7.0 起支持配置stateExtension可扩展自定义字段3.13.3 修复统计中记录真实request.retryCount的问题3.10.0 修复迁移/复活/续跑时统计丢失的问题3.17.0 修复了周期日志中失败请求增量计数错误的问题。八、运行生命周期run / stop / pause / resume / teardown8.1 run() 的多次运行3.3.2 起允许同一爬虫实例多次调用run()重复运行时继续消费同一个请求管理器上次已处理含失败的请求不会被再次处理爬虫自建的统计与会话池会在新一次运行前重置而注入的实例保持原状。若请求管理器中的请求全部已处理新一次run()会处理 0 个请求爬虫会给出“queue 不会自动清空请 purge 或换新队列”的警告。8.2 优雅停止stop()3.12.2 引入BasicCrawler.stop()停止接收新请求允许在途请求完成后再结束内部由CrawlerRun.stop(reason)置位stopRequested。3.16.0 修复了多次调用stop()的处理——重复调用是幂等的只会记录一次停止原因日志。run()会保持 pending 直到所有在途请求收尾。8.3 暂停与恢复pause() / resume()pause(timeoutSecs?)停止派发新请求、等待在途请求收尾超时可拒但不结束运行——run()仍挂起需resume()恢复派发。这常用于迁移migration场景pauseOnMigration()在收到MIGRATING/ABORTING事件时暂停派发等待至多 20 秒SAFE_MIGRATION_WAIT_MILLIS让健康请求完成再持久化RequestList状态3.1.3 修复了迁移事件处理中的内存泄漏。8.4 立即终止teardown() / destroy()teardown()立即结束当前运行不等待在途请求每次run()结束都会调用释放的是“单次运行”级别的资源会话池、事件管理器、任务循环destroy()释放跨运行存活的资源如浏览器爬虫的浏览器池适用于彻底弃用爬虫实例前调用Symbol.asyncDispose委托给destroy()因此支持await using语法。3.12.2 修复了CriticalError下的优雅清理3.18.0 修复了“avoid duplicate final crawler persistence”确保最终持久化只执行一次且最终状态消息isStatusMessageTerminal: true可靠送达3.18.0。九、事务化存储与 HTTP 客户端扩展9.1 transactionalStorage3.18.0 时代的源码中transactionalStorage默认开启true请求处理期间的存储写入被记录在横跨整个请求生命周期的StorageTransaction中仅当 requestHandler 成功时才提交因此异常抛出不会留下部分写入、重试也不会重复写入。可通过false关闭或用对象按存储类型覆盖策略如{ requestQueue: deferred }withDirectStorageAccess是单点绕过事务的出口useState()则刻意不参与事务。9.2 可插拔 HTTP 客户端3.12.0 起允许使用其他 HTTP 客户端。爬虫默认使用LazyDefaultHttpClient优先动态加载crawlee/impit-client的ImpitHttpClient提供代理支持与浏览器指纹未安装时回退到原生FetchHttpClient此时代理与指纹能力不可用并给出警告。可通过httpClient选项注入自定义客户端。sendRequest上下文助手即经由该客户端执行见 send-request.ts。十、实战示例组装一个完整的 BasicCrawler结合上述能力一个兼顾入口过滤、深度限制、robots.txt 与状态消息的典型配置如下import { BasicCrawler, Dataset, createBasicRouter } from crawlee; const router createBasicRouter(); router.addHandler(detail, async ({ request, sendRequest, log }) { const { body } await sendRequest({ url: request.url }); await Dataset.pushData({ url: request.url, html: body }); log.info(Processed ${request.url}); }); router.addDefaultHandler(async ({ request, log }) { log.info(Unlabelled request: ${request.url}); }); const crawler new BasicCrawler({ id: my-crawler, requestHandler: router, maxRequestsPerCrawl: 100, maxCrawlDepth: 2, sameDomainDelaySecs: 1, respectRobotsTxtFile: { userAgent: MyCrawlerBot/1.0 }, requestHandlerTimeoutSecs: 30, maxRequestRetries: 3, onSkippedRequest({ request, reason }) { console.log(Skipped ${request.url}: ${reason}); }, async requestHandler() {}, // 实际由 router 承担 }); await crawler.run([ { url: https://example.com/, label: detail, userData: { source: home } }, ]); await crawler.exportData(./out/result.json); // 导出默认数据集十一、版本演进速查表版本关键变化3.3.0setStatusMessage基础支持状态消息实现移入 Crawlee3.3.3Request.maxRetries支持单请求覆盖全局重试上限3.4.2internalTimeoutMillis兜底retryOnBlocked检测被封锁页面3.5.0状态消息可配置sameDomainDelay支持会话因代理错误轮换3.5.3默认数据集助手3.6.0crawler.exportData()3.7.0可配置统计retryOnBlocked记录原因3.8.0上下文访问状态/KVS/数据集自适应 Playwright 爬虫3.9.0tieredProxyUrls入队后通知自动伸缩池3.10.0ErrorSnapshotterRequestQueue v2 成为默认3.11.0sitemap 请求列表3.12.0可插拔 HTTP 客户端BasicCrawler.stop()优雅停止3.13.0简化 RequestQueueV2 实现3.13.1respectRobotsTxtFile选项RobotsTxtFile重命名3.13.2onSkippedRequest3.14.0maxCrawlDepth3.15.0TandemRequestProvidercollectAllKeys导出选项3.15.3自定义 userAgent robots.txt3.16.0多次stop()安全maxCrawlDepth警告只打一次3.17.0修复maxRequestsPerCrawl意外丢请求失败增量计数修正3.18.0按路由 label 的类型安全 userData 映射与 opt-in schema 校验修复最终持久化重复终端状态消息可靠送达十二、总结从 packages/basic-crawler/CHANGELOG.md 可以看出BasicCrawler的演进主线是把“可靠抓取”拆解为一系列可组合、可观测的机制请求来源统一到requestManager、并发交给ConcurrencySystem、超时分层handler 级 internal 级 导航窗口级、防封锁由会话池与 robots.txt 协作、生命周期由CrawlerRun统一管理。理解这些分层既能帮助排查“请求为什么被跳过”“为什么没有重试”“为什么队列空了还在运行”等常见问题也能在需要时通过taskLoopOptions、concurrencySystem、statistics、httpClient等注入点把BasicCrawler深度定制为适合自己业务的抓取引擎。【免费下载链接】crawleeCrawlee—A web scraping and browser automation library for Node.js to build reliable crawlers. In JavaScript and TypeScript. Extract data for AI, LLMs, RAG, or GPTs. Download HTML, PDF, JPG, PNG, and other files from websites. Works with Puppeteer, Playwright, Cheerio, JSDOM, and raw HTTP. Both headful and headless mode. With proxy rotation.项目地址: https://gitcode.com/GitHub_Trending/cr/crawlee创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考