只會爬蟲不會反爬蟲?動圖詳解利用 User-Agent 進行反爬蟲的原理和繞過方法!

閱讀本文大概需要 10 分鐘。

隨着 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.logerror.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)

它會讓你看起來更專業

報告部分截圖:

報告的結構如下所示:

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