Python 爬虫代理 IP 怎么用?requests 代理、隧道代理与 403 排查

爬虫遇到 403、验证码、IP 被封怎么办?本文从代理 IP 原理、requests proxies 写法、账号密码认证、短效代理和隧道代理选择、失败重试与合规边界讲清排查思路。

10 分钟阅读 requestsPython爬虫403代理 IP爬虫代理IP封禁

爬虫什么时候需要代理 IP

同一段爬虫代码,前几次请求正常,连续跑一会儿之后突然变成 403、验证码、空数据,或者接口直接提示访问受限,这时就要考虑是不是触发了 IP 维度的风控。

爬虫请求失败和 403 IP 封禁示意图

代理 IP 不是万能钥匙,它主要解决的是“目标服务器看到的访问出口是谁”这个问题。请求没有加代理时,目标站点看到的是你的本机出口 IP;请求加了代理后,请求会先到代理服务器,再由代理服务器访问目标站点,目标站点看到的就是代理服务器的出口 IP。

所以,代理 IP 适合这些场景:

  1. 学习或授权测试环境中,接口按 IP 限制访问频率。
  2. 同一 IP 连续请求后出现 403、验证码或临时封禁。
  3. 需要验证目标服务看到的出口 IP、请求头、Cookie 是否符合预期。
  4. 爬虫任务已经做好限速、超时、重试,但仍然被 IP 维度拦截。

反过来,如果问题出在 Cookie 失效、请求头缺失、接口参数错误、JS 渲染没处理,单纯换代理通常没有用。代理只换出口,不会自动补全登录态、请求签名和浏览器环境。

代理 IP 的工作方式

普通请求链路是:

你的程序 -> 目标网站

加代理之后,链路会变成:

你的程序 -> 代理服务器 -> 目标网站

目标网站通常只能看到代理服务器的出口 IP,而不是你本机的真实出口 IP。课程第 63 节里用“回显请求信息”的接口演示过这一点:不加代理时,接口返回的是本机出口 IP;加代理后,接口返回的 IP 会变成代理出口。

这也是代理能缓解 IP 封禁的原因:被限制的是访问出口,不一定是你的机器本身。

不过这里要分清两个地址:

名称 含义 常见表现
代理入口 你的程序连接的代理地址 域名或 IP + 端口
代理出口 代理服务器访问目标站点时暴露的 IP 目标站点真正看到的 IP

有些代理入口和出口是同一个 IP,有些代理会经过中转,入口和出口并不一致。判断代理是否生效时,不要只看你填进代码里的代理地址,要看目标服务器最终看到的出口 IP。

requests 代理怎么写

requests 里,代理通过 proxies 参数传入。它是一个字典,分别指定 httphttps 请求使用哪个代理。

import requests


url = "https://httpbin.org/ip"

proxies = {
    "http": "http://123.4.5.6:9999",
    "https": "http://123.4.5.6:9999",
}

response = requests.get(url, proxies=proxies, timeout=10)
print(response.text)

这里最容易误解的是 https 这一项。它表示“访问 HTTPS 目标地址时使用这个代理”,不表示代理服务器本身一定要用 HTTPS 协议。很多 HTTP 代理也可以代理 HTTPS 目标站点。

如果代理只支持 HTTP,就不要写成 https://123.4.5.6:9999。代理协议、目标网站协议、字典里的键名,是三个不同概念。

账号密码认证代理怎么写

付费代理通常需要账号密码。最常见的写法是把账号密码放进代理 URL:

import os
from urllib.parse import quote

import requests


proxy_host = "proxy.example.com"
proxy_port = "8000"

# 账号密码不要硬编码到源码里,建议从环境变量读取
proxy_user = quote(os.environ["PROXY_USER"], safe="")
proxy_pass = quote(os.environ["PROXY_PASS"], safe="")

proxy_url = f"http://{proxy_user}:{proxy_pass}@{proxy_host}:{proxy_port}"
proxies = {
    "http": proxy_url,
    "https": proxy_url,
}

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

如果账号或密码里包含 @:/ 这类特殊字符,直接拼进 URL 可能会解析错,所以这里用 quote 做了一次编码。

课程代码同步仓库可以放公共示例,但不要把真实代理账号、密码、AuthKey、提取链接提交进去。代理凭证一旦泄露,很容易被别人刷完额度。

