说句不好听的,2026年了,你要是还拿一个固定IP裸跑Scrapy,那跟大夏天穿短袖去工地搬砖没区别——不是不能干,是干不了两分钟就得歇。目标站点的风控早就不是2019年那个"你请求快了点我就限你个速"的水平了,人家现在看的是你的IP信誉分、请求指纹、行为轨迹,一套组合拳下来,你那个IP可能连首页都加载不出来。
所以今天这篇就聊一个很实际的事:Scrapy项目里怎么把代理IP池真正跑起来,不是那种"贴个代码就完事"的敷衍,而是从选型、接入、轮换策略到踩坑复盘,一步步给你讲透。我用的代理IP服务是神龙HTTP,后面会具体说为什么选它,但核心思路你换成任何一家能用的代理服务商都适用。
先搞清楚:你的爬虫到底为什么会被"盯上"
很多人一上来就问"代理IP怎么配",但没想过一个前置问题——你被限制的原因到底是什么。我见过太多人,明明只是跑了个简单的商品列表抓取,频率也就每秒两三个请求,结果IP还是被封了。为什么?
原因无非这几种:
第一,IP本身"不干净"。你用的那个免费代理或者小作坊代理,可能已经被几百个爬虫用过了,目标站点早就把这个IP段标记为"高风险"。你第一次请求过去,人家直接给你403,连个页面都不给看。这种IP你配一百个进去也没用,全是废的。
第二,行为模式太"机器"了。你每次请求的间隔是精确的1.5秒,User-Agent永远不变,请求头顺序永远一样。目标站点的WAF一看这轨迹,好家伙,这绝对是脚本。这时候你就算换了IP,如果行为模式不变,新IP也活不过三分钟。
第三,并发太高,触发限流。这个好理解,你一下子开了50个并发去请求同一个域名,人家服务器直接给你整个域名级别的限流,你换IP也没用,因为限的是你的请求模式而不是单个IP。
搞清楚原因之后,你才知道代理IP该怎么用。代理IP解决的是"身份"问题,但行为策略解决的是"习惯"问题,两个得配合着来。
Scrapy里代理池的完整接入流程
下面这段代码是我实际项目里在用的,不是那种网上抄来抄去、跑都跑不通的示例。我把它拆成几个部分来讲。
第一步:定义代理IP的获取逻辑。神龙HTTP提供API接口,你通过HTTP请求去拉取一个可用的代理IP,返回格式很简单,就是"ip:port"。这里我封装了一个工具类:
import requests
import random
import time
class ProxyManager:
def __init__(self, api_url, timeout=3):
"""
api_url: 神龙HTTP提供的代理提取接口地址
支持HTTP/HTTPS/SOCKS5协议
"""
self.api_url = api_url
self.timeout = timeout
self.proxy_cache = []
self.cache_expire = 0
def get_proxy(self):
"""从代理池获取一个可用IP,带本地缓存减少API调用"""
now = time.time()
缓存未过期且还有剩余,直接取
if now < self.cache_expire and self.proxy_cache:
return self.proxy_cache.pop()
缓存过期,重新拉取一批
try:
resp = requests.get(self.api_url, timeout=self.timeout)
if resp.status_code == 200:
lines = resp.text.strip().split('')
随机打乱,避免每次都用同一个
random.shuffle(lines)
self.proxy_cache = lines
self.cache_expire = now + 60 缓存60秒
return self.proxy_cache.pop()
except requests.RequestException:
pass
兜底:拿不到代理就用直连(或者抛异常,看你的业务需求)
return None
def get_proxies_dict(self):
"""返回Scrapy需要的proxies字典格式"""
proxy = self.get_proxy()
if proxy:
return {
'http': f'http://{proxy}',
'https': f'http://{proxy}'
}
return None第二步:在Scrapy的settings.py里配置。这里有个关键点——不要把所有代理IP一次性写死在settings里。那种"PROXIES = ['1.1.1.1:8080', '2.2.2.2:8080', ...]"的写法,在2026年已经过时了。IP是动态的,你得动态获取。
settings.py 关闭默认的下载延迟,我们用代理池来控制节奏 DOWNLOAD_DELAY = 0 CONCURRENT_REQUESTS = 10 CONCURRENT_REQUESTS_PER_DOMAIN = 5 关闭重试(我们自己控制重试逻辑) RETRY_ENABLED = False 请求超时 DOWNLOAD_TIMEOUT = 15 关闭cookie,避免被关联 COOKIES_ENABLED = False 随机User-Agent(配合代理IP使用效果更佳) ROBOTSTXT_OBEY = False
第三步:写一个中间件,在每次请求前动态注入代理。这是整个方案的核心:
middlewares.py
import random
from scrapy import signals
from scrapy.http import HtmlResponse
from myproject.utils.proxy import ProxyManager
class DynamicProxyMiddleware:
def __init__(self, api_url):
self.proxy_manager = ProxyManager(api_url)
self.fail_count = {} 记录每个IP的失败次数
@classmethod
def from_crawler(cls, crawler):
middleware = cls(
api_url=crawler.settings.get('PROXY_API_URL')
)
return middleware
def process_request(self, request, spider):
proxies = self.proxy_manager.get_proxies_dict()
if proxies:
request.meta['proxy'] = proxies['http']
把当前用的IP记下来,方便后面统计
request.meta['current_proxy'] = proxies['http'].replace('http://', '')
随机化请求头,降低指纹特征
request.headers['User-Agent'] = random.choice(spider.user_agents)
request.headers['Accept-Language'] = random.choice(
['zh-CN,zh;q=0.9', 'zh-CN,zh;q=0.9,en;q=0.8']
)
def process_response(self, request, response, spider):
proxy = request.meta.get('current_proxy')
if response.status in (403, 429, 503):
这个IP被限制了,标记一下
if proxy:
self.fail_count[proxy] = self.fail_count.get(proxy, 0) + 1
失败超过2次,下次不再用这个IP
if self.fail_count[proxy] >= 2:
self.proxy_manager.blacklist(proxy)
触发重试,换一个IP再来
request.meta['proxy_retry'] = request.meta.get('proxy_retry', 0) + 1
if request.meta['proxy_retry'] < 3:
return request Scrapy会重新走process_request
else:
spider.logger.warning(f"IP重试3次仍失败: {request.url}")
return response
def process_exception(self, request, exception, spider):
proxy = request.meta.get('current_proxy')
if proxy:
self.fail_count[proxy] = self.fail_count.get(proxy, 0) + 1
连接超时等异常,同样触发换IP重试
request.meta['proxy_retry'] = request.meta.get('proxy_retry', 0) + 1
if request.meta['proxy_retry'] < 3:
return request
return None第四步:注册中间件。
settings.py
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.DynamicProxyMiddleware': 543,
数值越小优先级越高,543是放在默认中间件之后的
}到这里,你的Scrapy项目就具备了动态获取代理IP、自动轮换、失败重试、IP黑名单的完整能力。每次请求都会从神龙HTTP的API拉取一个新鲜的IP,用完就换,不会让同一个IP暴露太久。
代理IP怎么选:短效、长效、固定,别搞混了
这是很多人最容易搞错的地方。你去看代理服务商的产品页,什么"短效动态""长效静态""固定IP",名字看着都差不多,但实际使用场景完全不一样。选错了,要么浪费钱,要么根本跑不动。
我直接上对比表,你对照自己的需求看:
| 维度 | 短效动态IP | 长效静态IP | 固定IP |
|---|---|---|---|
| 存活时间 | 3~30分钟(可定制) | 1~24小时(可定制) | 长期不变 |
| IP更换频率 | 很高,每次请求都可能不同 | 中等,几小时内稳定 | 不变 |
| 适合场景 | 大规模数据采集、需要频繁换IP | 需要一定稳定性的持续采集 | 需要"同一个身份"反复访问 |
| IP纯净度 | 高(每日更新去重) | 高(每日去重10万+) | 很高(99.83%可用率) |
| 计费方式 | 包量/包时 | 包量/包时 | 按个数,包时计费 |
| 并发能力 | 高 | 中高 | 中(IP数量有限) |
说几个我踩过的坑:
如果你做的是商品数据、资讯内容的采集,用短效动态IP就够了。神龙HTTP的短效池有3000万+资源,每日更新去重,3分钟、5分钟、10分钟、15分钟、30分钟的时效都能选。你设个5分钟的,配合Scrapy的轮换中间件,基本不会碰到同一个IP被标记的情况。延迟低,高并发也扛得住。
如果你的采集任务需要"看起来像同一个人"在持续浏览,比如你要跟踪某个店铺的价格变化,连续几小时都在看,那用长效静态IP更合适。1小时、4小时、8小时、12小时、24小时的时效可选,IP在这段时间内不变,行为轨迹是连贯的,不会让目标站点觉得"怎么这个IP每隔3分钟就变一次"。
固定IP适合什么场景呢?比如你的业务需要从一个固定的"出口"去访问某些接口,对方做了IP白名单或者IP绑定验证。神龙HTTP的固定IP是基于高性能云主机构建的,全部来自ISP正式分配,纯净度和可用率能到99.83%,稳定性拉满。但注意,固定IP是按个数卖的,如果你需要几百个固定IP,成本会比较高,按需选择。
还有一点很重要:神龙HTTP支持300+城市级精准定位,你可以指定要哪个省、哪个城市的IP。如果你的采集目标有地域属性(比如你只关心某个地区的门店信息),指定城市IP比随机IP效率高得多,不用浪费请求去试那些不相关的地区。
几个实战中容易踩的坑
坑一:代理IP的协议没对上。神龙HTTP支持HTTP、HTTPS、SOCKS5三种协议,但你的Scrapy配置里如果写的是"socks5://ip:port",而实际拿到的IP只支持HTTP,那请求直接失败。确认你的API返回格式和Scrapy里拼的协议前缀一致。一般HTTP协议最通用,除非目标站点强制要求SOCKS5,否则用HTTP就行。
坑二:请求头太"干净"了。很多人觉得代理IP换了就行,请求头随便写个默认的。但2026年的风控系统会分析你的请求头组合——Accept-Encoding、Accept、Connection、Upgrade-Insecure-Requests这些字段的顺序和值,如果跟真实浏览器不一致,IP再新也白搭。建议用Scrapy的UserAgentMiddleware配合一个真实的UA池,同时把请求头的顺序打乱一下。
坑三:忽略了对方的"软限制"。有些站点不会直接给你403,而是返回一个"验证页面"或者"人机验证"。你的代码如果只判断了status code是200就认为成功了,那实际上你抓到的全是垃圾数据。一定要在parse方法里加一层内容校验,发现是验证页面就标记这个IP失败,触发换IP重试。
坑四:并发开太大,代理IP"撑不住"。神龙HTTP的短效池虽然资源量大,但如果你一下子开200个并发,每个请求都要去API拉IP,API本身也有QPS限制。建议CONCURRENT_REQUESTS控制在10~30之间,配合本地缓存(就是我上面代码里那个60秒缓存),能大幅减少API调用次数。
坑五:没有监控IP的可用率。跑了一晚上,第二天一看,30%的请求都是失败的,但你根本不知道是哪个环节出了问题。神龙HTTP的个人中心有可视化数据统计,能看到IP使用情况、使用趋势这些关键指标。如果你的项目规模比较大,建议把Scrapy的日志也接上监控,哪些IP段失败率高、哪些时间段延迟大,一目了然,方便及时调整策略。
常见问题
Q1:我的Scrapy项目之前没用过代理IP,现在加进去会不会影响已有的逻辑?
基本不会。代理IP的注入是通过Downloader Middleware实现的,它工作在请求发出之前、响应返回之后,你的Spider、Item、Pipeline这些上层逻辑完全不用动。独特需要注意的是,如果你之前有基于IP做去重的逻辑(比如"同一个IP只请求一次"),加了代理之后这个逻辑就失效了,需要改成基于URL去重。中间件的注册顺序要注意,别把它放在RetryMiddleware前面,否则重试的时候不会换IP。
Q2:短效IP只有3分钟或5分钟,我的一个页面请求要解析十几秒,IP会不会中途过期?
不会。IP的"时效"指的是这个IP在代理池里的存活周期,不是指你单次请求的超时时间。你拿到一个IP后,用它发请求、等响应、解析数据,这个过程哪怕花了30秒,IP也是有效的。只有当你需要"下一个"IP的时候,之前那个可能已经过期了。所以你的单次请求超时时间(DOWNLOAD_TIMEOUT)设成15秒、20秒都没问题,跟IP时效是两回事。
Q3:我用神龙HTTP的API拉IP,有时候返回的IP连不上,怎么处理?
神龙HTTP的IP经过严格筛选验证,可用率标称99.9%,但网络环境复杂,偶尔出现个别IP不可达是正常的。处理策略就是我在上面代码里写的:请求失败后标记该IP,重试时自动换一个新的。你可以在本地维护一个"黑名单",同一个IP如果连续失败2~3次,短时间内不再使用。如果大面积出现连不上的情况(比如10%以上),那可能是网络环境的问题或者API调用方式有误,建议联系神龙HTTP的技术支持,他们提供7×24小时在线服务,响应很快。
Q4:我的采集量不算特别大,一天也就几千个请求,有必要用代理IP吗?
看情况。如果你只抓一两个站点,频率很低(比如每小时跑一次,每次几十个请求),用固定IP甚至直连可能都够。但如果你抓的站点有比较严格的风控(比如电商、社交平台),哪怕你频率不高,一个固定IP用个把星期也会被标记。这时候用神龙HTTP的固定IP池,按个数买一两个,包时计费,成本很低,但能确保你的IP是干净的、稳定的。如果你未来采集量会增长,一开始就把代理池的架构搭好,后面扩展起来省事得多。
最后说一句,代理IP不是"万能药",它解决的是IP层面的问题。你的请求频率、行为模式、数据解析逻辑,这些该优化的还是得优化。把代理池当成基础设施来对待,而不是"贴个配置就完事"的补丁,你的爬虫才能跑得稳、跑得久。神龙HTTP那边API文档和示例代码都挺全的,集成起来不复杂,技术团队也随叫随到,有搞不定的直接问就行,别自己闷头debug到凌晨三点。


