爬蟲防治系列文章來到最終章了,先前已經分享了防治初級爬蟲以及中級爬蟲,最後來要分享如何防治高級網頁爬蟲?這一類的爬蟲是比較難去辨認的,也因為不好辨認,所以說在防治的難度以及投入的資源,是比先前的兩類爬蟲要高上許多。
何謂高級爬蟲?
高級爬蟲與初中級爬蟲的差異從工具開始,不同於初中級爬蟲使用 HTTP 客戶端,高級爬蟲使用的是瀏覽器本身,無論是使用按鍵精靈或是 Selenium 這類的 WebDriver 等工具,來去驅動瀏覽器進行資料爬取的行為,這些我都把他們歸類為高級爬蟲,也因為是直接使用瀏覽器來抓取資料,在防治上就難以精準打擊、一招斃命。
偵測爬蟲
雖然高級爬蟲直接使用瀏覽器來抓取資料,使得我們沒辦法輕易就辨認出來實施打擊,但是我們也可以反過來利用瀏覽器來偵測爬蟲,而偵測的重點放在「人類使用者與爬蟲使用者的區別
」,底下我羅列了幾點人類使用者比較做不到或不會做的事情,用來當成區別人類及爬蟲的條件,而用來偵測的工具有:瀏覽器
、客戶端識別碼
、日誌系統
、防火牆
。
1. 短時間內大量瀏覽同一組路由
舉例來說,購物網站的商品頁面,大都是同一組路由,只有商品的識別碼不一樣而已,一般人比較做不到在短時間之內大量瀏覽商品頁面,但是爬蟲可以做得到,至於短時間是多短?大量是多大量?一秒六次?一秒十次?這個就觀察我們自己的網站來去定義了。
要偵測是否符合短時間內大量瀏覽同一組路由的條件,我們可以在前端做,也可以在後端做,前端的話,現代的瀏覽器大都提供了像是 Local Storage、IndexedDB 的儲存機制,我們可以將使用者一段時間內的瀏覽記錄存放在這些地方,利用前端使用者的電腦資源進行偵測。
再來,我們也可以選擇將使用者的瀏覽記錄送回後端的日誌系統存放,在後端進行偵測,當然這個就要多花一些硬體成本來做這件事。
2. 固定頻率瀏覽網頁
延伸第 1 點,一般人也不太會固定一個頻率來瀏覽我們的網頁,比如說十秒一次或是一分鐘一次,當出現這樣的瀏覽行為特徵大都是程式所為,這個我們可以延用第 1 點所提出的偵測方式來偵測爬蟲。
3. 使用 Headless 模式
有一些瀏覽器有提供 Headless 模式,像 Google Chrome 就有,啟動 Headless 模式瀏覽器會以無 GUI
的方式執行,一般人不會瀏覽一個看不到畫面的網頁,但是爬蟲需要節省運算資源、提升效率,就會去啟動 Headless 模式。
要偵測是否有啟動 Headless 模式,我們可以檢查瀏覽器的屬性,Headless 模式跟一般模式的瀏覽器屬性值會不一樣,至於可以檢查哪些瀏覽器屬性,我們可以參考 Detect Headless 這個 GitHub 儲存庫,裡面也有提供範例。
4. 頻繁地無痕瀏覽每一個網址
現代的瀏覽器大都有提供無痕模式
,讓我們關閉瀏覽器的同時就清除因瀏覽網站而儲存下來的資料,一般人比較不會用無痕模式瀏覽完一個網頁之後,關掉瀏覽器再打開無痕模式瀏覽下一個網頁,如此頻繁地開開關關,這樣的使用方式也不怎麼方便,但是爬蟲就不會因為頻繁地開關瀏覽器而感到困擾。
由於無痕模式的特性就是在關閉瀏覽器後,會清除因瀏覽網頁而產生的資料,包含 Cookies、Local Storage、Session Storage、IndexedDB、...等資料都會被清掉,所以這就提供了我們一個可以操作的空間,在防治中級網頁爬蟲的篇章有提到,我們會為客戶端產生一個 UUID
的 Cookie,這是瀏覽網頁必定會產生的資訊,如果當下沒有 UUID 就會產生一個新的。
我們可以實施記點的機制,只要客戶端產生新的 UUID 我們就在日誌系統記一點,在一定的時間之內達到一定的點數,我們就視為是爬蟲行為,至於多長時間?多少點數?一分鐘十次?一分鐘二十次?這個就我們自行衡量。
5. 不用滑鼠、鍵盤或觸控
目前應該很少有人瀏覽網頁不會用到滑鼠、鍵盤或觸控,但是爬蟲就不需要,爬蟲需要的是網頁裡面的文字資料。
這個我們可以延用第 4 點的偵測方式,在一定的時間之內沒有觸發滑鼠的點擊、滑鼠的捲動、鍵盤的敲擊、螢幕的觸控、螢幕的滑動、...等事件,就視為是爬蟲的行為,執行記點,至於這個一定時間之內要多久?十秒?二十秒?這個我們就自行定義。
挑戰
以上我大概羅列了 5 點偵測高級爬蟲的方法,那麼如果我們利用這些偵測手段偵測到是爬蟲的時候該怎麼辦?直接打死嗎?當然不是。
因為這些偵測方法有可能會偵測到正常的使用者,冒然就一巴掌拍死,可能會殺到正常的使用者,甚至是付費的使用者,所以偵測到爬蟲的時候,第一步應該先提出「挑戰
」,挑戰什麼?挑戰使用者「人類的身分
」。
最常使用的挑戰方法是使用「驗證碼
」,驗證碼機制我們可以自行打造,或是使用現成的像是 Google 的 reCAPTCHA 服務,不過我要介紹另一個簡單粗暴的方式 - window.prompt() 對話視窗。
當我們偵測到爬蟲的時候,就使用 window.prompt() 對話視窗,請使用者回答簡單的數學運算式,window.prompt() 對話視窗跳出來的時候,網頁執行進度就會被 Block 住,使用者回答正確的答案網頁才會繼續執行,如果使用者回答錯誤達到一定次數的時候,就請後端執行封鎖。
(function () {
let a = parseInt(Math.random() * 98 + 1);
let b = parseInt(Math.random() * 98 + 1);
let answer = prompt(`${a}+${b}=?`);
let wrong = 0;
while (!answer || answer.toString() !== (a + b).toString()) {
wrong++;
if (wrong >= 3) {
window.navigator.sendBeacon("/Home/BlockCrawler");
window.close();
} else {
a = parseInt(Math.random() * 98 + 1);
b = parseInt(Math.random() * 98 + 1);
answer = prompt(`${a}+${b}=?`);
}
}
})();
這一段程式碼我們可以封裝成一個 Web Api 放置在網頁的最開頭,當發現使用者疑似是爬蟲時,就執行這段程式碼對使用者提出挑戰。


