如何在爬虫中避免验证码(不违反规则)
爬虫过程中遇到的大多数验证码,都是网站对看起来像自动化的流量做出的反应。最省事的做法是不去触发它们。这意味着先寻找合法的数据来源,查阅网站的规则确认你可以做什么,减少请求数量并放慢节奏,同时避免重复抓取同一个页面。求解验证码是应对无法避免的情况时的备选方案,而不是首选方案。
先检查是否有官方 API 或数据源
许多经常被爬取的网站,都提供了无需验证码即可获取相同数据的途径:公开 API、数据导出、RSS 或站点地图,或者合作伙伴计划。官方数据源更可靠,不太会在没有通知的情况下变更,而且网站所有者可以对你的使用方式提出意见。可以留意以下几处:
- 网站页脚中的开发者或 API 板块,
sitemap.xml,其中列出了页面清单,- 页面
<head>中的 RSS 或 Atom 链接, - 用于批量导出的“数据”或“下载”页面。
如果其中某个途径能满足你的需求,就直接使用它。不请求 HTML 页面本身,就能避开大多数验证码。
遵守 robots.txt 及其要求的抓取频率
robots.txt 会告诉你哪些路径允许自动化客户端访问,有时还会说明两次请求之间应等待多久。Python 标准库可以替你读取它:
import time
from urllib.robotparser import RobotFileParser
import requests
USER_AGENT = "ExampleBot/1.0 (+https://example.com/bot)"
parser = RobotFileParser()
parser.set_url("https://example.com/robots.txt")
parser.read()
delay = parser.crawl_delay(USER_AGENT) or 5 # 若未设置则回退为保守的 5 秒
urls = [
"https://example.com/products?page=1",
"https://example.com/products?page=2",
]
last_request = 0.0
for url in urls:
if not parser.can_fetch(USER_AGENT, url):
print("skipped by robots.txt:", url)
continue
wait = delay - (time.monotonic() - last_request)
if wait > 0:
time.sleep(wait)
resp = requests.get(url, headers={"User-Agent": USER_AGENT}, timeout=30)
last_request = time.monotonic()
print(url, resp.status_code)
如果 robots.txt 禁止访问某个路径,诚实的做法是不去访问它,或者向网站所有者申请许可。改用浏览器绕过这一限制,并不会改变网站已经提出的要求。
放慢节奏,分散请求
稳定且适中的节奏很少触发挑战,而突发的大量请求几乎总会触发。以下几个习惯能带来明显的改善:
- 为每个网站确定一个请求频率并保持下去,而不是全速运行。
- 加入随机抖动,使请求时间不会过于规律。
- 当你看到 429 响应或验证码页面时,停下来退避一段时间。
- 如果网站的负载是个问题,请在业务低峰期运行任务。
缓存,避免重复抓取
你没有发出的每一个请求,都是网站不必处理的请求。将你抓取的页面连同日期和正文的哈希值一起保存,只在内容可能已经变化时才重新抓取。如果服务器支持,使用带有 If-Modified-Since 或 ETag 头的条件请求,可以让服务器以低成本回复“未修改”。
复用会话,发送常规请求头
在两次请求之间保留 cookie 的会话,比每次都从头开始的脚本更像一个正常访客。为每个网站使用 requests.Session(),并发送一个清楚、诚实的 User-Agent,注明你的机器人名称和联系方式。不要为了越过规则而假冒浏览器。如果某个网站需要 JavaScript 才能显示内容,请对这些页面使用浏览器工具,并遵守同样的频率限制。
验证码仍然出现时
有些网站无论你多么谨慎地操作,都会显示验证码。请在代码中检测到验证码后停止该任务,而不是持续刷新页面。检测以及针对 PopularCaptcha 的可选求解,在 网页抓取验证码 一文中有详细说明。
开始前先规划抓取计划
每天抓取全部内容的爬虫,是最容易触发验证码的方式。请先规划好工作:
- 确定优先级。 决定哪些页面更新频繁,哪些很少更新。经常更新的页面多抓取,其余的少抓取。
- 利用站点地图中的日期。 许多站点地图包含
lastmod值。跳过自上次访问以来未发生变化的 URL。 - 增量抓取。 为每个 URL 保存最近一次成功抓取的记录,失败后从该处继续,而不是从第一页重新开始。
- 分散负载。 将大型列表拆分成多个批次,全天分散运行,而不是一次全部执行。
这样的计划可以减少请求数量,而请求数量正是你最有效的控制手段。
把计划写成一个简短的配置文件或表格:包括站点、板块、刷新间隔,以及每个站点的每日请求预算。预算用完后,任务就停止,直到下一个时间窗口。把预算当作硬性上限而不是参考建议,是防止爬虫偏离、最终形成会触发验证码的流量模式的关键。每隔几周重新审视一次预算,因为一个不断增长的网站,其流量也可能随之增长,上个季度看似礼貌的频率,现在未必还礼貌。
负责任地抓取
- 在开始之前检查
robots.txt和服务条款,并遵守其中的规定。 - 保持较低的请求频率,并在网站表明其压力过大时停止。
- 不要收集你没有理由持有的个人数据,不需要时予以删除。
- 不要大规模转载受版权保护的内容。将数据用于分析、比较或你自己的产品,并在适当的情况下注明来源。
- 如果你需要更大的数据量,请联系网站所有者。他们可能会提供数据源。