Python之網絡爬蟲(驗證碼、代理IP、防反爬策略、封裝一個抓取頁面的函數)

一、使用tesseract做OCR驗證碼識別

1、cookie, session的作用
用戶曾經訪問過個這個網站,我們需要在HTTP協議之外用一些額外的信息和技術來標識這個用戶曾經來過爲了讓用戶體驗更好;所以我們的爬蟲程序可以巧妙的利用這個漏洞來達到登錄獲取信息的效果。

2、安裝tesseract
pip install pytesseract

3、測試tesseract
1)from pytesseract import *
2)然後,在captchaTest. py程序中有一張驗證碼的圖片
3)去噪音,找出驗證碼的色彩;
4)背景色與前景色的區分;
5)提取出一個個字符;
6)使用餘弦相似度來計算兩個字符之間的相似,
https: L /blog. csdn. net /whi terbear/article/details .
7)得到當前字符最大概率匹配的樣本集中的字

4、注意
驗證碼這一關是涉及機器學習方面的內容,裏面的水很深,因此處理驗證碼時一定要考慮好時間成本。如果圖像識別技術實在搞不定驗證碼這一關,可以選擇使用打碼平臺,最終都無法解決,那也只能手動輸入了。

5、打碼平臺運作的方式
1)首先在打碼平臺註冊賬號,獲取到合法的Key, ID等信息;
2)使用這些Key, ID去調對方的API:讀取文件成字節流,做一次Base64轉碼,去調API接口,返回結果;
3))到結果,進行下一-步操作;如果出錯,根據返回結果來判斷如果排錯。

二、代理服務器設置

1、代理服務器:藉助別人的手來完成訪問,將自己的ip地址給僞裝起來,爬蟲時不會被服務器發現惡意訪問

2、設置代理服務器IP:打開控制面板的Internet選項,點連接,設置,代理服務器,輸入對應的地址和端口號(port)

3、IP和MAC地址的區別
HTTP數據包在傳遞過程,源地址和目標地址是不變的。填寫的是IP地址,發包過程中經歷各個路由器使用的是MAC地址,不同的路由器就有不同的物理地址,即MAC地址,因此這個MAC地址是不斷變化的。

4、代理服務器使用方法

# 使用代理服務器來訪問:ProxyHandler需要接受的是一個字典
import urllib.request

def use_http_proxy(proxy_addr, url):
    # 構造代理服務區的handler
    proxyH = urllib.request.ProxyHandler({"https":proxy_addr})
    #創建一個Http 的opener
    opener = urllib.request.build_opener(proxyH,
                                urllib.request.HTTPHandler)
    # 把構造出來的opener載入到全局的urllib裏
    urllib.request.install_opener(opener)
    # 發起Http請求:設置超時爲6秒(超過6秒沒有反應就報錯)
    res = urllib.request.urlopen(url,timeout=6)
    # 讀取信息
    data = res.read().decode("utf-8")
    return data

if __name__ == "__main__":
    proxy_addr = "114.224.223.164:9999"  # 代理服務器
    data = use_http_proxy(proxy_addr, "https://www.sina.com.cn/")
    print(data)

三、反爬與防反爬

1、網站如何發現爬蟲

  • 單一IP非常規的訪問頻次;
  • 單一IP非常規的數據流量;
  • 大量重複簡單的網站瀏覽行爲,只下載網頁,沒有後續的JS,CSS請求;
  • 通過一些陷阱來發現爬蟲,例如一些通過CSS對用戶隱藏的鏈接,只有爬蟲纔會訪問。

2、網站一般如何進行反爬蟲

  • 大量使用動態網頁,是的爬蟲的爬取難度增加,重要數據都拿不到,即使爬蟲用了Web環境來渲染(內置瀏覽器),也會大大增加爬蟲的負擔和爬蟲時間; (當採用動態加載的技術,對服務器的負擔也會大大減輕)。
  • 基於流量的拒絕,開啓帶寬限制模塊,限制每個IP最多連接數,最大帶寬等。

3、如何發現自己被反爬了

  • 頻繁驗證碼的出現
  • Unusual content delivery delay非常規的延時;。
  • Freguent response with HTTP 403,404, 301 or 50x error

4、防反爬策略
1)User-Agent 池
2)代理服務器池
3)CookieJar 等的管理
4)協議的細節考慮,如:需要大量的實踐經驗總結的。抓取數據時不處理CSS,JS等,nofollow屬性,css 的display屬性,探測陷阱
驗證refer locator等。
5)使用分佈式的多機策略,爬慢點,把爬蟲放到訪問頻繁的主站IP子網下,比如教育網;
6)使用了規則來批量爬取,需對規則進行組合
7)驗證碼的處理:機器學習,圖像識別
8)儘可能遵守Robots協議

