Python Token 爬虫实战:如何优雅地搞定分布式采集与反爬策略 原创

2026-10-15 17:02

一、为什么「Token 爬虫」是块硬骨头普通的游客态爬虫(直接抓公开页面)已经很少见了。如今稍微有点价值的站点,几乎都走前后端分离 + Token 鉴权:请求不带Authorization: Bearer <token>直接 401;Token 有有效期(30min / 2h / 24h 不等),过期就采集中断;接口往往带签名校验或时间戳防重放,简单改个参数就被识破;同一 Token 高频请求会被标记为「机器行为」,连带封 IP。更麻烦的是,当你想用分布式把采集速度拉满时,这些麻烦会成倍放大:多个 Worker 各自刷新 Token 会互相挤掉、分散的 IP 各自为战、限流策略一旦不统一就被批量封。本文给你一套工程化、可落地的打法:Token 统一管理 + 分布式任务队列 + 反爬对抗三板斧 + 高质量代理 IP 池。代码基于httpx(async) +Redis+pydantic,复制即可跑。二、Token 机制与刷新策略:别让 Worker 各自为政分布式爬虫最致命的坑,就是每个 Worker 自己管 Token。结果就是 A 刷新了,B 还拿着旧 Token 请求 → 401 重试 → 又触发刷新 → 服务端以为被盗号,直接封号。正确姿势:集中式 Token 管理,所有 Worker 从同一个「Token 服务」取用。为什么这样做:asyncio.Lock+ 双重检查,保证「同一时刻只有一个协程去刷新」,其余协程复用结果。分布式多进程场景下,则把 Token 缓存到 Redis(见第四节),用SET NX抢刷新权。三、反爬三板斧:UA、频率、IP服务端识别爬虫,无非三招。我们挨个拆解应对:

反爬手段

识别逻辑

优雅应对

UA 指纹

固定/缺失 User-Agent、缺失常见请求头

维护 UA 池 + 补全Accept/Referer等头

请求频率

单位时间请求数超阈值、无随机间隔

令牌桶限速 + 随机抖动(jitter)

IP 信誉

同一 IP 高频访问、数据中心 IP 段

高质量代理 IP 池(关键,见第五节)

四、分布式采集架构:Redis 当任务总线核心思路:任务入队(Redis List/Stream)→ 多个 Worker 抢任务 → 结果落库。Worker 之间只通过 Redis 通信,彼此无状态,想横向扩到 100 台机器只需加进程。依赖:pip install httpx redis pydantic五、代理 IP 池实战:把亿牛云接进分布式前面三板斧里,IP 是最关键也最难自建的一环。把代理服务做成统一 getter 函数,Worker 无感知背后是哪家的池子——哪天换服务商只改一处。接入亿牛云时的三个工程要点:鉴权方式统一:亿牛云支持账密 / 子授权 Token 两种方式。分布式场景下推荐子授权 Token——每个 Worker 用不同子账号,万一某个出口 IP 被临时风控,不会连累整池。失败熔断:代理偶尔抽风是正常的。getter 里加重试 + 超时,连续 N 次失败则短暂降级(放慢采集),别让异常把 Worker 打挂。IP 复用策略:对「同一站点」适当复用出口 IP(避免每个请求都换 IP 反而触发「IP 跳跃」风控),对「不同站点」分散出口——这套策略在 getter 里用 LRU 缓存即可实现。六、限速与抖动:像人一样请求分布式不是「无脑并发」。哪怕有代理池,单 IP 的请求节奏依然要像人:令牌桶限速:全局 QPS 上限,例如每 IP 每秒 ≤ 2 次;随机抖动:sleep(uniform(0.5, 2.0)),消除「机器般精确」的间隔;指数退避:遇到 429 / 503,先等 1s,再 2s、4s…… 别硬刚。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

# rate_limiter.py

import asyncio

import random

import time

class TokenBucket:

"""简单令牌桶:保证全局速率可控。"""

def __init__(self, rate: float, capacity: float) -> None:

self._rate = rate          # 每秒补充令牌数

self._capacity = capacity  # 桶容量

self._tokens = capacity

self._last = time.monotonic()

async def acquire(self) -> None:

while True:

now = time.monotonic()

self._tokens = min(self._capacity, self._tokens + (now - self._last) * self._rate)

self._last = now

if self._tokens >= 1:

self._tokens -= 1

# 加一点随机抖动,避免请求间隔过于规整

await asyncio.sleep(random.uniform(0, 0.3))

return

await asyncio.sleep(0.1)

七、工程化最佳实践(上线前 checklist)配置外置:Redis 地址、代理鉴权、账号密码全走环境变量 /pydantic-settings,绝不硬编码进仓库。可观测:每次请求记录url / proxy / status / cost_ms到日志,封禁率异常立刻告警。幂等消费:任务队列用 Redis Stream + 消费者组,Worker 崩溃重启不丢任务、不重复采。优雅退出:SIGTERM时停止拉新任务、处理完在途请求再退出,别直接 kill 导致 Token 状态混乱。代理质量监控:定期统计各出口 IP 的成功率,低质量 IP 自动剔除——这也是选亿牛云这类带质量保障 SLA 服务商的价值所在。八、总结Token 爬虫的「优雅」,本质是把混乱收敛成几个清晰的职责边界:Token 集中管理,杜绝刷新内卷;任务用 Redis 解耦,Worker 无状态横向扩展;反爬三板斧(UA / 频率 / IP)层层设防,其中 IP 这一层交给专业代理服务限速 + 抖动 + 退避,让请求节奏像人。按这套骨架搭起来,你就能从「单机裸爬三天两头挂」,进化到「百 Worker 稳定运行、封禁率可控」的生产级采集系统。代码都已给出,缺的只是把example.com换成你的目标站、把代理 getter 换成你的服务商的提取 API。



阅读 6 / 评论 0

 相关视频教程更多课程