如何繞過反爬蟲技術分析
對于大型網站的爬取我們經常會面臨網站設定的反爬技術封鎖,比如輸入圖片驗證碼、識別圖中漢字,甚至直接禁止你的 ip 等。這樣我們的爬蟲可能剛開始運行不久就會遭受嚴重打擊,無法進行下去。如何才能偽裝的更像正常的請求是我們本節(jié)關注的重點。
1. 反爬封鎖應對措施
面對網站的反爬封鎖,我們往往有如下幾個應對措施,這些基本的方法已經能應對大部分網站的封鎖。更為深入的擬人技術還需要讀者自行去探索,多加實戰(zhàn)。
1.1 修改請求頭
我們前面爬蟲的第一步都是在請求頭中添加正常瀏覽器的 user-agent
,但是請求頭的參數不止這些,還有一個值也會經常被檢查到,那就是 referer
字段,它會告訴服務器該請求是從哪個頁面鏈接過來的,服務器基此可以獲得一些信息用于處理,因此為了更好的防止請求被封,我們也要盡量在請求 header 中加上referer
字段以及其正確的值。
另外,user-agent
是每個反爬網站必查的字段,如果大量請求重復使用固定的 user-agent
值,在某些程序中也會被判定為爬蟲。因此我們可以對所有的請求采用隨機的 user-agent
值來避免這種反爬檢測。Scrapy 中的中間件 UserAgentMiddleware
就是專門用來設置請求頭中 user-agent
值的。
1.2 降低請求頻率
正常情況下,我們的每次請求都會被記錄,如果我們請求太快,比如1秒內發(fā)送10次請求,這明顯不是手動實現的,很容易被識別然后采取相應措施。我們不管是在普通的爬蟲代碼還是 scrapy 中都要設置請求的間隔時間,防止被檢測出來。在普通的爬蟲程序中,我們直接使用 python 的 time 模塊,調用 time.sleep()
方法進行延時調用,而在 Scrapy 中,我們只需要在 settings.py 中設置 DOWNLOAD_DELAY
的值即可。例如:
DOWNLOAD_DELAY = 2
上述的配置表示我們將兩次請求的間隔設置為 2 秒,這已經是一個符合人正常操作的時間間隔了。不過太過于規(guī)律的請求同樣會被更為智能的程序察覺,為了避免出現這樣的情況,在 Scrapy 框架的配置中提供了 RANDOMIZE_DOWNLOAD_DELAY
參數。當它設置為 True
時 ,Scrapy 會在兩次下載請求間設置一個 0.5 * DOWNLOAD_DELAY
到 1.5 * DOWNLOAD_DELAY
之間的一個隨機延遲時間。
1.3 禁用Cookie
有些網站會通過 Cookie 來發(fā)現爬蟲的蹤跡,因此,我們在進行普通網站數據爬取時可以考慮關閉 Cookie:
COOKIES_ENABLED = False
不過,對于一些需要登錄后進行操作的,比如我們之前爬取起點用戶書架以及刪除書架上數據的爬蟲,則需要使用 COOKIES 的情況下,我們不能禁用 Cookie。所以,特定場景需要特定的配置,不能一概而論。
1.4 驗證碼突破
許多做的非常好的網站都會有驗證碼校驗,比如京東、淘寶的登錄。更為復雜的還有12306網站那個讓人頭暈的識圖驗證等等。目前而言,驗證碼技術從原來的簡單數字、字母識別,到滑塊拖動、拼圖認證以及最新的圖片識別、漢字倒立等,已經越來越復雜和難辨。很多基于機器學習以及深度學習的高難度識別算法應運而生,但這些對于普通程序員而言,難以企及。我們唯有兩方面突破:
- 花錢買服務:網上有不少專門的驗證碼識別服務提供商,比如幾年前比較流行的若快平臺 (目前官網無法訪問,似乎已經涼了)等;
- 開源項目:如果舍不得花錢買服務的,我們只能寄希望于部分開源工具。好在還是有不少大神愿意將他們的研究代碼、工具進行開源,這也使得我們能有機會去學習和使用這些工具去突破驗證碼的限制;
1.5 使用 ip 代理
對于一些比較狠的網站,會對一些爬蟲的程序客戶端 ip 進行封殺,這樣我們至少在一段時間內無法在運行該程序去爬取網站數據。此時,我們可以使用代理 ip 去隱藏真實的請求 ip,這樣又可以訪問網站并爬取數據。如果這個代理 ip 被封了怎么辦?那就需要有多個可用的代理 ip,一旦發(fā)現該 ip 被封,我們立馬換下一個 ip 代理繼續(xù)請求數據。假設我們有十萬代理 ip,每個代理能支撐我們爬取 1分鐘數據,那么我們至少能順利爬取2個多月,且一般1-2天左右,原來被封的 ip 又會被解除禁封。這樣,只要我們有大量的 ip 代理,我們就不怕網站的封殺,能源源不斷獲取相應的網站數據。那么到哪里去獲取這樣免費的代理 ip 呢?同樣有兩種途徑:
- 網絡上的免費 ip 代理:免費、不穩(wěn)定且大部分不可用;
- 付費 ip 代理池:略貴、大部分穩(wěn)定可用;
在 github 上也有許多維護和獲取免費代理服務器地址的項目,我們也可以直接使用這些免費的項目幫我們抓取并維護可用的代理 ip 池。
2. 偽裝成隨機瀏覽器
我們來看看 Scrapy 給我們提供的、用于偽裝 User-Agent 字段的中間件:UserAgentMiddleware
。其定義位于 scrapy/downloadermiddlewares/useragent.py
文件中,我們來看看其具體內容:
class UserAgentMiddleware:
"""This middleware allows spiders to override the user_agent"""
def __init__(self, user_agent='Scrapy'):
self.user_agent = user_agent
@classmethod
def from_crawler(cls, crawler):
o = cls(crawler.settings['USER_AGENT'])
crawler.signals.connect(o.spider_opened, signal=signals.spider_opened)
return o
def spider_opened(self, spider):
self.user_agent = getattr(spider, 'user_agent', self.user_agent)
def process_request(self, request, spider):
if self.user_agent:
request.headers.setdefault(b'User-Agent', self.user_agent)
從上面的代碼我們可以看到,該中間件會從 settings.py 中取得 USER_AGENT
參數值, 然后進行實例化:
o = cls(crawler.settings['USER_AGENT'])
在處理請求的核心方法 process_request()
會將該值賦給請求頭中的 User-Agent
字段。注意該中間件屬于下載中間件,在 Scrapy 中默認被啟用,如下圖所示:
我們來看看如何在這個中間件的基礎上實現隨機的 User-Agent
請求:
-
編寫一個基于
UserAgentMiddleware
的中間件類,可以放到 scrapy 項目的 middlewares.py 文件中。這里我們使用 fake-useragent 模塊來幫我們生成各種各樣的user-agent
值,這樣避免我們手工維護一個 user-agent 的值列表。該模塊的使用非常簡單:(scrapy-test) [root@server china_pub]# python Python 3.8.1 (default, Dec 24 2019, 17:04:00) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from fake_useragent import UserAgent >>> ua = UserAgent() >>> ua.random 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.124 Safari/537.36' >>> ua.random 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2049.0 Safari/537.36' >>> ua.random 'Mozilla/5.0 (Windows NT 6.2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1464.0 Safari/537.36' >>> ua.random 'Mozilla/5.0 (Windows NT 6.2; rv:21.0) Gecko/20130326 Firefox/21.0' >>>
注意:使用這個模塊需要聯網,根據相應的版本要請求網站的相應接口,獲取相應數據。例如我這里的版本是0.1.11,于是請求的 URL 及其接口數據如下:
來看我們自定義的中間件代碼如下:
# 寫入位置:scrapy項目的middlewares.py文件中
from scrapy.downloadermiddlewares.useragent import UserAgentMiddleware
from fake_useragent import UserAgent
# ...
class MyUserAgentMiddleware(UserAgentMiddleware):
def process_request(self, request, spider):
ua = UserAgent()
user_agent = ua.random
if user_agent:
request.headers.setdefault(b'User-Agent', user_agent)
return None
-
另外,我們這里繼承了
UserAgentMiddleware
中間件,那么原來的這個中間件就失去了意義、因此,在 settings.py 中,我們要啟用新的設置 User-Agent 的中間件且關閉原來的中間件:# 代碼位置:scrapy項目的settings.py文件中 DOWNLOADER_MIDDLEWARES = { '項目名稱.middlewares.MyUserAgentMiddleware': 500, 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None, }
3. 使用代理
在 Scrapy 項目中使用代理是非常簡單的一件事情,我們只需要在發(fā)送的 Request 請求中添加 meta 參數即可實現代理功能:
yield Request(url, callback=回調方法, errback=錯誤回調, meta={"proxy": proxy, "download_timeout": 10})
上面生成的 Request 請求帶上了 meta 參數,該參數中又設置了代理服務器的地址以及相應的超時時間。我們可以簡單來看看代理服務器的使用:
-
首先我們來自己搭建一個 Nginx 服務,具體的搭建過程可以參考這個教程: Nginx入門手冊;
-
接著我們使用9999這個端口做為代理轉發(fā)端口,相關的配置如下:
# nginx.conf server { resolver 114.114.114.114; resolver_timeout 5s; listen 9999; location / { proxy_pass $scheme://$host$request_uri; proxy_set_header Host $http_host; proxy_buffers 256 8k; proxy_max_temp_file_size 0; proxy_connect_timeout 30; proxy_cache_valid 200 302 10m; proxy_cache_valid 301 1h; proxy_cache_valid any 1m; } }
-
啟動 nginx 服務后,我們這個代理服務就搞定了。我們可以使用 requests 測試下這個代理服務,看看是不是生效了:
import requests proxies = { "http": "http://180.76.152.113:9999", } response = requests.get("http://www.china-pub.com/browse/", proxies=proxies) response.encoding = 'gbk' print(response.text)
上述代碼位于另一臺云服務器,請求的是互動出版物的圖書分類頁面,這次我們會在 requests 請求中加上我們剛剛配置的代理,使用 nginx 代理轉發(fā)請求,請看視頻演示:
往往在 Scrapy 中,我們往往會采用這樣的方式去使用代理服務:
- 準備好一個 redis 和 web 服務。其中 web 服務往往會使用 django 或者 flask 等 web 框架開發(fā),用于爬取免費的代理 ip,同時會對爬取到的
ip:port
進行校驗。如果代理 ip 有效則將其緩存至 redis 服務中,形成有效的 ip 代理池; - web 服務會定期檢查 ip 代理池的所有數據,對于無效的 ip 及時清除。同時,也會定時爬取新的有效的代理 ip 并保存到 redis 中;
- web 服務會提供一個獲取當前 ip 池內有效 HTTP 代理地址的接口,這樣外部應用只需要請求這個接口就能獲取一個有效的代理地址;
此外,在 Scrapy 的項目中,我們往往會按照如下的思路進行代理爬?。?/p>
限于篇幅,這里不再對整個流程做案例演示,有興趣的讀者可以下去后一步步完成從 ip 代理池服務的開發(fā)到最后對接 Scrapy 框架的整個過程,推薦測試的網站為新浪微博,這也是 github 上許多案例的測試靶場。
4. 小結
本小節(jié)中我們主要講述了三方面的內容:
- 詳細介紹了常見的突破反爬封鎖措施;
- 完成一個 Scrapy 中間件實現偽裝隨機瀏覽器功能;
- 介紹代理技術與 Scrapy 項目結合的方式;
當然這些對于淘寶、微博等大廠的反爬技術而言還遠遠不夠。后續(xù)讀者可以技術深入研究如何突破各種驗證碼的校驗、模擬人點擊的軌跡,真正實現模擬用戶瀏覽器請求操作,才能實現無所不爬的 Spider。