[創意料理] 反爬蟲三部曲 - 如何防治高級網頁爬蟲?

爬蟲防治系列文章來到最終章了,先前已經分享了防治初級爬蟲以及中級爬蟲,最後來要分享如何防治高級網頁爬蟲?這一類的爬蟲是比較難去辨認的,也因為不好辨認,所以說在防治的難度以及投入的資源,是比先前的兩類爬蟲要高上許多。

何謂高級爬蟲?

高級爬蟲與初中級爬蟲的差異從工具開始,不同於初中級爬蟲使用 HTTP 客戶端,高級爬蟲使用的是瀏覽器本身,無論是使用按鍵精靈或是 Selenium 這類的 WebDriver 等工具,來去驅動瀏覽器進行資料爬取的行為,這些我都把他們歸類為高級爬蟲,也因為是直接使用瀏覽器來抓取資料,在防治上就難以精準打擊、一招斃命。

偵測爬蟲

雖然高級爬蟲直接使用瀏覽器來抓取資料,使得我們沒辦法輕易就辨認出來實施打擊,但是我們也可以反過來利用瀏覽器來偵測爬蟲,而偵測的重點放在「人類使用者與爬蟲使用者的區別」,底下我羅列了幾點人類使用者比較做不到或不會做的事情,用來當成區別人類及爬蟲的條件,而用來偵測的工具有:瀏覽器客戶端識別碼日誌系統防火牆

1. 短時間內大量瀏覽同一組路由

舉例來說,購物網站的商品頁面,大都是同一組路由,只有商品的識別碼不一樣而已,一般人比較做不到在短時間之內大量瀏覽商品頁面,但是爬蟲可以做得到,至於短時間是多短?大量是多大量?一秒六次?一秒十次?這個就觀察我們自己的網站來去定義了。

要偵測是否符合短時間內大量瀏覽同一組路由的條件,我們可以在前端做,也可以在後端做,前端的話,現代的瀏覽器大都提供了像是 Local StorageIndexedDB 的儲存機制,我們可以將使用者一段時間內的瀏覽記錄存放在這些地方,利用前端使用者的電腦資源進行偵測。

再來,我們也可以選擇將使用者的瀏覽記錄送回後端的日誌系統存放,在後端進行偵測,當然這個就要多花一些硬體成本來做這件事。

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,所以如果真的遇到仔細研究我們網站的高手,爬我們網站的任何資料也是信手捻來,不過如果我們都設下了重重障礙阻止爬蟲隨意抓取資料,都還是有爬蟲一直要來,改為收費或許是另一條可行的路。

相關資源

C# 指南
ASP.NET 教學
ASP.NET MVC 指引
Azure SQL Database 教學
SQL Server 教學
Xamarin.Forms 教學