当前位置:   article > 正文

Python爬虫热点项目之实现代理IP池(IP proxy pool)_ip proxy pool api

ip proxy pool api

代理池概述

代理池就是由多个稳定可用代理IP组成的池子。用来应对ip反爬,而网上的免费代理稳定可用的极少,更有甚者连收费的也不都是稳定可用。

开发环境:

windous,python3,sublime text

使用的主要模块:

requests,lxml,pymongo,Flask

完整源码请前往我的github仓库查看:https://github.com/R2h1/ProxyPool 欢迎star哦!!!

代理池工作流程

文字描述:

代理IP采集模块:抓取代理IP—>校验代理IP的可用性—>可用存储数据库

校验模块:读取数据库的代理IP—>校验代理IP可用性—>更新或删除代理IP

代理API模块:从数据库中获取稳定可用的代理IP,供其他爬虫使用

代理池项目结构: 

项目结构中模块及其作用:

mongo_pool模块:代理IP增删改查模块

proxy_spider包:采集代理IP

httpbin_validator模块:检测代理的可用性—speed,协议类型,匿名程度(原因: 网站上所标注的协议类型和匿名类型是不准确的)

proxy_api模块:提供爬虫或稳定可用代理IP和指定不可用域名的接口

proxy_test模块:获取数据库中代理IP,定期检测可用性

dbmodle模块:代理IP数据模型

main模块:程序入口

http模块:提供随机User-Agent的请求头

log模块:记录日志

settings模块:项目配置文件

项目实现思路

先实现不依赖其他模块的基础模块,然后在实现具体功能模块

1. 实现代理IP的数据模型类(dbmodle.py)

  1. '''
  2. 代理ip数据模型模块
  3. 定义一个类, 继承object
  4. 实现init方法, 负责初始化, 包含如下字段:
  5. ip: 代理的IP地址
  6. port: 代理IP的端口号
  7. protocol: 代理IP支持的协议类型,http是0, https是1, https和http都支持是2
  8. nick_type: 代理IP的匿名程度, 高匿:0, 匿名: 1, 透明:2
  9. speed: 代理IP的响应速度, 单位s
  10. area: 代理IP所在地区
  11. score: 代理IP的评分, 默认分值可以通过配置文件进行配置. 在进行代理可用性检查的时候, 每遇到一次请求失败就减1份, 减到0的时候从池中删除. 如果检查代理可用, 就恢复默认分值
  12. disable_domains: 不可用域名列表, 有些代理IP在某些域名下不可用, 但是在其他域名下可用
  13. 创建配置文件: settings.py; 定义MAX_SCORE = 50,
  14. '''
  15. from settings import MAX_SCORE
  16. class Proxy(object):
  17. def __init__(self,ip,port,protocol=-1,nick_type=-1,speed=-1,area=None,score=MAX_SCORE,disuseble_dommains=[]):
  18. #代理ip的地址
  19. self.ip = ip
  20. #代理ip的端口号
  21. self.port = port
  22. #代理ip支持协议类型:支持http为0,支持https为1,都支持为2
  23. self.protocol = protocol
  24. #代理ip的匿名程度:高匿为0,匿名为1,透明为2
  25. self.nick_type =nick_type
  26. #代理ip的响应速度
  27. self.speed = speed
  28. #代理ip所在地区
  29. self.area = area
  30. #代理ip的评分,衡量代理ip的可用性
  31. self.score =score
  32. #代理ip的不可用域名列表
  33. self.disuseble_dommains =disuseble_dommains
  34. def __str__(self):
  35. #返回数据字符串
  36. return str(self.__dict)

 

2. 实现日志记录模块(log.py)

目的:

  • 能够方便的对程序进行调试
  • 能够记录程序的运行状态
  • 记录错误信息

