← 全部文章

深入浏览器配置文件:`unblocker: true` 的底层实现原理

一个标志控制三个功能:真实的浏览器 header、匹配的连接签名,以及针对 gzip 和 brotli 的自动解压缩。本文将介绍它的作用和原因。

大多数爬虫甚至在服务器读取第一个 header 之前就已经失败了。

服务器会检查客户端在网络连接层留下的签名特征,以此判断你是一个真正的浏览器,还是一个伪装成浏览器的客户端库。Python requests、Go 的 net/http、原生 curl,它们在握手建立连接的瞬间就会暴露各自独特的指纹。注重防护的网站(如 Datadome、Akamai、Imperva 以及 Cloudflare 托管防护)在你的 User-Agent 字符串发挥作用之前,就会直接断开连接或返回验证页面。

这正是 unblocker: true 在 FourA 上要解决的问题。在过去的一个月中,我们确定了确保其可靠运行的核心组件。

新增功能

unblocker: true 是任意 /api/single 调用中的一个参数标志。开启后,我们会执行三项操作:注入浏览器标头集、通过匹配真实浏览器底层流量的传输层发送请求,以及自动解压服务器返回的所有内容(gzip、brotli、deflate)。前两项功能在公测阶段已上线。第三项(brotli 自动解压)已于 3 月 25 日发布,版本固定功能也在次日上线,以确保 headers 和传输层保持同步。

工作原理

以下是请求调用的示例:

curl -X POST "https://api.foura.ai/api/single" \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_API_KEY" \
  -d '{
    "url": "https://example.com/products",
    "method": "GET",
    "unblocker": true
  }'

底层运行着三个层级。

**Header 注入。**我们设置了完整的浏览器 header 集合:User-Agent、Sec-Ch-Ua、Sec-Ch-Ua-Platform、Sec-Fetch-Site、Sec-Fetch-Mode、Sec-Fetch-Dest、Accept、Accept-Language 以及 Accept-Encoding。顺序非常关键。真实浏览器会按特定顺序发送这些 header,检测库会专门核对这一点。

**连接特征签名。**我们的传输层匹配了最新浏览器会话的字节级特征:相同的扩展顺序、相同的密码套件偏好、相同的握手细节。标准的 cURL、Python requests 和 Go 的 net/http 生成的签名,在受防护的基础设施上几毫秒内就会被机器识别并标记。

**自动解压。**启用 unblocker 时,我们会将 Accept-Encoding 设置为 gzip, deflate, br,传输层会自动解压 body。你将直接获得解码后的字符串(如果传入 returnBuffer: true 则为 Buffer)。无需手动处理 brotli,也无需担心站点选用 deflate 而非 gzip 时出现的 header 与 body 不匹配问题。

为什么版本锁定至关重要

连接特征签名是绑定特定版本的。浏览器本月的底层协议细节与上个月不同,进行精细指纹识别的站点会捕捉到这种差异。我们将这些变动组件锁定在一起,确保 header、navigator 对象以及连接签名全部指向完全一致的浏览器版本。

如果这听起来很繁琐,事实确实如此。我们在 3 月进行 monorepo 迁移时就曾遭遇过不匹配问题,当时某个组件自动更新导致其余部分脱节。修复只用了两次提交:锁定变动组件,并且永远不要相信包管理器会自动帮你保持一致。

实际效果

在针对验证请求方身份的站点(金融、旅游、大型电商)进行的内部测试中,unblocker: false 与 unblocker: true 之间的差异就是验证码挑战页面与 200 状态码的差异。普通的 HTTP 客户端初次请求往往会遭遇 403。而使用 unblocker: true 请求相同的 URL 则能直接获取页面,因为该 request 看起来与它所声明的浏览器完全一致。

但对于不进行指纹识别的站点(多数公开 API、老旧 CMS 模板、仅通过 IP rate limit 限制的任何服务),保持 unblocker 关闭完全可行,还能省去几毫秒的协商时间。请根据实际需求使用。

高级用户指南

以下是几个值得了解的模式。

当目标站点同时检查 IP 信誉时,将 unblocker 与住宅 proxy 配合使用。数据中心 IP 加上完美的连接签名,在目标站点已加入黑名单的 ASN 上依然会被标记。我们的 proxy endpoint (/api/proxy) 按目标域名轮换,因此在 request 中添加 "proxy": "residential" 通常就足够了。

调用不关注浏览器的 JSON API 时,请跳过 unblocker。对于预期接收程序化客户端调用的 API(例如调用自身微服务的后端),额外的 header 反而可能引起怀疑。

如果目标网站在展示页面前使用 JavaScript 对访问者进行校验,仅靠 unblocker 是不够的。你需要使用 browser endpoint,它会在完整的浏览器中运行页面的 JavaScript。这是另一款产品,采用不同的 credit 计费方式,我们在 Browser Tasks: How to Scrape JavaScript-Heavy Sites 中对其进行了详细说明。

你还可以将 unblocker 与 validate 块结合使用,以拒绝那些状态码虽为 200 但实际包含质询页面的 response:

{
  "url": "https://example.com/products",
  "method": "GET",
  "unblocker": true,
  "validate": {
    "data": { "fail": ["captcha", "Access Denied"] }
  }
}

这会将静默失败转化为已分类的失败,对你在 dashboard 中的成功率追踪非常重要。

下一步计划

浏览器每四周发布一个新的稳定版。我们会同步升级技术栈。你无需在自身侧进行任何改动:unblocker: true 始终指向我们已完成端到端验证的浏览器版本。

更具挑战性的工作还在后面。大型网站已经开始引入 HTTP/3 校验,QUIC 传输层比旧版传输层更难模拟,从静态 header 组合向真正动态模拟的迁移也已经启动。受防护的网站已转向校验 HTTP/2 帧顺序,“形似浏览器的库”与“真实浏览器”之间的差距将从两端进一步缩小。相关功能上线后,我们会撰文详细说明。