news 2026/10/29 2:17:43

爬虫踩坑半年:从“爬10页必封IP”到“10万条零检测”,核心就改了这5处代码

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
爬虫踩坑半年:从“爬10页必封IP”到“10万条零检测”,核心就改了这5处代码

做数据采集这五年,见过最多的新手问题就是:为什么我刚爬十几页就被封IP?

绝大多数人的第一反应都是换代理、加UA,钱花了不少,IP换了一批又一批,还是封得飞快。我刚入行的时候也踩过一模一样的坑:写个for循环拼上URL,加个User-Agent就开跑,sleep(1)还觉得自己已经很克制了,结果每次跑个十几二十页就403,换代理也撑不过半小时。

后来跟着团队老大哥一点点调,从请求头细节到请求频率,从代理调度逻辑到底层协议指纹,前前后后改了五处核心代码。再跑同样的普通资讯站点,稳定跑了十万多条数据,没出现过一次大规模封禁,连验证码都很少触发。

今天就把这五处优化完整分享出来,全是踩坑踩出来的实战经验,照着改,绝大多数普通站点都能稳下来。

一、第一处:请求头从「只加UA」到「全量对齐」——90%的封禁都栽在细节上

这是最低级也最高发的错误。很多人写爬虫,整个请求头就一个User-Agent,觉得反正告诉对方我是浏览器了,应该就不会被认出来了。

现实是:现在的风控系统判断你是不是爬虫,从来不是看某一个字段,是看整组请求头的完整性、合理性和顺序。缺字段、字段格式不对、顺序异常,全都是典型的爬虫特征。你只改一个UA,其他全是默认值,在对方面前基本等于裸奔。

错误写法(90%的新手都这么写)

importrequests headers={"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"}response=requests.get(url,headers=headers)

正确写法:完整复刻浏览器请求头

打开浏览器DevTools,把请求里的所有头字段原封不动抄下来,一个都别漏,顺序也尽量和浏览器保持一致。重点是Accept、Accept-Language、Referer这几个最容易被忽略的字段。

headers={"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8","Accept-Language":"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7","Accept-Encoding":"gzip, deflate, br","Referer":"[https://example.com/list?page=1](https://example.com/list?page=1)","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","Connection":"keep-alive","Upgrade-Insecure-Requests":"1","Sec-Fetch-Dest":"document","Sec-Fetch-Mode":"navigate","Sec-Fetch-Site":"same-origin","Sec-Fetch-User":"?1"}

踩坑提醒

  1. Referer要对应页面路径:爬第二页列表,Referer就应该是第一页;爬详情页,Referer就应该是列表页。永远写死一个首页Referer,甚至不带Referer,一眼就会被识别。
  2. 不要加多余的自定义头:很多人画蛇添足加一堆自定义字段,反而会让请求头特征更扎眼。浏览器有什么你就加什么,没有的别瞎加。
  3. Cookie要带完整的浏览轨迹:不要只带一个会话ID,正常用户的Cookie是随着浏览过程逐步增加的。访问过哪些页面、看过哪些内容,Cookie里都会有痕迹,空Cookie天生高风险。

就这一处改完,绝大多数低风控站点,封禁概率直接就能降一半。

二、第二处:休眠从「固定sleep」到「正态分布随机」——规律是最大的暴露点

很多人知道要加延迟,但写法极其敷衍:time.sleep(1),固定一秒钟一次请求。还觉得我都加了延迟了,总不会封我了吧。

恰恰相反,固定间隔本身就是最典型的程序特征。真人操作的间隔是随机波动的:看一篇文章可能停3秒,看另一篇可能8秒,偶尔还会停下来喝口水。只有程序才会精准到毫秒,每次间隔分毫不差。

错误写法

importtimeforurlinurl_list:response=requests.get(url,headers=headers)time.sleep(1)# 固定间隔,特征极其明显

正确写法:正态分布生成随机间隔

