免费咨询热线135-3532-1113
免费获取方案
首页/新闻资讯/爬虫技术/爬虫怎么配置代理IP?Python requests 与 Scrapy 的轮换、失败重试实现

爬虫怎么配置代理IP?Python requests 与 Scrapy 的轮换、失败重试实现

发布: 栏目:爬虫技术 作者:万户网络 阅读:4
从代码角度讲清楚爬虫代理IP的配置方法:requests 的 proxies 参数、代理轮换与失败重试,以及 Scrapy 下载中间件的实现,并强调只采集公开数据、遵守 robots 与网站条款。

爬虫怎么配置代理IP?Python requests 与 Scrapy 的轮换、失败重试实现

爬虫配置代理IP,在 Python 里主要有两种写法:用 requests 时,给请求传 proxies 参数;用 Scrapy 时,在下载中间件里给 request.meta['proxy'] 赋值。真正让代理"好用"的不是单次设置,而是轮换、失败切换和重试这三件事做对,否则代理池再大也会不断报错。

本文只讲技术实现,并且默认你的采集对象是公开可访问的数据,采集行为遵守目标网站的 robots 协议、服务条款和相关法律法规。代理不是用来绕过合法限制的工具,而是用来分散合理访问的出口、避免单个 IP 因正常批量任务被误封。关于共享、独享和自建代理池的选择,可以参考我们另一篇代理IP选型指南,这里不重复。

一、先弄清楚代理地址的格式

常见的 HTTP/HTTPS 代理地址格式有两种:

无认证:  http://127.0.0.1:8080
带认证:  http://用户名:密码@代理主机:端口

SOCKS5 代理则是 socks5://主机:端口。使用 SOCKS5 时,requests 需要额外安装依赖:

pip install requests[socks]

后文示例里的代理地址都是占位符,请替换成你自己合法购买或自建的代理。不要把真实的账号密码写进代码仓库,建议用环境变量读取。

二、requests 最基础的代理设置

requests 的 proxies 参数是一个字典,键是协议,值是代理地址:

import requests

proxies = {
    "http": "http://user:pass@proxy.example.com:8000",
    "https": "http://user:pass@proxy.example.com:8000",
}

resp = requests.get(
    "https://httpbin.org/ip",
    proxies=proxies,
    timeout=10,
)
print(resp.json())

这里有三个要点:

  1. https 键的值通常也写 http:// 开头的代理地址,意思是"通过这个 HTTP 代理去访问 https 网站"(走 CONNECT 隧道),不是写错了;
  2. 一定要设置 timeout,否则遇到失效代理,程序可能一直卡住;
  3. 用 https://httpbin.org/ip 这类回显 IP 的接口先验证代理是否生效,再去跑业务。

也可以用环境变量全局指定,适合快速测试:

import os
os.environ["HTTP_PROXY"] = "http://user:pass@proxy.example.com:8000"
os.environ["HTTPS_PROXY"] = "http://user:pass@proxy.example.com:8000"

注意:requests 默认会读取系统代理环境变量,如果你的机器本身开了系统代理,可能出现"明明没写代理却走了代理"的现象。需要强制直连时,可以用 Session.trust_env = False。

三、代理轮换:每次请求换一个出口

实际项目里很少只用一个代理。最简单的轮换方式是准备一个代理列表,每次请求随机或顺序取一个:

import random
import requests

PROXY_POOL = [
    "http://user:pass@proxy1.example.com:8000",
    "http://user:pass@proxy2.example.com:8000",
    "http://user:pass@proxy3.example.com:8000",
]

def get_proxies():
    p = random.choice(PROXY_POOL)
    return {"http": p, "https": p}

def fetch(url):
    return requests.get(url, proxies=get_proxies(), timeout=10)

如果想保证每个代理被均匀使用,可以用 itertools.cycle:

from itertools import cycle
pool = cycle(PROXY_POOL)

def get_proxies():
    p = next(pool)
    return {"http": p, "https": p}

另一种常见场景是登录态:需要同一个会话始终使用同一个 IP,否则网站可能因 IP 频繁变化要求重新验证。这时应该为每个会话绑定一个固定代理,而不是每次请求都换。