短效代理、隧道代理、代理池怎么选

不同代理产品的使用方式差别很大。初学者不要先纠结品牌,先把类型分清。

类型 工作方式 适合场景 注意点
短效提取代理 先调用提取接口拿一个 IP,再把这个 IP 写入 proxies 小规模练习、单次任务、手动验证 IP 有效期短,需要自己处理失效和重取
隧道代理 代理入口固定,服务商在后面自动换出口 IP 不想自己维护代理切换逻辑 不一定能随时强制换 IP,要看产品规则
自建代理池 自己维护可用代理、失效检测、评分和调度 长期任务、复杂采集系统 维护成本高,免费代理质量通常很差
免费代理 网上公开可搜到的代理 临时测试代理参数写法 慢、不稳定、失效率高,不适合正式任务

课程第 63 节里演示过两种常见流程:一种是手动提取短效 IP,再写入 proxies;另一种是使用固定隧道入口,让服务商自动切换出口 IP。真正做项目时,建议先从隧道代理或稳定付费代理开始,别把时间浪费在大量不可用的免费代理上。

短效代理如何自动重取

短效代理的问题是有效期短。手动复制一个 IP 可以演示原理,但写进爬虫任务里,就要考虑失败后自动重取。

下面是一个简化版写法。真实代理平台返回格式不同,你需要按实际文档解析。

import time

import requests
from requests import RequestException


PROXY_API = "https://proxy.example.com/get"


def fetch_proxy():
    response = requests.get(PROXY_API, timeout=5)
    response.raise_for_status()

    # 假设平台直接返回 123.4.5.6:9999
    proxy_addr = response.text.strip()
    return f"http://{proxy_addr}"


def request_with_proxy(url, max_retries=3):
    last_error = None

    for attempt in range(max_retries):
        proxy_url = fetch_proxy()
        proxies = {
            "http": proxy_url,
            "https": proxy_url,
        }

        try:
            response = requests.get(url, proxies=proxies, timeout=10)
            if response.status_code != 403:
                return response
        except RequestException as exc:
            last_error = exc

        # 失败后稍微停一下,避免连续打满目标站点和代理服务
        time.sleep(1 + attempt)

    if last_error:
        raise last_error
    raise RuntimeError("代理重试后仍然被拒绝")

这段代码的重点不是“无限换 IP”,而是给失败设置边界:最多重试几次、每次有超时、失败后停一下。爬虫稳定性不是靠疯狂请求堆出来的,而是靠限速、超时、重试和状态判断一起配合。

如果你还没处理过 timeoutConnectionErrorHTTPError 这些异常,可以先看 Python 爬虫 requests 异常处理完全指南

Session、Cookie 和代理要一起看

很多人遇到 403 后第一反应是换代理,但真实原因可能是登录态丢了。

例如一个接口需要先访问首页拿 Cookie,再请求数据接口。如果你每次都用新的 requests.get(),Cookie 没有被自动保留,目标站点可能会把你当成异常请求。

这种场景应该先用 Session 保持 Cookie:

import requests


session = requests.Session()
session.headers.update({
    "User-Agent": "Mozilla/5.0",
})

proxy_url = "http://123.4.5.6:9999"
session.proxies.update({
    "http": proxy_url,
    "https": proxy_url,
})

# 先访问首页,让 Session 保存服务器下发的 Cookie
session.get("https://example.com/", timeout=10)

# 再请求数据接口,Cookie 会自动带上
response = session.get("https://example.com/api/data", timeout=10)
print(response.status_code)

如果用了代理还是 403,排查顺序建议是:

  1. 先确认目标接口是否需要登录态或 Cookie。
  2. 再确认 User-AgentRefererAccept 等请求头是否缺失。
  3. 然后检查请求频率是否过高。
  4. 最后再判断是否需要换代理 IP。

Cookie 自动保持和验证码重试可以继续看 requests Session 教程:Cookie 自动保持、验证码重试与 403 排查

aiohttp 和 Playwright 里怎么加代理

如果你用的是 aiohttp,代理通常写在单次请求的 proxy 参数里:

import aiohttp


async def fetch(session, url, proxy_url):
    async with session.get(url, proxy=proxy_url, timeout=10) as response:
        return await response.text()

