接入之前需要确认哪些前置条件?
动手写代码之前,先确认三件事,能避免配置阶段反复踩坑。
| 确认项 | 需要确认的内容 | 影响 |
|---|---|---|
| 鉴权方式 | 账密鉴权还是IP白名单鉴权 | 决定代理URL的拼写格式 |
| 协议类型 | HTTP、HTTPS还是SOCKS5 | 决定依赖库选择和proxies字典的key |
| 接入模式 | 隧道模式还是API提取模式 | 决定IP轮换逻辑写在服务端还是客户端 |
鉴权方式直接决定代理URL怎么拼。账密鉴权把用户名密码写进URL;IP白名单鉴权需要先在服务商控制台添加本机出口IP,代理URL里不带认证信息。
协议类型决定Python这边要装什么库。HTTP/HTTPS用requests自带支持就够;SOCKS5需要额外安装PySocks。
接入模式是最容易被忽略的一项。隧道模式下,每次请求自动从服务端换IP,客户端代码只需要指向一个固定的隧道入口地址;API提取模式下,客户端需要先调用API获取一批IP列表,再自己实现轮换逻辑。两种模式的代码结构完全不同。
requests库的基础代理配置怎么写?
用requests接入代理IP,核心就是构造proxies字典,传给请求方法。
账密鉴权的基础配置:
import requests
# 账密鉴权格式:http://用户名:密码@代理地址:端口
proxy_url = "http://your_user:your_pass@proxy.example.com:8080"
proxies = {
"http": proxy_url,
"https": proxy_url,
}
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
timeout=10,
)
print(response.json())IP白名单鉴权的基础配置:
import requests
# 白名单鉴权:无需用户名密码,控制台已绑定本机IP
proxy_url = "http://proxy.example.com:8080"
proxies = {
"http": proxy_url,
"https": proxy_url,
}
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
timeout=10,
)
print(response.json())两段代码的唯一区别在于proxy_url是否包含认证信息。注意两个细节:
- proxies字典的key必须同时写http和https,否则HTTPS请求不会走代理
- timeout必须设置,代理连接比直连多一跳,不设超时会导致请求长时间挂起
账密鉴权和白名单鉴权分别适合什么场景?
两种鉴权方式的技术差异不大,但适用场景有明显区分。
| 维度 | 账密鉴权 | IP白名单鉴权 |
|---|---|---|
| 配置方式 | 用户名密码写进代理URL | 在控制台绑定服务器出口IP |
| 适合环境 | 动态IP环境、本地开发、多机部署 | 固定IP的云服务器、长期运行的采集节点 |
| 安全考量 | 密码明文在代码中,需做环境变量管理 | 无密码泄露风险,但换服务器需重新绑定 |
| 切换成本 | 改代码里的用户名密码即可 | 需登录控制台修改白名单 |
实际选择建议:
- 网站采集器场景下,如果采集节点部署在固定IP的云服务器上,白名单鉴权更省事,不用操心密码管理
- 舆情监测场景下,如果采集任务分布在多台机器且IP会变动,账密鉴权更灵活
无论哪种方式,都建议把鉴权信息放在环境变量或配置文件中,不要硬编码在脚本里:
import os
proxy_user = os.environ.get("PROXY_USER", "")
proxy_pass = os.environ.get("PROXY_PASS", "")
proxy_host = os.environ.get("PROXY_HOST", "proxy.example.com")
proxy_port = os.environ.get("PROXY_PORT", "8080")
if proxy_user:
proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
else:
proxy_url = f"http://{proxy_host}:{proxy_port}"怎么实现IP自动轮换?
IP轮换的实现方式取决于接入模式。隧道模式和API提取模式的代码结构完全不同。
隧道模式:服务端自动轮换
隧道模式下,代理服务商在服务端完成IP切换,客户端每次请求指向同一个隧道入口地址即可,每次请求出口IP自动变化。代码最简单:
import requests
# 隧道入口地址固定不变,服务端每次请求自动换IP
tunnel_proxy = "http://your_user:your_pass@tunnel.example.com:9090"
proxies = {"http": tunnel_proxy, "https": tunnel_proxy}
for i in range(10):
resp = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
timeout=10,
)
print(f"第{i+1}次请求,出口IP:{resp.json()['origin']}")隧道模式的优势是零轮换代码,劣势是无法控制具体使用哪个IP。
API提取模式:客户端主动轮换
API提取模式下,客户端先调用服务商的API接口获取一批可用IP,再自行实现轮换逻辑。典型流程:
import requests
import itertools
import time
def fetch_ip_list(api_url, count=20):
"""从服务商API提取一批可用IP"""
resp = requests.get(api_url, params={"count": count}, timeout=10)
# 返回格式通常为每行一个 ip:port
return resp.text.strip().split("\n")
def build_proxies(ip_port):
"""将 ip:port 构造为proxies字典"""
proxy_url = f"http://{ip_port}"
return {"http": proxy_url, "https": proxy_url}
# 提取IP列表
api_url = "http://api.example.com/getip"
ip_list = fetch_ip_list(api_url, count=20)
# 使用轮询策略逐个使用
ip_cycle = itertools.cycle(ip_list)
for i in range(50):
current_ip = next(ip_cycle)
proxies = build_proxies(current_ip)
try:
resp = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
timeout=10,
)
print(f"请求{i+1},代理:{current_ip},出口:{resp.json()['origin']}")
except requests.RequestException as e:
print(f"请求{i+1}失败,代理:{current_ip},错误:{e}")API提取模式需要额外处理两个问题:
| 问题 | 处理方式 |
|---|---|
| IP过期 | 记录每个IP的提取时间,到期前主动刷新IP列表 |
| 提取频率限制 | 服务商通常对API调用频率有限制,需做本地缓存,避免频繁请求 |
HTTPS和SOCKS5代理怎么配置?
HTTP代理是最常见的协议,但部分采集场景需要HTTPS或SOCKS5支持。
HTTPS代理
requests库原生支持HTTPS代理,配置方式和HTTP一致,只需确保proxies字典中https的key指向正确地址:
proxies = {
"http": "http://proxy.example.com:8080",
"https": "http://proxy.example.com:8080", # 注意:value仍用http://开头
}一个常见误区:proxies字典中https对应的value,协议头仍然写http://而不是https://。这是因为这里指定的是代理服务器的连接协议,不是目标网站的协议。
SOCKS5代理需要额外安装PySocks库:
pip install pysocks requests[socks]配置方式:
import requests
proxies = {
"http": "socks5h://your_user:your_pass@proxy.example.com:1080",
"https": "socks5h://your_user:your_pass@proxy.example.com:1080",
}
response = requests.get(
"https://httpbin.org/ip",
proxies=proxies,
timeout=15,
)注意socks5h和socks5的区别:
| 协议标识 | DNS解析位置 | 适用场景 |
|---|---|---|
| socks5 | 本地解析DNS | 目标域名在本地可解析时 |
| socks5h | 代理服务器端解析DNS | 需要通过代理解析目标域名时,推荐使用 |
多数采集场景建议用socks5h,让DNS解析也走代理通道,提升访问环境的隔离性。
异常处理和重试机制怎么写?
生产环境中,代理请求的失败率比直连高。网络波动、IP临时不可用、目标站点的访问频率控制都可能导致请求失败。没有异常处理的采集脚本,跑不过一个小时。
基础重试机制:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_session_with_retry(
retries=3,
backoff_factor=0.5,
status_forcelist=(500, 502, 503, 504, 429),
):
"""创建带自动重试的Session"""
session = requests.Session()
retry_strategy = Retry(
total=retries,
backoff_factor=backoff_factor,
status_forcelist=status_forcelist,
allowed_methods=["GET", "POST"],
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("http://", adapter)
session.mount("https://", adapter)
return session
session = create_session_with_retry()
proxies = {"http": "http://proxy.example.com:8080",
"https": "http://proxy.example.com:8080"}
try:
resp = session.get(
"https://target-site.com/data",
proxies=proxies,
timeout=10,
)
resp.raise_for_status()
except requests.exceptions.ProxyError:
print("代理连接失败,检查代理地址和鉴权")
except requests.exceptions.ConnectTimeout:
print("代理连接超时,可能代理节点负载过高")
except requests.exceptions.HTTPError as e:
print(f"HTTP错误:{e.response.status_code}")
except requests.exceptions.RequestException as e:
print(f"请求异常:{e}")几个关键参数的设置建议:
| 参数 | 建议值 | 说明 |
|---|---|---|
| retries | 3 | 超过3次仍失败,大概率是IP本身不可用,继续重试无意义 |
| backoff_factor | 0.5 | 重试间隔按0.5s、1s、2s递增,避免瞬间打满代理带宽 |
| status_forcelist | 429, 500-504 | 429是访问频率控制信号,5xx是服务端异常,都值得重试 |
| timeout | 10-15s | 代理链路比直连多一跳,10秒是合理基线 |
进阶:失败自动换IP
在API提取模式下,可以在重试时自动切换到下一个IP:
def request_with_fallback(url, ip_list, max_attempts=3):
"""请求失败时自动切换代理IP"""
for attempt, ip_port in enumerate(ip_list[:max_attempts]):
proxies = {"http": f"http://{ip_port}", "https": f"http://{ip_port}"}
try:
resp = requests.get(url, proxies=proxies, timeout=10)
resp.raise_for_status()
return resp
except requests.RequestException as e:
print(f"第{attempt+1}次尝试失败({ip_port}):{e}")
return None并发采集和会话保持还需要注意什么?
基础配置跑通之后,生产环境还有两个常见的优化方向。
并发控制
多线程或异步采集时,需要注意并发量不要超过代理服务商的通道限制。超限后请求会排队甚至被拒绝。
import concurrent.futures
import requests
def fetch_with_proxy(url, proxies, timeout=10):
try:
resp = requests.get(url, proxies=proxies, timeout=timeout)
return resp.status_code, resp.text[:100]
except requests.RequestException as e:
return None, str(e)
proxies = {"http": "http://proxy.example.com:8080",
"https": "http://proxy.example.com:8080"}
urls = [f"https://target-site.com/page/{i}" for i in range(100)]
# max_workers不要超过代理通道的并发上限
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
futures = {
executor.submit(fetch_with_proxy, url, proxies): url
for url in urls
}
for future in concurrent.futures.as_completed(futures):
status, content = future.result()
print(f"{futures[future]} -> {status}")并发数设多少合适,取决于代理套餐的通道数限制。一般建议:
| 代理套餐类型 | 建议并发数 | 原因 |
|---|---|---|
| 按通道计费 | 不超过已购通道数 | 超限请求会排队或返回错误 |
| 按流量计费 | 10-20并发起步,逐步调高 | 观察成功率拐点,找到最优并发数 |
| 隧道模式 | 参考服务商文档的QPS限制 | 通常有每秒请求数上限 |
会话保持
部分采集场景需要在多个请求之间保持同一个出口IP,比如拓客数据采集中的登录态维持、广告监测中的多步操作链。隧道模式通常支持通过在代理URL中追加会话标识参数来保持IP:
import requests
# 通过session参数保持同一出口IP(具体参数名以服务商文档为准)
session_proxy = "http://your_user:your_pass@tunnel.example.com:9090?session=abc123"
proxies = {"http": session_proxy, "https": session_proxy}
session = requests.Session()
session.proxies = proxies
# 同一session内的多次请求,出口IP保持不变
for step in ["login", "list", "detail"]:
resp = session.get(f"https://target-site.com/{step}", timeout=10)
print(f"{step} -> {resp.status_code}")会话保持的关键是确认服务商支持的保持时长。如果操作链耗时超过保持时长,中途IP会切换,导致前面的登录态失效。建议在配置前确认服务商文档中标注的会话保持上限。
FAQ
Q:requests提示ProxyError: Cannot connect to proxy怎么排查?
依次检查4项:代理地址和端口是否正确,鉴权信息是否有效,白名单鉴权模式下本机出口IP是否已添加到控制台,代理服务是否处于正常运行状态。用curl命令直接测试代理连通性是最快的排查方式:curl -x http://proxy:port https://httpbin.org/ip。
Q:HTTP代理能代理HTTPS请求吗?
可以。HTTP代理通过CONNECT方法建立隧道来转发HTTPS流量,客户端和目标服务器之间的TLS加密不受影响。proxies字典中https的key对应的value写http://开头是正确的,这指的是客户端到代理服务器的连接协议,不是最终目标的协议。
Q:隧道模式和API提取模式哪个更适合生产环境?
取决于采集任务的特点。隧道模式的优势是零轮换代码、接入简单,适合高频短会话的网站采集器场景。API提取模式的优势是可以精确控制每个IP的使用策略,适合需要会话保持或定向IP分配的舆情监测等场景。团队技术能力强且需要精细控制的,选API提取;追求快速上线和低维护成本的,选隧道。
Q:用了代理IP之后请求速度明显变慢怎么办?
代理链路比直连多一跳,延迟增加20-100ms属于正常范围。如果延迟超过500ms,检查三个方面:代理节点的地理位置是否离目标服务器过远,代理服务商的带宽是否在高峰期被打满,并发数是否超过了通道限制导致请求排队。优先选择和目标服务器同区域的代理节点可以显著降低延迟。
Q:异步框架aiohttp怎么配置代理?
aiohttp的代理配置通过请求方法的proxy参数传入,不用字典:async with session.get(url, proxy="http://proxy:port") as resp:。SOCKS5代理需要额外安装aiohttp-socks库。注意aiohttp的代理参数是单数proxy,不是requests的复数proxies。
Q:采集过程中频繁出现403或429状态码是代理的问题吗?
不一定。403通常是目标站点的访问频率控制机制触发,说明请求频率过高或请求特征被识别,需要降低并发、增加请求间隔、补充请求头信息。429是明确的频率限制信号,需要按响应头中的Retry-After字段等待后重试。如果换了不同服务商的代理仍然出现,大概率是采集策略而非代理本身的问题。