四、失败切换与重试:让程序自己"换路"

代理失效、超时、被目标站返回 403/429 都很常见。靠人工盯着不现实,应该在代码里处理:

import random
import time
import requests

def fetch_with_retry(url, max_retries=3):
    last_err = None
    for attempt in range(1, max_retries + 1):
        p = random.choice(PROXY_POOL)
        proxies = {"http": p, "https": p}
        try:
            resp = requests.get(url, proxies=proxies, timeout=10)
            # 429/403 通常表示访问过快或被限制,换代理并退避
            if resp.status_code in (403, 429):
                raise requests.HTTPError(f"status {resp.status_code}")
            resp.raise_for_status()
            return resp
        except requests.RequestException as e:
            last_err = e
            # 指数退避:1s、2s、4s ...
            time.sleep(2 ** (attempt - 1))
    raise RuntimeError(f"请求失败:{url},最后错误:{last_err}")

几个值得注意的点:

  • 指数退避:每次失败后等待时间翻倍,既给对方服务器减压,也避免自己在故障期疯狂重试;
  • 区分错误类型:连接超时、代理认证失败、目标站 403 的原因不同,生产环境最好把失败原因记录下来,方便判断是代理问题还是目标站策略问题;
  • 控制总并发:代理不是"无限出口",并发过高仍然会触发对方限流,甚至给目标网站造成压力,应当控制速率。

用 requests 自带的重试机制也可以,对网络层错误和指定状态码自动重试:

import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

session = requests.Session()
retry = Retry(
    total=3,
    backoff_factor=1,
    status_forcelist=[429, 500, 502, 503, 504],
    allowed_methods=["GET"],
)
session.mount("http://", HTTPAdapter(max_retries=retry))
session.mount("https://", HTTPAdapter(max_retries=retry))

注意这种内置重试不会自动换代理,同一个代理失败后还是用它重试。要"失败就换代理",仍然需要像上面那样自己封装。

五、检测代理是否可用

大批量代理里总有失效的,上线前先做一轮健康检查,把可用的筛出来:

import concurrent.futures
import requests

def check(proxy):
    try:
        r = requests.get(
            "https://httpbin.org/ip",
            proxies={"http": proxy, "https": proxy},
            timeout=6,
        )
        return proxy if r.ok else None
    except requests.RequestException:
        return None

with concurrent.futures.ThreadPoolExecutor(max_workers=10) as ex:
    alive = [p for p in ex.map(check, PROXY_POOL) if p]
print("可用代理:", len(alive))

建议定时检查,并给每个代理记录成功率和响应时间,长期成功率低的直接剔除。

六、Scrapy 里配置代理:下载中间件

Scrapy 的代理通过 request.meta['proxy'] 指定。最简单的做法是在单个请求里写:

yield scrapy.Request(
    url,
    meta={"proxy": "http://user:pass@proxy.example.com:8000"},
    callback=self.parse,
)

更实用的做法是写一个下载中间件,统一给所有请求分配代理:

# middlewares.py
import random

class RandomProxyMiddleware:
    def __init__(self, proxies):
        self.proxies = proxies

    @classmethod
    def from_crawler(cls, crawler):
        return cls(crawler.settings.getlist("PROXY_LIST"))

    def process_request(self, request, spider):
        if "proxy" not in request.meta and self.proxies:
            request.meta["proxy"] = random.choice(self.proxies)

在 settings.py 里启用并配置代理列表:

PROXY_LIST = [
    "http://user:pass@proxy1.example.com:8000",
    "http://user:pass@proxy2.example.com:8000",
]

DOWNLOADER_MIDDLEWARES = {
    "myproject.middlewares.RandomProxyMiddleware": 543,
}

中间件的优先级数字要在内置 HttpProxyMiddleware(默认 750)之前,这样内置中间件才能正确处理你设置的 meta['proxy'],包括带账号密码的认证头。

七、Scrapy 的失败重试与换代理

Scrapy 自带 RetryMiddleware,通过设置控制重试:

RETRY_ENABLED = True
RETRY_TIMES = 3
RETRY_HTTP_CODES = [429, 500, 502, 503, 504]
DOWNLOAD_TIMEOUT = 15

# 控制节奏,避免对目标站造成压力
DOWNLOAD_DELAY = 1
CONCURRENT_REQUESTS_PER_DOMAIN = 4
AUTOTHROTTLE_ENABLED = True

要实现"重试时换代理",可以在中间件里处理失败的响应和异常,换一个代理后重新发起请求:

class RandomProxyMiddleware:
    # ... 前面的 __init__ / from_crawler 同上 ...

    def process_response(self, request, response, spider):
        if response.status in (403, 429):
            return self._retry_with_new_proxy(request)
        return response

    def process_exception(self, request, exception, spider):
        return self._retry_with_new_proxy(request)

    def _retry_with_new_proxy(self, request):
        retries = request.meta.get("proxy_retries", 0)
        if retries >= 3:
            return None  # 超过次数,交给后续处理
        new_req = request.copy()
        new_req.meta["proxy"] = random.choice(self.proxies)
        new_req.meta["proxy_retries"] = retries + 1
        new_req.dont_filter = True  # 避免被去重过滤
        return new_req

这里 dont_filter = True 很关键,否则重新发出的请求会被 Scrapy 的去重机制当成"已访问"丢弃。同时一定要设置最大重试次数,防止在所有代理都不可用时进入死循环。

八、合规与礼貌:配置代理不等于可以无限制采集

技术手段要放在合规的前提下使用:

  1. 看 robots.txt:目标网站明确不允许抓取的路径,不要抓取;
  2. 读服务条款:有些网站明确禁止自动化采集,应当遵守;
  3. 只采公开数据:不要用代理绕过登录、付费墙或访问控制去获取非公开信息;
  4. 保护个人信息:采集内容涉及个人信息时,必须符合个人信息保护相关法律法规;
  5. 控制频率:设置合理的延时和并发,不要对目标服务器造成压力;
  6. 标明身份:在合适的场景下使用清晰的 User-Agent,必要时与网站方沟通授权。

为了避免被封而把代理当成"隐身衣",并不是长久之计;合规采集加上合理速率,才是稳定运行的基础。

九、常见问题

1. 为什么设置了代理,请求还是用的本机 IP? 检查 proxies 的键是否写全了 http 和 https,以及请求的 URL 协议与键是否匹配;再用回显 IP 的接口验证。

2. 代理能连通,但访问目标网站很慢? 可能是代理本身带宽差、距离远,或目标站对该 IP 段限速。可以记录每个代理的响应时间,淘汰慢的。

3. requests 和 Scrapy 该怎么选? 几十到几千个页面的小任务,用 requests 加简单封装更灵活;需要大规模、持续、可恢复的采集,Scrapy 的调度、去重和中间件体系更合适。

4. 代理 IP 要用动态的还是静态的? 需要保持登录态的,用静态或会话保持的;只做公开页面批量访问的,动态轮换更方便。具体取舍见选型指南。

5. 遇到验证码怎么办? 验证码本身就是网站在限制自动化访问。应当先评估这次采集是否被允许,不要试图用技术手段绕过安全验证;确有合法需求时,应联系网站方获得授权或数据接口。

十、小结

爬虫配置代理IP的核心是三件事:正确设置、合理轮换、失败重试。requests 用 proxies 加封装的重试函数,Scrapy 用下载中间件加 RETRY_* 配置,再配合限速和健康检查,就能搭出一套稳定的基础设施。在此基础上,始终遵守 robots 与网站条款,采集公开数据,才能让爬虫长期可用。

如果你的业务需要定制的数据采集系统、分布式爬虫或代理调度平台,广州万户网络可以按你的需求设计和开发,电话 13535321113,具体方案和报价以实际需求为准。

需要网站建设、软件开发或爬虫定制?

模板建站1280元起,价格公开不加价。电话/微信 13535321113

免费获取方案
电话
免费咨询热线13535321113
微信
微信二维码
微信号:13535321113
点击复制
拨打电话 加微信