Python 接入代理IP:爬虫网络优化实战

在爬虫、数据采集和多店铺运营中,代理 IP 是绕过目标站频率限制和地理封锁的常用手段。但直接给 requests.get() 加一个 proxies 参数只是第一步,真正影响稳定性和效率的是代理轮换、Session 复用、超时重试、并发控制和请求指纹

本文整理一套可直接落地的 Python 代理接入与网络优化方案,适用于 requests、Scrapy 和异步爬虫。

一、最小接入:requests 如何正确配置代理

1.1 基础写法

python 复制代码
import requests

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

resp = requests.get(
    "https://target-site.com/api",
    proxies=proxies,
    timeout=(5, 20),          # (connect_timeout, read_timeout)
    headers={"User-Agent": "Mozilla/5.0"},
)
print(resp.status_code)

注意:访问 https:// 目标时,代理地址通常仍写成 http://(代理通过 CONNECT 建立隧道)。如果代理本身支持 HTTPS 入口,才写成 https://

1.2 特殊字符密码

如果密码包含 @:# 等特殊字符,直接拼 URL 会解析出错。建议用 urllib.parse.quote 编码:

python 复制代码
from urllib.parse import quote

user = "myuser"
password = "pa@ss:123"
proxy_url = f"http://{user}:{quote(password, safe='')}@proxy.example.com:8080"

二、Session 复用:减少握手开销

每次请求都新建 TCP/TLS 连接会显著增加延迟。使用 requests.Session() 可以复用连接池:

python 复制代码
import requests
from requests.adapters import HTTPAdapter

session = requests.Session()
session.proxies = {
    "http": "http://user:password@proxy.example.com:8080",
    "https": "http://user:password@proxy.example.com:8080",
}

# 限制连接池大小和重试次数
session.mount("http://", HTTPAdapter(pool_connections=10, pool_maxsize=20))
session.mount("https://", HTTPAdapter(pool_connections=10, pool_maxsize=20))

for i in range(100):
    try:
        resp = session.get(
            f"https://target-site.com/items/{i}",
            timeout=(5, 20),
            headers={"User-Agent": "Mozilla/5.0"},
        )
        print(i, resp.status_code)
    except requests.exceptions.RequestException as e:
        print(i, type(e).__name__, e)

对于代理池,建议为每个代理地址维护一个 Session,避免不同认证/出口混在一起导致连接复用失败。

三、代理轮换:避免单节点被封

3.1 简单轮询

python 复制代码
from itertools import cycle
import requests

proxy_list = [
    "http://user:pass@proxy-a.example.com:8080",
    "http://user:pass@proxy-b.example.com:8080",
    "http://user:pass@proxy-c.example.com:8080",
]
proxy_iter = cycle(proxy_list)

for i in range(100):
    proxy = next(proxy_iter)
    try:
        resp = requests.get(
            "https://target-site.com/api",
            proxies={"http": proxy, "https": proxy},
            timeout=(5, 20),
        )
    except requests.exceptions.RequestException:
        continue

3.2 按失败率动态剔除

更稳健的做法是记录每个代理的成功率和延迟,优先使用表现好的节点。核心思路:维护一个 ProxyStats 对象,每次请求后更新 total/ok/latencies,选择时按 success_ratep95 综合评分排序,避免一直用已经失败的节点。

python 复制代码
from dataclasses import dataclass, field
from collections import deque


@dataclass
class ProxyStats:
    url: str
    total: int = 0
    ok: int = 0
    latencies: deque = field(default_factory=lambda: deque(maxlen=50))

    @property
    def success_rate(self) -> float:
        return self.ok / self.total if self.total else 0.0

选择时按 success_rate * 0.7 + (1 / p95) * 0.3 综合评分排序即可。

四、超时与重试:不要把所有失败都重试

不是所有异常都值得重试。建议只重试连接超时、读取超时和 5xx,403/407/429 应先分析原因:

python 复制代码
from functools import wraps
import requests


def fetch_with_retry(session, url, max_retries=3, backoff=1.0):
    for attempt in range(max_retries + 1):
        try:
            resp = session.get(url, timeout=(5, 20))
            if resp.status_code >= 500:
                if attempt == max_retries:
                    return resp
                time.sleep(backoff * (2 ** attempt))
                continue
            return resp
        except (requests.exceptions.ConnectTimeout,
                requests.exceptions.ReadTimeout) as e:
            if attempt == max_retries:
                raise
            time.sleep(backoff * (2 ** attempt))
        except (requests.exceptions.ProxyError,
                requests.exceptions.SSLError) as e:
            # 代理认证或 TLS 错误,重试通常无效
            raise

注意:重试前最好换代理。同一节点重复请求 429,会加速被封。

五、并发控制:aiohttp 与异步请求

对于 I/O 密集型爬虫,异步能显著提升吞吐:

python 复制代码
import asyncio
import aiohttp


