先别急着装软件,把"池子要干什么"写成一张纸
见过太多人上来就买服务器、装 Redis、写个 Python 脚本开始拉 IP,跑了两天发现池子里的 IP 十有八九连不上,采集任务还是卡在那里。问题不在代码,在于一开始没算过账。
动服务器之前,先在纸上回答三个问题:一天大概要发出多少次请求;目标站点对同一个 IP 的容忍度是多少(比如连续 20 次就弹验证);允许的失败率上限是多少。举个具体的数:一天 30 万次请求,单个 IP 平均只能用 15 次,那理论上至少需要 2 万个可用 IP 的吞吐量,再按 50% 的冗余算,池子规模得奔着 3 万去。
这个数字直接决定了后面所有的设计——用不用集群、健康检查多频繁、IP 资源是自己采还是外部供给。先算容量,再谈架构,这一步跳过去,后面全是返工。
部署结构:入口、调度、资源,三层别搅在一起
一个能长期跑下去的代理池,结构上其实很朴素,就三层。真实项目里出问题,八成是这三层混在一台机器、一个脚本里,改一行代码全线崩。
入口层负责对外提供一个取 IP 的口子。你的采集程序不要直接连数据库,而是调一个 HTTP 接口,比如 GET /get?count=5&area=shanghai,拿回几个 JSON 格式的 IP。这层用 Nginx 加一个轻量服务就够了,关键是要做限流——不然某个写错的循环能把池子瞬间抽干。
调度层是大脑。它记着每个 IP 的成功次数、失败次数、平均响应时间、上次使用时间,每次有人来取 IP 就按分数排个序,把状态最好的推到前面。这里用 Redis 的有序集合最顺手,一行 ZADD 就打好了分。
资源层就是 IP 本身从哪来。短效动态 IP 适合高频、允许不断更换的场景;长效静态 IP 适合需要会话保持的登录态操作;固定 IP 适合白名单对接。三类常常是混着用的,池子里给每个 IP 打上来源标签就行。
| 层 | 放什么 | 常见故障 | 应急手段 |
|---|---|---|---|
| 入口层 | 取 IP 接口、限流 | 接口被打爆,响应超时 | 按调用方配额限速,加本地缓存 |
| 调度层 | 打分、排序、剔除 | 分数不刷新,坏 IP 一直排前面 | 定时重算,设分数下限自动踢出 |
| 资源层 | 动态 IP、静态 IP、固定 IP | 供给断档,池子见底 | 预留备用供给渠道,提前扩容 |
机器怎么分?小项目一台 4 核 8G 的机器把三层塞一起也能跑,但只要日请求量过了十万,建议调度层单独一台,Redis 单独一台小配置,入口层随便挂在一台上都行。别小看这个拆法,调度层重启的时候入口层还能靠缓存顶几分钟,采集任务不会直接断。
调度服务怎么写:一段能跑起来的骨架
不用搞得太复杂,一个健康检查 + 一个取 IP 接口,差不多就够用了。下面这段是最小可跑版本,思路比代码重要。
import time, redis, requests
r = redis.Redis(host="127.0.0.1", port=6379, db=0)
POOL_KEY = "proxy:pool" # 成员 = ip:port 分值 = 健康分
def add_ip(ip, score=60):
r.zadd(POOL_KEY, {ip: score})
def pick(n=5):
# 分数从高到低取,只取前 100 个候选,避免一次拉全量
cands = r.zrevrange(POOL_KEY, 0, 99)
return cands[:n]
def report(ip, ok, cost):
# 成功加分,失败扣分,带响应时间惩罚
delta = 2 if ok else -15
if ok and cost < 1.0:
delta += 1
new_score = r.zincrby(POOL_KEY, delta, ip)
if new_score < 20: # 低于阈值直接移出池子
r.zrem(POOL_KEY, ip)
def health_check(ip, timeout=3):
t0 = time.time()
try:
resp = requests.get("http://httpbin.org/ip",
proxies={"http": f"http://{ip}", "https": f"http://{ip}"},
timeout=timeout)
ok = resp.status_code == 200
except Exception:
ok = False
report(ip, ok, time.time() - t0)
几个细节值得多说两句。健康检查的目标地址要选轻量的、稳定的,别拿你要采集的业务站当靶子,容易把人家惹毛。检查频率别设太密,一个 IP 五分钟摸一次足够,一分钟摸一次纯属浪费带宽。还有那个分数下限,一定要有,不然失效 IP 会一直躺在池子里占位置,把候选列表挤满。
另外提前想好日志格式。每条记录至少要有 IP、时间、成功与否、耗时,后面排查问题全靠它。没有日志的池子,出了问题只能靠猜。
日常维护:每天花十分钟看四个数
池子搭起来只是开始,真正耗精力的是维护。我的习惯是每天早上打开监控面板扫一眼四个数字,异常了再往下钻。
可用IP数量——关注趋势而不是绝对值。如果昨天 8000 今天掉到 3000,说明供给端出问题了,别等见底才补。平均响应时间——这条曲线抬头通常比可用率下降更早,是个预警信号。请求成功率——按目标站点分开看,某一站掉得厉害,可能是那站更新了风控,跟你的 IP 没关系。新增与剔除的数量——进和出要平衡,只出不进就是慢慢等死。
维护动作也有优先级。失效 IP 剔除必须是自动的,靠人工看表格删太不现实;补充新 IP 建议半自动,脚本拉回来先跑一轮体检再入池;日志里的异常要每周翻一次,特别是连着报同一种错的 IP 段,往往整段都不能用。
还有两个容易忽略的点。一是定期给 IP 打标签更新,一个 IP 这个月在上海,下个月可能就跑到广州了,地区标签不刷新,按地区取 IP 的请求就会拿到一堆错位的。二是给自己留一个手动操作的口子,命令行敲一条就能把某个 IP 拉黑或者提权,排障的时候非常省事。
IP 从哪来:自采 vs 正规授权,算笔时间账
绕不开的一个问题是:池子里的 IP 谁提供。自己找免费源去采当然可以,但你要付出的隐性成本是——源站随时改版、IP 存活几分钟就死、质量参差不齐需要大量清洗逻辑。有人算过,维护一套自采脚本的人力,折算下来比直接买服务贵得多。
实际项目里更稳的做法是走正规授权的资源。像神龙HTTP这种,国内三大运营商正规授权,有 3000 万+ 的代理 IP 储备,可用率标称 99.9%,支持 HTTP/HTTPS/SOCKS5 三种协议,短效动态、长效静态、固定 IP 三类产品线都有,正好对应前面说的三种使用场景。对做公开数据采集和 AI 大模型训练语料整理的团队来说,比较实用的一点是它能做到 300+ 城市的精准定位——你要某个城市的本地 IP,直接在请求参数里指定就行,不用自己维护一张地域对照表去碰运气。
接入方式也简单,API 兼容主流爬虫语言,文档里有现成的示例代码,把上面那段 add_ip() 换成调它的提取接口,池子的后半截逻辑基本不用改。个人中心还有可视化的用量统计和实时监控,能看出哪些时段消耗高、哪些 IP 段异常,这个对调容量挺有帮助。
比较省心的组合是:自采只作为补充和兜底,主供给走授权接口,池子里按来源打标签,授权 IP 的分数权重给高一点。这样既控制了成本,又不会因为某个源挂了整条链路停摆。
常见问题
Q:一台 2 核 4G 的服务器能撑起一个代理池吗?
看规模。日请求在五万以内、池子几千个 IP,一台机器把入口、调度、Redis 全塞进去完全够用,注意把 Redis 的最大内存设个上限,别让它把机器吃满。往上涨就先拆 Redis,再拆调度层,一步步来,不必一上来就上集群。
Q:健康检查多久跑一次比较合适?检查太频繁会不会有问题?
五分钟一轮是大多数场景的甜点区。短效动态 IP 存活时间本身就短,可以缩到两三分钟;长效静态 IP 十分钟一次也行。检查太频繁除了浪费带宽,还会让目标检测站把你当成异常流量,检查地址尽量选轻量、公开、稳定的接口。
Q:池子里 IP 数量明明不少,采集成功率还是上不去,问题出在哪?
先看成功率是按站点分开统计的还是混在一起的,混着看会掩盖问题。然后看你的取 IP 策略——是不是随机取的,把一堆低分 IP 也抽出来了。改成按分数取前 N 个,成功率通常立刻能上一个台阶。最后确认目标站点是不是对请求频率有硬限制,那就得在业务侧加间隔,而不是继续往池子里塞 IP。
Q:调度服务重启之后,池子里的数据会不会全丢?
会,如果只用内存存的话。所以 IP 列表和分数必须落到 Redis 里,并且给 Redis 配持久化。更稳一点的做法是每天定时把整个池子快照导出成一个文件,真出事了十分钟就能恢复,比从零开始重新灌 IP 快得多。





