Python爬虫实现搭建代理ip池
在进行爬虫开发时,我们经常需要使用代理 IP 来避免被目标网站封禁或反爬虫。但是,使用免费代理 IP 很容易被封禁,所以我们需要自己搭建一个代理 IP 池,以便更好地应对反爬虫策略。
本文将介绍用 Python 实现搭建代理 IP 池的步骤。具体来说,我们将介绍如何爬取免费代理 IP,并将其存储到数据库中,然后编写一个代理 IP 池模块,以便我们在爬虫中使用这些代理 IP。
## 爬取免费代理 IP
首先,我们需要爬取免费代理 IP。以西刺代理(https://www.xicidaili.com/)为例,它提供了免费的 HTTP 和 HTTPS 代理 IP。我们可以使用 requests 库和 Beautiful Soup 库来抓取代理 IP。
import requests
from bs4 import BeautifulSoup
def get_proxy_ips():
"""从西刺代理抓取 HTTP 和 HTTPS 代理 IP"""
url = 'https://www.xicidaili.com/wn/'
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
}
proxies = []
for i in range(1, 6): # 爬取前五页的代理 IP
resp = requests.get(url + str(i), headers=headers)
soup = BeautifulSoup(resp.text, 'html.parser')
table = soup.find(name='table', attrs={'id': 'ip_list'})
tr_list = table.find_all(name='tr')[1:] # 个 tr 是表头,忽略掉
for tr in tr_list:
td_list = tr.find_all(name='td')
ip = td_list[1].text
port = td_list[2].text
protocol = td_list[5].text.lower()
proxies.append(f'{protocol}://{ip}:{port}')
return proxies
以上代码中,我们定义了一个 get_proxy_ips 函数,它从西刺代理抓取前 5 页的 HTTP 和 HTTPS 代理 IP。我们使用 requests 库发送请求,并使用 Beautiful Soup 库解析 HTML 页面。然后,我们从页面的表格中提取代理 IP 的部分信息(ip, port, protocol),并将其存储到一个列表中。最后,我们返回这个代理 IP 列表。
## 存储代理 IP 到数据库
我们需要一个数据库,来存储从网上获取的代理 IP。我们将使用 MongoDB,一个 NoSQL 数据库。首先,我们安装 pymongo 库:pip install pymongo。
然后,我们创建一个名为 proxypool 的数据库,并在其中创建一个名为 proxies 的集合,用于存储代理 IP。在 PyMongo 中,一个 MongoDB 数据库对应一个 MongoClient 对象,一个 MongoDB 集合对应一个 Collection 对象。
import pymongo
class MongoDBProxyPool:
"""基于 MongoDB 的代理 IP 池"""
def __init__(self):
self.client = pymongo.MongoClient('localhost', 27017)
self.db = self.client['proxypool']
self.collection = self.db['proxies']
def add_proxies(self, proxies):
"""将代理 IP 添加到 MongoDB 数据库"""
for proxy in proxies:
self.collection.update_one({'ip': proxy}, {'$set': {'ip': proxy}}, upsert=True)
def get_proxies(self):
"""从 MongoDB 数据库获取代理 IP"""
proxies = []
cursor = self.collection.find()
for c in cursor:
proxies.append(c['ip'])
return proxies
def clear_proxies(self):
"""清空 MongoDB 数据库"""
self.collection.delete_many({})
以上代码中,我们定义了一个 MongoDBProxyPool 类。在构造函数中,我们初始化了一个 MongoDB 客户端、一个数据库对象和一个集合对象。我们实现了三个方法:
- add_proxies:将一个代理 IP 添加到 MongoDB 集合中。MongoDB 的 update_one 方法会尝试更新一个文档,如果文档不存在,将插入一个新文档。upsert=True 用于控制文档不存在时自动插入。
- get_proxies:从 MongoDB 集合中获取所有的代理 IP,并返回它们的列表。
- clear_proxies:清空 MongoDB 集合中的内容。
## 搭建代理 IP 池
我们需要一个代理 IP 池模块,使我们可以在爬虫中使用免费代理 IP。实现一个代理 IP 池有许多细节问题,比如:
- 如何检测代理 IP 是否可用?
- 如何保证代理 IP 足够多?
- 如何实现轮询代理 IP?
以下是一个简单的代理 IP 池实现,它可以帮助你理解这些问题。
import random
import requests
import time
from multiprocessing import Process, Lock
from ProxyPool import MongoDBProxyPool
class ProxyPool:
"""代理 IP 池"""
def __init__(self, capacity=100):
self.capacity = capacity # 代理 IP 池的容量
self.pool = [] # 代理 IP 池
self.lock = Lock() # 进程锁
self.mongo = MongoDBProxyPool()
def get_random_proxy(self):
"""随机从代理 IP 池中获取一个代理 IP"""
with self.lock:
if len(self.pool) == 0:
self.pool = self.mongo.get_proxies()
if len(self.pool) == 0:
raise ValueError('Proxy pool is empty')
return random.choice(self.pool)
def fetch_proxies(self):
"""更新代理 IP 池中的代理 IP"""
with self.lock:
self.mongo.clear_proxies()
while True:
proxies = get_proxy_ips()
self.mongo.add_proxies(proxies)
self.pool = self.mongo.get_proxies()
if len(self.pool) >= self.capacity:
break
time.sleep(10)
def start(self):
"""定期更新代理 IP 池"""
p = Process(target=self.fetch_proxies)
p.start()
def close(self):
"""关闭代理 IP 池"""
with self.lock:
self.mongo.client.close()
if __name__ == '__main__':
proxy_pool = ProxyPool()
proxy_pool.start()
time.sleep(1)
while True:
proxy = proxy_pool.get_random_proxy()
print(proxy)
resp = requests.get('http://httpbin.org/ip', proxies={'http': proxy})
print(resp.text)
time.sleep(3)
以上代码中,我们定义了一个 ProxyPool 类。在构造函数中,我们初始化了代理 IP 池、进程锁和 MongoDBProxyPool 对象。我们实现了以下方法:
- get_random_proxy:随机从代理 IP 池中获取一个代理 IP。如果代理 IP 池为空,则从 MongoDB 中获取代理 IP,并将它们添加到代理 IP 池中。
- fetch_proxies:更新代理 IP 池中的代理 IP。这个方法在后台进程中运行。首先,我们调用 clear_proxies 方法清空 MongoDB 数据库中的 Proxy 集合。然后,我们循环调用 get_proxy_ips 函数,获取代理 IP,并存储到 MongoDB 中。我们不断循环这个过程,直到代理 IP 池中的代理 IP 数量达到了设定的容量,并调用 time.sleep(10) 让当前进程挂起 10 秒。
- start:启动一个后台进程,定期更新代理 IP 池。在本例中,我们定期更新代理 IP 池的时间间隔是 10 秒。
- close:关闭代理 IP 池。我们调用 MongoDBProxyPool 对象的 close 方法,关闭 MongoDB 客户端连接。
在 main 函数中