用正态分布模拟真人的操作节奏:大部分间隔在均值附近,偶尔有短停留,偶尔有长停留,符合真实用户的行为规律。

importtimeimportrandomimportnumpyasnpdefrandom_sleep(mean=1.5,std=0.6,min_val=0.5,max_val=5):"""生成符合正态分布的随机休眠时间,模拟真人操作节奏"""sleep_time=np.random.normal(mean,std)sleep_time=max(min_val,min(sleep_time,max_val))# 偶尔插入长停留,模拟用户看内容、翻页的停顿ifrandom.random()<0.1:sleep_time+=random.uniform(2,4)time.sleep(sleep_time)

踩坑提醒

  1. 不要用random.uniform均匀随机。均匀分布同样不真实,真人的操作间隔是中间多两边少,符合正态分布才最自然。
  2. 间隔不要太短。普通资讯站均值设1.5-2秒就够了,强风控站点建议拉到3-5秒。稳永远比快重要,跑一天总比跑十分钟就封强。
  3. 翻页、跳转分类的时候,适当加长停留时间。真人换页面会有阅读和思考的停顿,不要所有请求都是同一个间隔。

这一处改完,你会发现同样的请求频率,验证码出现的概率会低很多。

三、第三处:代理池从「随机轮换」到「IP行为绑定」——乱换IP死得更快

这是最多人的认知误区:觉得IP换得越勤越安全,每次请求都随机换一个IP,以为这样对方就抓不到我。

现实恰恰相反。真实普通用户不会一分钟换十个IP,更不会同一个IP一会儿看母婴用品、一会儿看数码评测、一会儿又去看汽车论坛。IP行为越跳脱,风险分越高,反而死得越快。

错误写法

importrandom proxy_list=["ip1:port","ip2:port","ip3:port"]defget_random_proxy():returnrandom.choice(proxy_list)# 每次请求都随机换IP,行为极其混乱forurlinurl_list:proxy=get_random_proxy()requests.get(url,proxies={"http":proxy,"https":proxy})

正确写法:IP绑定业务,连贯行为降低风险

同一个IP固定负责同一类目的任务,持续连贯地浏览,模拟真实用户的行为轨迹;失败的IP进冷却池,连续失败的直接拉黑,不要反复用废IP污染池子。

importtimeimportrandomclassProxyScheduler:def__init__(self):self.available={}# 可用IP -> 绑定的类目self.cool_down={}# 冷却IP -> 冷却到期时间defassign_proxy(self,category):"""优先分配已经在跑同类目的IP,没有才分配新的"""# 先找同类目已绑定的IPmatched=[ipforip,catinself.available.items()ifcat==category]ifmatched:returnrandom.choice(matched)# 分配空闲IPidle=[ipforip,catinself.available.items()ifcatisNone]ifidle:ip=random.choice(idle)self.available[ip]=categoryreturnip# 取冷却到期的IPnow=time.time()ready=[ipforip,tinself.cool_down.items()ifnow>t]ifready:ip=random.choice(ready)delself.cool_down[ip]self.available[ip]=categoryreturnipreturnNonedefreport_fail(self,ip,fail_count=1):"""IP失败,移入冷却池,连续失败拉黑"""ifipinself.available:delself.available[ip]iffail_count>=3:# 连续失败3次,永久拉黑return# 冷却5分钟self.cool_down[ip]=time.time()+300

踩坑提醒

  1. 不要频繁换IP。同一个IP稳定跑同类任务,比十分钟换八个IP安全得多。IP频繁跳动本身就是高风险特征。
  2. IP和会话、Cookie绑定使用。同一个IP对应一套Cookie和浏览器指纹,交叉混用会导致特征矛盾,反而更容易被识别。
  3. 不要迷信IP数量。一百个乱调度的废IP,不如十个行为连贯的优质IP好用。池子再大,用不对也是白搭。

很多人代理买了不少,就是用不对,天天封。把调度逻辑一改,IP的平均存活时间至少能翻三倍。

四、第四处:访问从「直连详情页」到「完整路径还原」——没有行为的访问都是送人头