四、封裝一個抓取頁面的函數

以下是完整代碼,關於各個地方的詳細說明都在註釋中了。


# 庫的導入
import logging
import sys
import random
from urllib import request
from urllib import error
from urllib import parse
import time

# 日誌的寫入
# 獲取logger的實例
logger = logging.getLogger("testLogger")
# 指向logger的輸出格式
formatter = logging.Formatter('%(asctime)s \t  %(levelname)s \t %(message)s')
# 文件,終端日誌對象
file_handler = logging.FileHandler("testLogger2.log")
# 將文件日誌按照剛剛設置的格式來寫
file_handler.setFormatter(formatter)
console_handler = logging.StreamHandler(sys.stdout)
# 終端日誌按照指定的格式來寫
console_handler.setFormatter(formatter)
# 可以設置日誌的級別:INFO高於DEBUG
logger.setLevel(logging.DEBUG)
# 把文件日誌、終端日誌,添加到logger日誌處理器
logger.addHandler(file_handler)
logger.addHandler(console_handler)

# 代理服務器允許設置的範圍
minRangeForProxy = 1
maxRangeForProxy = 10
minSleepTime = 1
maxSleepTime = 3

# 下載網頁的函數大封裝
def downloadHtml(url, headers=[],
                 proxy={}, useProxyRate=0,
                 timeout=None, decodeInfo="utf-8",
                 num_retries=5):
    """
    這是一個爬取網頁數據的函數
    :param url:網頁地址
    :param headers:請求頭,包括User-Agent,Cookie,Host,Connection
    :param proxy:代理服務器的社會中
    :param timeout:超時設置,超過時間就報錯
    :param decodeInfo:網頁編碼的指定
    :param num_retries:如果錯誤,重新嘗試的次數
    :return:服務器返回錯誤狀態碼的處理,如果是400+則直接記錄日誌,如果是500+則重新跑幾次downloadHtml函數
    """
    # 控制代理服務器的使用頻率:若useProxyRate==0,則表示不使用代理服務器
    if random.randint(minRangeForProxy,
                      maxRangeForProxy) >= useProxyRate:
        proxy = None  # 就不使用代理服務器了
        print("No using proxy!")

    # 創建proxy handler
    proxy_handler = request.ProxyHandler(proxy)

    # 創建opener
    opener = request.build_opener(proxy_handler)

    # 把proxy_handler安裝到urllib中
    request.install_opener(opener)

    # 設置http的request頭信息
    opener.addheaders = headers

    # 發起請求
    html = None  # 設置一個初始值,如果成功了則替換獲取到的html信息,否則則是一個空(也算有默認值)
    try:
        res = request.urlopen(url)  # 如果這裏設置timeout,則是socket的超時設置
        html = res.read().decode(decodeInfo)
    # 拋出異常,則將異常寫入日誌
    except UnicodeDecodeError:
        logger.error("UnicodeDecodeError")
    # 可能還會有其他類型的異常,比如狀態碼
    except error.URLError or error.HTTPError as e:
        logger.error("Download Error")
        # 對於300+的錯誤是跳轉一般不需要額外處理

        # 如果是400+的狀態碼,403禁止訪問,是客戶端訪問的問題,則直接記錄日誌
        logger.error("Client Error!")
        return html

        # 如果是500+的狀態碼,則獲取錯誤的狀態碼500+(服務器的問題),使用遞歸重新再跑幾次
        if num_retries > 0:
            # 在嘗試之前先休息一下,緩一緩:隨機休息1-3秒
            time.sleep(random.randint(minSleepTime, maxSleepTime))
            if hasattr(e, 'code') and 500 <= e.code < 600:
                # 出現這類訪問錯誤,則重再跑一次函數
                html = downloadHtml(url, headers,
                     proxy, useProxyRate,
                     timeout, decodeInfo, num_retries-1)

    return html

# 參數的設定
headers = [("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.106 Safari/537.36",)]
proxy_addr = {"http": "114.224.223.164:9999"}

# 調用函數
print(downloadHtml("http://www.baidu.com/",headers=headers,
             proxy=proxy_addr))

# 不使用日誌後,將日誌移除
logger.removeHandler(file_handler)
logger.removeHandler(console_handler)

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