Python构建代理IP池:免费IP抓取与Crawler类实现

周叙
周叙
发布时间:2026-10-13 | 6人浏览过
原创

本文介绍如何使用Python构建代理IP池的第一步:获取IP。通过结合Requests和PyQuery库,利用自定义的ProxyMetaclass元类自动识别以crawl开头的方法,实现从多个免费代理网站批量抓取IP和端口。代码采用生成器模式返回数据,便于后续存储与检测,为反爬策略提供灵活的IP轮换方案。

代理IP池构建背景与必要性

在进行网页爬虫开发时,绝大多数目标网站都部署了反爬机制,常见手段包括限制单一IP的访问频率或总次数。一旦超出阈值,该IP将被封禁。针对访问频率限制,通常只需在请求间设置合理的时间间隔即可规避;但针对访问次数限制,单纯延时无法解决,此时引入代理IP池成为必要手段,通过轮换使用多个代理IP访问目标地址,可有效绕过此类限制。

当前网络上有大量提供代理服务的平台,部分提供免费代理,但稳定性和可用性往往较差。若业务对代理质量要求较高,建议购买付费代理服务,其可用性和响应速度通常更有保障。

为降低成本并提高可控性,可自行构建代理池。具体流程是从各类代理服务网站抓取代理IP,利用稳定网址(最好是目标爬取网站本身)检测其可用性,并将有效IP存入数据库,供后续调用。

完整代码仓库地址:https://github.com/Stevengz/Proxy_pool

本系列其他文章链接: Python搭建代理IP池(二)- 存储 IP Python搭建代理IP池(三)- 检测 IP Python搭建代理IP池(四)- 接口设置与整体调度

本文重点讲解构建代理IP池的基础环节:IP获取。

主要依赖的Python库包括:requests用于网络请求,pyquery用于HTML解析。

免费代理源与数据抓取实现

以下列举部分提供免费代理的服务平台(排名不分先后):

厂商名称 地址
66代理 http://www.66ip.cn/
西刺代理 https://www.xicidaili.com
全网代理 http://www.goubanjia.com
云代理 http://www.ip3366.net
IP海 http://www.iphai.com
快代理 https://www.kuaidaili.com
免费代理IP库 http://ip.jiangxianli.com

代理服务网站列表

代理获取的核心代码逻辑:将各网站的IP提取方法整合,运行时只需调用相应方法即可获取数据。

为保证代码扩展性,统一规定获取代理的方法以“crawl”为前缀。未来新增代理源时,只需添加以crawl开头的新方法即可,无需修改核心逻辑。

本文实现了几个示例方法,分别抓取66代理、西刺代理、云代理和快代理这四个免费网站的IP。所有方法均定义为生成器,通过yield语句返回解析后的“IP:端口”格式数据。流程为先获取网页源码,再使用PyQuery解析提取。

crawler.py模块说明

Crawler类与元类设计


import json
import re
from utils import get_page
from pyquery import PyQuery as pq

# 元类
class ProxyMetaclass(type):
 def __new__(cls, name, bases, attrs):
  count = 0
  attrs['__CrawlFunc__'] = []
  for k, v in attrs.items():
   if 'crawl_' in k:
    attrs['__CrawlFunc__'].append(k)
    count += 1
  attrs['__CrawlFuncCount__'] = count
  return type.__new__(cls, name, bases, attrs)