这是中高级风控的核心检测点。很多人收集了一堆详情页URL,直接循环请求,觉得我带了Referer、加了延迟,总没问题了吧。

其实差远了。真实用户的访问是有完整路径的:先打开首页,再进分类列表,翻几页,看到感兴趣的再点进详情页。你直接跳深层页面,没有任何前置浏览轨迹,就算带了Referer也是假的,请求间隔、资源加载顺序、页面停留时长全对不上。

优化思路:还原真实浏览链路

不要上来就怼详情页,模拟完整的用户访问路径:

  1. 先访问一次首页,初始化Cookie和会话
  2. 进入列表第一页,停留几秒,模拟浏览
  3. 翻2-3页列表,每页随机停留
  4. 从列表页点击进入详情页,停留阅读
  5. 看完返回列表,继续翻页,重复循环

甚至可以在页面里加一些模拟滚动的操作(如果是浏览器方案),让整个访问链路和真人别无二致。

defsimulate_browse(session,category_url):"""模拟完整的浏览路径,降低风险分"""# 1. 访问首页session.get("[https://example.com/](https://example.com/)")random_sleep(2,1)# 2. 进入列表第1页session.get(category_url+"?page=1")random_sleep(3,1.5)# 3. 翻2页列表forpageinrange(2,4):session.get(f"{category_url}?page={page}")random_sleep(2.5,1)# 4. 再开始爬详情页detail_urls=get_detail_urls_from_list()forurlindetail_urls:resp=session.get(url)random_sleep(3,1.5)parse_detail(resp)

踩坑提醒

  1. 不要觉得伪造Referer就够了。Referer只是个字符串,好伪造,但请求的时间线、Cookie的变化、资源加载的顺序,这些是骗不了人的。
  2. 不要所有详情页都看一样久。有的长文停留久一点,有的短文停留短一点,符合真实阅读习惯。
  3. 中途穿插返回列表、翻下一页的动作,不要一条路走到黑,一直往下点详情。

这一处改完,中等风控的站点,基本就能做到长时间跑不触发验证了。

五、第五处:协议层从「原生requests」到「指纹对齐」——底层露馅,上层改再多也没用

如果前面四步都做了,还是频繁出验证码、被封IP,那基本就是底层协议指纹的问题了。

这也是很多人最容易忽略的一层:你HTTP头改得再像,TLS握手的特征是改不了的。Python requests、aiohttp这些库,底层的TLS加密套件顺序、扩展列表、HTTP/2伪头顺序,和真实浏览器完全不一样。对方只要看JA3/JA4指纹,一眼就能认出你是Python程序,上层改再多都白搭。

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

curl_cffi是基于curl-impersonate封装的Python库,底层直接复刻Chrome、Firefox等浏览器的TLS指纹,JA3/JA4和真实浏览器完全一致,不需要自己折腾底层协议。

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

就一行配置,底层协议指纹直接和真实Chrome对齐,比你在requests里改一百个头字段都管用。

踩坑提醒

  1. 不要浪费时间在requests上改来改去。底层指纹不对,怎么改都没用。强风控站点,协议层对齐是基础。
  2. 指纹版本要和你UA里的浏览器版本对应上。你UA写的Chrome 128,指纹就模拟Chrome 128,版本对不上反而会暴露。
  3. HTTP/2站点优先用支持HTTP/2的客户端。原生requests不支持HTTP/2,对方只要看协议版本就知道你不是浏览器。

这一步是强风控站点的入场券。过了这一关,前面的行为优化才能真正发挥作用。

实测效果与优化优先级

我当时在普通资讯站点做过完整的对照测试:

  • 原生写法(只加UA+固定sleep):平均跑12页就触发403
  • 改完请求头+随机休眠:能跑80-100页
  • 加上代理调度+路径还原:稳定跑几千页,偶发验证码
  • 再对齐协议指纹:十万条数据跑完,无大规模封禁,验证码出现率低于2%

