踩坑Cloudflare两周:从一请求就403到90%通过率,三套分层方案亲测可用(不用付费代理)

本文系统解析Cloudflare三级防护机制:一级协议层指纹检测、二级5秒JS挑战、三级托管式人机验证。提出三套分层应对方案——curl_cffi对齐TLS指纹解决一级防护;Playwright过盾+HTTP客户端复用应对二级;叠加行为优化(访问路径模拟、频率随机化、IP-指纹-会话绑定、错误降级)攻克三级防护。强调避免换IP万能论、curl_cffi局限性、无头浏览器滥用等五大误区,聚焦信息技术领域中的反爬对抗核心策略。

做公开资讯采集这几年,Cloudflare绝对是绕不开的一道坎。前两年碰上海外一批技术博客和资讯站,清一色套了Cloudflare防护,原生requests上去直接403,连页面HTML都摸不到。

一开始想当然觉得是IP的问题,前前后后换了三家代理供应商,钱花了小两千,结果新IP撑不过三个请求就进挑战页,整体通过率不到30%。后来抓包逐层拆才反应过来:根本不是IP的锅——我们的请求从TCP握手那一刻起,TLS指纹就已经明明白白写着“我是Python程序”,对方根本不用看请求内容,协议层一对就直接打回。

前后折腾了两周,从纯HTTP客户端摸到无头浏览器,摸透了不同防护等级的应对逻辑,总结出三套分层方案。不用上来就砸钱买高端住宅代理,普通防护的站点,零代理费用就能做到90%以上的通过率。

一、先搞清楚:你面对的是哪个等级的防护?

很多人上来就问“怎么过Cloudflare”,其实它的防护是分级的,不同等级对应的方案天差地别,搞错了就是白费力气。先判断清楚类型,再选对应方案,效率能高好几倍。

一级防护:协议层指纹检测

最轻的一档,只校验TLS指纹、HTTP头完整性、请求特征,没有JS挑战。

  • 典型表现:浏览器直接访问正常,原生requests/httpx请求直接返回403;没有“Just a moment”的等待页面。
  • 常见场景:个人博客、普通资讯站、技术文档站。
  • 应对成本:极低,改个客户端就能解决。

二级防护:5秒盾JS挑战

最常见的一档,返回包含JS的挑战页面,客户端执行完JS计算、生成cf_clearance Cookie后才能正常访问。

  • 典型表现:打开页面先显示“正在验证浏览器”,5秒左右自动跳转;页面源码里有大量混淆JS。
  • 常见场景:中小型社区、内容站、普通企业官网。
  • 应对成本:中等,需要能执行JS的环境过挑战。

三级防护:托管式人机挑战

最高的一档,结合浏览器环境检测、行为分析,可能弹出Turnstile无感人机验证,甚至滑块、点选。

  • 典型表现:频繁弹出验证、IP稍一频繁就出挑战页、纯脚本环境完全通不过。
  • 常见场景:大型社区、电商平台、高价值数据站点。
  • 应对成本:较高,需要完整浏览器环境+行为优化。

绝大多数人日常碰到的都是前两档,根本用不着最重型的方案。先定位清楚等级,再对症下药,性价比最高。

二、方案一:轻量级,curl_cffi对齐协议指纹,搞定一级防护

这是性价比最高的方案,改两行代码就能解决80%的基础防护站点,也是我日常用得最多的。

为什么原生requests永远403?

很多人以为改了User-Agent就等于模拟浏览器了,其实差得远。
TLS握手的时候,客户端会发送加密套件列表、扩展列表、椭圆曲线参数等信息。Python requests、httpx这类库的底层网络栈和真实浏览器完全不同,生成的JA4指纹差异极大。Cloudflare只需要看一眼握手指纹,就能准确判断你是Python程序还是真实Chrome,你上层头改得再像都没用。

这就是为什么很多人百思不得其解:我UA、Referer、Cookie全抄浏览器的,怎么还是403?问题出在更底层的协议层。

解决方案:curl_cffi一键对齐浏览器指纹

curl_cffi 基于 curl-impersonate 封装,底层直接复刻Chrome、Firefox等浏览器的完整TLS栈,JA3/JA4指纹和真实浏览器完全一致,协议层直接和真实浏览器无差别。

安装和使用都非常简单:

pip install curl_cffi

基础会话封装:

from curl_cffi import requests
import random
import time

# 直接指定模拟的浏览器版本,TLS指纹自动对齐
session = requests.Session(impersonate="chrome128")

