1. 程式人生 > 實用技巧 >詳細解析反爬手段以及處理方案

詳細解析反爬手段以及處理方案

詳細解析反爬手段以及處理方案

前言

​ 網際網路時代,無論在工作上,還是生活上都離不開網路,而網路能給我們帶來什麼?

​ 新聞,小說,資料,各行業的資料或者報表等等;

​ 比如:快畢業了為了論文,在各種網站上爬取需要的資料進行分析;還有一些為了興趣愛好,爬取各種型別的圖片,視訊,文章,資料等。

​ 各網站的開發人員為了約束這種行為,開始絞盡腦汁,採取各種手段去約束爬蟲,於是,有了反爬機制!

常見反爬機制

1,通過對 User-Agent 過濾來控制訪問

無論是瀏覽器,程式,還是爬蟲,在向伺服器發起網路請求時,都會先發送一個請求標頭檔案 headers ,

比如:

{
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 
    "Accept-Encoding": "gzip, deflate, br", 
    "Accept-Language": "zh-CN,zh;q=0.9,zh-HK;q=0.8", 
    "Host": "httpbin.org", 
    "Sec-Fetch-Dest": "document", 
    "Sec-Fetch-Mode": "navigate", 
    "Sec-Fetch-Site": "none", 
    "Upgrade-Insecure-Requests": "1", 
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36", 
    "X-Amzn-Trace-Id": "Root=1-5fe2b4fe-6e4edc1c4dbbe85a3c25492b"
  }
}

# "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"

請求頭大部分的欄位主要是瀏覽器向服務端 “表明自己的身份”用的,很多網站都會建立 user-agent 白名單,只有在正常範圍內的 user-agent 才能正常訪問 。

user-agent 是一個閱讀器標誌,使用者都是一中閱讀器,網站很粗糙的辨別你有咩有作弊,必須要結構不同的閱讀器標誌,不然就會認為你是爬蟲,寧殺錯,不放過,你說氣不氣;

缺點:很容易偽造頭部

處理方案:

修改閱讀器標誌,模擬其他閱讀器的標誌(定義一個標誌庫,隨機獲取一個),能夠通過API介面實現各種閱讀器的收集模擬;

# 定義 user-agent/標誌庫
# 第一種方法
def get_user_agent():
    """
    模擬headers的user-agent欄位,
    返回一個隨機的user-agent字典型別的鍵值對
    """
    agents = [
    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36",
    "Mozilla/5.0 (Windows NT 10.0;) Gecko/20100101 Firefox/61.0",
    "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36",
    "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
    "Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15",
    ]
    
    fakeheader = {}
    fakeheader['User-agent'] = agents[random.randint(0,len(agents))]
    return fakeheader

def get_html(url):
    try:
        r= requests.get(url, timeout=30,headers=get_user_agent())
        r.raise_for_status
        r.encoding = r.apparent_encding
        return r.status_code
   except:
    	return "someting wrong!"
    
 # 第二種方法
def get_html():
    agents = [...]
    headers = {
        'User-Agent': random.choice(user_agent_list),
    	'Referer': 'https://www.baidu.com',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
        'Accept-Encoding': 'gzip, deflate, br',
        'Accept-Language': 'en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7',
        'Cookie': '...'
    }
    try:
        r= requests.get(url, timeout=30,headers=headers)
        r.raise_for_status
        r.encoding = r.apparent_encding
        return r.status_code
   except:
    	return "someting wrong!"
2,IP訪問頻率限制

如果一個固定的IP在短暫的時間內,訪問太快或者快速大量的訪問一個網站,網站就會認為你不是一個人後臺管理員可以編寫IP限制,不讓該IP繼續訪問

解決方案:

設定頻率的閾值,限制訪問時間;

比如,每隔5秒訪問一次頁面,但是如果遇到比較刁鑽的網站,會去檢測你的訪問時間,如果你長時間訪問了幾十個頁面,且每次訪問的時間間隔都剛好5秒鐘,又不是機器怎麼可能做到這麼準確的時間間隔呢?如果使用的是selenium來訪問的話,也就不會出現這個問題了,因為它本來也要開啟頁面,雖然效率不咋地,但是繞開了頻率檢查的反爬機制。

