爬蟲之代理池學習(一)

爬蟲之代理池學習(一)

目錄

  • 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章