如何在 Python 中旋转代理

掌握 Python 中的代理轮换技术,绕过 IP 封禁并提升你的网络爬取流程效率。
4 min read
如何在 Python 中旋转代理

在本指南中,你将学到:

  • 什么是代理以及为什么要使用代理
  • 什么是代理轮换以及何时需要它
  • 如何在 Python 中旋转代理
  • 在 Python 中旋转代理时常见的限制

让我们开始吧!

什么是代理?

代理是一种服务器,它在用户与网络资源之间充当中介。在 Internet 上,你可以把代理视作一个“中间人”,在请求方和被请求方之间转发请求和响应。

为什么在 Python 中使用代理?

当你使用代理向某网站发送请求时,请求会先经过代理服务器,然后由代理将请求转发给网站,并将响应返回给你。这个过程会隐藏你的真实 IP,使网站看来请求来自代理服务器而不是你的设备。

通常在网页请求自动化或网络爬取的场景下,你会想要使用代理。在这些场景里,Python 拥有丰富的库以及庞大的社区支持,是最适合进行网页爬取的语言之一

什么是代理轮换?为什么需要它?

如果从同一 IP 地址发出过多请求,网站可能会通过速率限制或直接封禁 IP。在这种情况下,使用代理来进行代理轮换就能派上用场。

通过系统地在不同代理服务器之间切换来发送网络请求,是IP 地址轮换的最佳方式之一,这么做能够帮助你绕过常见的反爬虫策略,带来如下好处:

  • 避免 IP 被封禁:将请求分散到多个 IP,让网站更难检测并封禁你的爬取行为。
  • 绕过速率限制:许多网站会对单个 IP 在一定时间内的请求数量进行限制。代理轮换可以让你在某个 IP 达到请求上限后,继续使用其他 IP 进行爬取。
  • 访问受地理位置限制的内容:一些网站会根据地理位置显示不同内容。使用来自不同国家/地区的代理来轮换,可以获取特定地区的内容。

在 Python 中如何旋转代理:三种方法

现在你已经了解了什么是代理以及为什么要对其进行轮换,接下来我们将从实践出发,使用不同的方式和库介绍如何在 Python 中旋转代理。

三个脚本示例中都会用到相同的目标站点,即/ip端点,它会返回调用方的 IP 地址,非常适合用来测试代理是否在轮换。

让我们正式开始在 Python 中旋转代理吧!

环境要求

要复制文中的示例,你需要在本机安装 Python 3.7 及以上版本

先决条件

假设你的主项目文件夹名为 proxy_rotation/。在完成所有操作后,目录结构将如下所示:

proxy_rotation/
    ├── requests_file.py
    ├── async.py
    ├── scrapy_rotation/
    └── venv/

其中:

  • requests_file.pyasync.py 分别存储使用 Requests 和 AIOHTTP 进行代理轮换的逻辑。
  • scrapy_rotation/ 是一个 Scrapy 项目文件夹,稍后会创建和初始化。
  • venv/ 包含虚拟环境。

你可以按以下方式创建 venv/ 虚拟环境

python -m venv venv

在 Windows 上,激活环境:

venv\Scripts\activate

在 macOS 或 Linux上,执行:

source venv/bin/activate

最后,你需要获取一个代理列表。本文示例可使用我们的 免费代理列表

如何使用 Requests 在 Python 中旋转代理

在此部分,你将学习如何使用 Requests 库来实现 Python 中的代理轮换。

步骤 #1:安装依赖

在已激活的虚拟环境中,运行:

pip install requests

步骤 #2:定义轮换逻辑

requests_file.py 文件中添加如下代码:

import random
import requests

# Define a list of proxies and return a random one
def get_random_proxy():
    proxies = [
        "http://PROXY_1:PORT_X",
        "http://PROXY_2:PORT_Y",
        "http://PROXY_3:PORT_X",
        # Add more proxies here...
    ]

    # Randomly pick a proxy
    return random.choice(proxies)

for i in range(3):
    proxy_url = get_random_proxy()
    proxies = {
        "http": proxy_url,
        "https": proxy_url,
    }
    response = requests.get("https://httpbin.io/ip", proxies=proxies)
    print(response.text)

说明:

  • get_random_proxy() 函数存储了你获取的代理列表,并使用 random.choice() 随机返回其中一个。
  • for 循环会遍历随机选出的代理列表,并通过 requests.get() 方法发送实际请求。更多用法可参考我们关于在 Python Requests 中使用代理的指南