实现:日志模块在网上有很多现成的实现, 我们开发的时候, 通常不会再自己写; 而是使用拿来主义,拿来用就完了。

  • 把日志模块中的相关配置信息放到配置文件中
  • 修改日志模块代码,使用配置文件中的配置信息
  1. '''
  2. 记录日志的模块
  3. '''
  4. import sys,os
  5. #Python的标准日志模块:logging
  6. import logging
  7. #将上级目录添加到搜索路径中
  8. sys.path.append("../")
  9. from settings import LOG_LEVEL,LOG_FMT ,LOG_DATEFMT,LOG_FILENAME
  10. class Logger(object):
  11. def __init__(self):
  12. #获取一个logger对象
  13. self._logger = logging.getLogger()
  14. #设置format对象
  15. self.formatter = logging.Formatter(fmt=LOG_FMT,datefmt=LOG_DATEFMT)
  16. #设置日志输出——文件日志模式
  17. self._logger.addHandler(self._get_file_handler(LOG_FILENAME))
  18. #设置日志输出——终端日志模式
  19. self._logger.addHandler(self._get_console_handler())
  20. # 4. 设置日志等级
  21. self._logger.setLevel(LOG_LEVEL)
  22. def _get_file_handler(self, filename):
  23. '''
  24. 返回一个文件日志handler
  25. '''
  26. # 获取一个输出为文件日志的handler
  27. filehandler = logging.FileHandler(filename=filename,encoding="utf-8")
  28. # 设置日志格式
  29. filehandler.setFormatter(self.formatter)
  30. # 返回
  31. return filehandler
  32. def _get_console_handler(self):
  33. '''
  34. 返回一个输出到终端日志handler
  35. '''
  36. #获取一个输出到终端的日志handler
  37. console_handler = logging.StreamHandler(sys.stdout)
  38. #设置日志格式
  39. console_handler.setFormatter(self.formatter)
  40. # 返回handler
  41. return console_handler
  42. #属性装饰器,返回一个logger对象
  43. @property
  44. def logger(self):
  45. return self._logger
  46. # 初始化并配一个logger对象,达到单例
  47. # 使用时,直接导入logger就可以使用
  48. logger = Logger().logger
  49. if __name__ == '__main__':
  50. print(logger)
  51. logger.debug("调试信息")
  52. logger.info("状态信息")
  53. logger.warning("警告信息")
  54. logger.error("错误信息")
  55. logger.critical("严重错误信息")

 

3.校验代理IP的协议类型、匿名程度,速度(httpbin_validator.py)

  1. '''
  2. 代理IP速度检查: 就是发送请求到获取响应的时间间隔
  3. 匿名程度检查:
  4. 对 http://httpbin.org/get 或 https://httpbin.org/get 发送请求
  5. if : origin 中有','分割的两个IP就是透明代理IP
  6. if : headers 中包含 Proxy-Connection 说明是匿名代理IP
  7. else : 就是高匿代理IP
  8. 检查代理IP协议类型:
  9. 如果 http://httpbin.org/get 发送请求可以成功, 说明支持http协议
  10. 如果 https://httpbin.org/get 发送请求可以成功, 说明支持https协议
  11. '''
  12. import sys
  13. import time
  14. import requests
  15. import json
  16. sys.path.append("..")
  17. from proxy_utils import random_headers
  18. from settings import CHECK_TIMEOUT
  19. from proxy_utils.log import logger
  20. from dbmodle import Proxy
  21. def check_proxy(proxy):
  22. '''
  23. 分别判断http和https是否请求成功
  24. '''
  25. #代理ip
  26. proxies = {
  27. 'http':'http://{}:{}'.format(proxy.ip,proxy.port),
  28. 'https':'https://{}:{}'.format(proxy.ip,proxy.port),
  29. }
  30. http,http_nick_type,http_speed = http_check_proxies(proxies)
  31. https,https_nick_type,https_speed = http_check_proxies(proxies,False)
  32. if http and https:
  33. proxy.protocol = 2 #支持https和http
  34. proxy.nick_type =http_nick_type
  35. proxy.speed = http_speed
  36. elif http:
  37. proxy.protocol = 0 #只支持http
  38. proxy.nick_type =http_nick_type
  39. proxy.speed = http_speed
  40. elif https:
  41. proxy.protocol = 1 #只支持https
  42. proxy.nick_type =https_nick_type
  43. proxy.speed = https_speed
  44. else:
  45. proxy.protocol = -1
  46. proxy.nick_type = -1
  47. proxy.speed = -1
  48. #logger.debug(proxy)
  49. return proxy
  50. def http_check_proxies(proxies,isHttp = True):
  51. '''
  52. 代理ip请求校验ip
  53. '''
  54. nick_type = -1 #匿名程度变量
  55. speed = -1 #响应速度变量
  56. if isHttp:
  57. test_url = 'http://httpbin.org/get'
  58. else:
  59. test_url = 'https://httpbin.org/get'
  60. #requests库请求test_url
  61. try:
  62. #响应时间
  63. start_time = time.time()
  64. res = requests.get(test_url,headers = random_headers.get_request_headers(),proxies = proxies,timeout = TCHECK_TIMEOUT)
  65. end_time = time.time()
  66. cost_time =end_time-start_time
  67. if res.status_code == 200:
  68. #响应速度
  69. speed = round(cost_time,2)
  70. #转换为字典
  71. res_dict = json.loads(res.text)
  72. #获取请求来源ip
  73. origin_ip = res_dict['origin']
  74. #获取响应请求头中'Proxy-Connection',若有,说明是匿名代理
  75. proxy_connection = res_dict['headers'].get('Proxy-Conntion',None)
  76. if "," in origin_ip:
  77. #如果响应内容中的源ip中有‘,’分割的两个ip的话及时透明代理ip
  78. nick_type = 2 #透明
  79. elif proxy_connection:
  80. #'Proxy-Connection'存在说明是匿名ip
  81. nick_type = 1 #匿名
  82. else:
  83. nick_type =0 #高匿
  84. return True,nick_type,speed
  85. else:
  86. return False,nick_type,speed
  87. except Exception as e:
  88. #logger.exception(e)
  89. return False,nick_type,speed
  90. if __name__ == '__main__':
  91. proxy = Proxy('60.13.42.94','9999')
  92. result = check_proxy(proxy)
  93. print(result)