给大家一个优化优先级,照着顺序改,性价比最高:
请求头全量对齐 > 随机频率优化 > 代理行为绑定 > 访问路径还原 > 协议指纹对齐

普通低风控站点,前三步就足够稳了;中等强度站点,加上第四步基本就通了;只有Cloudflare、强风控电商这类硬骨头,才需要第五步打底。

最后说几句真心话

做采集这么多年,我最大的感受是:稳永远比快重要。

很多人一上来就追求高并发、快速度,跑十分钟就全军覆没,然后换代理、换账号,折腾一天也没多少有效数据。反而把节奏放慢,把行为做正常,安安稳稳跑一天,产出量是猛冲的好几倍。

反爬对抗从来不是比谁手段更暴力,是比谁更像真实用户。你越接近正常人的浏览习惯,风险分就越低,就能跑得越稳。

最后还是要提醒一句:技术是中立的,采集数据一定要遵守《网络安全法》和网站的robots协议,只爬取公开允许的内容,不要碰个人隐私和受保护的数据。技术要用在合规的方向上,才能走得长远。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/29 2:17:02

4.7 video组件弹幕

video组件弹幕本次课程作业围绕微信小程序video视频组件展开&#xff0c;目标是实现视频播放与自定义弹幕发送功能。按照老师PPT案例要求完成基础页面布局&#xff0c;同时我尝试把播放素材替换成自己上传到云开发存储中的视频&#xff0c;踩了不少坑&#xff0c;在这里记录一下…

作者头像 李华
网站建设 2026/10/29 2:17:01

SCR定义及实际Heric单相机中具体对应关系表

一、SCR核心定义SCR&#xff08;短路电流比&#xff09;的具体计算方法如下&#xff1a;其中I_sc为短路电流&#xff0c;I_N为额定容量。短路容量由电网阻抗决定&#xff0c;单相机中满足如下公式&#xff1a;对电网侧串联电感L_g&#xff08;忽略电阻&#xff0c;或者X/R很大时…

作者头像 李华
网站建设 2026/10/29 2:16:41

视频片段剪辑软件怎么选:轻量裁剪还是完整编辑器?

一段四十分钟的录屏&#xff0c;你只想留下其中三十秒&#xff1b;另一段同样长的录屏&#xff0c;还要删掉等待、补上字幕、放大按钮。两者都要剪视频&#xff0c;但对软件的要求不同。 选择视频片段剪辑软件时&#xff0c;先确定要改什么&#xff1a;只截取一个时间段&#…

作者头像 李华
网站建设 2026/10/29 2:16:40

YouTube 营销怎么做?把一次产品演示变成一组 AI 短视频

用短视频做 YouTube 营销&#xff08;YouTube marketing&#xff09;&#xff0c;可以从已有的产品演示、教程或答疑中选片。每条短片回答一个客户问题&#xff0c;再把感兴趣的观众引向完整内容。 AI 可以先生成候选片段&#xff0c;编辑再按客户的问题筛选、补充必要画面。发…

作者头像 李华
网站建设 2026/10/29 2:16:30

国赛避坑|AI 生成的代码再漂亮,也比不上统计软件的真实结果

转眼间又到了一年数学建模大赛。和往年相比&#xff0c;今年的比赛可能有一个明显变化&#xff1a;越来越多参赛者开始使用AI辅助建模。 不理解题目&#xff1f;问AI。不知道使用什么模型&#xff1f;问AI。不会写Python代码&#xff1f;让AI生成。甚至论文框架、模型解释、图…

作者头像 李华
网站建设 2026/10/29 2:15:13

spring 的 @CookieValue 注解 用法

CookieValue 是 Spring MVC 中用于访问 HTTP Cookie 的核心注解&#xff0c;它提供了一种简洁高效的方式将 Cookie 值绑定到控制器方法参数上。核心属性详解属性类型默认值说明value/nameString“”Cookie 名称requiredbooleantrue是否必须存在defaultValueStringValueConstan…

作者头像 李华