訪問時間設定一個隨機值。例如0-10之間的隨機秒數

修改訪問頻率的場景如下:

(1)使用 request 指令碼爬蟲

# 放在request請求之後
random = random.randint(3,10)
time.sleep(random)

(2)scrapy 指令碼爬蟲或者scrapy_redis 分散式爬蟲

RETRY_TIMES = 10 # 重新請求次數
DOWNLOAD_DELAY = 1 # 間隔時間
CONCURRENT_REQUESTS = 5 # 請求併發數

https://www.cnblogs.com/beiyi888/p/11283823.html

(3)針對業務性寫爬蟲

個別網站,比如,hwt,伺服器會限制你的訪問頻率,但是不會封IP,頁面將持續顯示403(伺服器拒絕訪問),偶爾顯示200(請求成功),那麼就證明(前提是我們設定過請求頭等資訊),這樣的反爬機制,只是限制了請求的頻率,但是並不會影響到正常的內容採集,這種可以忽略的情況並不多見,所以我們還是更具網站業務有針對性的編寫爬蟲

(4)伺服器效能原因,相應較慢(響應超時,導致終止請求)

小網站出現的比較多(DYW),在我們將請求的引數都安排好後,卻發現,由於伺服器的效能原因,採集程式持續報網頁404,出現這種情況只能延長響應超時的時長;

try:
    resp = requests.get(url=url,headers=headers,proxies=proxies,verify=False,timeout=120)

(5)代理IP或者分散式爬蟲

如果對頁的爬蟲的效率有要求,那就不能通過設定訪問時間間隔的方法來繞過頻率檢查了。

代理IP訪問可以解決這個問題。如果用100個代理IP訪問100個頁面,可以給網站造成一種100個人,每個人訪問1頁的錯覺。

但是代理IP經常不穩定,隨便搜一個”免費代理“,會出現很多網站,每個網站也會給你很多的代理IP,但實際上,真正可以用的代理IP並不多。你需要維護一個可用的代理IP池,但是一個免費的代理IP,也許會在幾分鐘後失效,這也是很正常的事情。網上有免費和付費的,但是質量都層次不齊。如果是企業裡需要的話,可以通過自己購買叢集雲服務來自建代理池。

可以使用http://icanhazip.com/ 這個網站來檢測你的代理IP是否設定成功。當你直接使用瀏覽器訪問這個網站的時候,它會返回你的IP地址。

通過requests,我們可以設定代理訪問網站,在requests的get方法中,有一個proxies引數,它接收的資料是一個字典,在這個字典中我們可以設定代理。

大家可以在requests的官方中文文件中看到關於設定代理的更多資訊:http://docs.python-requests.org/zh_CN/latest/user/advanced.html#proxies

我選擇第一個HTTP型別的代理來給大家做測試,執行效果如下圖所示:

import requests
proxy = {'http': 'http://182.253.69.34:8080'}

html = requests.get('http://icanhazip.com/')
html_proxy = requests.get('http://icanhazip.com/', proxies=peoxy)

分散式爬蟲會部署在多臺伺服器上,每個伺服器上的爬蟲統一從一個地方拿網址。這樣平均下來每個伺服器訪問網站的頻率也就降低了。

設定IP代理池

def get_proxy():
    """
    模擬代理池
    返回一個字典型別的鍵值對
   
    """
    proxy = [
        "http://182.253.69.34:8080",
        "http://39.81.63.233:9000",
        "http://58.253.153.164:9999",
        "http://182.92.233.137:8118",
        "http://41.65.201.162:8080",
        "http://45.224.149.219:8080",
    ]
    fakepxs = {}
    fakepxs['http'] = proxy[random.randint(0, len(proxy))]
    return fakepxs

驗證代理IP是否失效?

ip.txt 檔案的內容如下:

182.253.69.34:8080
39.81.63.233:9000
58.253.153.164:9999
182.92.233.137:8118
41.65.201.162:8080
142.93.57.37:80
45.224.149.219:8080
27.220.49.194:9000
5.9.112.247:3128
36.250.156.230:9999
36.249.48.6:9999

proxy_agent.py 檔案

import random
import requests

