開篇
隨著 Python 和大資料的火熱,大量的工程師蜂擁而上,爬蟲技術由於易學、效果顯著首當其衝的成為了大家追捧的物件,爬蟲的發展進入了高峰期,因此給伺服器帶來的壓力則是成倍的增加。企業或為了保證服務的正常運轉或為了降低壓力與成本,不得不使出各種各樣的技術手段來阻止爬蟲工程師們毫無節制的向伺服器索取資源,我們將這種行為稱為『反爬蟲』。
『反爬蟲技術』是網際網路技術中為了限制爬蟲而產生的技術總稱,而反爬蟲的繞過則是所有爬蟲工程師要面對的問題,也是中高階爬蟲工程師面試中最關注的方面。
問題所在
但是在平時的交流中,筆者發現大多數的初級爬蟲工程師只會拿著網上別人寫的技術文章唾沫橫飛,除了知道在請求的時候偽造瀏覽器請求頭資訊中的 User-Agent 以外,對於:
- 為什麼要這麼做?
- 這麼做有什麼好處?
- 我可以用別的方法實現麼?
- 它的原理是怎麼樣的?
- 它是如何識別我的爬蟲的?
- 我應該用什麼方式繞過它?
一無所知。如果你既不知道原理又不知道實現方式,那麼當目標網站稍微調整一下反爬蟲策略的時候,你還是一臉懵逼
對,就是一臉懵逼。
作者心聲
我也在嘗試著,能夠將這樣的知識分享出來,讓大家在閒暇之餘能夠通過這篇文章學習到反爬蟲知識中比較簡單的反爬蟲原理和實現方法,再熟悉他的繞過操作。比如 User-Agent 反爬手段,瞭解它的原理並且親手實現反爬蟲,再親手繞過它。或許通過這個小小的案例,就可以開啟你思維的大門、撬開你思路的下水道。
正文
上面是空談,下面是實踐。一位偉人曾經表達過這麼一個意思:
管你黑貓白貓,抓不到老鼠的貓,它就不是個好貓
什麼是 User-Agent
User Agent中文名為使用者代理,簡稱 UA,它是一個特殊字串頭,使得伺服器能夠識別客戶使用的作業系統及版本、CPU 型別、瀏覽器及版本、瀏覽器渲染引擎、瀏覽器語言、瀏覽器外掛等。一些網站常常通過判斷 UA 來給不同的作業系統、不同的瀏覽器傳送不同的頁面,因此可能造成某些頁面無法在某個瀏覽器中正常顯示,但通過偽裝 UA 可以繞過檢測。瀏覽器向伺服器發起請求的流程圖,可以用下圖表示:
這裡以火狐瀏覽器和谷歌瀏覽器為例,UA 的格式或者說表現形式是這樣的:
Firefox 的 User-Agent:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:63.0) Gecko/20100101 Firefox/63.0
複製程式碼
Chrome 的 User-Agent:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36
複製程式碼
User-Agent 在網路請求中充當什麼角色?
瀏覽器的角色,如上圖方框中所示,那麼 User-Agent 的角色,就是表明身份。在網路請求當中,User-Agent 是標明身份的一種標識,伺服器可以通過請求頭引數中的 User-Agent 來判斷請求方是否是瀏覽器、客戶端程式或者其他的終端(當然,User-Agent 的值為空也是允許的,因為它不是必要引數)。
為什麼反爬蟲會選擇 User-Agent 這個引數呢?
從上面的介紹中,可以看出它是終端的身份標識。意味著伺服器可以清楚的知道,這一次的請求是通過火狐瀏覽器發起的,還是通過 IE 瀏覽器發起的,甚至說是否是應用程式(比如 Python )發起的。
網站的頁面、動效和圖片等內容的呈現是藉助於瀏覽器的渲染功能實現的,瀏覽器是一個相對封閉的程式,因為它要確保資料的成功渲染,所以使用者無法從瀏覽器中大規模的、自動化的獲取內容資料。
而爬蟲卻不是這樣的,爬蟲生來就是為了獲取網路上的內容並將其轉化為資料。這是兩種截然不同的方式,你也可以理解為通過編寫程式碼來大規模的、自動化的獲取內容資料,這是一種騷操作。
回到正題,為什麼會選擇 User-Agent 這個引數呢?
因為程式語言都有預設的標識,在發起網路請求的時候,這個標識在你毫不知情的情況下,作為請求頭引數中的 User-Agent 值一併傳送到伺服器。比如 Python 語言通過程式碼發起網路請求時, User-Agent 的值中就包含 Python 。同樣的,Java 和 PHP 這些語言也都有預設的標識。
反爬蟲的黑名單策略
既然知道程式語言的這個特點,再結合實際的需求,那麼反爬蟲的思路就出來了。這是一中黑名單策略,只要出現在黑名單中的請求,都視為爬蟲,對於此類請求可以不予處理或者返回相應的錯誤提示。
為什麼用黑名單策略不用白名單策略?
現實生活中,瀏覽器型別繁多(火狐瀏覽器、谷歌瀏覽器、360 瀏覽器、傲遊瀏覽器、歐普拉瀏覽器、世界之窗瀏覽器、QQ 瀏覽器等),
想要將所有的瀏覽器品牌、型別以及對應的標識收集並放到名單中,那是不實際的,假如漏掉了哪一種,那麼對網站來說是一種損失。再者說來,很多的服務並不僅僅開放給瀏覽器,有些時候這些服務以 API 的形式嚮應用程式提供服務,比如安卓軟體的後端 API ,為安卓軟體程式提供資料服務,而軟體本身只承擔介面和結構的任務,而資料則從後端 API 獲取。這個時候,發起的請求中, User-Agent 就會變成 Android 。
以上就是不能使用白名單策略的原因。
而黑名單在於簡單,當你希望遮蔽來自於 Python 程式碼的請求或者來自於 Java 程式碼的請求時,只需要將其加入黑名單中即可。
通過 Nginx 服務日誌來檢視請求頭中的 User-Agent
Nginx 是一款輕量級的 Web 伺服器/反向代理伺服器及電子郵件(IMAP/POP3)代理伺服器。其特點是佔有記憶體少,併發能力強,事實上 Nginx 的併發能力確實在同型別的網頁伺服器中表現較好,使用 Nginx 企業有:百度、京東、新浪、網易、騰訊、淘寶等。
Nginx 的安裝與啟動
通常可以使用系統本身的安裝工具(Centos 的 yum、Debian 系的 apt-get 以及 MacOS 的 brew)安裝 Nginx,以 linux 系統為例,在終端中輸入:
sudo apt-get install nginx
複製程式碼
接下來根據提示選擇,即可完成 Nginx 的安裝。
接著在終端通過命令:
sudo systemctl start nginx
複製程式碼
即可啟動 Nginx 服務。
備註:由於各個系統差別以及版本差異,安裝和啟動命令略有差別,解決辦法自行搜尋
Nginx 的日誌
Nginx 為使用者提供了日誌功能,其中記錄了每次伺服器被請求的狀態和其他資訊,包括 User-Agent。 Nginx 的預設日誌存放路徑為:
/var/log/nginx/
複製程式碼
在終端通過命令
cd /var/log/nginx && ls
複製程式碼
可以進入到日誌存放目錄並列出目錄下的檔案,可以看到其中有兩個主要的檔案,為 access.log
和 error.log
它們分別記錄著成功的請求資訊和錯誤資訊。我們通過 Nginx 的訪問日誌來檢視每次請求的資訊。
發起請求的幾種辦法
瀏覽器
Nginx 啟動後,預設監聽 80 埠,你只需要訪問 IP 地址或者域名即可。假設 IP 地址為 127.0.0.1
,那麼可以在瀏覽器輸入:
http://127.0.0.1
複製程式碼
回車後,瀏覽器就會向伺服器發起請求,和你平時上網是一樣的。
Python 程式碼
這裡我們利用 Requests 庫來發起網路請求。在本地新建一個名為 gets.py
的檔案,其中程式碼為:
import requests
# 向目標發起請求,並列印返回的 http 狀態碼
resp = requests.get("http://127.0.0.1")
print(resp.status_code)
複製程式碼
Postman
Postman是一款功能強大的網頁除錯與傳送網頁HTTP請求的工具(Postman下載地址),它可以模擬瀏覽器,訪問指定的 Url 並輸出返回內容,實際使用如下圖所示:
Curl
這是一個利用URL語法在命令列下工作的傳輸工具,它不僅支援 url 地址訪問還支援檔案上傳和下載,所以可以稱它為綜合傳輸工具。他也可以模擬瀏覽器,訪問指定的 Url,實際使用如下圖所示:
Nginx 日誌記錄結果
上面使用了 4 種方法來向伺服器發起請求,那麼我們看看 Nginx 的日誌中,記錄了什麼樣的資訊。在終端通過命令:
sudo cat access.log
複製程式碼
來檢視日誌檔案。可以看到這幾次的請求記錄:
# 請求記錄
127.0.0.1 - - [04/Nov/2018:22:19:07 +0800] "GET / HTTP/1.1" 200 396 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
127.0.0.1 - - [04/Nov/2018:22:19:07 +0800] "GET /favicon.ico HTTP/1.1" 404 200 "http://127.0.0.1/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
127.0.0.1 - - [04/Nov/2018:22:20:36 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36"
127.0.0.1 - - [04/Nov/2018:22:27:14 +0800] "GET /z_stat.php?id=1256772952&web_id=1256772952 HTTP/1.1" 404 144 "http://appstore.deepin.org/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/538.1 (KHTML, like Gecko) deepin-appstore/4.0.9 Safari/538.1"
127.0.0.1 - - [04/Nov/2018:22:42:10 +0800] "GET / HTTP/1.1" 200 396 "-" "PostmanRuntime/7.3.0"
127.0.0.1 - - [04/Nov/2018:22:42:51 +0800] "GET / HTTP/1.1" 200 612 "-" "curl/7.60.0"
複製程式碼
無論是 Python 還是 Curl 或者瀏覽器以及 Postman 的請求,都被記錄在日誌檔案中,說明 Nginx 可以識別發起請求的終端型別。
實現反爬蟲
之前的理論和邏輯,在實驗中都得到了驗證,那麼接下來我們就通過黑名單策略將 Python 和 Curl 發起的請求過濾掉,只允許 Firefox 和 Postman 的請求通過,並且對被過濾的請求返回 403 錯誤提示。
反爬蟲的過程如上圖所示,相當於在伺服器和資源之間建立了一道防火牆,在黑名單中的請求將會被當成垃圾丟棄掉。
配置 Nginx 規則
Nginx 提供了配置檔案以及對應的規則,允許我們過濾掉不允許通過的請求,本次反爬蟲我們使用的就是它。Nginx 的配置檔案通常放在/etc/nginx/
目錄下,名為nginx.conf
,我們通過檢視配置檔案來看一看,站點的配置檔案在什麼地方。再通過系統自帶的編輯器(筆者所用系統自帶 Nano,其他系統可能自帶 Vim)來編輯配置檔案。在配置檔案中找到站點配置檔案地址(筆者所用電腦存放路徑為/etc/nginx/sites-enable
),再到站點配置檔案中找到local
級別的配置,並在其中加上一下內容:
if ($http_user_agent ~* (Python|Curl)) {
return 403;
}
複製程式碼
這段配置的釋義是判斷請求中請求頭字串中是否包含有 Python或者 Curl,如果包含則直接返回 403 錯誤,否則返回正常的資源。完成配置後儲存,再通過命令:
sudo nginx -s reload
複製程式碼
整個操作過程如上圖所示,讓 Nginx 伺服器重新載入配置檔案,使得剛才的配置生效。
反爬蟲效果測試
重複上面訪問的步驟,通過瀏覽器、Python 程式碼、Postman 工具和 Curl發起請求。從返回的結果就可以看到,與剛才是有所區別的。
- 瀏覽器返回的是正常的頁面,說明沒有收到影響;
- Python 程式碼的狀態碼變成了 403,而不是之前的 200
- Postman 跟之前一樣,返回了正確的內容;
- Curl 跟 Python 一樣,無法正確的訪問資源,因為它們發起的請求都被過濾掉了。
提示:你可以繼續修改 Nginx 的配置來進行測試,最終會發現結果會跟現在的一樣:只要在黑名單中,請求就會被過濾掉並且返回 403 錯誤。
提示:這就是你平時編寫爬蟲程式碼時,需要在請求頭中偽造瀏覽器的原因。
繞過 User-Agent 方式的反爬蟲
通過上面的學習,我們知道了 User-Agent 反爬蟲這種手段的原理,並且通過 Nginx 來實現了反爬蟲,接下來我們一起學習如何繞過這種反爬蟲措施。
Python 繞過反爬蟲
在 Requests 庫中,允許使用者自定義請求頭資訊,所以我們可以在請求頭資訊中將 User-Agent 的值改為瀏覽器的請求頭標識,這樣就能夠欺騙 Nginx 伺服器,達到繞過反爬蟲的目的。將之前的 Python 程式碼改為:
import requests
# 偽造請求頭資訊 欺騙伺服器
headers = {"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:9527.0) Gecko/20100101 Firefox/9527.0"}
resp = requests.get("http://127.0.0.1", headers=headers)
print(resp.status_code)
複製程式碼
程式碼中我們用到的是 Firefox 瀏覽器的請求頭資訊,而且為了更好的觀察效果,我們可以更改瀏覽器的版本號(改成9527)以區分真實瀏覽器(這不會影響請求結果)。執行這個檔案,看看得到的返回結果:
200
複製程式碼
不是 403 了,說明已經繞過了這種型別的反爬蟲(你看,這就是網上那些文章所寫的,需要修改請求頭資訊才能繞過反爬蟲,現在你明白是怎麼回事了吧)。
練習:使用 Postman 再測試一下
一個測試也許不準確,你還可以通過 Postman 再來測試一下,還記得怎麼做嗎?
- 將需要過濾的標識(Postman)新增到 Nginx 的配置檔案中
- 過載配置檔案,使其生效
- 通過 Postman 發起請求看看是否會被過濾
- 再次使用 Postman 工具,並且攜帶上瀏覽器的標識再發起請求,看看是否會被過濾
小提示:這個練習如果你自己來做的話,會更容易理解其中的原理,並且可以加深你的映像。
總結
回顧一下,整篇文章的過程:
我們從遇到的反爬蟲現象開始入手,接著學習了 User-Agent 這種反爬蟲策略的原理,並且通過 Nginx 實現了反爬蟲,最後通過 Python 程式碼示例和 Postman 示例來驗證我們的想法,最終清清楚楚、明明白白的瞭解到其中的緣由,待目標改變了它的策略時,我們也可以清楚的知道可以使用哪些方法來繞過。
思考:示例中,我僅僅是使用 Python 編寫爬蟲來演示,那麼 Java 寫的爬蟲呢?PHP 編寫的爬蟲呢?安卓端發起的請求呢?
你可以依次測試,結果肯定讓你小有收穫。
如果你是一名爬蟲愛好者或者初級爬蟲工程師,同時你希望提升自己的水平,我們可以一起交流,掃碼關注吧!
在微信公眾號回覆『反爬蟲報告』即可獲得下面這個反爬蟲結果報告文件(PDF)
它會讓你看起來更專業
報告部分截圖:
報告的結構如下所示: