首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >抓不到数据先别急,看看 Scrapy 代理有没有真起作用

抓不到数据先别急,看看 Scrapy 代理有没有真起作用

原创
作者头像
小白学大数据
发布2026-07-24 16:50:27
发布2026-07-24 16:50:27
950
举报

你写的 Scrapy 爬虫跑了一晚上,库里还是空的。第一反应通常是目标站反爬升级,或者 XPath 写错。这两种都有可能,但我遇到更多的是代理压根没生效:请求用本机 IP 直连出去,被封或者拿到假页面,你却在调选择器。这篇文章讲清楚代理在 Scrapy 里是怎么真正生效的,以及接入亿牛云动态转发代理时几个会咬人的细节。Scrapy 处理代理的两条路径Scrapy 不要求你写中间件才能用代理。内置的 HttpProxyMiddleware(默认优先级 750)会在下载阶段读取 request.meta['proxy'],把它交给底层的下载处理器。也就是说,只要请求的 meta 里带 proxy 字段,代理就会生效,和你有没有自定义中间件无关。所以代理失效通常不是"没设上",而是下面几类情况:第一,自定义中间件优先级设得比 750 还大,跑在 HttpProxyMiddleware 之后,等它去读 meta 时已经晚了。自定义中间件要放在 750 之前,常见做法是 100 到 400 之间。第二,认证没带对。亿牛云动态转发走 HTTP 基础认证,代理地址要写成 http://用户名:密码@网关:端口。如果只填了网关地址、proxy 里没有账号密码,代理服务器回 407,请求要么被 RetryMiddleware(优先级 500)反复重试,要么降级成直连,现象就是"时灵时不灵"。第三,meta 被中途清掉。RedirectMiddleware(优先级 600)默认会保留 meta,但如果你在 process_response 或者 process_exception 里重建了 Request 却没把 proxy 带过去,重定向后的请求就丢了代理。先确认出口 IP 到底换没换别靠猜,让响应自己说话。最直接的办法是请求一个回显客户端 IP 的地址,比如 httpbin.org/ip,或者用亿牛云后台的出口 IP 查询接口。把返回的 IP 和本机公网 IP 比一下:

代码语言:txt
复制
import scrapy

class CheckIpSpider(scrapy.Spider):
    name = 'check_ip'
    start_urls = ['https://httpbin.org/ip']

    def parse(self, response):
        self.logger.info('出口 IP: %s', response.text)

另一个更隐蔽的核对点是 response.ip。Scrapy 的下载处理器会把响应来源 IP 写进 response.ip。走 HTTP 代理时,这个字段通常就是代理的 IP;走 HTTPS 的 CONNECT 隧道时,它记录的是隧道对端的 IP。所以 response.ip 适合做快速校验,要当作权威证据还是得靠专门的 IP 回显接口。如果回显的 IP 和本机一样,代理就没生效,后面再怎么调反爬都是白费。接亿牛云动态转发的最小可用写法亿牛云的动态转发有两种用法。一是动态转发网关 t.16yun.cn:31111,每次请求经网关出去时由亿牛云自动换出口 IP,你不需要自己维护 IP 池;二是通过他们的提取接口拉一批 IP 回来,由你自己做轮换。这里先讲网关模式。

代码语言:txt
复制
class YiniuProxyMiddleware:
    def __init__(self, proxy):
        self.proxy = proxy

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings.get('YINIU_PROXY_URL'))

    def process_request(self, request, spider):
        request.meta['proxy'] = self.proxy

settings.py 里:

代码语言:txt
复制
YINIU_PROXY_URL = 'http://your_user:your_pass@t.16yun.cn:31111'

DOWNLOADER_MIDDLEWARES = {
    'myproject.middlewares.YiniuProxyMiddleware': 100,
    'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 750,
}

your_user、your_pass 换成亿牛云后台的账号,t.16yun.cn:31111 是动态转发网关,端口按套餐可能不同,以后台为准。这里显式把 HttpProxyMiddleware 写进配置是为了让优先级关系一目了然:你的中间件在 100 设好 meta,750 的内置中间件再去消费它。账号密码编在 URL 的 userinfo 里时,HttpProxyMiddleware 会自动取出凭据生成 Proxy-Authorization 头,HTTPS 的 CONNECT 隧道也复用同一套认证,一般不用手动加头。如果你用的是亿牛云的提取接口(自己管 IP 池),就把 process_request 改成从一个队列里取下一个 IP,并在请求失败时把该 IP 标记失效,避免反复打到同一个坏节点。把出口 IP 落进日志,事后能查设了代理不代表链路一直健康。我习惯在中间件里把实际用的代理记下来,数据异常时翻日志能直接定位:

代码语言:txt
复制
def process_request(self, request, spider):
 request.meta['proxy'] = self.proxy
 spider.logger.debug('egress via %s -> %s', self.proxy, request.url)

更稳的是配一个定时探测 spider,每隔一段时间请求一次 IP 回显接口,把结果写进日志。连续多次出口 IP 没变、或者和本机 IP 相同,基本就能判定代理链路断了或者被限了。在异常里做代理切换只设代理还不够。生产环境里代理会失效、429 和 407 会冒出来。可以在中间件里接 process_exception 和 process_response 做基础容错:

代码语言:txt
复制
from scrapy.exceptions import IgnoreRequest
class YiniuProxyMiddleware:
 # __init__ / from_crawler / process_request 同上
 def process_response(self, request, response, spider):
 if response.status in (407, 429):
 spider.logger.warning('代理异常状态码 %s,丢弃该请求', response.status)
 raise IgnoreRequest('proxy returned %s' % response.status)
 return response
 def process_exception(self, request, exception, spider):
 spider.logger.warning('代理请求抛异常: %s', exception)
 raise IgnoreRequest('proxy error')

注意这样只是丢弃,不会自动换新 IP。要真正轮换,得把代理来源做成可变的(API 拉取的池子),在 process_request 里每次取不同的出口,再配合上面的异常丢弃,坏 IP 自然就被绕开了。调试顺序先用 curl 直连,确认账号和网关本身没问题,这一步能排除"不是代码锅,是账号废了":

代码语言:txt
复制
curl -x http://your_user:your_pass@t.16yun.cn:31111 https://httpbin.org/ip

再把 Scrapy 日志开到 DEBUG,重点看三件事:request.meta 里有没有 proxy、响应状态码是不是 407/429、以及 response.ip 是不是代理的 IP。并发也要留意。CONCURRENT_REQUESTS_PER_PROXY 控制同一个代理 IP 上的并发上限。动态转发网关对所有请求是同一个地址,所以这个值会直接卡住你的整体吞吐,需要按网关承载能力调大。HTTPS 单独看。走代理发 HTTPS 时,Scrapy 先发 CONNECT 给代理建隧道,认证失败在这一步的表现和 HTTP 不一样,容易被误判成目标站的问题。HTTPS 抓不到,先看 CONNECT 那一步的返回码。写在最后抓不到数据时,先花两分钟确认代理是不是真在干活,通常比埋头改解析省事。把出口 IP 打出来、把 response.ip 和日志对上,问题在代理还是在本机代码,一眼分开。需要的话,我可以把这套验证加容错直接做成一个可挂的中间件,配个探测 spider,跑起来就能实时看到出口 IP 和异常切换。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档