async def fetch(session, url, proxy):
    async with session.get(url, proxy=proxy, timeout=aiohttp.ClientTimeout(total=20)) as resp:
        return await resp.text(), resp.status


async def main():
    urls = [f"https://target-site.com/items/{i}" for i in range(100)]
    proxy = "http://user:password@proxy.example.com:8080"
    connector = aiohttp.TCPConnector(limit=20, limit_per_host=5)
    async with aiohttp.ClientSession(connector=connector) as session:
        tasks = [fetch(session, url, proxy) for url in urls]
        results = await asyncio.gather(*tasks, return_exceptions=True)
    return results


asyncio.run(main())

关键点:limit 控制全局连接数,limit_per_host 控制单目标站连接数,避免触发目标站限流。并发不是越大越好,先压测到稳定阈值;异步场景下也要做代理轮换和失败统计。

六、请求指纹:降低被识别概率

代理只是绕过 IP 限制的一部分。如果请求头、Cookie、TLS 指纹或行为模式一致,仍然容易被识别。建议:

维度 建议
User-Agent 准备 5--10 个常见浏览器 UA,按请求或会话轮换
Accept/Language 与 UA 所在地区匹配
Cookie/Session 不同账号/会话使用不同 Cookie 池
请求间隔 加入随机抖动,如 time.sleep(random.uniform(0.5, 2.5))
并发节奏 避免短时间内集中请求同一接口
请求头顺序 尽量贴近真实浏览器,必要时使用真实浏览器驱动

七、日志与监控:把问题拆清楚

建议在日志中记录以下字段:

字段 用途
target_url 判断是否只有某个目标失败
proxy_url 定位问题节点
status_code 区分 403/407/429/5xx
exception_type 区分 ConnectTimeout / ReadTimeout / ProxyError
latency_ms 判断延迟是否异常
retry_count 评估重试策略是否有效
body_preview 快速判断返回的是正常页还是校验页

一个最小日志模板:

python 复制代码
import logging
import time

logger = logging.getLogger("crawler")


def log_request(url, proxy, start, resp=None, exc=None):
    latency = (time.perf_counter() - start) * 1000
    extra = {"target": url, "proxy": proxy, "latency_ms": round(latency, 2)}
    if exc:
        extra["exception"] = type(exc).__name__
        logger.warning("req_fail", extra=extra)
    else:
        extra["status"] = resp.status_code
        logger.info("req_ok", extra=extra)

八、常见坑

  1. 代理地址协议写错 :访问 HTTPS 目标时,代理地址通常仍是 http://,不是 https://
  2. 密码特殊字符未编码 :含 @: 的密码不编码会导致 URL 解析失败。
  3. 不复用 Session:每个请求新建连接会显著增加 P95 延迟。
  4. 重试 403/429:直接重试会加速被封,应先降速或换代理。
  5. 只换 IP 不换指纹:UA、Cookie、请求节奏一致仍会被识别。
  6. 并发过高:代理网关和节点都有容量上限,超过后成功率会断崖式下降。

九、总结

Python 爬虫接入代理 IP,核心不是"能不能挂上代理",而是能否把代理稳定地放进工程闭环:

  • Session 复用降低握手开销;
  • 代理轮换 + 失败统计避免单节点失效拖垮整体;
  • 分层超时 + 选择性重试减少无效请求;
  • 异步并发控制提升吞吐但不突破代理容量;
  • 请求指纹管理降低被识别概率;
  • 结构化日志让问题可追踪。

把这些环节串起来,才能把代理从"能跑"变成"稳定跑、可监控、可优化"。如果你使用 IPdodo 的跨境专线或静态住宅 IP,可以把同样的接入逻辑套用到其提供的入口上,结合自身业务目标站做压测和调优。

参考资料

相关推荐
szwyyx21 小时前
目前知名的BEL电源分销商哪家性价比高
人工智能·python
华如锦21 小时前
【AGENT开发】python学习 Python Level 5
python·学习·elasticsearch
忘路之远近i21 小时前
Selenium + 自研小型 Agent:用脚本加简易agent搭建简单数据工作流
人工智能·python
乐启国际旅行社有限公司1 天前
SpringSecurity+JWT实现文旅系统细粒度数据权限管控(多角色数据隔离)
开发语言·python
红莲凪是1 天前
使用二次封装的Excel COM 组件操作Excel\WPS ET IExcelRange 高级应用
python·excel·wps
AI人工智能集结号1 天前
AI回答采集数据清洗:Python实现空回答、拒绝与无关样本识别
开发语言·人工智能·python
kisloy1 天前
【爬虫入门第10讲】Scrapy 框架深度解析(一):五大组件与中间件
爬虫·scrapy·中间件
依晨照旧1 天前
uv 常用命令速查:一个工具干掉 pip + venv + pyenv + pipx + poetry,Python 工具链从此清爽
python
渣男教父1 天前
Python自动化操作 Word 和 PDF
后端·python