# 请求头要和模拟的浏览器版本完全对应,一个都别漏
headers = {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.8,zh-TW;q=0.7,en-US;q=0.5,en;q=0.3",
    "Accept-Encoding": "gzip, deflate, br",
    "Referer": "[https://example.com/](https://example.com/)",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1",
    "Sec-Fetch-Dest": "document",
    "Sec-Fetch-Mode": "navigate",
    "Sec-Fetch-Site": "none",
    "Sec-Fetch-User": "?1"
}

def fetch_page(url):
    try:
        resp = session.get(url, headers=headers, timeout=10)
        resp.raise_for_status()
        return resp.text
    except Exception as e:
        print(f"请求失败: {e}")
        return None

实测效果与踩坑提醒

普通技术博客、资讯站这类一级防护站点,原生requests通过率20%左右,换成curl_cffi之后直接拉到95%以上,连代理都不用开。

几个必踩的坑提前说:

  1. 不是万能的。它只能解决协议层检测,带JS挑战的二级防护站点过不去,因为它不会执行JS,拿不到cf_clearance。
  2. 指纹版本要和UA对应。你模拟Chrome 128,UA就必须写Chrome 128,版本对不上反而会因为特征矛盾拉高风险分。
  3. 要维护会话Cookie。不要每次请求都新建会话,正常用户是有连续Cookie的。空会话、空Cookie天生高风险。

三、方案二:中阶,浏览器拿Cookie + HTTP客户端复用,兼顾速度与通过率

碰到带5秒盾的二级站点,纯HTTP客户端就不够用了——它跑不了JS,拿不到通关用的cf_clearance Cookie。

很多人这时候就全程上Playwright硬爬,慢得要死还占资源,其实完全没必要。浏览器只用来过挑战拿Cookie,真正采集数据交给HTTP客户端,通过率和真实浏览器一致,速度却能快好几倍。

核心思路

  1. 用Playwright启动浏览器,访问目标站首页,等待5秒盾自动完成挑战,拿到完整Cookie
  2. 把Cookie、UA等信息同步到curl_cffi会话中
  3. 后续所有采集请求都用HTTP客户端发,速度和纯原生请求一致
  4. Cookie过期前,再用浏览器刷新一次即可,不用全程挂着浏览器

完整实现代码

from playwright.sync_api import sync_playwright
from curl_cffi import requests

def get_cf_cookies(url):
    """用浏览器过5秒盾,拿到有效Cookie"""
    with sync_playwright() as p:
        browser = p.chromium.launch(
            headless="new",  # 必须用新无头模式,旧无头很容易被检测
            channel="chrome"
        )
        context = browser.new_context(
            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
            viewport={"width": 1920, "height": 1080},
            locale="zh-CN",
            timezone_id="Asia/Shanghai"
        )
        page = context.new_page()
        
        # 访问首页,等待挑战自动通过
        page.goto(url, wait_until="networkidle")
        # 额外等待几秒,确保挑战完成、Cookie写入
        page.wait_for_timeout(6000)
        
        # 提取所有Cookie
        cookies = context.cookies()
        browser.close()
        return cookies

def build_session(cookies):
    """把浏览器Cookie同步到HTTP会话"""
    session = requests.Session(impersonate="chrome128")
    for cookie in cookies:
        session.cookies.set(cookie['name'], cookie['value'], domain=cookie['domain'])
    return session

# 使用流程
if __name__ == "__main__":
    target_url = "[https://example.com](https://example.com)"
    # 1. 浏览器拿Cookie
    cookies = get_cf_cookies(target_url)
    # 2. 构建HTTP会话
    session = build_session(cookies)
    # 3. 后续批量采集全走HTTP,速度拉满
    for i in range(100):
        resp = session.get(f"{target_url}/page/{i}")
        # 解析数据...

关键注意点

  1. 必须用新无头模式。旧版无头的环境特征非常明显,很容易被检测出来;headless=new 模式底层就是真实Chrome,特征差异极小。
  2. 不要刚打开就抓Cookie。等页面跳转完成、cf_clearance 写入之后再取,不然拿到的是无效Cookie。
  3. Cookie有有效期。一般几个小时到一天不等,失效了就重新用浏览器刷一次,不用频繁刷。

这套方案应对中等防护的站点非常稳,通过率接近真实浏览器,采集速度却是全程无头浏览器的5-10倍,性价比极高。

四、方案三:高阶,行为优化+会话预热,应对高强度防护

碰到防护等级高的站点,比如大型社区、内容平台,光有指纹和Cookie还不够。你环境是对的,但行为全是爬虫特征,照样会频繁出验证、被限流。

