做数据采集的朋友应该都经历过这种场景:代码逻辑没问题,请求头也伪装了,单次测试都能通过,但一上量就开始出现各种网络层的报错——ConnectionTimeout、ReadTimeout、ConnectionResetError,或者更隐蔽的:请求返回 200,但响应体是空的。
这些问题大多不是目标网站的反爬策略,而是网络层配置没做到位。本文从超时、重试、连接池三个角度,整理一套可落地的调优方案。
很多人在 requests.get() 里只写一个 timeout=10,以为就设了 10 秒超时。实际上,requests 的 timeout 参数如果传单个数值,会同时作用于连接超时和读取超时。但这两种超时的含义完全不同:
正确写法是传一个元组:
import requests
# 连接超时 5 秒,读取超时 60 秒
response = requests.get(
url,
timeout=(5, 60)
)如果是流式响应,读取超时要设得更长,建议 120 秒以上。
网络抖动是常态,尤其是跨区域请求。没有重试机制的爬虫,稳定性完全靠运气。
requests 本身不提供自动重试,需要借助 urllib3 的 Retry 类:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
retry = Retry(
total=3, # 最多重试 3 次
backoff_factor=1, # 重试间隔:1s, 2s, 4s
status_forcelist=[500, 502, 503, 504], # 这些状态码触发重试
allowed_methods=["GET", "POST"]
)
adapter = HTTPAdapter(max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)注意 backoff_factor 的指数退避效果:第一次重试等 1 秒,第二次等 2 秒,第三次等 4 秒。这样能避免短时间内密集重试把目标网站打挂。
requests 底层依赖 urllib3 的连接池,默认每个主机最多保持 10 个连接。如果你开了 50 个线程同时请求同一个域名,多出来的线程就在等空闲连接,等不到就卡死。
解决方案是根据并发数调整连接池大小:
adapter = HTTPAdapter(
pool_connections=20, # 连接池数量
pool_maxsize=50, # 每个池最大连接数
max_retries=3
)
session.mount("http://", adapter)
session.mount("https://", adapter)pool_maxsize 建议不小于并发线程数。如果并发是 50,就设 50 或更大。
以上配置都调好后,如果仍然出现大面积超时或连接重置,问题可能出在网络出口上。
数据中心 IP(云服务器 IP)在访问部分海外站点时,容易被目标服务器的防护策略限速或拒绝。表现为:连接能建立,但读取阶段频繁超时,或者大文件下载中途被重置。这是因为目标站点对机房 IP 段有更严格的速率限制。
此时可以尝试更换网络出口为住宅 IP。住宅 IP 的 ASN 归属于当地运营商,在目标站点的访问策略中优先级更高,连接稳定性通常有明显改善。
以下是配置住宅 IP 出口的代码示例:
# ============================================
# 使用住宅IP出口(示例:1024Proxy)
# 官网:https://1024proxy.com/?kwd=hyj-txy
# ============================================
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
session = requests.Session()
# 配置住宅IP出口
proxy_url = "http://用户名:密码@gateway.example.com:端口"
session.proxies.update({
"http": proxy_url,
"https": proxy_url
})
# 同时配置重试和连接池
retry = Retry(total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504])
adapter = HTTPAdapter(pool_connections=20, pool_maxsize=50, max_retries=retry)
session.mount("http://", adapter)
session.mount("https://", adapter)
response = session.get(url, timeout=(5, 60))注意代码中的 gateway.example.com 是占位符,实际使用时替换为服务商提供的网关地址即可。
随机延迟比固定延迟更安全:time.sleep(1) 不如 time.sleep(random.uniform(0.5, 2.0))。固定间隔的请求模式容易被识别为自动化流量。
异常捕获要区分类型:ConnectionError、Timeout、HTTPError 要分开处理。Timeout 可以重试,HTTPError 要看状态码决定是否重试。
先小规模测试:任何网络配置调整后,先用 100 个请求验证稳定性,再上量。不要一上来就跑几千条,出了问题不好定位。
爬虫的稳定性,一半靠代码逻辑,一半靠网络层配置。超时设置、重试机制、连接池大小,每一项都直接影响任务的成功率。把这些细节调好,比盲目换 IP 更有效。
如果目标是海外站点,且本地出口是云服务器 IP,可以尝试将出口切换为住宅 IP 观察效果。具体的服务商选择需要根据实际业务场景测试,建议先用少量请求验证稳定性。
本文仅做技术交流,请遵守相关平台规则与法律法规。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。