网页抓取最佳实践
让爬虫在第一周之后依然能正常运行的关键在于:按主机设置的速率限制、采取“少抓取而非更努力抓取”的策略、不会造成累积影响的重试机制,以及在数据集悄然耗尽之前向你发出预警的那个关键指标。
- 无限带宽 — 缓存和重新抓取不会影响当月的费用。
- 按需轮换 — 在任务边界处释放已失效的 IP,而不是按定时器轮换。
- 专用硬件 —— 您的阻塞率反映的是您的行为,而非陌生人的行为。
- 粘性会话 — 长时间运行的任务将在其启动时的地址上完成。
按调制解调器计费,因此重新抓取或缓存未命中不会产生额外费用。
当区块费率上升时,在您选择的阈值处按需切换。
让爬虫正常运行是容易的部分。而要让它持续运行——面对不断扩大的目标列表,突破初始速率限制,同时确保费用增长速度不比数据增长更快——则是一门完全不同的学问,其核心在于克制。本页介绍的是集群的运维部分;关于代理本身的连接方法,请参阅支柱指南。
经得起考验的速率与并发预算
对大多数爬虫而言,最有用的改进就是按目标主机分配请求配额,而不是全局分配。分散在八个域名上的八个并行请求属于普通流量;而针对单一域名的八个并行请求则属于突发流量,而目标主机实际感受到的只有后者。
| 控制 | 合理的起点 | 如何调音 |
|---|---|---|
| 每个主机的并发请求数 | 2 到 5 | 只有在错误率保持稳定时才加注;一旦出现波动,立即撤出 |
| 劳动力总数 | 在许多主机上为10到20 | 在吞吐量持续攀升时扩大规模;当吞吐量趋于平稳时停止扩张 |
| 对同一域名的延迟 | 几秒钟;说10秒才算真正有礼貌 | 如果 robots.txt 文件中设置了 Crawl-delay,那就是答案——使用它 |
| 自适应节气门 | 从第一天起 | 让观测到的延迟来决定延迟值,而不是使用一个固定的常数 |
适应性比上述任何具体数值都更为重要。当目标出现压力时能够减缓速度的爬虫才能存活;而那些在延迟增加时仍保持恒定速率的爬虫,则表明其并未监测到另一端的任何情况。
减少请求次数:缓存与条件请求
每一个你没有发出的请求,都是一个无法被拦截、无法被限流、也绝不会出错的请求。在提高并发量之前,不妨先采取这种成本更低的优化方案——减少请求次数:
- 将已获取的数据缓存起来 — 本地或共享缓存可将重新运行转换为差异比对,而非全量抓取。对于分布式工作者而言,中央存储库可防止它们独立地重新获取相同的页面。
- 使用条件请求 — 当服务器提供 ETag 或 Last-Modified 时,应发送 If-None-Match 或 If-Modified-Since 请求头。这样,未更改的页面只需返回 304 状态码,而非完整页面内容,这对双方来说都更节省资源。
- 逐步爬行 — 按照预定计划重新遍历整个目录,是导致因数据未发生变化而造成资源阻塞的典型原因。建议优先使用站点地图、数据源和变更检测机制,而非进行全面扫描。
- 寻找页面背后的端点 — 渲染后的页面通常是从一个内部 JSON 端点获取的,你可以直接调用该端点——与包裹在其中的标记相比,它体积更小、更稳定,也更容易解析。
- 在排队前进行去重 — 对 URL 进行规范化处理,并过滤掉已访问过的 URL。跟踪参数和会话标识符会将一个页面拆分成数十个看似独立的请求。
对于带宽无限制的代理服务器而言,支持缓存的理由并非传输成本——而是每次避免的请求,就意味着被察觉的机会减少一次。
重试、死信和自适应退避
重试机制正是礼貌的爬虫悄然转变为激进爬虫的转折点。以下三条规则能确保其行为得体:
- 先执行“Retry-After”,然后按指数级减少尝试次数
当服务器告知需要等待多长时间时,那就是答案。如果没有给出具体时间,应逐步延长间隔时间,而不是重复使用固定的间隔——并加入抖动,以避免并发的工作线程同时唤醒,从而再次引发突发情况。
- 限制尝试次数并使用死信队列
一个已失败五次的URL正在向你传达某种信息,而第六次尝试也无法改变这一结果。请将其暂存以供检查,而不是任其无限期地运行,从而不断消耗预算。
- 确保重试具有幂等性
读取操作可自由重试;但对于远端状态会发生变化的操作,请务必谨慎。将自动重试限制在 GET 和 HEAD 请求范围内是安全的默认设置。
轮换 IP 地址并立即重试并不是一种退避策略。429 状态码是对你请求速率的反馈,而通过更改 IP 地址来应对,无异于消耗 IP 地址资源,以目标服务器已经拒绝的速率换取几分钟的缓冲时间。
在数据被阻断前就知晓自己已被屏蔽
爬虫很少会突然出现明显故障。它们通常会开始返回状态码为 200 的挑战页面,或者返回能够被干净解析的空结果集,而处理流程却会连续数天报告成功。针对这种特定故障的排查方法:
| 指标 | 它向你传达了什么 | 何时采取行动 |
|---|---|---|
| 每个目标的阻塞率 | 你收到的最早的真实信号是 | 它确实会上升——这种变化发生得比你的数据集还要早 |
| 缺少必填字段的记录 | 捕获返回 HTTP 200 的软阻塞 | 该股偏离了其正常基准线 |
| 第95百分位响应时间 | 目标菌株,或正在进行的挑战 | 它在上升,而吞吐量却没有上升 |
| 队列深度 | 无论你是维持现状还是不断积累 | 它在整个运行过程中稳步增长 |
一个既实用又经济的补充措施是“金丝雀”:即一个已知正常的URL,其响应也已知正常,并按计划定期抓取。当“金丝雀”开始返回意料之外的结果时,你就知道问题出在你这边,而不是解析器,而且你能在当天的数据写入之前就发现这个问题。
当区块费率发生变化时, 积木分级指南 内容涵盖了在开始做出改变之前,先找出阻碍你的因素。
获取专用爬虫代理
PXM2实时节点——选择您希望目标用户看到请求来自的国家/地区,即可获得一个专属的4G/5G IP地址,该地址提供无限带宽和轮询功能:
法国
印度
新加坡
常见问题解答
在进行数据抓取时,我可以发起多少个并发请求?
按目标主机分配预算,而非全局分配。针对单个主机,2 到 5 个并发请求是一个合理的起点,而跨多个主机的 10 到 20 个工作进程池也属常见。当吞吐量持续攀升且错误率保持稳定时,可以增加进程池规模;一旦错误率上升而吞吐量未随之增长,就说明已达到上限。
两次请求之间应该间隔多长时间?
如果 robots.txt 文件中指定了 Crawl-delay 参数,请遵循该设置——这正是运营商给出的明确指引。 如果没有指定,对同一域名的延迟设置为几秒钟是保守的做法,且很少出错;对于小规模抓取而言,延迟10秒确实相当礼貌。相比延迟,适应性更为重要:当目标服务器发出压力信号时放慢速度,才是确保抓取能够持续进行的关键。
如何进行数据抓取而不被封号?
减少请求次数,并保持行为一致。缓存已有的内容,使用条件请求以确保未更改的页面不产生任何成本,采用增量爬取而非重新遍历整个目录,并保持每个主机的并发请求数较低。大多数封禁都是由请求量和请求频率导致的,而非由任何单个请求引起。
在数据抓取流程中,我应该监控哪些内容?
与其他指标相比,阻塞率是最重要的——它比数据传输更早发生变化。其次是成功率、第95百分位响应时间、队列深度,以及数据质量检查(例如缺少必填字段的记录占比)。即使某个爬虫在遇到验证页面时会默默返回,它仍会报告HTTP 200状态码和一个空结果集。
是轮换代理好,还是降低网速好?
先放慢节奏。IP轮换解决的是身份识别问题;而429错误则是速率限制问题,通过轮换到新的IP地址来维持相同速率,无非是消耗IP地址来换取几分钟的时间。应降低并发量,遵守“Retry-After”规则,并在某个IP地址确实出现问题时再进行轮换。
相关移动代理指南
本页介绍的是操作规范;集群的其余部分则涵盖了该规范所适用的机制。