很多人觉得过了5秒盾就完事了,结果爬个三五页就又弹挑战,核心原因就是行为太反常。小红书、知乎这类国内内容平台的逻辑也高度相似:重点从来不是破解什么盾,是把风险分压在阈值以下。

分享几个经过验证的行为优化思路,做完之后验证码出现频率能降80%:

1. 完整访问路径,不要直奔目标

真实用户一定是从首页、列表页、搜索页一步步进到详情页的。直接请求深层接口、跳过所有前置路径,风险分直接拉满。

  • 采集前先访问首页,初始化会话
  • 先走2-3页列表页,模拟浏览翻页
  • 再逐步进入详情页,Referer和上一页严格对应
  • 中途穿插返回列表、翻下一页的动作,不要一条路走到黑

2. 频率随机化,不要固定节拍

固定间隔、匀速请求是最典型的程序特征。真人操作的节奏一定是波动的:看长文章停得久,看短文章翻得快,偶尔还会停下来干点别的。

  • 用正态分布生成请求间隔,不要用均匀随机,更不要固定值
  • 每请求10-15页,插入一次10-20秒的长停留,模拟用户阅读、喝水的间隙
  • 高峰期适当降速,不要半夜猛冲,正常用户不会凌晨三点每分钟刷十几页

3. IP、指纹、会话三者绑定

不要交叉混用:同一个IP对应一套Cookie、一套指纹、一个浏览器环境。频繁换IP、同IP乱换Cookie、同Cookie换不同指纹,都会触发异常检测。

  • 稳定的IP比频繁换IP更安全。同一个IP稳定跑正常行为,比十分钟换八个IP的风险分低得多。
  • 不要一个IP跑多个不相关的业务。不同站点、不同类型的任务分开用不同的IP段。

4. 错误降级,不要硬冲

碰到挑战页、403不要立刻疯狂重试,越重试风险分越高。

  • 第一次出挑战,先停30秒再刷新一次
  • 连续两次失败,就换会话重新过挑战,不要死磕
  • 整体失败率上升时,主动降速,而不是加并发

五、最容易踩的五个误区

误区1:换IP就能解决一切

这是新手最容易陷进去的坑。403了就换IP,换了还封就买更贵的IP,结果钱花了不少,问题没解决。
很多时候封的根本不是IP,是你的客户端指纹。指纹不对,换多少IP都是刚上去就被识别,白费钱。先把底层特征对齐了,再考虑IP的事。

误区2:curl_cffi是万能的

网上很多文章吹“curl_cffi秒过Cloudflare”,都是标题党。它只能解决协议层的检测,带JS挑战的站点根本过不去,更别说三级的人机验证了。
对它的定位要准:是基础层的优化,不是万能钥匙。

误区3:全程用无头浏览器采集

很多人为了稳,全程Playwright跑,结果速度慢、并发低、服务器成本高。
浏览器只应该用来过挑战、拿Cookie。真正的批量数据采集,交给HTTP客户端效率高得多。能轻量就不要重型,架构越简单越稳。

误区4:只改UA,其他头全丢

请求头是一个整体,不是改个UA就完事了。Accept、Accept-Language、Sec-Fetch系列、Referer,缺一个或者格式不对,都会拉高风险分。
最稳妥的方式:从浏览器DevTools里把请求头原封不动抄下来,一个字段都别改,顺序也尽量保持一致。

误区5:追求速度,忽略稳定性

很多人总想着一秒钟爬多少页,结果跑十分钟就全封了,一天下来没多少有效数据。
采集这个事,稳永远比快重要。慢一点但能24小时不间断跑,产出量远高于猛冲半小时就全军覆没。把频率降下来,行为做正常,长期产出反而更高。

最后总结

Cloudflare的对抗从来不是单点突破,是分层匹配的游戏。

  • 一级防护:对齐TLS指纹就够了,curl_cffi两行代码解决,零成本
  • 二级防护:浏览器拿Cookie + HTTP复用,兼顾速度和通过率
  • 三级防护:再加上行为优化和会话预热,把风险分压到阈值以下

不要上来就买最贵的代理、上最重的浏览器方案,先判断防护等级,从最轻的方案开始试。能用低成本解决的问题,就没必要堆资源。

最后还是要提醒一句:本文仅讨论公开数据采集的请求优化技术,所有数据采集行为请严格遵守《网络安全法》及目标网站的robots协议,只爬取公开允许的内容,不得用于非法商业用途。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员威哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值