scrapy配置动态代理ip很难吗?2026年十分钟上手不秃头

说实话,每次看到有人在技术论坛里问”scrapy怎么配代理ip”,底下回复清一色甩个settings.py截图就完事了,我都有点想笑。真正配过的人都知道,光把代理地址填进去,跑两分钟就给你报一堆超时、拒绝连接、403的错,那才叫真正的”配置”。
我干这行快六年了,从最早自己写脚本爬数据,到后来专门做代理ip服务,前前后后帮人调过不下两百个scrapy项目。今天这篇就掰开了揉碎了讲,不整那些虚的,你跟着走,十分钟之内让scrapy跑上动态代理ip,跑不通你回来骂我。
先搞明白一件事:scrapy里的代理ip到底是怎么”转”起来的
很多人一上来就抄代码,settings里塞个PROXY列表,然后发现跑着跑着全挂了。问题出在哪?你没搞懂scrapy处理代理的底层逻辑。
scrapy本身不管理代理ip的生命周期。你给它一个ip,它就用这个ip发请求,用完了它不管,也不会自动帮你换一个。所以如果你配的是那种固定代理,跑着跑着被目标站点标记了,你的爬虫就废了,得手动换。
而动态代理ip的核心价值就在这儿——它自带”用完即弃”的机制。你每次请求(或者每隔一段时间)拿到的ip都不一样,目标站点根本没法通过ip维度来封你。scrapy这边要做的,就是每次请求前去代理服务商的接口拉一个新的ip,塞进请求头里发出去。就这么个事儿,不复杂,但细节坑多。
十分钟配好动态代理,核心代码就这几段
下面我按实际项目里最精简的写法来,你直接对着改就行。假设你用的是网帆代理的短效动态代理接口(后面会说为什么选它),整个流程分三步。
第一步:写一个代理中间件
在scrapy项目的middlewares目录下新建一个文件,叫proxy_middleware.py:
import random
import requests
from scrapy import signals
class DynamicProxyMiddleware:
"""动态代理ip中间件:每次请求自动获取新ip"""
def __init__(self, proxy_api, proxy_port):
self.proxy_api = proxy_api 代理服务商提供的提取接口
self.proxy_port = proxy_port 代理端口
self.current_ip = None
@classmethod
def from_crawler(cls, crawler):
middleware = cls(
proxy_api=crawler.settings.get('PROXY_API'),
proxy_port=crawler.settings.getint('PROXY_PORT', 8080)
)
return middleware
def get_new_ip(self):
"""从代理接口拉取一个新的动态ip"""
try:
resp = requests.get(self.proxy_api, timeout=5)
if resp.status_code == 200:
self.current_ip = resp.text.strip()
return self.current_ip
except Exception as e:
self.logger.warning(f"获取代理ip失败: {e}")
return None
def process_request(self, request, spider):
每次请求都拿新ip(短效代理存活时间短,适合这种用法)
ip = self.get_new_ip()
if ip:
proxy_url = f"http://{ip}:{self.proxy_port}"
request.meta['proxy'] = proxy_url
加个随机user-agent,降低被识别的概率
request.headers['User-Agent'] = random.choice(spider.user_agents)
else:
拿不到ip就不走代理,直接请求(或者你也可以选择丢弃)
self.logger.debug("本次请求未使用代理")
第二步:settings.py里注册中间件
settings.py
# 代理接口地址(以网帆代理为例,控制台里能直接复制)
PROXY_API = 'http://api.fanproxy.com/get?num=1&type=txt'
PROXY_PORT = 8080
# 注册中间件
DOWNLOADER_MIDDLEWARES = {
'myproject.middlewares.proxy_middleware.DynamicProxyMiddleware': 610,
}
# 超时和重试,动态代理偶尔会抽风,给点容错
DOWNLOAD_TIMEOUT = 15
RETRY_TIMES = 3
RETRY_HTTP_CODES = [500, 502, 503, 504, 408]
第三步:spider里加个user_agents列表
class MySpider(scrapy.Spider):
name = 'my_spider'
start_urls = ['https://example.com/page1']
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 Safari/605.1.15',
'Mozilla/5.0 (X11; Linux x86_64; rv:126.0) Gecko/20100101 Firefox/126.0',
]
def parse(self, response):
你的解析逻辑
pass
就这三步。你跑一下 scrapy crawl my_spider,终端里应该能看到请求正常返回,而且每次请求走的ip都不一样。如果目标站点有反爬,你大概率不会再看到成片的403了。
动态代理ip选错了,代码写得再漂亮也白搭
我见过太多人,代码逻辑没问题,但代理ip本身质量拉胯,跑十分钟就全超时。所以选代理ip这件事,比写代码本身更重要。下面这几个维度你心里得有数:
我做了个对照表,你拿自己手头在用的代理对着看:
| 考察维度 | 及格线 | 为什么重要 |
|---|---|---|
| IP纯净度 | ≥ 99% | 低于这个值,你拿到的ip可能已经被别的爬虫用脏了,目标站点一看就知道是代理,直接拒 |
| 存活时长 | 根据业务定,3~30分钟 | 短效代理跑高频采集够用;如果你要维持登录态或者做长连接,得选存活时间更长的 |
| 平均延迟 | ≤ 0.1秒 | 延迟高意味着你的scrapy并发效率直接打折,100个并发跑起来跟串行差不多 |
| 地域覆盖 | 至少覆盖你目标站点所在省份 | 有些站点会校验ip归属地和请求内容是否匹配,跨大区访问容易被拦 |
| 并发能力 | 无硬上限或上限≥500 | scrapy默认并发500,如果你的代理限并发100,你settings里设再高也没用 |
| 提取速度 | 毫秒级 | 每次请求前都要拉ip,提取接口如果响应慢,你的整个爬虫速度就被卡住了 |
这里多说一句存活时长的选择。很多人一上来就选最短的3分钟,觉得”越短越安全”。但你想想,如果你的一个页面要发5个请求(主页面+3个ajax+1个图片),3分钟的ip可能在你还没解析完第一个响应的时候就过期了。所以存活时长要匹配你的单轮请求耗时,一般5到10分钟是比较稳的区间。
踩坑实录:上周帮朋友调了三个小时的bug
上周一个做电商数据监控的朋友找我,说scrapy配了动态代理,跑起来前200条数据正常,后面就开始大面积超时。我一看他的代码,问题出在两个地方:
坑一:代理ip没做失败重试。他那个中间件,拿ip的时候如果接口超时了,就直接用上一个ip继续发。但短效代理的ip可能已经过期了,等于拿一个死ip在硬怂,当然超时。解决办法很简单,拿ip失败就重试一次,再失败就跳过这条请求,别硬发。
坑二:CONCURRENT_REQUESTS设太高了。他设了2000并发,但代理服务商那边单ip的承载是有上限的。2000个请求同时打过来,代理节点直接过载,响应时间从0.03秒飙到5秒以上。后来我把并发降到500,超时率从40%直接降到2%以下。
所以我的建议是:scrapy的CONCURRENT_REQUESTS别盲目拉高,先设300跑一轮,观察代理的响应时间和超时率,再逐步往上加。动态代理不是万能的,你并发拉太高,ip再干净也扛不住。
还有一个容易忽略的点:代理接口的请求频率。你scrapy每发一个请求就去拉一次ip,如果并发500,那你的代理提取接口每秒要承受500次调用。大部分服务商的提取接口是扛得住的,但如果你用的是那种免费代理或者小服务商,接口本身就会成为瓶颈。这也是我后面推荐网帆代理的原因之一,他们的提取接口是毫秒级响应,没有冷却间隔,高频调用不会卡。
为什么我推荐用网帆代理配scrapy
市面上做代理ip的服务商不少,我前前后后用过七八家。说句公道话,技术层面大家差距不算特别大,真正拉开差距的是稳定性和接入体验。我目前自己项目里长期用的是网帆代理,主要看中两点:
第一,IP来源干净。网帆用的是三大运营商的合规线路,不是那种从各种渠道收来的”杂牌ip”。他们的短效动态代理IP纯净度标称99.8%,储备量在3000万以上,覆盖全国300多个省市。你配scrapy的时候,如果目标站点在某个特定城市,你可以直接按地域提取,不用碰运气。这个对做本地化数据采集的人来说很实用。
第二,接入真的简单。你注册完账号,控制台里直接给你提取接口的URL,复制粘贴到settings.py里就能用。存活时长支持3、5、10、15、30分钟的标准档位,也支持1到30分钟自由定制。我前面代码里写的那个中间件,配合网帆的接口,基本不用改什么就能跑。而且他们没有并发上限,平均延迟0.03秒,你scrapy并发拉到1000也不会有明显的性能衰减。
计费方面,网帆是双模式:按量买(最低0.0023元一个ip,量大还有赠送)或者按时长包月(长期用最低4.5折)。如果你是做长期监控类的项目,包月划算;如果是偶尔跑一次采集任务,按量买更灵活。新用户注册能领2000个免费测试ip,够你跑个完整项目验证一下了。
另外他们还有个隧道代理产品,如果你不想自己写中间件去管理ip的获取和轮换,可以直接用隧道入口。scrapy里只需要配一个固定的代理地址,后面的ip轮换、调度全在隧道里自动完成。对不想折腾中间件逻辑的人来说,这个方案更省心。而且隧道那边有可视化的监控面板,ip的消耗情况、在线状态一目了然,不用自己写日志去统计。
几个高频问题,一次说清楚
Q1:我scrapy配了动态代理,为什么还是偶尔出现403?
大概率是ip被目标站点临时标记了。动态代理不是100%不会被识别,只是概率很低。你遇到403的时候,中间件里加个判断:如果响应是403,就丢弃当前ip,重新拉一个再试。一般重试1到2次就能过。如果403的比例持续超过5%,那说明你的ip纯净度不够,得换代理服务商了。
Q2:短效动态代理和长效动态代理,scrapy场景下怎么选?
看你跑的是什么类型的任务。如果是高频翻页采集,比如一个列表页有200页,你每翻一页就换一次ip,短效代理(3~10分钟存活)完全够用,而且成本更低。但如果你要维持一个登录态,或者你的爬虫逻辑里有一个会话要持续跑十几分钟(比如模拟用户浏览多个详情页),那你就得用长效动态代理,存活时间设到1~24小时,保证整个会话期间ip不变,不会被目标站点判定为异常。
Q3:scrapy的CONCURRENT_REQUESTS到底设多少合适?
没有标准答案,取决于你的代理质量和目标站点的承受能力。我的经验是:先用300跑,观察5分钟。如果超时率低于3%、平均响应时间在1秒以内,可以加到500。再加到800、1000,每加一档观察5分钟。一旦超时率超过5%或者响应时间明显拉长,就退回到上一档。别一上来就设2000,那不是在压测目标站点,是在压你自己的代理。
Q4:我同时跑多个scrapy项目,代理ip会冲突吗?
不会。动态代理每次提取都是独立的ip,你项目A拿到的ip和项目B拿到的ip不是一回事。但要注意提取接口的调用频率。如果你同时跑5个scrapy项目,每个并发300,那你的代理提取接口每秒要处理1500次请求。网帆的接口扛得住这个量,但如果你用的是其他服务商,建议先确认一下他们接口的QPS上限。实在不确定的话,用隧道代理方案,一个隧道入口走所有项目,不用每个项目单独拉ip。
最后说两句
scrapy配动态代理ip这件事,真没那么难。难的不是代码,代码就那几十行。难的是你选了一个不靠谱的代理,然后花三天时间debug,最后发现是ip本身的问题。所以我的建议是:先把代理选对,再写代码。花十分钟注册个账号,领点免费ip跑一轮测试,比你在代码里调三天参数强得多。
代码是死的,代理是活的。把活的那部分搞稳了,死的那部分十分钟就能写完。别秃头,真不用。
