爬蟲入門到放棄系列07:js混淆、eval加密、字型加密三大反爬技術
阿新 • • 發佈:2021-04-02
## 前言
如果再說IP請求次數檢測、驗證碼這種最常見的反爬蟲技術,可能大家聽得耳朵都出繭子了。當然,也有的同學寫了了幾天的爬蟲,覺得爬蟲太簡單、沒有啥挑戰性。所以特地找了三個有一定難度的網站,希望可以有興趣的手動實踐一下。
此篇文章只作知識擴充套件和思路引導,其中涉及的網站反爬技術,僅做技術學習探討。
## 字型加密
字型加密總結成一句話:你看到的不是你看到的。
### 地址
貓眼電影:[https://maoyan.com/films/343568](https://maoyan.com/films/343568)
### 問題還原
最近的哥斯拉大戰金剛看了沒啊,好看不,評分高不高,票房多少了?讓我們去貓眼看一看吧。
![哥斯拉大戰金剛](https://img-blog.csdnimg.cn/20210331234314505.jpg)
這一看問題就來了:*為什麼評分和票房在原始碼裡都是"口口"*?在頁面中看到的評分和票房去哪兒了?
### 追根溯源
話不多說,先看原始碼:
![](https://img-blog.csdnimg.cn/20210401144814754.png)
看完更疑問了,這個&#x又是啥?這個其實是html中的轉義序列,表示後面跟著的是十六進位制,處理後在控制檯列印一下,如圖:
![](https://img-blog.csdnimg.cn/20210401150150368.png)
這些數字和票房目前是一分錢關係都沒有。那就想辦法讓他們有所關聯。
從網頁中找到了以下程式碼:
![font-face](https://img-blog.csdnimg.cn/20210331234852112.jpg)
其實這就是在css中使用@font-face通過woff檔案自定義了字型,原始碼中的十六進位制數字必須通過這個字型對映才能正確顯示。就像UTF-8和GBK的關係,編碼和解碼一致才不會出現亂碼。
這裡我將woff字型檔案下載到本地並用工具開啟。
![字型內容](https://img-blog.csdnimg.cn/20210401160901727.jpg)
從網頁上看到票房是5.74億,這裡就主要關注數字5。從上圖可以看出5對應的是glyph11。
使用工具將woff檔案轉換成xml格式:
![](https://img-blog.csdnimg.cn/20210401160901737.jpg)
glyph11對應的是id=11的glyph,其對應的name為uniE8CD。接著在xml中找到uniE8CD對應的十六進位制:
![十六進位制對應](https://img-blog.csdnimg.cn/20210401160901789.jpg)
如圖,uniE8CD對應的是0xe8cd,也就是說**數字5對應的是0xe8cd**,正是在控制檯輸出的第一個數字。
## eval() & JS加密
js被加密後放在eval()中執行。如果想還原js,在開發者控制檯使用console.log()輸出解密後的js。因為不論是eval()還是log(),js解析執行最終都依賴於瀏覽器核心。
### 地址
TV貓:[https://www.tvmao.com/program/CCTV](https://www.tvmao.com/program/CCTV)
### 問題還原
在頻道劇集頁,分為早間、午間、晚間節目。如圖:
![網頁內容](https://img-blog.csdnimg.cn/20210329221238341.jpg)
在發起請求獲取頻道劇集資料的時候,發現返回內容只有早間節目資料,12點以後的劇集資料獲取不到。
檢視網頁原始碼:
![劇集網頁原始碼](https://img-blog.csdnimg.cn/20210329221238724.jpg)
### 追根溯源
我們在控制檯的請求中,搜尋網頁中的關鍵字"熊熊樂園",害,果不其然,還真搜著了。
![](https://img-blog.csdnimg.cn/20210329221238251.jpg)
這個響應結果是一個數組,下標0代表標誌位:1代表獲取到了資料,0代表沒有獲取到資料;下標1是資料位,對應介面的返回資料。
解析此響應結果的程式碼比較繁雜,需要對多餘內容進行替換。
程式碼如下:
![解析程式碼](https://img-blog.csdnimg.cn/20210329232233301.jpg)
其實上面程式碼它並不重要!!接著我們順著網線去看他的請求部分:
![請求](https://img-blog.csdnimg.cn/20210329221238202.jpg)
從請求頭中可以看出,請求就一個引數p,1、2、3... 整整186位,你看這個引數它又長悠長,像那寂寥的雨巷。雖然等不來那撐著油紙傘的姑娘,但是至少可以先看看這個引數p是怎麼生成的。
在搜尋框搜尋api和pg關鍵字,找到下面程式碼:
![](https://img-blog.csdnimg.cn/20210330100007480.jpg)
別管其他,帶有ajax字樣十有八九就是ajax請求了,引數p的值是變數a,在生成變數a的程式碼處設定斷點,點選頁面中的"檢視更多"按鈕觸發斷點,接著進入A.d()方法:
![](https://img-blog.csdnimg.cn/20210330100008422.jpg)
往上翻,檢視js上部分:
![](https://img-blog.csdnimg.cn/20210330100008658.jpg)
其實到這裡就已經可以結束了,你看在d()中又呼叫了w(),w()也呼叫了A中其他方法,將這個js中方法呼叫鏈搞清楚,將每個方法程式碼都內聯起來,最後計算出引數p,就可以了。
*那麼,說好的eval呢,說好的加密的js呢?*
![](https://img-blog.csdnimg.cn/20210330135450340.gif)
少俠莫慌,這就帶您繼續看下去。如果你仔細看,你就會發現上面的js的檔名是匿名/臨時的,所以說這不是網站原有的js檔案,而是瀏覽器核心解析後的js。
*那該怎麼找到原來的js檔案?*
不知少俠可知搜尋功能,你看上面的js中有**keyStr**這個關鍵字,咱不妨搜尋一波。
![](https://img-blog.csdnimg.cn/2021033010000979.jpg)
這不,如圖,eval()有了,加密js也有了,拷貝成文字如下:
```
eval(function(h, b, i, d, g, f) {
g = function(a) {
return (a < b ? "" : g(parseInt(a / b))) + ((a = a % b) > 35 ? String.fromCharCode(a + 29) : a.toString(36))
}
;
if (!"".replace(/^/, String)) {
while (i--) {
f[g(i)] = d[i] || g(i)
}
d = [function(a) {
return f[a]
}
];
g = function() {
return "\\w+"
}
;
i = 1
}
while (i--) {
if (d[i]) {
h = h.replace(new RegExp("\\b" + g(i) + "\\b","g"), d[i])
}
}
return h
}('5 A={z:"1o+/=",1b:"1l=1k",J:j(a){5 b="";5 c,L,M,14,16,O,N;5 i=0;a=A.1g(a);1t(i