4. 实现数据库模块(增删改查功能和api功能——mongo_pool.py)

定义MongoPool类, 继承object

1. 在`init`中, 建立数据连接, 获取要操作的集合, 在 `del` 方法中关闭数据库连接
2. 提供基础的增删改查功能
   1. 实现插入功能
   2. 实现修改该功能
   3. 实现删除代理: 根据代理的IP删除代理
   4. 查询所有代理IP的功能
3. 提供代理API模块使用的功能
   1. 实现查询功能: 根据条件进行查询, 可以指定查询数量, 先分数降序, 速度升序排, 保证优质的代理IP在上面.
   2. 实现根据协议类型 和 要访问网站的域名, 获取代理IP列表
   3. 实现根据协议类型 和 要访问网站的域名, 随机获取一个代理IP
   4. 实现把指定域名添加到指定IP的disable_domain列表中.

  1. '''
  2. 针对proxies集合进行数据库的增删改查的操作,并提供代理api使用
  3. '''
  4. import random
  5. import pymongo
  6. import sys
  7. sys.path.append("..")
  8. from settings import MONGO_URL
  9. from proxy_utils.log import logger
  10. from dbmodle import Proxy
  11. class MongoPool(object):
  12. def __init__(self):
  13. #连接数据库
  14. self.client = pymongo.MongoClient(MONGO_URL)
  15. #获取操作字典集
  16. self.proxies = self.client['proxy_pool']['proxies']
  17. def __del__(self):
  18. #关闭数据库连接
  19. self.client.close()
  20. def insert(self,proxy):
  21. '''
  22. 代理ip插入方法
  23. '''
  24. count = self.proxies.count_documents({'_id':proxy.ip})
  25. if count == 0:
  26. #Proxy对象转换为字典
  27. proxy_dict = proxy.__dict__
  28. #主键
  29. proxy_dict['_id'] = proxy.ip
  30. #向proxies字典集中插入代理ip
  31. self.proxies.insert_one(proxy_dict)
  32. logger.info('插入新的代理:{}'.format(proxy))
  33. else:
  34. logger.warning('已经存在的代理{}'.format(proxy))
  35. def update(self,proxy):
  36. '''
  37. 修改更新数据库中代理ip
  38. '''
  39. self.proxies.update_one({'_id':proxy.ip},{'$set':proxy.__dict__})
  40. logger.info('更新代理ip:{}'.format(proxy))
  41. def delete(self,proxy):
  42. '''
  43. 删除数据库中代理ip
  44. '''
  45. self.proxies.delete_one({'_id':proxy.ip})
  46. logger.info('删除代理ip:{}'.format(proxy))
  47. def find_all(self):
  48. '''
  49. 查询数据库中所有的代理ip
  50. '''
  51. cursor = self.proxies.find()
  52. for item in cursor:
  53. #删除_id键值对
  54. item.pop('_id')
  55. proxy = Proxy(**item)
  56. #生成器yield
  57. yield proxy
  58. def limit_find(self,conditions = {},count = 0):
  59. '''根据条件进行查询,
  60. 可以指定查询数量, 先分数降序, 速度升序排,
  61. 保证优质的代理IP在上面'''
  62. cursor = self.proxies.find(conditions,limit = count).sort([
  63. ('score',pymongo.DESCENDING),('speed',pymongo.ASCENDING)])
  64. #接受查询所得代理IP
  65. proxy_list = []
  66. for item in cursor:
  67. itme.pop('_id')
  68. proxy = Proxy(**item)
  69. proxy_list.append(proxy)
  70. return proxy_list
  71. def get_proxies(self,protocol =None,domain = None,nick_type =0,count = 0):
  72. '''
  73. 实现根据协议类型和要访问网站的域名, 获取代理IP列表
  74. '''
  75. conditions = {'nike_type':nick_type}
  76. if protocol is None:
  77. conditions['protocol'] = 2
  78. elif protocol.lower() == 'http':
  79. conditions['protocol'] ={'$in':[0,2]}
  80. else:
  81. conditons['protocol'] ={'$in':[1,2]}
  82. if domain:
  83. conditons['disable_domains'] = {'$nin':[domain]}
  84. return self.limit_find(conditions,count = count)
  85. def random_proxy(self,protocol = None,domain =None,count = 0,nick_type =0):
  86. '''
  87. 根据协议类型 和 要访问网站的域名, 随机获取一个代理IP
  88. '''
  89. proxy_list = self.get_proxies(protocol =protocol,domain = domain ,count = count ,nick_type =nick_type)
  90. return random.choice(proxy_list)
  91. def add_disable_domain(self,ip,domain):
  92. '''
  93. 把指定域名添加到指定IP的disable_domain列表中,没有才添加
  94. '''
  95. count = self.proxies.count_documents({'_id':ip,'disable_domains':domain})
  96. if count == 0:
  97. self.proxies.update_one({'_id':ip},{'$push':{'disable_domains':domain}})
  98. if __name__ == '__main__':
  99. mongo = MongoPool()
  100. #插入测试
  101. #proxy = Proxy('202.104.113.32','53281')
  102. #mongo.insert(proxy)
  103. #更新测试
  104. #proxy = Proxy('202.104.113.32','8888')
  105. #mongo.update(proxy)
  106. #删除测试
  107. #proxy = Proxy('202.104.113.32','8888')
  108. #mongo.delete(proxy)
  109. #查询所有测试
  110. #for proxy in mongo.find_all():
  111. #print(proxy)