步骤 #3:运行脚本

执行:

python requests_file.py

预期返回结果示例:

{
  "origin": "PROXY_3:PORT_K"
}
{
  "origin": "PROXY_1:PORT_N"
}
{
  "origin": "PROXY_2:PORT_P"
}

完美!你的脚本在请求时已成功轮换了不同的出口 IP。

如何使用 AIOHTTP 在 Python 中旋转代理

使用 Requests 随机选择代理的主要限制在于一次只能使用一个代理,需要等某次请求结束后才能开始下一次代理使用。

为避免这一限制,可以使用 AIOHTTP。该库允许你执行异步请求,从而以非阻塞方式同时使用多个代理。换言之,你可以通过并发方式发送请求并在其中轮换你的代理列表。详见我们关于异步网络爬取的介绍。

以下部分展示如何在 Python 中使用 AIOHTTP 进行代理轮换。

步骤 #1:安装依赖

在已激活的虚拟环境中,执行:

pip install aiohttp

步骤 #2:定义轮换逻辑

要在 Python 中使用 AIOHTTP 来旋转代理,请在 async.py 文件中写入以下代码:

import asyncio
import aiohttp

# Define a list of proxies
proxies_list = [
    "http://PROXY_1:PORT_X",
    "http://PROXY_2:PORT_Y",
    "http://PROXY_3:PORT_X",
    # Add more proxies here...
]

async def fetch_ip(session, proxy_address, attempt):
    print(f"Attempt {attempt} using proxy: {proxy_address}")
    async with session.get("https://httpbin.io/ip", proxy=proxy_address) as response:
        json_response = await response.json()
        print(f"Response from httpbin.io/ip (Attempt {attempt}):")
        print(f"IP Address: {json_response.get('origin', 'Unknown')}")
        print("-" * 40)
        return json_response

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = []
        num_attempts = 3
        for i in range(num_attempts):
            # Rotate proxies using the modulus operator.
            proxy_address = proxies_list[i % len(proxies_list)]
            tasks.append(fetch_ip(session, proxy_address, i + 1))
        # Run all requests concurrently
        await asyncio.gather(*tasks)

# Launch the script
asyncio.run(main())

此代码实现了以下功能:

  • fetch_ip() 函数:接收 session、代理和尝试次数作为参数,向目标网站发起 GET 请求并打印响应结果。
  • main() 函数:
    • 通过 aiohttp.ClientSession() 方法创建 session 来管理 HTTP 连接。
    • 设置一个任务列表来并发发送多个请求,以及请求次数。
    • 使用取模运算(i % len(proxies_list))进行轮询,以便在请求数量大于代理数量时也能重用代理。更多用法可阅读我们关于在 AIOHTTP 中设置代理的教程。
    • 使用 asyncio.gather() 方法并发执行所有任务。

步骤 #3:运行脚本

执行:

python async.py

预期输出结果:

Attempt 1 using proxy: http://PROXY_1:PORT_X
Attempt 2 using proxy: http://PROXY_2:PORT_Y
Attempt 3 using proxy: http://PROXY_3:PORT_Z

Response from httpbin.io/ip (Attempt 3):
IP Address: xxx.xxx.xxx.xxx
----------------------------------------
Response from httpbin.io/ip (Attempt 1):
IP Address: yyy.yyy.yyy.yyy
----------------------------------------
Response from httpbin.io/ip (Attempt 2):
IP Address: zzz.zzz.zzz.zzz
----------------------------------------

非常好!各次请求均成功使用不同 IP。

如何配合 Python Scrapy 进行代理轮换

在一篇早前文章中,我们提到可以通过Scrapy及其scrapy-rotating-proxies库来实现 Python 中的代理轮换。

下面便是相关操作的简要实践!

步骤 #1:安装依赖

在已激活的虚拟环境中,执行:

pip install scrapy scrapy-rotating-proxies

步骤 #2:创建新的 Scrapy 项目

在主项目文件夹 proxy_rotation/ 内,使用以下命令初始化一个新的 Scrapy 项目:

scrapy startproject scrapy_rotation

上述命令会新建一个名为 scrapy_rotation/ 的子文件夹,其结构大致如下:

scrapy_rotation/
  ├── scrapy_rotation/
  │   ├── __init__.py
  │   ├── items.py # Defines the data structure for scraped items
  │   ├── middlewares.py # Custom middlewares
  │   ├── pipelines.py # Handles post-processing of scraped data
  │   ├── settings.py # Project settings
  │   └── spiders/ # Folder for all spiders
  └── scrapy.cfg # Scrapy configuration file

