跳到正文
← 返回博客
LAB / 实验

验证码页面不一定是验证码问题

PropertyGuru 分页在默认 HTTP 栈上变成 Cloudflare 挑战页。真正翻转结果的变量是 TLS / HTTP2 栈,不是解出 Turnstile。

看见 Just a moment 和 Turnstile 站点键,第一反应通常是去解验证码。

新加坡房产列表页把这个反应证伪了。第一页用普通 HTTP 栈可以打开,并直接从 SSR 的 __NEXT_DATA__ 里读到房源。第二页却稳定变成 Cloudflare 管理挑战。浏览器自动化能打开第一页,到第二页仍停在组件上;偶发拿到的 clearance cookie 和 TLS 指纹绑在一起,换回普通客户端继续 403。

如果把挑战页当成必须求解的验证码,下一步会是识别组件、拟人点击、保存 token。这次真正翻转结果的变量不在组件里。

先做对照,再决定解哪一层

对照很简单,只改客户端栈,不改 URL 业务参数。

默认 Python / curl 栈访问第二页:403,标题是挑战页。把 TLS / HTTP2 指纹换成接近 Chrome 的配置,并先访问首页和第一页做会话预热,第二页返回 200,HTML 里重新出现 __NEXT_DATA__,解析出的房源 id 与第一页无交集。同一条件下,第三页同样给出新的一页列表。

这说明分页 URL 的风控权重更高,但门禁打在边缘指纹,而不是业务签名。页面上的 Turnstile 是挑战形态,不是这次成功路径必须穿过的算法。

Playwright 无论无头还是有头,都可能卡在组件上。继续在浏览器里“把验证码点过”是错层。自动化特征本身可能让挑战升级;即使拿到 clearance,它也不一定能迁移到另一个 TLS 栈。

判定挑战页时也不能只搜 challenge-platform 字符串。合法业务 HTML 里也可能出现这类资源路径。更稳的条件是:没有 __NEXT_DATA__,并且标题或主体落入挑战形态。否则会把正常列表页误判成未通过。

这一层证明了什么

它证明三件事。

第一,第一页和第二页不是同一强度的边缘策略。能读第一页,不能外推到分页。

第二,挑战页出现时,应先问“哪个客户端变量让页面在挑战 HTML 和 SSR 数据之间切换”,而不是先问“验证码怎么解”。这次切换变量是 TLS / HTTP2 与预热会话。

第三,联系方式等登录门控是另一层。公开列表页的 SSR 数据、自动完成接口和经纪人详情 API 的 401,不能写成同一个问题。

它没有证明可以无视 Cloudflare 策略无限抓取,也没有证明所有站点的挑战页都是指纹问题。策略升级后,当前客户端画像可能重新落到交互式验证码。公开文章不提供客户端配置或绕过步骤。

和签名实验共用一个顺序

《签名正确,为什么 API 仍然不给真实数据?》 把算法、会话、传输和业务数据拆开。分页挑战是同一顺序的另一现场:先确认挡在传输 / 边缘,还是挡在组件求解。

《脚本变大了,并不等于协议换了》 说混淆变化不等于协议变化。这里是对称句:验证码外观不等于验证码求解。两者都要求在改实现之前,先用单变量对照找到真实挡路层。

实验记录只保留对照结论和停止位置。第二页能读到结构化列表,验收就停在“可复现的分页读取”。交互式验证码、登录后的个人数据和高频采集效果,都还在未知里。