5.实现随机获取User-Agent 的请求头模块(random_headers.py)

  1. '''
  2. 获取随机User-Agent的请求头
  3. '''
  4. import random
  5. #用户代理User-Agent列表
  6. USER_AGENTS = [
  7. "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
  8. "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50",
  9. "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0",
  10. "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3; rv:11.0) like Gecko",
  11. "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)",
  12. "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)",
  13. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
  14. "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
  15. "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
  16. "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1",
  17. "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11",
  18. "Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11",
  19. "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
  20. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)",
  21. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)",
  22. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
  23. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)",
  24. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)",
  25. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)",
  26. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser)",
  27. "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)",
  28. "Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
  29. "Mozilla/5.0 (iPod; U; CPU iPhone OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
  30. "Mozilla/5.0 (iPad; U; CPU OS 4_3_3 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8J2 Safari/6533.18.5",
  31. "Mozilla/5.0 (Linux; U; Android 2.3.7; en-us; Nexus One Build/FRF91) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
  32. "MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
  33. "Opera/9.80 (Android 2.3.4; Linux; Opera Mobi/build-1107180945; U; en-GB) Presto/2.8.149 Version/11.10",
  34. "Mozilla/5.0 (Linux; U; Android 3.0; en-us; Xoom Build/HRI39) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13",
  35. "Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.337 Mobile Safari/534.1+",
  36. "Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; en-US) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.70 Safari/534.6 TouchPad/1.0",
  37. "Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124",
  38. "Mozilla/5.0 (compatible; MSIE 9.0; Windows Phone OS 7.5; Trident/5.0; IEMobile/9.0; HTC; Titan)",
  39. "UCWEB7.0.2.37/28/999",
  40. "NOKIA5700/ UCWEB7.0.2.37/28/999",
  41. "Openwave/ UCWEB7.0.2.37/28/999",
  42. "Mozilla/4.0 (compatible; MSIE 6.0; ) Opera/UCWEB7.0.2.37/28/999",
  43. # iPhone 6:
  44. "Mozilla/6.0 (iPhone; CPU iPhone OS 8_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/8.0 Mobile/10A5376e Safari/8536.25",
  45. ]
  46. #随机获取一个用户代理User-Agent的请求头
  47. def get_request_headers():
  48. headers = {
  49. 'User-Agent':random.choice(USER_AGENTS),
  50. 'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
  51. 'Accept-language':'zh-CN,zh;q=0.9,en;q=0.8',
  52. 'Referer':'https://www.baidu.com',
  53. 'Accept-Encoding':'gzip, deflate,br',
  54. 'Connection':'keep-alive',
  55. }
  56. return headers
  57. if __name__ == '__main__':
  58. #测试随机与否
  59. print(get_request_headers())
  60. print("------------"*20)
  61. print(get_request_headers())

