技巧 #14:限速做得好,代理消耗少一半
不少爬虫工程师把封禁归咎于代理质量,真正的原因往往是请求频率失控。再优质的住宅代理也扛不住每秒几十次的并发轰炸。先做限速,再谈更换 IP,才是提高成功率、控制代理成本的正确顺序。
令牌桶:平滑限速又允许小幅突发
令牌桶是限速的经典算法:系统以固定速率往桶里放令牌,每个请求消耗一枚,桶空则等待。相比死板的固定间隔,它允许短暂的突发流量,更接近真实用户的节奏。一个线程安全的简易实现:
import time, threading
class TokenBucket:
def __init__(self, rate, capacity):
self.rate = rate # 每秒生成的令牌数
self.capacity = capacity # 桶的最大容量
self.tokens = capacity
self.last = time.monotonic()
self.lock = threading.Lock()
def acquire(self):
with self.lock:
now = time.monotonic()
self.tokens = min(self.capacity,
self.tokens + (now - self.last) * self.rate)
self.last = now
if self.tokens < 1:
time.sleep((1 - self.tokens) / self.rate)
self.tokens -= 1
随机延时:拒绝机械节奏
固定间隔的请求在服务端日志里呈现完美的等差数列,是最典型的机器特征。应在基础间隔上叠加随机抖动,例如取 2 到 6 秒之间的均匀分布;翻页或提交表单前适当加大停顿,模拟人类阅读和输入所需的时间。
模拟人类行为的细节清单
- 访问路径: 先请求首页或列表页,再进入详情页,不要直接深链轰炸内页。
- 请求头: 携带完整的 Accept、Referer 与真实浏览器 User-Agent,同一会话内保持 Cookie 一致。
- 活跃时段: 把任务安排在目标用户群的活跃时间,凌晨四点的匀速请求同样可疑。
分布式环境下的全局限速
多机部署时,每台机器各自限速并不等于总体受控。应把令牌桶集中放在 Redis 等共享存储中,用原子操作发放令牌,或者按目标站点维度统一调度,确保对单一站点的总请求量始终落在安全线以内。
Article ID: 74
(本文仅供技术交流,请遵守法律法规)