Socks5Proxy

Scrapy代理设置,使用meta proxy为每次请求使用不同的IP

本指南说明在Scrapy中通过meta proxy配置Socks5Proxy的HTTP代理,使用API获取的代理列表实现请求级轮换的下载中间件,以及针对目标站点的速率调优策略。

Scrapy如何处理代理?

Scrapy默认启用了HttpProxyMiddleware。在请求的meta字典中添加proxy键和地址,请求将通过该代理发送,地址中的用户名和密码将由中间件转换为Proxy-Authorization头。HTTPS地址通过CONNECT隧道传输。

Scrapy的默认下载处理器支持HTTP代理。如果必须使用SOCKS5,则需要单独的处理器,因此建议在Scrapy中使用同一IP的HTTP端口。目标网站看到的IP是相同的。

示例使用IP 198.51.100.23,HTTP端口8000,用户名spx_user,密码spx_pass。

pip install scrapy
scrapy startproject shop
cd shop

如何为单个请求配置代理?

在start_requests创建Request时,通过meta添加proxy参数即可。回调中请求后续页面时也需采用相同方式。如需保持会话连续性,可从先前响应的meta中继承proxy值以维持相同IP。

import scrapy

PROXY = "http://spx_user:spx_pass@198.51.100.23:8000"

class IpCheckSpider(scrapy.Spider):
    name = "ip_check"

    def start_requests(self):
        yield scrapy.Request("https://api.ipify.org?format=json", meta={"proxy": PROXY})

    def parse(self, response):
        self.logger.info("seen as %s", response.json()["ip"])

如何实现每次请求轮换IP?

创建一个下载器中间件,将所有请求放入下一个代理中,这样无需更改爬虫代码即可实现轮换。以下中间件在启动时通过API获取列表,如果meta中已有proxy,则保持不变,以便同时使用会话固定和轮换。

建议将API密钥存放在settings.py或环境变量中。注册顺序需早于默认HttpProxyMiddleware(750),这样中间件注入的地址才能被基础中间件处理。

import itertools
import requests

class RotatingProxyMiddleware:
    def __init__(self, key):
        text = requests.get(
            "https://socks5proxy.io/api/v1/proxies/download",
            params={"format": "user:pass@ip:port", "protocol": "http"},
            headers={"Authorization": "Bearer " + key},
            timeout=30,
        ).text
        self.pool = itertools.cycle(["http://" + l.strip() for l in text.splitlines() if l.strip()])

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings.get("SPX_API_KEY"))

    def process_request(self, request, spider):
        if "proxy" not in request.meta:  # keep a proxy that was set on purpose (sticky sessions)
            request.meta["proxy"] = next(self.pool)

如何调整速度与重试策略?

CONCURRENT_REQUESTS_PER_DOMAIN控制同一域名的并发请求数。代理IP越多越可提高该值,单个IP负载保持不变。例如500个IP发送50并发时,单个IP平均仅承担1/10请求量。

启用AUTOTHROTTLE可根据响应速度自动调节请求间隔。429错误增多时可通过RETRY_HTTP_CODES重试,清除meta.proxy让中间件切换下一代理也是有效方法。

在流水线中添加验证采集结果是否为实际页面的环节,可提升数据质量。若响应中缺少目标元素,可自动切换其他IP重新请求。

  • 根据IP数量调节各域名并发请求数
  • 通过AUTOTHROTTLE实现响应速度自适应间隔
  • 429和5xx错误请更换IP重试
  • 通过响应内容检测过滤验证页面

IP授权连接有何不同?

在控制台注册运行Scrapy服务器的IPv4后,meta proxy可直接使用http://198.51.100.23:8000格式无需认证。获取列表时选择format=ip:port格式即可。多服务器运行爬虫时需注册所有服务器IP(最多10个)。

如何将需要会话的请求绑定到相同IP?

对于登录、购物车等需要保持Cookie连贯的操作,必须使用相同IP。首次请求时直接设置meta proxy,在回调中创建后续请求时传递response.meta中的proxy值,循环中间件就不会更换IP。配合Scrapy的cookiejar meta键还能实现会话级Cookie隔离。

每个会话绑定独立代理与Cookie组,可实现多会话并行不混淆。

import scrapy

PROXIES = [
    "http://spx_user:spx_pass@198.51.100.23:8000",
    "http://spx_user:spx_pass@198.51.100.24:8000",
]

class SessionSpider(scrapy.Spider):
    name = "sessions"

    def start_requests(self):
        for i, proxy in enumerate(PROXIES):
            yield scrapy.Request("https://example.com/", meta={"proxy": proxy, "cookiejar": i}, dont_filter=True)

    def parse(self, response):
        # follow-up request keeps the same proxy and the same cookie jar
        yield response.follow("/account", meta={"proxy": response.meta["proxy"], "cookiejar": response.meta["cookiejar"]}, callback=self.parse_account)

    def parse_account(self, response):
        self.logger.info("status %s via %s", response.status, response.meta["proxy"])

如何临时移除故障代理?

在中间件的 process_response 中收到 403 或 429 响应时,可以让该代理休息几分钟,然后将相同的请求发送到其他代理。复制请求,删除 meta 中的代理,并打开 dont_filter,然后返回,下一个代理就会接管。

通过字典管理闲置代理列表和复用时间最为简便。避免立即重复使用相同IP,为目标网站留出限制解除时间。

在Scrapy中如何节省带宽?

Scrapy默认只接收HTML,因此比浏览器工具消耗的带宽少得多。此外,默认启用的HttpCompressionMiddleware会接收压缩响应,进一步减少传输量。这就是为什么在共享计划的月带宽内可以采集大量页面的原因。

开发时启用HTTPCACHE_ENABLED可避免重复获取相同页面,测试时无需反复调用目标网站和代理。正式运营时应保持去重功能(默认开启),仅对必须重新获取的请求使用dont_filter参数。

限制链接的爬取范围,避免不必要地深入子页面,也很重要。明确收集范围可以减少时间、带宽和目标网站的负担。

常见问题如何解决?

出现407响应时请检查地址中的用户名和密码。若密码含特殊字符需进行百分号编码。TunnelError表示代理未收到CONNECT请求,请确认使用的是HTTP端口。

若所有请求均通过同一IP发出,可能因中间件顺序号在HttpProxyMiddleware之后,或爬虫直接使用meta proxy所致。请同步检查settings.py顺序与爬虫代码。

Scrapy代理设置,使用meta proxy和循环中间件 | Socks5Proxy