被判成反爬封IP,真凶却是无头浏览器:携程页面抓不到数据的复盘

发布于 4天前 ❤ 0 👁 8 阅读 💬 3 评论

被判成反爬封IP,真凶却是无头浏览器:携程页面抓不到数据的复盘

上周要做会展酒店的竞品价格监控,抓携程某个城市的热门酒店列表。第一版脚本跑得很顺,返回 HTTP 200,日志里没有报错,但提取出来的酒店数量是 0。第一反应是 IP 被封了,换了个出口,还是 0。来回折腾了半小时,才定位到真正的原因:不是网络,是我用的无头浏览器。

背景:为什么选 Playwright

这类页面我一开始就判断是 JS 渲染的,所以直接上 Playwright 无头模式,等 networkidle 之后取 content。这种做法在大多数站点都没问题,也省去了手写异步请求。问题在于我把它当成了万能解,没做验证。

真实症状:200 但内容是错的

判断封 IP 的常见依据是 403 或 429,或者响应极短。我这次拿到的是完整的 HTML,长度几万字符,节点也能选到,只是里面全是「登录」「注册」的字样,酒店列表一个都没有。也就是说页面结构和预期完全不是一回事,但我当时只盯着「有没有报错」,没有校验「有没有目标数据」。

改成三步验证之后真相很快出来:

第一,同一个出口 IP,换成最朴素的 urllib 加真实 UA 请求一次。如果这个能拿到数据,就不可能是 IP 被封。

第二,把两种方式拿到的 HTML 存下来 diff 标题和关键文本。浏览器版是我之前设置过的登录页面,纯 HTTP 版是完整的酒店列表页。

第三,对比请求头。无头浏览器会带一串自动化特征,服务端识别到之后不给你 403,而是安静地把你导向登录页,HTTP 状态依然是 200。这就是最容易误判的地方:反爬没有拒绝你,它只是给了你一份你不想要的内容。

结论很直接:纯 HTTP 先上,浏览器只做兜底。同 IP 情况下,简单请求反而没被抓,说明这类站点认的是浏览器指纹而不是出口地址。

第二个坑:类名是哈希,老选择器永远匹配不到

换成纯 HTTP 之后数据出来了,但地址字段一直是空。排查发现站点用的是 CSS Modules,类名形如 addressText__a1b2c,后面带一段随构建变化的哈希。我早期写法是包含 address_text 这种下划线命名,一直匹配不到,因为源码里是驼峰。

解决办法是用前缀模糊匹配,把哈希忽略掉,只认稳定部分。这条路在大多数前端构建产物里都适用,前提是先看一遍真实 DOM,别按自己想象的类名写选择器。

第三个坑:有些数据静态 HTML 里根本没有

页面上的房型和实时房价是异步加载的。静态 HTML 能拿到的只有酒店名称、星级、评分、地址、开业年份和图片,房型列表拿不到。这不是解析问题,是数据压根没在那份响应里。

还有一个反直觉的结论:页面上看到的 ¥150、¥288 不是房价,那是餐饮或 SPA 的报价。真实房价对未登录访客永远显示「登录看低价」。这是源站的策略,靠改请求头绕不过去,需要的是登录态,而登录态本身有合规成本,我最后放弃了这部分字段,只做主表信息。

可复用的四条经验

一,判断被反爬之前先换一种请求方式做对照,同 IP 两套客户端的结果差异,比换 IP 更能定位问题。

二,只看 HTTP 状态码会误判,必须校验「拿到的内容里有没有目标字段」,把这条写成断言。

三,选择器先看真实 DOM 再写,遇到哈希类名用前缀匹配,不要按命名习惯猜。

四,先摸清哪些字段在静态 HTML 里,哪些要异步加载,再和需求方对齐能交付的范围。省下的是后半程的返工。

评论 (3)

Meicheng · 站长
Meicheng · 站长 4天前
补充一个我踩过的坑:被判反爬不一定是 UA 或 IP 的问题,很多时候是「浏览器指纹」没对上。无头浏览器默认 navigator.webdriver=true、缺真实 GPU/字体/屏幕参数,WAF 一验就穿。实操三板斧:① Playwright 用 stealth 插件把 webdriver 抹掉;② 随机化 viewport、时区、语言;③ 请求节奏加随机抖动,别固定间隔。另外如果页面是 Cloudflare 盾,直接降级到真实浏览器(关键流程留人肉兜底),别死磕 headless。
Meicheng · 站长
Meicheng · 站长 3天前
补充一个我踩过的坑:被判反爬不一定是 UA 或 IP 的问题,很多时候是「浏览器指纹」没对上。无头浏览器默认的 WebDriver 标记(navigator.webdriver=true)、屏幕分辨率、字体列表这些特征,和真实浏览器对不上,风控一眼就认出来。我的做法是:先别急着上无头浏览器,试试直接请求接口拿数据——很多页面的数据其实在 XHR 里,比渲染 DOM 快得多;实在要渲染,就把指纹对齐:隐藏 webdriver 标记、指定真实分辨率、带完整请求头,成功率会高一个量级。
Meicheng · 站长
Meicheng · 站长 1天前
补一个更省事的思路:先别急着和无头浏览器死磕。打开 F12 的 Network 面板,很多页面背后藏着现成的 XHR/JSON 接口,直接调接口比渲染页面稳定得多。实在要抓页面,就降并发、加随机延迟、换 IP 池,别用无头浏览器硬刚风控评分。

登录 后参与评论