6.实现通用爬虫,作为具体爬虫的父类(base_poxies.py)

  1. '''
  2. 通用爬虫:通过指定URL列表, 分组XPATH和组内XPATH, 来提取不同网站的代理IP
  3. 定义一个BaseSpider类, 继承object
  4. - 提供三个类成员变量:urls, group_xpath, detail_xpath: ip, port, area
  5. - 提供初始方法, 传入爬虫URL列表, 分组XPATH, 详情(组内)XPATH
  6. - 对外提供一个获取代理IP的方法
  7. '''
  8. import requests
  9. import sys
  10. import time,random
  11. from lxml import etree
  12. sys.path.append('..')
  13. from proxies_utils.random_headers import get_request_headers
  14. from dbmodle import Proxy
  15. class BaseSpider(object):
  16. #类成员变量
  17. #代理IP网址的URL的列表
  18. urls =[]
  19. #分组XPATH, 获取包含代理IP信息标签列表的XPATH
  20. group_xpath = ''
  21. #组内XPATH, 获取代理IP详情的信息XPATH, 格式为: {'ip':'xx', 'port':'xx', 'area':'xx'}
  22. detail_xpath ={}
  23. def __init__(self,urls =[],group_xpath='',detail_xpath={}):
  24. #提供初始方法, 传入爬虫URL列表, 分组XPATH, 详情(组内)XPATH
  25. if urls:
  26. self.urls =urls
  27. if group_xpath:
  28. self.group_xpath =group_xpath
  29. if detail_xpath:
  30. self.detail_xpath=detail_xpath
  31. def get_proxies(self):
  32. #获取页面数据
  33. for url in self.urls:
  34. page_html = self.get_page(url)
  35. proxies =self.get_html_proxies(page_html)
  36. #yeild from 返回的是proxies内的数据
  37. yield from proxies
  38. def get_page(self,url):
  39. #请求页面数据
  40. res =requests.get(url,headers = get_request_headers())
  41. #每次请求url休眠1秒
  42. time.sleep(random.uniform(1,5))
  43. return res.content
  44. def get_html_proxies(self,page_html):
  45. element = etree.HTML(page_html)
  46. trs = element.xpath(self.group_xpath)
  47. for tr in trs:
  48. ip = self.get_list_first(tr.xpath(self.detail_xpath['ip']))
  49. port = self.get_list_first(tr.xpath(self.detail_xpath['port']))
  50. area = self.get_list_first(tr.xpath(self.detail_xpath['area']))
  51. proxy = Proxy(ip,port,area=area)
  52. yield proxy
  53. def get_list_first(self,lst):
  54. #返回列表的第一个元素
  55. return lst[0] if len(lst) !=0 else ''
  56. if __name__ == '__main__':
  57. config = {
  58. 'urls':['http://www.ip3366.net/free/?stype=1&page={}'.format(i) for i in range(1,3)],
  59. 'group_xpath':'//*[@id="list"]/table/tbody/tr',
  60. 'detail_xpath':{
  61. 'ip':'./td[1]/text()',
  62. 'port':'./td[2]/text()',
  63. 'area':'./td[5]/text()',
  64. }
  65. }
  66. spider = BaseSpider(**config)
  67. for proxy in spider.get_proxies():
  68. print(proxy)