异步爬虫一定要额外注意并发数。代理 IP 能换出口,但不能替你承受无限并发。用 aiohttp 时建议配合 asyncio.Semaphore 控制并发,具体可以看 Python aiohttp 教程:异步请求、并发爬虫与超时重试完整指南

如果你用的是 Playwright,代理通常在启动浏览器时配置:

from playwright.sync_api import sync_playwright


with sync_playwright() as p:
    browser = p.chromium.launch(
        proxy={
            "server": "http://123.4.5.6:9999",
        }
    )
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()

动态网页、JS 渲染、接口找不到时,Playwright 比单纯换代理更重要。相关基础可以看 Playwright Python 教程:从安装到动态网页自动化实战

403 和 IP 封禁的排查清单

遇到 403,不要一上来就写代理池。先按下面顺序排查:

  1. 看状态码和响应内容:确认是 403、验证码页、空数据,还是接口参数错误。
  2. 看是否只有连续请求才失败:第一次正常,连续多次失败,更像频率或 IP 风控。
  3. 看请求头是否完整:特别是 User-AgentRefererAcceptContent-Type
  4. 看 Cookie 是否保持:登录后接口、验证码接口、下单接口通常都依赖 Cookie。
  5. 看是否需要 JS 渲染:源代码里没有数据,不代表接口拿不到,可能需要分析 Network。
  6. 看请求频率是否过高:同一秒大量请求,即使用代理也容易被拦。
  7. 最后再换代理 IP:代理是出口层面的解决方案,不是所有 403 的根因。

请求头排查可以看 HTTP 请求头与响应头实战;静态页面和动态页面判断可以看 网页源代码和 Elements 有什么区别

不要把代理当成“无限请求许可证”

代理 IP 只能降低单一出口 IP 被限制后的影响,不能改变爬虫行为本身。如果请求频率过高、采集范围过大、抓取了不该抓的数据,换代理只会把风险放大。

写爬虫代理逻辑时,至少要守住几条底线:

  1. 只在学习、测试、授权或合规范围内使用。
  2. 不采集、扩散个人敏感信息。
  3. 给每个请求设置超时,不让任务无限挂起。
  4. 给重试设置上限,不做无限循环。
  5. 给并发设置上限,不把目标服务打崩。
  6. 发现异常拦截、验证码、封禁提示时,先降频或停止排查。

如果你不确定某个采集行为是否合适,先看 爬虫合法吗?一文讲清爬虫法律边界与风险

常见问题

免费代理能不能用?

可以用来测试 proxies 参数写法,但不建议用于正式任务。免费代理常见问题是可用率低、速度慢、出口混乱、随时失效。你会把大量时间花在“这个代理到底还能不能用”上,而不是解决真正的采集逻辑。

为什么换了代理还是 403?

通常是因为 403 的原因不是 IP。常见原因包括 Cookie 缺失、请求头不完整、接口参数错误、访问频率异常、页面需要 JS 渲染、账号状态异常等。代理只改变出口 IP,不会自动模拟真实浏览器行为。

代理越多,爬虫就越快吗?

不一定。代理会增加一次中转,速度通常比直连慢。如果代理质量差,并发越高,失败率越高。真正稳定的做法是控制并发、设置超时、失败重试、记录状态,再根据需要增加代理资源。

用代理后本机就绝对安全了吗?

不是。目标站点看到的可能是代理出口 IP,但账号、Cookie、请求行为、浏览器指纹、访问路径仍然可能暴露异常特征。代理只能隔离一部分 IP 风险,不能替代合规边界和工程控制。

和课程代码怎么衔接

这篇文章对应课程里“代理的使用”这一段,适合放在 requests、Session/Cookie、验证码、异步爬虫之后学习。课程同步代码仓库在 Gitee:xfeicode/crawler,里面按“爬虫入门、动态网站突破、异步爬虫、验证码识别”等方向组织示例。

建议学习顺序是:

  1. 先掌握 requests 基础请求和异常处理。
  2. 再学 Session、Cookie、请求头排查。
  3. 接着理解动态网页和 Playwright。
  4. 最后再把代理 IP 放进完整爬虫流程里。

这样你遇到 403 时,才不会只会“换 IP”,而是能判断到底是请求写错、状态丢失、频率过高,还是确实触发了 IP 维度的限制。

Practice

读完这一节,去靶场里验证一下。

去挑战广场练习