[
6月8日
-
9分钟阅读
[
拯救
如何在Python中旋转代理服务器
使用Python建立一个自定义的代理轮换器,以避免在网络搜刮时被封锁。从一个自动健康检查的IP池中随机挑选。

一个代理可以隐藏你的IP,但当它被禁止时,会发生什么?你将需要一个新的IP。或者你可以维护一个列表,为每个请求轮换代理。最后的选择是使用智能轮换代理,后面会有更多介绍。
现在,我们将专注于建立我们的自定义代理轮换器。我们将从一个常规代理列表开始,检查它们以标记工作的代理,并提供简单的监控以从工作列表中删除失败的代理。所提供的例子使用了Python,但这个想法在任何语言中都是可行的。
让我们深入了解一下吧
前提条件
为了使代码工作,你需要安装python3。有些系统已经预装了它。之后,通过运行pip install ,安装所有必要的库。
pip install aiohttp
代理列表
你可能没有一个带有域名+端口列表的代理供应商。不要担心,我们将看到如何获得一个。
网上有几个免费的代理列表。对于演示,抓住其中的一个,并将其内容(只是URLs)保存在一个文本文件(rotating_proxies_list.txt )。或者使用下面的那些。

从网站上导出并复制代理服务器
免费代理并不可靠,下面的代理可能对你不起作用。他们通常都是短命的。
167.71.230.124:8080192.155.107.211:108077.238.79.111:8080167.71.5.83:3128195.189.123.213:31288.210.83.33:8080.48.119.28:8080152.0.209.175:8080187.217.54.84:80169.57.1.85:8123
然后,我们将读取该文件并创建一个包含所有代理的数组。读取文件,剥离空位,并分割每一行。保存文件时要小心,因为我们不会对有效的IP:port 字符串进行任何理智检查。我们将保持简单。
proxies_list = open("rotating_proxies_list.txt", "r").read().strip().split("\n")
检查代理机构
让我们假设我们想大规模地运行搜刮器。这个演示是简化的,但是我们的想法是将代理和它们的 "健康状态 "存储在一个可靠的媒介中,比如数据库。我们将使用内存中的数据结构,在每次运行后都会消失,但你会明白这个意思。
首先,让我们写一个简单的函数来检查代理是否工作。为此,调用ident.me,它将返回IP。这是一个简单的页面,符合我们的用例。我们将使用 [asyncio](https://docs.python.org/3/library/asyncio.html)和 [aiohttp](https://docs.aiohttp.org/en/stable/),一个类似于著名的requests 的 "异步HTTP客户端/服务器"。它更适合我们,因为它的目的是异步工作,而且在同时检查几个代理时它会帮助我们。
目前,它从代理列表中抽取一个项目并调用所提供的URL。大部分的代码都是模板,很快就会被证明是有用的。有两种可能的结果。
- 如果一切顺利,它会打印出响应的内容和状态代码(即200),这可能是代理的IP。
- 由于超时或其他原因,会打印出一个错误。它通常意味着代理机构不可用或不能处理该请求。在使用免费代理时,会出现许多这样的错误。
import aiohttpimport asyncio
我们故意使用HTTP而不是HTTPS,因为许多免费代理不支持SSL。
添加更多的检查来验证结果
异常意味着请求失败,但还有其他的选项我们应该检查,比如状态代码。我们将只考虑有效的特定代码,其余的标记为错误。这个列表不是一个详尽的列表,请根据你的需要调整它。例如,你可能认为404 "未找到 "不是有效的,应该再次测试。
我们还可以添加其他检查,比如验证响应是否包含IP地址。
VALID_STATUSES = [200, 301, 302, 307, 404]
遍历所有代理机构
很好!我们现在需要对所有代理进行检查。我们现在需要对数组中的每个代理运行检查。我们将在代理列表中循环调用get ,就像以前一样。但是,我们将使用asyncio.gather 来启动所有的请求并等待它们完成,而不是按顺序进行。异步使代码更加复杂,但它能加快网络刮削的速度。
为了安全起见,列表被硬编码为最多获得10个项目,以避免数百个非自愿请求。
async def check_proxies(): proxies = proxies_list[0:10] # limited to 10 to avoid too many requests async with aiohttp.ClientSession() as session: tasks = [ get("http://ident.me/", session, proxy=proxy) for proxy in proxies ] await asyncio.gather(*tasks, return_exceptions=True)
我们还应该限制并发请求的数量。我们将使用Semaphore来做到这一点,这是一个将获得和释放锁的对象。它将维护一个内部计数器,只允许这么多的调用(在本例中为10),从而创造一个最大的并发量。
我们也需要改变如何调用check_proxies 。
sem = asyncio.Semaphore(10)# ...async def get(url, session, proxy): async with sem: try: # ...
从失败的代理中分离出工作的代理
检查输出日志远远不够理想,不是吗?我们应该为代理列表保留一个内部状态。我们将把它们分成三组。
- unchecked:未知状态,有待检查。
- working:最后一次使用这个代理的调用是成功的。
- not working: the last request failed.
与数组相比,从sets中添加或删除项目更容易,而且它们有避免重复的优点。我们可以在列表之间移动代理,而不用担心同一个代理会出现两次。如果它存在,它就不会被添加。这将简化我们的代码:从一个集合中删除一个项目,并将其添加到另一个集合中。为了实现这一点,我们需要稍微修改代理存储。
将存在三个集合,上面看到的每个组都有一个。最初的一个,unchecked ,将包含文件中的代理。一个集合可以从一个数组中初始化,使我们很容易创建它。
proxies_list = open("rotating_proxies_list.txt", "r").read().strip().split("\n")unchecked = set(proxies_list[0:10]) # limited to 10 to avoid too many requests# unchecked = set(proxies_list)working = set()not_working = set()
现在,编写辅助函数来在不同的状态之间移动代理。每个状态都有一个辅助函数。它们将把代理添加到一个集合中,并把它--如果存在的话--从另外两个集合中移除。这里是集合的用武之地,因为我们不需要担心检查代理是否存在或在数组上循环。调用 "丢弃 "来删除,如果存在或被忽略,但不会引发异常。
例如,当一个请求成功时,我们将调用set_working 。而该函数将从未选中或不工作的集合中删除代理,同时将其添加到工作集合中。
def reset_proxy(proxy): unchecked.add(proxy) working.discard(proxy) not_working.discard(proxy)
我们错过了关键的部分!我们需要编辑get ,在每次请求后调用这些函数。set_working 用于成功的请求,set_not_working 用于其余的请求。
async def get(url, session, proxy): async with sem: try: async with session.get(url, proxy=f"http://{proxy}", timeout=timeout) as response: if response.status in VALID_STATUSES: # valid proxy set_working(proxy) else: set_not_working(proxy) except Exception as e: set_not_working(proxy)
目前,在脚本的结尾处添加一些痕迹,看看它是否运行良好。unchecked 集应该是空的,因为我们运行了所有的项目。而这些项目将填充到其他两个集合中。希望working 不是空的 😅 - 这可能发生在免费代理上。
#...loop = asyncio.get_event_loop()loop.run_until_complete(check_proxies())
使用工作中的代理机构
这是一个检查代理的直接方法,但还不是真正有用。我们现在需要一种方法来获得工作代理,并将它们用于真正的原因:网络搜刮实际内容。我们将创建一个函数,选择一个随机的代理。
在我们的例子中,我们包括了工作和未检查的代理,如果适合你的需要,请随意只使用工作代理。稍后我们将看到为什么未被选中的也会出现。
random 对集合不起作用,所以我们要用 来转换它们。tuple
import random
接下来,我们可以编辑get 函数,如果没有的话,就使用一个随机代理。proxy 参数现在是可选的。我们将使用该参数来检查初始代理,就像我们之前所做的那样。但在那之后,我们可以忘记代理列表,在没有它的情况下调用get 。一个随机的代理将被使用,并在失败的情况下添加到not_working 集。
由于我们现在想获得实际的内容,我们需要返回响应或引发异常。有了aiohttp ,与requests 不同,响应的内容必须是await 。下面是最终的版本。
async def get(url, session, proxy=None): if not proxy: proxy = get_random_proxy()
在脚本下面包括你想搜刮的内容。我们将只是再次调用相同的测试URL来进行演示。
我们的想法是,从这里开始,在这个主干上建立一个真实世界的搜刮器。为了扩大规模,将项目存储在持久性存储中,如数据库(即Redis)。
#....loop = asyncio.get_event_loop()loop.run_until_complete(check_proxies())
对于假阴性或一次性错误会发生什么?一旦我们将一个代理发送到not_working 集,它将永远留在那里。没有回头路了。
重新检查不工作的代理
我们应该不时地重新检查失败的代理。有很多原因:失败是由于网络问题,一个错误,或者代理提供者修复了它。
在任何情况下,Python允许我们设置 [Timers](https://docs.python.org/3/library/threading.html#timer-objects),"一个只在一定时间后运行的动作"。有不同的方法来达到同样的目的,这很简单,用三行就可以运行。
还记得reset_proxy 功能吗?我们直到现在都没有使用它。我们将设置一个Timer ,为每个被标记为不工作的代理运行该功能。20秒对于现实世界的情况来说是一个小数字,但对于我们的测试来说足够了。我们排除一个失败的代理,并在一段时间后将其移回未选中。
这就是在get_random_proxy 中同时使用工作和未检查集的原因。修改该函数,只使用工作中的代理,以获得更强大的用例。然后,你可以定期运行check_proxies ,它将循环检查未检查的元素--在这种情况下,失败的代理在罪恶仓中停留了一段时间。
from threading import Timer
对于更强大的系统,还有一个最后的选择,但我们将把实施权留给你。存储每个代理的分析和使用情况,例如,它失败的次数和最后一次是什么时候。利用这些信息,调整重新检查的时间--对于多次失败的代理,要延长时间。如果工作代理的数量低于阈值,甚至可以设置一些警报。
总结
构建一个普通的代理轮换器对于小的搜刮脚本来说似乎是可以做到的,但它会变得很痛苦。但是,嘿,你做到了!!。
这些是我们遵循的步骤。
- 将代理列表存储为纯文本
- 以数组形式从文件中导入
- 检查其中的每一个
- 分离出工作的代理
- 检查失败的代理,并把它们从工作列表中删除。
- 不时地重新检查不工作的代理。
需要注意的是,在搜刮登录或任何其他类型的会话/cookies时,不要轮流使用IP。
如果你不想担心手动旋转代理,你总是可以使用我们的ZenRows,一个包括智能旋转代理的网络刮削API。它像普通代理一样工作--有一个单一的URL--但为每个请求提供不同的IP。
谢谢你的阅读。
原文发表于 https://www.zenrows.com