7.实现具体的爬虫类(proxy_spiders.py)

  1. '''
  2. 实现具体的爬虫类
  3. '''
  4. #import time
  5. #import random
  6. import requests
  7. import sys
  8. sys.path.append('../')
  9. #import re
  10. #import js2py
  11. from proxy_spider.base_spider import BaseSpider
  12. class XiciSpider(BaseSpider):
  13. '''西刺代理爬虫 '''
  14. urls = ['http://www.xicidaili.com/nn/{}'.format(i) for i in range(1,21)]
  15. group_xpath = '//*[@id="ip_list"]//tr[position()>1]'
  16. detail_xpath = {
  17. 'ip':'./td[2]/text()',
  18. 'port':'./td[3]/text()',
  19. 'area':'./td[4]/a/text()',
  20. }
  21. class Ip3366Spider(BaseSpider):
  22. '''
  23. ip3366代理爬虫
  24. '''
  25. urls = ['http://www.ip3366.net/free/?stype={}&page={}'.format(i,j) for i in range(1,4,2) for j in range(1,8)]
  26. group_xpath = '//*[@id="list"]/table/tbody/tr'
  27. detail_xpath = {
  28. 'ip':'./td[1]/text()',
  29. 'port':'./td[2]/text()',
  30. 'area':'./td[5]/text()',
  31. }
  32. class kuaiSpider(BaseSpider):
  33. '''
  34. 快代理爬虫
  35. '''
  36. urls = ['http://www.kuaidaili.com/free/in{}/{}'.format(i,j) for i in ['ha','tr'] for j in range(1,21)]
  37. group_xpath = '//*[@id="list"]/table/tbody/tr'
  38. detail_xpath = {
  39. 'ip':'./td[1]/text()',
  40. 'port':'./td[2]/text()',
  41. 'area':'./td[5]/a/text()',
  42. }
  43. '''
  44. def get_page(self,url):
  45. #随机等待时间
  46. time.sleep(random.uniform(1,2))
  47. return super().get_page(url)
  48. '''
  49. class Free89ipSpider(BaseSpider):
  50. '''
  51. 89ip代理爬虫
  52. '''
  53. urls = ['http://www.89ip.cn/index{}.html'.format(i) for i in range(1,17)]
  54. group_xpath = '//div[3]//table/tbody/tr'
  55. detail_xpath = {
  56. 'ip':'./td[1]/text()',
  57. 'port':'./td[2]/text()',
  58. 'area':'./td[3]/text()',
  59. }
  60. def get_page(self,url):
  61. return super().get_page(url).decode()
  62. def get_proxies(self):
  63. proxies = super().get_proxies()
  64. for item in proxies:
  65. item.ip = str(item.ip).replace("\n","").replace("\t","")
  66. item.area = str(item.area).replace("\n","").replace("\t","")
  67. item.port = str(item.port).replace("\n","").replace("\t","")
  68. #返回Proxy对象
  69. yield item
  70. if __name__ == '__main__':
  71. spider = Free89ipSpider()
  72. count= 0
  73. for proxy in spider.get_proxies():
  74. count+=1
  75. print(proxy)

8. 实现运行爬虫模块(run_spider.py)

  1. '''
  2. 创建RunSpider类
  3. run方法运行爬虫, 作为运行爬虫的入口,获取爬虫列表并运行,检测代理IP,可用,写入数据库并处理爬虫内部异常
  4. 使用协程异步来执行每一个爬虫任务, 以提高抓取代理IP效率
  5. 使用schedule模块, 实现每隔一定的时间, 执行一次爬取任务
  6. '''
  7. from gevent import monkey
  8. monkey.patch_all()
  9. from gevent.pool import Pool
  10. import importlib
  11. import sys,time
  12. import schedule
  13. sys.path.append('../')
  14. from settings import PROXIES_SPIDERS,SPIDERS_RUN_INTERVAL
  15. from proxy_validate.httpbin_validator import check_proxy
  16. from proxies_db.mongo_pool import MongoPool
  17. from proxies_utils.log import logger
  18. class RunSpider(object):
  19. def __init__(self):
  20. self.mongo_pool = MongoPool()
  21. self.coroutine_pool = Pool()
  22. def get_spider_from_settings(self):
  23. '''
  24. 获取配置文件中的具体爬虫列表创建对象
  25. '''
  26. for full_class_name in PROXIES_SPIDERS:
  27. module_name,class_name = full_class_name.rsplit('.',maxsplit =1)
  28. #动态导入模块
  29. module = importlib.import_module(module_name)
  30. cls = getattr(module,class_name)
  31. spider = cls()
  32. yield spider
  33. def run(self):
  34. '''
  35. 遍历爬虫对象,执行get_proxies方法
  36. '''
  37. spiders = self.get_spider_from_settings()
  38. for spider in spiders:
  39. self.coroutine_pool.apply_async(self.__run_one_spider,args=(spider,))
  40. #当前线程等待爬虫执行完毕
  41. self.coroutine_pool.join()
  42. def __run_one_spider(self,spider):
  43. try:
  44. for proxy in spider.get_proxies():
  45. time.sleep(0.1)
  46. checked_proxy = check_proxy(proxy)
  47. if proxy.speed != -1:
  48. self.mongo_pool.insert(checked_proxy)
  49. except Exception as er:
  50. logger.exception(er)
  51. logger.exception("爬虫{} 出现错误".format(spider))
  52. @classmethod
  53. def start(cls):
  54. '''
  55. 类方法,依据配置文件汇总的时间间隔run爬虫,单位小时
  56. '''
  57. rs = RunSpider()
  58. rs.run()
  59. schedule.every(SPIDERS_RUN_INTERVAL).hours.do(rs.run)
  60. while 1:
  61. schedule.run_pending()
  62. time.sleep(60)
  63. if __name__ == '__main__':
  64. #类方法调用
  65. RunSpider.start()
  66. #app = RunSpider()
  67. #app.run()
  68. #测试schedue
  69. '''def task():
  70. print("haha")
  71. schedule.every(10).seconds.do(task)
  72. while 1:
  73. schedule.run_pending()
  74. time.sleep(1)'''

