爬虫怎么配置代理IP?Python requests 与 Scrapy 的轮换、失败重试实现
爬虫怎么配置代理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())
这里有三个要点:
https键的值通常也写http://开头的代理地址,意思是"通过这个 HTTP 代理去访问 https 网站"(走 CONNECT 隧道),不是写错了;- 一定要设置
timeout,否则遇到失效代理,程序可能一直卡住; - 用
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 的去重机制当成"已访问"丢弃。同时一定要设置最大重试次数,防止在所有代理都不可用时进入死循环。
八、合规与礼貌:配置代理不等于可以无限制采集
技术手段要放在合规的前提下使用:
- 看 robots.txt:目标网站明确不允许抓取的路径,不要抓取;
- 读服务条款:有些网站明确禁止自动化采集,应当遵守;
- 只采公开数据:不要用代理绕过登录、付费墙或访问控制去获取非公开信息;
- 保护个人信息:采集内容涉及个人信息时,必须符合个人信息保护相关法律法规;
- 控制频率:设置合理的延时和并发,不要对目标服务器造成压力;
- 标明身份:在合适的场景下使用清晰的 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