然后进入 scrapy_rotation/ 文件夹:

cd scrapy_rotation

接下来你可以创建一个新的爬虫来访问目标网站:

scrapy genspider rotation http://httpbin.io/ip

这条命令会在 spiders/ 目录下生成 rotation.py 文件。

步骤 #3:定义轮换逻辑

可以在 settings.py 文件中添加以下设置来管理代理轮换:

# Enable the rotating proxies middleware
DOWNLOADER_MIDDLEWARES = {
    "rotating_proxies.middlewares.RotatingProxyMiddleware": 610,
    "rotating_proxies.middlewares.BanDetectionMiddleware": 620,
}

# List of proxies to rotate
ROTATING_PROXY_LIST = [
    "http://PROXY_1:PORT_X",
    "http://PROXY_2:PORT_Y",
    "http://PROXY_3:PORT_Z",
    # Add more proxies as needed
]

# Configure retry settings
RETRY_TIMES = 5  # Number of retries for failed requests
RETRY_HTTP_CODES = [500, 502, 503, 504, 408]  # HTTP codes to retry

实现代理轮换的关键是 DOWNLOADER_MIDDLEWARES 中的 rotating_proxies.middlewares.RotatingProxyMiddleware: 610 配置,它负责从 ROTATING_PROXY_LIST 中选择代理,并为每个请求分配一个代理。

同时,rotating_proxies.middlewares.BanDetectionMiddleware: 620 配置可帮助识别 IP 是否被目标网站封禁。如果请求失败,该中间件会使用新代理重试请求。它与 RotatingProxyMiddleware 协同工作,自动避免已被封禁的代理。

接着,在 spiders/ 文件夹中的 rotation.py 中编写以下内容:

import scrapy

class IpSpider(scrapy.Spider):
    name = "ip_spider"
    start_urls = ["http://httpbin.io/ip"]
    def parse(self, response):
        # Extract and print the IP address from the response
        ip = response.json().get("origin")
        self.log(f"IP Address: {ip}")

该类会启动爬虫并在每次请求时打印返回的 IP。

步骤 #4:运行脚本

使用 IpSpider 类的名称(即 ip_spider)来启动爬虫:

scrapy crawl ip_spider

Scrapy 在命令行输出的信息会比较详细,如果一切正常,你将在大量输出信息中看到类似:

2025-02-18 14:55:17 [rotating_proxies.expire] DEBUG: Proxy <http://PROXY_1:PORT_X> is GOOD
2025-02-18 14:55:17 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://httpbin.io/robots.txt> (referer: None)
2025-02-18 14:55:24 [rotating_proxies.middlewares] INFO: Proxies(good: 1, dead: 0, unchecked: 2, reanimated: 0, mean backoff time: 0s) unchecked: 2, reanimated: 0, mean backoff time: 0s)

上述代理轮换方法的局限性

以上提到的几种代理轮换方法都非常实用,但也存在一些局限:

  • 需要手动获取和管理代理列表。
  • 包含不少模板化的代码。
  • 如果代理本身不够优质,依旧有被禁 IP 的可能性。

如果你想要更高效、稳定的代理轮换方式,Bright Data 提供了市面上最佳的旋转代理之一。只需要一个单一的代理 URL 袖手整合到你的 HTTP 客户端或爬取库,就可以免去模板化轮换代码和手动管理的麻烦。

此方法的其它关键好处包括:

  • 自动 IP 轮换,支持设置固定 IP
  • 可使用 72+ 百万住宅 IP
  • 可控制代理服务器所在的地理位置
  • 支持 HTTP、HTTPS 和 SOCKS 协议

让代理管理更轻松——了解我们的 自动旋转代理

总结

本文介绍了如何使用 Requests、AIOHTTP 和 Scrapy 这三种库,在 Python 中实现代理轮换。通过上文详述的步骤,你可以在代码里轻松添加轮换逻辑。

但同时,也需注意:

  • 上述方法包含不少相关的“样板代码”,可维护性较低。
  • 你需要自己管理并维护一个庞大的代理列表。

幸运的是,通过 Bright Data 的自动旋转代理,你可以避免这些麻烦,从而更高效地在 Python 中进行代理轮换。

Bright Data 拥有全世界顶尖的代理网络资源,为 500 强企业及逾 2 万客户提供服务,提供多种类型的代理:

立即注册 Bright Data 的免费账号来测试我们各种代理与爬取解决方案吧!