ua_list = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.75 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.119 Safari/537.36",
    "Mozilla / 5.0(Windows NT 6.1;WOW64) AppleWebKit / 537.36(KHTML, likeGecko) Chrome / 45.0.2454.101Safari / 537.36"
    ]
def _load_text_file(txt_file):
    """
        載入text檔案
        :param txt_file:
        :return:
        """
    text_content = []
    with open(txt_file, 'r', encoding='utf-8-sig') as text_file:
        while True:
            content = text_file.readline()
            if not content:
                break
                line = content.strip('\n')
                text_content.append(line)
                FileUtils._check_format(txt_file, text_content)
                return text_content
# 隨機取出一個Ip
def get_random_ip(path):
    black_ = []
    client = _load_text_file(path)
    print(client)
    url_for_test = 'https://baidu.com/'
    user_agent = random.choice(ua_list)
    headers = {
        'Accept': 'text/html, application/xhtml+xml, application/xml;',
        'Accept-Encoding': 'gzip, deflate, sdch',
        'Accept-Language': 'zh-CN,zh;q=0.8',
        'Referer': 'https://baidu.com/',
        'User-Agent': user_agent
    }
    for useful_proxy in client:

        proxy = {
            'http': 'http://' + useful_proxy,
            #'https': 'http://' + useful_proxy
        }

        response = requests.get(url_for_test,headers=headers,proxies=proxy,timeout=10)
        if response.status_code == 200:
            return useful_proxy

        else:
            print('此ip - {0} 已失效!!!'.format(useful_proxy))
            black_.append(useful_proxy)
            get_random_ip(path)

if __name__ == '__main__':
    path = r'ip.txt'
    print(get_random_ip(path))

session 訪問限制

後臺統計登入使用者的操作,比如短時間的點選時間,請求資料事件,與正常值比對,用於區分使用者是否處理異常狀態,如果是,則限制登入使用者操作許可權。比如,每次訪問之後,剷除快取,這樣能有用躲避部分網站的檢測,如果都是新連結從IP宣佈,也會被斷定回絕(直接403回絕訪問),因此有些爬蟲會去剖析網站的cookies 快取內容,然後進行修改。

缺點:需要增加資料埋點功能,閾值設定不好,容易造成誤操作

解決方案:註冊多個賬號,模擬正常操作

Spider Trap

蜘蛛陷阱導致網路爬蟲進入無限迴圈之類的東西,這回浪費蜘蛛的資源,降低生產力,並且在編寫得不好得爬蟲得情況下,可能導致程式崩潰。禮貌蜘蛛在不同主機之間交替請求,並且不會每隔幾秒鐘從同一伺服器請求多次文件,這意味著”禮貌“網路爬蟲比”不禮貌“爬蟲得影響程度要小得多。

反爬方式:建立無限深度得目錄結構

缺點:反爬方式1,2會增加很多無用目錄或檔案,造成資源浪費,也對正常的SEO十分不友好,可能會被懲罰。

# 比如:
HTTP://example.com/bar/foo/bar/foo/bar/foo/bar /

動態頁面,為網路爬蟲生成無限數量的文件。如由演算法生成雜亂的文章頁面。

文件中填充了大量字元,使解析文件的詞法分析器崩潰。

此外,帶蜘蛛陷阱的網站通常都有robots.txt告訴機器人不要進入陷阱,因此合法的“禮貌”機器人不會陷入陷阱,而忽視robots.txt設定的“不禮貌”機器人會受到陷阱的影響。

解決方案:

把網頁按照所引用的css檔案進行聚類,通過控制類裡最大能包含的網頁數量防止爬蟲進入trap後出不來,對不含css的網頁會給一個penalty,限制它能產生的連結數量。這個辦法理論上不保證能避免爬蟲陷入死迴圈,但是實際上這個方案工作得挺好,因為絕大多數網頁都使用了css,動態網頁更是如此。

驗證碼驗證

