爬蟲之代理池學習(一)
目錄
- 1、代理池概述
- 2、代理池的設計
- 3、實現代理的思路
- 4、實現代理IP模型類
- 5、代理IP的工具模塊
- 6、實現代理IP的校驗模塊
下一篇:爬蟲之代理池學習(二)
1、代理池概述
1.1、什麼是代理池
代理池就是由代理IP組成的池子,提供多個穩定可用的代理IP
1.2、爲什麼實現代理池
(1)爬蟲時,最常見反爬手段,IP反爬:當同一個IP訪問一個網站次數過多,頻率過高,就會限制這個IP的訪問,需要經常換IP來解決這個問題。
(2)免費代理都是非常不穩定的,10%可用已經很不錯了
1.3、代理池開發環境
- Windows
- 開發語言:Python3
- 開發工具:PyCharm
- 使用的主要技術:
- requests:發送請求,獲取頁面數據
- lxml:使用XPATH從頁面提取到我們想要的數據
- pymongo:把提取到代理IP存儲到MongoDB數據庫中和從 MongoDB數據庫中讀取代理IP,給爬蟲使用。
- Flask:用於提供Web服務
2、代理池的設計
2.1、代理池的工作流程
2.2、代理池模塊及其作用
代理池分五大核心模塊
- 爬蟲模塊:採集代理IP
- ①從代理IP網站上採集代理
- ②進行校驗(獲取代理響應速度,協議類型,匿名類型)
- ③把可用代理IP存儲到數據庫中
- 代理IP的校驗模塊:獲取指定代理的響應速度,支持的協議及匿名程度
- ①原因:網站上所有標註的響應速度,協議類型和匿名類型是不準確的
- ②這裏使用httpbin.org進行檢測
- 數據庫模塊:實現對代理IP的增刪改查操作
- ①這裏使用MongoDB來存儲代理IP
- 檢測模塊:定時的對代理池中代理進行檢測,保證代理池中代理的可用性
- ①從數據庫讀取所有代理IP
- ②對代理IP進行逐一檢測,可用開啓多個協程,以提高檢測速度
- ③如果該代理不可用,就讓這個代理分數-1,當代理的分數爲0,就刪除該代理,如果檢測到代理可用就回復爲滿分。
- 代理IP服務接口:提供高可用的代理IP給爬蟲使用
- ①根據協議類型和域名獲取隨機的高質量代理IP
- ②根據協議類型和域名獲取多個高質量代理IP
- ③根據代理IP,不可用域名,告訴代理池這個代理IP在該域名下可用,下次獲取這個域名的代理ip時,就不會再獲取這個代理ip了,從而保證代理IP高可用性。
代理池的其它模塊
-
數據模型:domain.py
- 代理IP的數據模型,用於封裝代理ip相關信息。
-
程序啓動入口:main.py
-
工具模塊:
- 日誌模塊:記錄日誌信息
- http模塊:獲取隨機User-Agent的請求頭
-
配置文件:settings.py
- 用於默認代理的分數,配置日誌格式,文件,啓動的爬蟲,檢驗的間隔時間等。
2.3、代理池的項目結構
3、實現代理的思路
3.1、思路一
- 依據項目設計的流程圖,一步一步進行實現
- 遇到依賴於其它模塊的地方,暫停當前的模塊,去實現其它模塊中需要使用的功能
- 其它模塊實現後,再回來接着寫當前模塊
3.2、思路二
- 先實現基礎模塊,這些模塊不依賴於其它的模塊,比如:數據模型,校驗模塊,數據庫模塊
- 然後實現具體的功能模塊,比如:爬蟲模塊,檢測模塊,代理API模塊
3.3、對比
- 思路1:按流程一步一步實現,,適合一個人獨立完成,流程清晰。
- 思路2:把項目拆分爲多個相對獨立的模塊,每個人實現一個模塊,分工合作,對最初設計要求比較高,需提前設計好要使用到的接口。
4、實現代理IP模型類
4.1、步驟:
- 定義Proxy類,繼承Object
- 實現__init__方法,負責初始化,包含如下字段:
- ①ip:代理的IP地址
- ②port:代理IP的端口號
- ③protocol:代理Ip支持的協議類型,http是0,https是1,http和https都支持是2.
- ④nick_type:代理IP的匿名程度,高匿:0,匿名:1,透明:2
- ⑤speed:代理Ip的響應速度,單位s
- ⑥area:代理ip所在的地區
- ⑦score:代理IP的評分,衡量代理的可用性;默認分支通過配置文件進行配置。
- ⑧disable_domains:不可用域名列表,有些代理IP在某些域名下可用,但是在其它域名下不可用。
- 在配置文件:settings.py中定義MAX_SCORE=50,表示代理IP的默認最高分數
- 提供__str__方法,返回數據字符串
4.2、代碼:
domain.py
from settings import MAX_SCORE
"""
#### 4、實現代理IP模型類
4.1、步驟:
- 定義Proxy類,繼承Object
- 實現__init__方法,負責初始化,包含如下字段:
①ip:代理的IP地址
②port:代理IP的端口號
③protocol:代理Ip支持的協議類型。http是0,https是1,http和https都支持是2.
④nick_type:代理IP的匿名程度,高匿:0,匿名:1,透明:2
⑤speed:代理Ip的響應速度,單位s
⑥area:代理ip所在的地區
⑦score:代理IP的評分,衡量代理的可用性;默認分支通過配置文件進行配置。
⑧disable_domains:不可用域名列表,有些代理IP在某些域名下可用,但是在其它域名下不可用。
- 在配置文件:settings.py中定義MAX_SCORE=50,表示代理IP的默認最高分數
- 提供__str__方法,返回數據字符串
"""
class Proxy(object):
def __init__(self,ip,port,protocol=-1,nick_type=-1,speed=-1,area=None,score=MAX_SCORE,disable_domains=[]):
# ①ip:代理的IP地址
self.ip = ip
# ②port: 代理IP的端口號
self.port = port
# ③protocol: 代理Ip支持的協議類型。http是0,https是1,http和https都支持是2.
self.protocol = protocol
# ④nick_type: 代理IP的匿名程度,高匿:0,匿名:1,透明:2
self.nick_type = nick_type
# ⑤speed: 代理Ip的響應速度,單位s
self.speed = speed
# ⑥area: 代理ip所在的地區
self.area = area
# ⑦score:代理IP的評分,衡量代理的可用性;默認分支通過配置文件進行配置。
self.score = score
# ⑧disable_domains: 不可用域名列表,有些代理IP在某些域名下可用,但是在其它域名下不可用。
self.disable_doamains=disable_domains
# - 提供__str__方法,返回數據字符串
def __str__(self):
# 返回數據字符串
return str(self.__dict__)
settings.py
# - 在配置文件:settings.py中定義MAX_SCORE=50,表示代理IP的默認最高分數
MAX_SCORE=50
5、代理IP的工具模塊
5.1、日誌模塊
log.py
import sys
import logging
import settings
class Logger(object):
def __init__(self):
# 獲取一個logger對象
self._logger = logging.getLogger()
# 設置format對象
self.formatter = logging.Formatter(fmt=settings.LOG_FMT,datefmt=settings.LOG_DATEFMT)
# 設置日誌輸出
# 設置文件日誌模式
self._logger.addHandler(self._get_file_handler(settings.LOG_FILENAME))
# 設置終端日誌模式
self._logger.addHandler(self._get_console_handler())
# 設置日誌等級
self._logger.setLevel(settings.LOG_LEVEL)
def _get_file_handler(self,filename):
'''返回一個文件日誌handler'''
# 1、獲取一個文件日誌handler
filehandlerr = logging.FileHandler(filename=filename,encoding="utf-8")
# 2、設置日誌格式
filehandlerr.setFormatter(self.formatter)
# 3、返回
return filehandlerr
def _get_console_handler(self):
'''返回一個輸出到終端日誌handler'''
# 1、獲取一個輸出到終端日誌handler
console_handler = logging.StreamHandler(sys.stdout)
# 2、設置日誌格式
console_handler.setFormatter(self.formatter)
# 3、返回handler
return console_handler
@property
def logger(self):
return self._logger
# 初始化並配置一個logger對象,達到單例的
# 使用時,直接導入logger就可以使用
logger = Logger().logger
if __name__ == '__main__':
logger.debug("調試信息")
logger.info("狀態信息")
logger.warning("警告信息")
logger.error("錯誤信息")
logger.critical("嚴重錯誤信息")
settings.py
# - 在配置文件:settings.py中定義MAX_SCORE=50,表示代理IP的默認最高分數
MAX_SCORE=50
# 日誌的配置信息
import logging
LOG_LEVEL = logging.DEBUG # 默認等級
LOG_FMT = '%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s: %(message)s' # 默認日誌格式
LOG_DATEFMT = '%Y-%m-%d %H:%M:%S' # 默認時間格式
LOG_FILENAME = 'log.log' # 默認日誌文件名稱
http.py
import random
"""
步驟:
1、準備User-Agent的列表
2、實現一個方法,獲取隨機user-agent的請求頭
"""
# 1、準備User-Agent的列表
USER_AGENTS = [
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
"Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
"Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
"Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
"Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
"Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
"Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
"Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
"Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
"Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
"Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
]
def get_request_headers():
# 定義請求頭
return {
'User-Agent': random.choice(USER_AGENTS),
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 接收類型
'Accept-Language': 'en-US,en;q=0.5', # 接收語言
'Connection': 'keep-alive', # 保持連接
'Accept-Encoding': 'gzip, deflate', # 壓縮方式
}
# 2、實現一個方法,獲取隨機user-agent的請求頭
if __name__ == '__main__':
print(get_request_headers())
print(get_request_headers())
6、實現代理IP的校驗模塊
6.1、步驟:
-
檢查代理IP速度和匿名速度:
- 代理IP速度:從發送請求到獲取響應的時間間隔 -
- 匿名程度檢查:
- 對http://httpbin.org/get或https://httpbin.org/get發送請求
- 如果響應的orign中有‘,’分割兩個ip的就是透明代理ip
- 如果 響應的headers 中包含 Proxy-Connection 說明是匿名代理IP
- 否則就是高匿代ip
-
檢查代理ip協議類型
- 如果http://httpbin.org/get發送請求可以成功,說明支持http協議
- 如果https://httpbin.org/get發送請求可以成功,說明支持https協議
6.2、代碼:
httpbin_validator.py
import time
import requests
import json
from settings import TEST_TIMEOUT
from utils.http import get_request_headers
from utils.log import logger
from domain import Proxy
"""
6、實現代理IP的校驗模塊
**6.1、步驟:**
- 檢查代理IP速度 和 匿名速度:
- 代理IP速度:從發送請求到獲取響應的時間間隔 -
- 匿名程度檢查:
- 對http://httpbin.org/get或https://httpbin.org/get發送請求
- 如果響應的orign中有‘,’分割兩個ip的就是透明代理ip
- 如果 響應的headers 中包含 Proxy-Connection 說明是匿名代理IP
- 否則就是高匿代ip
- 檢查代理ip協議類型
- 如果http://httpbin.org/get發送請求可以成功,說明支持http協議
- 如果https://httpbin.org/get發送請求可以成功,說明支持https協議
"""
def check_proxy(proxy):
"""
用於檢查指定 代理IP 響應速度,匿名程度,支持協議類型
:param proxy: 代理IP模型對象
:return: 檢查後的代理IP模型對象
"""
# 準備代理IP字典
proxies = {
'http':'http://{}:{}'.format(proxy.ip,proxy.port),
'https':'https://{}:{}'.format(proxy.ip,proxy.port),
}
# 測試代理IP
http,http_nick_type,http_speed = __check_http_proxies(proxies)
https,https_nick_type,https_speed = __check_http_proxies(proxies,False)
# 代理Ip支持的協議類型。http是0,https是1,http和https都支持是2.
if http and https:
proxy.protocol = 2
proxy.nick_type = http_nick_type
proxy.speed = http_speed
elif http:
proxy.prptocol = 0
proxy.nick_type = http_nick_type
proxy.speed = http_speed
elif https:
proxy.prptocol = 1
proxy.nick_type = https_nick_type
proxy.speed = https_speed
else:
proxy.prptocol = -1
proxy.nick_type = -1
proxy.speed = -1
def __check_http_proxies(proxies,is_http=True):
# 匿名類型:高匿:0,匿名:1,透明:2
nick_type = -1
# 響應速度,單位s
speed = -1
if is_http:
test_url = 'http://httpbin.org/get'
else:
test_url = 'https://httpbin.org/get'
try:
# 獲取開始時間
start = time.time()
# 發送請求,獲取響應數據
response = requests.get(test_url,headers=get_request_headers(),proxies=proxies,timeout=TEST_TIMEOUT)
if response.ok:
# 計算響應速度
speed = round(time.time()-start, 2)
# 匿名程度
# 把響應的json字符串轉換爲字典
dic = json.loads(response.text)
# 獲取來源IP:orign
orign = dic['orign']
proxy_connection = dic['headers'].get('Proxy-Connection',None)
if ','in orign:
# -1 如果響應的orign中有‘,’分割兩個ip的就是透明代理ip
nick_type = 2
# -2如果響應的headers中包含Proxy - Connection說明是匿名代理IP
elif proxy_connection:
nick_type = 1
else:
# -3否則就是高匿代ip
nick_type = 0
return True,nick_type,speed
return False,nick_type,speed
except Exception as ex:
# logger.exception(ex)
return False,nick_type,speed
if __name__ == '__main__':
proxy = Proxy('',port='')
print(check_proxy(proxy))
settings.py
# - 在配置文件:settings.py中定義MAX_SCORE=50,表示代理IP的默認最高分數
MAX_SCORE=50
# 日誌的配置信息
import logging
LOG_LEVEL = logging.DEBUG # 默認等級
LOG_FMT = '%(asctime)s %(filename)s [line:%(lineno)d] %(levelname)s: %(message)s' # 默認日誌格式
LOG_DATEFMT = '%Y-%m-%d %H:%M:%S' # 默認時間格式
LOG_FILENAME = 'log.log' # 默認日誌文件名稱
# ---------------------
# 設置超時時間
TEST_TIMEOUT = 10