保護
上面偵測方法的第 5 點有說到「爬蟲需要的是網頁裡面的文字資料
」,一般人都是看瀏覽器渲染出來的畫面,比較不會鑽進網頁原始碼裡面去瀏覽資料,而爬蟲則是會鑽進原始碼找資料。
所以利用這一點,我們可以做一些資料的保護機制,讓網頁原始碼裡面的文字資料是錯的,但是渲染出來的畫面使用者看上去是對的,爬蟲即使把資料取走了,也不能用。
那要怎麼讓網頁畫面上的資料看起來是對的,但是原始碼裡面的文字資料是錯的呢?我這邊列舉幾種方法:圖片偽裝
、字型偽裝
、SVG 偽裝
、CSS 偏移
、CSS 偽元素
,我這邊就只針對 CSS 偏移及 CSS 偽元素做介紹,其他的就點到為止,對其他的保護方法有興趣的朋友,網路的資源很多,可以自行用關鍵字搜尋。
CSS 偏移
CSS 偏移是在講說將亂序的原始碼文字資料,排版成可正常閱讀的樣子,有可能原始資料是 321,排版之後呈現是 123,由於 CSS 不是我的強項,我請教了我熟悉 CSS 的同事之後,弄出了其中一種 CSS 偏移的方法。

CSS 偽元素
CSS 偽元素也是我請教熟悉 CSS 的同事弄出來的,它可以讓原始資料是缺的,最終呈現出來是完整的。

文章的最後,我要很不甘心地說高級爬蟲在目前階段略勝一籌,面對高級爬蟲我們只能儘可能地增加取得資料的障礙,只要爬蟲裝得愈像人,我們就愈難辨認出來,再加上 WebDriver 可以任意地調整瀏覽器的參數、安插執行 JavaScript,所以如果真的遇到仔細研究我們網站的高手,爬我們網站的任何資料也是信手捻來,不過如果我們都設下了重重障礙阻止爬蟲隨意抓取資料,都還是有爬蟲一直要來,改為收費或許是另一條可行的路。