驗證碼(CAPTCHA)是“Completely Automated Public Turing test to tell Computers and Humans Apart”(全自動區分計算機和人類的圖靈測試)的縮寫,是一種區分使用者是計算機還是人的公共全自動程式。可以防止:惡意破解密碼、刷票、論壇灌水,有效防止某個黑客對某一個特定註冊使用者用特定程式暴力破解方式進行不斷的登陸嘗試,實際上用驗證碼是現在很多網站通行的方式,我們利用比較簡易的方式實現了這個功能。這個問題可以由計算機生成並評判,但是必須只有人類才能解答。由於 計算機無法解答CAPTCHA的問題,所以回答出問題的使用者就可以被認為是人類。

(1)圖片驗證碼

​ 複雜性

​ 打碼平臺僱傭了人力,專門幫人識別驗證碼。識別完把結果傳回去。總共的過程用不了幾秒時間。這樣的打碼平臺還有記憶功能。圖片被識別為“鍋鏟”之後,那麼下次這張圖片再出現的時候,系統就直接判斷它是“鍋鏟”。時間一長,圖片驗證碼伺服器裡的圖片就被標記完了,機器就能自動識別了。

簡單型

​ OCR識別技術(利用python第三方庫--tesserocr)來識別,經過灰度變換和二值化後,由模糊的驗證碼背景變成清晰可見的驗證碼。對於容易迷惑人得圖片驗證碼,在這種驗證碼,語言一般自帶圖形庫,新增上扭曲就成了這個樣子,我們可以利用9萬張圖片進行訓練,完成類似人的精準度,到達識別驗證碼的效果

(2)簡訊驗證碼

​ 用Webbrowser技術,模擬使用者開啟簡訊的行為,最終獲取簡訊驗證碼。

(3)計算題圖片驗證碼

​ 把所有可能出現的漢字都人工取出來,儲存為黑白圖片,把驗證碼按照字型顏色二值化,去除噪點,然後將所有圖片依次與之進行畫素對比,計算出相似值,找到最像的那張圖片

(4)滑動驗證碼

​ 我們可以利用圖片的畫素作為線索,確定好基本屬性值,檢視位置的差值,對於差值超過基本屬性值,我們就可以確定圖片的大概位置。

(5)圖案驗證碼

​ 對於這種每次拖動的順序不一樣,結果就不一樣,我們怎麼做來識別呢?

利用機器學習所有的拖動順序,利用1萬張圖片進行訓練,完成類似人的操作,最終將其識別

利用selenium技術來模擬人的拖動順序,窮盡所有拖動方式,這樣達到是別的效果

(6)標記倒立文字驗證碼

​ 首先點選前兩個倒立的文字,可確定7個文字的座標, 驗證碼中7個漢字的位置是確定的,只需要提前確認每個字所在的座標並將其放入列表中,然後人工確定倒立文字的文字序號,將列表中序號對應的座標即可實現成功登入。

解決方法:接入第三方驗證碼平臺,實時破解網站得驗證碼

缺點:影響正常得使用者體驗操作,驗證碼越複雜,網站體驗感越差。

通過robots.txt 來限制爬蟲

​ robots.txt(統一小寫)是一種存放於網站根目錄下的ASCII編碼的文字檔案,它通常告訴網路搜尋引擎的漫遊器(又稱網路蜘蛛),此網站中的哪些內容是不應被搜尋引擎的漫遊器獲取的,哪些是可以被漫遊器獲取的。因為一些系統中的URL是大小寫敏感的,所以robots.txt的檔名應統一為小寫。robots.txt應放置於網站的根目錄下。如果想單獨定義搜尋引擎的漫遊器訪問子目錄時的行為,那麼可以將自定的設定合併到根目錄下的robots.txt,或者使用robots元資料(Metadata,又稱元資料)。

robots.txt協議並不是一個規範,而只是約定俗成的,所以並不能保證網站的隱私。注意robots.txt是用字串比較來確定是否獲取URL,所以目錄末尾有與沒有斜槓“/”表示的是不同的URL。robots.txt允許使用類似"Disallow: *.gif"這樣的萬用字元。

https://itunes.apple.com/robots.txt

User-agent: *
Disallow: /WebObjects/MZFastFinance.woa
Disallow: /WebObjects/MZFinance.woa
Disallow: /WebObjects/MZPersonalizer.woa
Disallow: /WebObjects/MZStoreElements.woa
Disallow: /station/idst.
Disallow: /WebObjects/*
Allow: /WebObjects/MZStore.woa/wa/viewMultiRoom?*
Disallow: /search*
Disallow: /*/rss/*
Disallow: /*/lookup?