9. 代理IP检查模块(proxy_test.py)

  1. '''
  2. 定期检测数据库中的代理ip的可用性,分数评级,更新数据库
  3. '''
  4. from gevent import monkey
  5. monkey.patch_all()
  6. from gevent.pool import Pool
  7. from queue import Queue
  8. import schedule
  9. import sys
  10. sys.path.append('../')
  11. from proxy_validate.httpbin_validator import check_proxy
  12. from proxies_db.mongo_pool import MongoPool
  13. from settings import TEST_PROXIES_ASYNC_COUNT,MAX_SCORE,TEST_RUN_INTERVAL
  14. class DbProxiesCheck(object):
  15. def __init__(self):
  16. #创建操作数据库对象
  17. self.mongo_pool = MongoPool()
  18. #待检测ip队列
  19. self.queue = Queue()
  20. #协程池
  21. self.coroutine_pool = Pool()
  22. #异步回调函数
  23. def __check_callback(self,temp):
  24. self.coroutine_pool.apply_async(self.__check_one,callback = self.__check_one())
  25. def run(self):
  26. #处理检测代理ip核心逻辑
  27. proxies = self.mongo_pool.find_all()
  28. for proxy in proxies:
  29. self.queue.put(proxy)
  30. #开启多异步任务
  31. for i in range(TEST_PROXIES_ASYNC_COUNT):
  32. #异步回调,死循环执行该方法
  33. self.coroutine_pool.apply_async(self.__check_one,callback =self.__check_one())
  34. #当前线程等待队列任务完成
  35. self.queue.join()
  36. def __check_one(self):
  37. #检查一个代理ip可用性
  38. #从队列中获取一个proxy
  39. proxy = self.queue.get()
  40. checked_proxy = check_proxy(proxy)
  41. if checked_proxy.speed == -1:
  42. checked_proxy.score -= 1
  43. if checked_proxy.score == 0:
  44. self.mongo_pool.delete(checked_proxy)
  45. else:
  46. self.mongo_pool.update(checked_proxy)
  47. else:
  48. checked_proxy.score = MAX_SCORE
  49. self.mongo_pool.updata(checked_proxy)
  50. #调度队列的task_done方法(一个任务完成)
  51. self.queue.task_done()
  52. @classmethod
  53. def start(cls):
  54. '''
  55. 类方法,依据配置文件的时间间隔运行检测数据库中的ip可用性,单位小时
  56. '''
  57. test = DbProxiesCheck()
  58. test.run()
  59. schedule.every(TEST_RUN_INTERVAL).hours.do(test.run)
  60. while 1:
  61. schedule.run_pending()
  62. time.sleep(60)
  63. if __name__ == '__main__':
  64. DbProxiesCheck.start()
  65. #test = DbProxiesCheck()
  66. #test.run()