class Crawler(object, metaclass=ProxyMetaclass):
 def get_proxies(self, callback):
  proxies = []
  for proxy in eval("self.{}()".format(callback)):
   print('成功获取到代理', proxy)
   proxies.append(proxy)
  return proxies

 def crawl_daili66(self, page_count=4):
  start_url = 'http://www.66ip.cn/{}.html'
  urls = [start_url.format(page) for page in range(1, page_count + 1)]
  for url in urls:
   print('Crawling', url)
   html = get_page(url)
   if html:
    doc = pq(html)
    trs = doc('.containerbox table tr:gt(0)').items()
    for tr in trs:
     ip = tr.find('td:nth-child(1)').text()
     port = tr.find('td:nth-child(2)').text()
     yield ':'.join([ip, port])

 def crawl_xicidaili(self):
  for i in range(1, 3):
   start_url = 'http://www.xicidaili.com/nn/{}'.format(i)
   headers = {
    'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    'Host':'www.xicidaili.com',
    'Referer':'http://www.xicidaili.com/nn/3',
    'Upgrade-Insecure-Requests':'1',
   }
   html = get_page(start_url, options=headers)
   if html:
    find_trs = re.compile('<tr class.*?>(.*?)</tr>', re.S)
    trs = find_trs.findall(html)
    for tr in trs:
     find_ip = re.compile('<td>(\d+\.\d+\.\d+\.\d+)</td>') 
     re_ip_address = find_ip.findall(tr)
     find_port = re.compile('<td>(\d+)</td>')
     re_port = find_port.findall(tr)
     for address,port in zip(re_ip_address, re_port):
      address_port = address+':'+port
      yield address_port.replace(' ','')

 def crawl_ip3366(self):
  for i in range(1, 4):
   start_url = 'http://www.ip3366.net/?stype=1&page={}'.format(i)
   html = get_page(start_url)
   if html:
    find_tr = re.compile('<tr>(.*?)</tr>', re.S)
    trs = find_tr.findall(html)
    for s in range(1, len(trs)):
     find_ip = re.compile('<td>(\d+\.\d+\.\d+\.\d+)</td>')
     re_ip_address = find_ip.findall(trs[s])
     find_port = re.compile('<td>(\d+)</td>')
     re_port = find_port.findall(trs[s])
     for address,port in zip(re_ip_address, re_port):
      address_port = address+':'+port
      yield address_port.replace(' ','')

 def crawl_kuaidaili(self):
  for i in range(1, 4):
   start_url = 'http://www.kuaidaili.com/free/inha/{}/'.format(i)
   html = get_page(start_url)
   if html:
    ip_address = re.compile('<td data-title="IP">(.*?)</td>') 
    re_ip_address = ip_address.findall(html)
    port = re.compile('<td data-title="PORT">(.*?)</td>')
    re_port = port.findall(html)
    for address,port in zip(re_ip_address, re_port):
     address_port = address+':'+port
     yield address_port.replace(' ','')

代码中定义了一个ProxyMetaclass元类,Crawl类将其设为元类。在元类的__new__方法中,遍历类的属性字典,若方法名包含“crawl_”,则将其加入CrawlFunc列表属性中,从而自动注册所有抓取方法。

该架构支持灵活添加代理源,不仅限于免费代理,也可接入付费代理。付费代理的提取逻辑类似,通过Web请求获取数据后解析(可能更简单,如解析纯文本或JSON),最终以相同格式返回,支持用户自行扩展。

utils.py模块说明

网络请求工具封装


import requests
from requests.exceptions import ConnectionError

base_headers = {
 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36',
 'Accept-Encoding': 'gzip, deflate, sdch',
 'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7'
}

def get_page(url, options={}):

 headers = dict(base_headers, **options)
 print('正在抓取', url)
 try:
  response = requests.get(url, headers=headers)
  print('抓取成功', url, response.status_code)
  if response.status_code == 200:
   return response.text
 except ConnectionError:
  print('抓取失败', url)
  return None

封装了get_page函数,用于访问指定链接并返回网页HTML内容,便于后续提取具体数据。该函数供各crawler模块统一调用。

执行抓取操作

getter.py模块说明

主程序调度与执行


from crawler import Crawler
from setting import *
import sys

class Getter():
 def __init__(self):
  self.crawler = Crawler()
 
 def run(self):
  print('获取器开始执行')
  for callback_label in range(self.crawler.__CrawlFuncCount__):
   callback = self.crawler.__CrawlFunc__[callback_label]
   # 获取代理
   all_ip = self.crawler.get_proxies(callback)

if __name__ == '__main__':
 get = Getter()
 get.run()

程序运行输出示例

成功抓取到网站上的免费IP,但其实际可用性尚需后续步骤验证。

整体流程本质上是一个普通的爬虫程序,且目标网站反爬措施较弱。既然已涉及代理IP的使用,代码中的访问与抓取细节应易于理解。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表