User-agent: Googlebot      
Disallow: /*/album/*/*?i=*
Disallow: /*/tv-season/*/*?i=*
Disallow: /*/podcast/*/*?i=*

Sitemap: http://sitemaps.itunes.apple.com/sitemap_index.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_1.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_2.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_3.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_4.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_5.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_6.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_7.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_8.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_9.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_10.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_31.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_41.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_51.xml
Sitemap: http://sitemaps.itunes.apple.com/sitemap_index_61.xml

可以參考 https://www.moyublog.com/notes/323.html

解決方案:

如果使用scrapy框架,只需將settings檔案裡的ROBOTSTXT_OBEY 設定值為 False

資料動態載入

python的requests庫只能爬取靜態頁面,爬取不了動態載入的頁面。使用JS載入資料方式,能提高爬蟲門檻。

解決方案:

抓包獲取資料url

通過抓包方式可以獲取資料的請求url,再通過分析和更改url引數來進行資料的抓取。

示例:

https://image.baidu.com 這部分的包。可以看到,這部分包裡面,search下面的那個 url和我們訪問的地址完全是一樣的,但是它的response卻包含了js程式碼。

通過抓包工具,尋找URL得規律,對URL進行構造便可獲取所有照片

使用selenium

​ 通過使用selenium來實現模擬使用者操作瀏覽器,然後結合BeautifulSoup等包來解析網頁通過這種方法獲取資料,簡單,也比較直觀,缺點是速度比較慢。

缺點:如果資料API沒做加密處理,容易曝光介面,讓爬蟲使用者更容易獲取資料。

資料加密-使用加密演算法

前端加密

通過對查詢引數、user-agent、驗證碼、cookie等前端資料進行加密生成一串加密指令,將加密指令作為引數,再進行伺服器資料請求。該加密引數為空或者錯誤,伺服器都不對請求進行響應。

網站的請求如果加密過,那就看不清請求的本來面目,這時候只能靠猜想,通常加密會選用簡略的編碼,如:base64、urlEncode等,如果過於複雜,只能窮盡的去嘗試;

伺服器端加密

在伺服器端同樣有一段加密邏輯,生成一串編碼,與請求的編碼進行匹配,匹配通過則會返回資料。

缺點:加密演算法明文寫在JS裡,爬蟲使用者還是可以分析出來。

解決方案:

JS加密破解方式,就是要找到JS的加密程式碼,然後使用第三方庫js2py在Python中執行JS程式碼,從而得到相應的編碼。

案例參考:

https://blog.csdn.net/lsh19950928/article/details/81585881

資料加密-使用字型檔案對映

​ 伺服器端根據字型對映檔案先將客戶端查詢的資料進行變換再傳回前端,前端根據字型檔案進行逆向解密。

對映方式可以是數字亂序顯示,這樣爬蟲可以爬取資料,但是資料是錯誤的。

破解方式:

其實,如果能看懂JS程式碼,這樣的方式還是很容易破解的,所以需要做以下幾個操作來加大破解難度。

對JS加密

使用多個不同的字型檔案,然後約定使用指定字型檔案方式,比如時間戳取模,這樣每次爬取到的資料對映方式都不一樣,對映結果就不一樣,極大提高了破解的難度。

該種方式相比使用加密演算法方式難度更高,因為加密演算法是固定的幾種,對方很容易獲取並破解,而字型檔案對映可以按任意規則對映,正常的資料使之錯誤顯示,爬蟲不容易察覺。

參考案例:https://www.jianshu.com/p/f79d8e674768

缺點:需要生成字型檔案,增加網站載入資源的體量。

非可視區域遮擋

​ 此方式主要針對使用senlium進行的爬蟲,如果模擬介面未進入可視區域,則對未見資料進行遮擋,防止senlium的click()操作。這種方式只能稍稍降低爬蟲的爬取速度,並不能阻止繼續進行資料爬取。

參考出處:https://www.jianshu.com/p/66b7105c046c