10. 代理IP池的API模块(proxy_api.py)

  1. '''
  2. 为爬虫提供稳定可用的代理ip的接口
  3. 根据协议类型和域名,提供随机的稳定可用ip的服务
  4. 根据协议类型和域名,提供获取多个高可用代理ip的服务
  5. 给指定ip上追加不可用域名的服务
  6. '''
  7. from flask import Flask
  8. from flask import request
  9. import json
  10. from proxies_db.mongo_pool import MongoPool
  11. from settings import PROXIES_MAX_COUNT
  12. class ProxyApi(object):
  13. def __init__(self):
  14. self.app = Flask(__name__)
  15. #操作数据库的对象
  16. self.mongo_pool = MongoPool()
  17. #获取接口url中参数
  18. @self.app.route('/random')
  19. def random():
  20. protocol = request.args.get('protocol')
  21. domain = request.args.get('domain')
  22. proxy = self.mongo_pool.random_proxy(protocal,domain,count = PROXIES_MAX_COUNT)
  23. if protocol:
  24. return '{}://{}:{}'.format(protocol,proxy.ip,proxy.port)
  25. else:
  26. return '{}:{}'.format(proxy.ip,proxy.port)
  27. @self.app.route('/proxies')
  28. def proxies():
  29. protocol = request.args.get('protocol')
  30. domain = request.args.get('domain')
  31. proxies =self.mongo_pool.get_proxies(protocol,domain,count =PROXIES_MAX_COUNT)
  32. #proxies是proxy对象构成的列表,需要转换为字典的列表
  33. proxies_dict_list =[proxy.__dict__ for proxy in proxies]
  34. return json.dumps(proxies_dict_list)
  35. @self.app.route('/disabldomain')
  36. def disable_domain():
  37. ip = request.args.get('ip')
  38. domain = request.args.get('domain')
  39. if ip is None:
  40. return '请提供ip参数'
  41. if domain is None:
  42. return '请提供域名domain参数'
  43. self.mongo_pool.add_disable_domain(ip,domain)
  44. return '{} 禁用域名 {} 成功'.format(ip,domain)
  45. def run(self,debug):
  46. self.app.run('0.0.0.0',port = 16888,debug = debug)
  47. @classmethod
  48. def start(cls,debug = None):
  49. proxy_api = cls()
  50. proxy_api.run(debug = debug)
  51. if __name__ == '__main__':
  52. ProxyApi.start(debug = True)
  53. #proxy_api = ProxyApi()
  54. #proxy_api.run(debug = True)

11. 代理池启动入口(main.py)

  1. '''
  2. 代理池统一入口:
  3. 开启多个进程,分别启动,爬虫,检测代理ip,WEB服务
  4. '''
  5. from multiprocessing import Process
  6. from proxy_spider.run_spider import RunSpider
  7. from proxy_test import DbProxiesCheck
  8. from proxy_api import ProxyApi
  9. def run():
  10. process_list = []
  11. #启动爬虫
  12. process_list.append(Process(target = RunSpider.start))
  13. #启动检测
  14. process_list.append(Process(target = DbProxiesCheck.start))
  15. #启动web服务
  16. process_list.append(Process(target = ProxyApi.start))
  17. for process in process_list:
  18. #设置守护进程
  19. process.daemon = True
  20. process.start()
  21. #主进程等待子进程的完成
  22. for process in process_list:
  23. process.join()
  24. if __name__ == '__main__':
  25. run()

12 .配置文件模块(settings.py)

  1. #默代理IP的默认最高分数
  2. MAX_SCORE =50
  3. import logging
  4. #日志模块默认配置:
  5. # 默认等级
  6. LOG_LEVEL = logging.DEBUG
  7. #默认日志格式
  8. LOG_FMT = '%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s: %(message)s'
  9. # 默认时间格式
  10. LOG_DATEFMT = '%Y-%m-%d %H:%M:%S'
  11. # 默认日志文件名称
  12. LOG_FILENAME = 'log.log'
  13. #请求超时参数
  14. CHECK_TIMEOUT = 10
  15. #mongodb的URL配置
  16. MONGO_URL = 'mongodb://127.0.0.1:27017/'
  17. #具体爬虫的配置列表
  18. PROXIES_SPIDERS = [
  19. "proxy_spider.proxy_spiders.XiciSpider",
  20. "proxy_spider.proxy_spiders.Ip3366Spider",
  21. "proxy_spider.proxy_spiders.kuaiSpider",
  22. "proxy_spider.proxy_spiders.Free89ipSpider",
  23. ]
  24. #爬虫间隔自动运行时间
  25. SPIDERS_RUN_INTERVAL = 4
  26. #配置检测代理ip的异步数量
  27. TEST_PROXIES_ASYNC_COUNT = 10
  28. #db中ip间隔自动运行时间
  29. TEST_RUN_INTERVAL = 2
  30. #随机获取代理ip的最大数量
  31. PROXIES_MAX_COUNT = 50

完整源码请前往我的github仓库查看:https://github.com/R2h1/ProxyPool 欢迎star哦!!!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/209233
推荐阅读
相关标签
  

闽ICP备14008679号