Scrapy 如何应对反爬虫机制?
Scrapy 处理反爬,不是简单地把 User-Agent 换成浏览器就结束了。更稳的做法是先判断网站的限制来自哪里:是请求频率过高、Cookie 会话缺失、IP 被限流,还是前端渲染和验证码把页面内容挡住了。Scrapy 能做的是把请求行为变得更接近正常访问,并把失败、限速、代理、登录态这些环节放进可维护的配置和中间件里。上线前最好先用小样本跑通完整链路,确认列表页、详情页、翻页和异常页都能被识别,否则反爬策略还没生效,解析层就已经把脏页面当成正常数据了。真正的边界也要说清楚:遇到强验证码、设备指纹、复杂 JS 加密时,Scrapy 本身不是万能钥匙,继续硬撞通常只会浪费代理和时间。
追问
只改 User-Agent 能绕过反爬吗?
不能把 User-Agent 当成反爬方案的全部,它最多解决一部分“默认爬虫特征太明显”的问题。很多网站会同时检查 Accept、Accept-Language、Referer、Cookie、请求间隔,甚至会观察同一 IP 的访问路径是否像真人。取舍上,随机请求头实现成本低,适合轻度限制的网站;但如果每次请求头组合都乱跳,反而可能显得更异常。踩坑最多的是只改 UA、不保留会话,结果登录页、列表页、详情页之间的 Cookie 对不上,服务端直接返回空数据或 403。还有一种隐蔽情况是服务端返回状态码 200,但正文其实是风控提示页,解析器照样能跑,却会把错误内容写进数据库。
python# middlewares.py import random class RandomHeaderMiddleware: agents = [ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 Chrome/124 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/123 Safari/537.36" ] def process_request(self, request, spider): request.headers.setdefault("User-Agent", random.choice(self.agents)) request.headers.setdefault("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8")
请求速度应该怎么控制?
Scrapy 的 DOWNLOAD_DELAY、RANDOMIZE_DOWNLOAD_DELAY 和 AutoThrottle 都能控制速度,但它们解决的问题不一样。固定延迟简单可预测,适合小站点和低并发任务;AutoThrottle 会根据响应延迟动态调整,更适合站点负载变化明显的场景。边界是 AutoThrottle 不是“越开越安全”,如果目标站本身响应很快但限制按分钟计数,它可能仍然跑得太快。实际项目里建议从保守参数开始,看 429、403、超时比例再调,不要一上来把 CONCURRENT_REQUESTS 拉满。如果任务有时效要求,可以按域名拆分队列,让高价值页面优先抓取,而不是所有 URL 使用同一套并发策略。
python# settings.py CONCURRENT_REQUESTS = 8 CONCURRENT_REQUESTS_PER_DOMAIN = 4 DOWNLOAD_DELAY = 1.5 RANDOMIZE_DOWNLOAD_DELAY = True AUTOTHROTTLE_ENABLED = True AUTOTHROTTLE_START_DELAY = 1 AUTOTHROTTLE_MAX_DELAY = 10 AUTOTHROTTLE_TARGET_CONCURRENCY = 2.0
代理池什么时候该用,什么时候不该用?
代理池适合 IP 维度限流明显的网站,例如同一 IP 连续访问几十次后开始返回 403 或验证码。它不适合拿来掩盖所有问题,因为代理质量差会带来超时、脏 IP、地区不一致、TLS 指纹异常等新麻烦。取舍上,少量稳定代理加合理限速,通常比大量廉价代理随机切换更可靠。踩坑点是登录态和代理绑定:如果登录请求用 A 代理,后续详情页突然换到 B 代理,服务端很可能判定会话异常。
pythonclass ProxyMiddleware: def process_request(self, request, spider): proxy = spider.proxy_pool.pick() request.meta["proxy"] = proxy
登录、Cookie 和验证码怎么处理?
需要登录的网站先用 FormRequest 或接口请求建立会话,让 Scrapy 的 CookieMiddleware 保持后续请求状态。简单验证码可以接第三方识别服务,但这会增加成本和失败率,也可能触碰站点规则边界。更推荐的思路是先确认数据是否有公开接口、RSS、站点地图或授权数据源,不要默认把验证码当成必须绕过的障碍。常见坑是登录成功后没有检查响应内容,只看状态码 200 就继续抓,最后抓到一堆登录页 HTML。
pythondef start_requests(self): yield scrapy.FormRequest( url="https://example.com/login", formdata={"username": "user", "password": "pwd"}, callback=self.after_login ) def after_login(self, response): if "退出登录" not in response.text: self.logger.warning("login failed") return yield scrapy.Request("https://example.com/profile")
反爬失败时怎么判断问题出在哪?
不要只看异常类型,要同时记录状态码、响应长度、重定向地址、命中的代理、请求头和重试次数。403 常见于权限或规则拦截,429 多半是频率问题,200 但内容为空则可能是登录态、JS 渲染或被返回了假页面。取舍上,日志越详细越容易定位,但也要避免记录密码、Cookie 等敏感信息。一个实用做法是对异常响应保存少量样本 HTML,排查完及时清理,避免把脏数据继续送进解析流程。日志里还可以记录指纹字段,例如代理、UA、下载延迟和重试次数,这样才能判断是单个代理坏了,还是整套策略被目标站识别。
结论
Scrapy 的反爬处理重点是分层:请求头和 Cookie 解决基础识别,限速解决频率,代理解决 IP 限制,中间件负责把这些策略做成可复用逻辑。遇到强验证码、设备指纹和复杂加密时,要先评估成本和合规边界,而不是把所有问题都交给代理池。