反爬篇 | 手把手教你處理 JS 逆向之字體反爬

大家好,我是安果!
上一篇文章我們講解了 JS 逆向中一種常見的反爬方案「 圖片偽裝 」
本篇文章將聊聊另一種更加常見的反爬方案「 字體反爬 」
它的實(shí)現(xiàn)原理為通過自定義的字體替換網(wǎng)頁元素中的部分內(nèi)容來實(shí)現(xiàn)的反爬策略
常見的字體格式包含:ttf、eot、woff,我們一般在網(wǎng)頁中通過關(guān)鍵字「 @font-face 」定義字體樣式,然后再設(shè)置到元素控件樣式中去
//定義字體樣式
@font-face{
?font-family:"字體文件的名字,比如:gzfont";
?src:url('字體文件的鏈接');
}
//?給某個(gè)元素控件設(shè)置字體樣式
.gzfont?{
????font-family:?gzfont;
}
目前有很多主流網(wǎng)站引入了字體反爬,比如:某 8 同城、某車之家等
今天研究的目標(biāo)對(duì)象是:
aHR0cHM6Ly93d3cuZ3VhemkuY29tL2J1eQ==
1、分析一下
打開目標(biāo)頁面及瀏覽器的開發(fā)者工具欄,我們發(fā)現(xiàn)汽車價(jià)格、首付金額、公里數(shù)中的部分內(nèi)容在源碼中顯示為亂碼,但在界面中展示正常
元素的字體樣式名稱為「?gzfont 」

我們?cè)诰W(wǎng)頁源碼中通過關(guān)鍵字「?gzfont 」嘗試查找字體的實(shí)際 URL 地址
但是源碼中并未出現(xiàn)字體定義的邏輯,因此我們可以推測(cè)字體實(shí)際加載地址是通過 Ajax 加載的

我們?cè)凇?Sources 」面板下,通過上面的關(guān)鍵字全局搜索該關(guān)鍵字出現(xiàn)的所有代碼塊進(jìn)行逐一分析
通過新增一個(gè)斷點(diǎn),找到?gzfont 字段生成的邏輯及字體 URL 地址

在瀏覽器的新窗口中,通過字體 URL 下載字體文件,然后在 PC 上使用軟件?FontCreator 打開查看
下載地址:
https://www.pcsoft.com.cn/soft/21156.html

我們發(fā)現(xiàn)字體映射關(guān)系很簡(jiǎn)單,數(shù)目也很少,并且映射關(guān)系是固定的
PS:每次重新加載頁面,字體 URL 地址、映射關(guān)系都是固定的
所以,我們可以通過一個(gè)字典定義它們的映射關(guān)系
#?字體映射關(guān)系
# PS:由于字體數(shù)目很少,所以可以直接寫死字體的映射關(guān)系
font_relation_map?=?{
????'uniE9CE':?0,
????'uniE41D':?1,
????'uniE630':?2,
????'uniEAF2':?3,
????'uniE325':?4,
????'uniE891':?5,
????'uniEC4C':?6,
????'uniE1D0':?7,
????'uniE76E':?8,
????'uniE52E':?9
}最后,我們分析發(fā)現(xiàn)頁面數(shù)據(jù)都是通過下面的網(wǎng)絡(luò)請(qǐng)求獲取的
我們只需要將里面的亂碼數(shù)據(jù)根據(jù)字體映射關(guān)系替換為正常的數(shù)據(jù)即可

2、具體實(shí)現(xiàn)
下面,我們聊聊爬取網(wǎng)頁內(nèi)容的步驟
首先,我們需要安裝字體解析的依賴庫?fonttools
#?安裝依賴
pip3?install?fonttools
然后,解析字體文件,根據(jù)上面響應(yīng)數(shù)據(jù)中的數(shù)據(jù)格式,重新組成一個(gè)新的字典數(shù)據(jù)
def?get_font_map():
????"""
????獲取字體映射關(guān)系
????:return:
????"""
????font?=?TTFont(r'gzfont.woff2')
????#?字體文件轉(zhuǎn)為xml文件
????#?font.saveXML(r"font.xml")
????font_map?=?font.getBestCmap()
????font.close()
????#?print(font_map)
????new_font_map?=?{}
????#?遍歷字典,重新組成一個(gè)新的映射字典
????for?index,?key?in?enumerate(font_map):
????????value?=?font_map[key]
????????#?捕獲異常
????????try:
????????????temp?=?font_relation_map[value]
????????except:
????????????temp?=?''
????????if?temp?!=?'':
????????????#?根據(jù)響應(yīng)結(jié)果中字體反爬數(shù)據(jù)格式,將鍵值前面添加字符串,用于匹配
????????????new_font_map[''?+?str(key)?+?";"]?=?temp
????return?new_font_map接著,模擬上面的網(wǎng)絡(luò)請(qǐng)求獲取響應(yīng)數(shù)據(jù);遍歷上面的字典鍵值對(duì),判斷鍵?key 是否包含在響應(yīng)結(jié)果中
如果包含,就將響應(yīng)結(jié)果中的鍵 key 全部替換該鍵對(duì)應(yīng)的值 value
import?json
import?time
import?requests
def?get_car_list(pagenum:?int,?new_font_map:?dict):
????"""
????獲取車列表數(shù)據(jù)
????:param?new_font_map:
????:return:
????"""
????url?=?f"https://mapi.**.com/car-source/carList/pcList?osv=IOS&minor=&sourceType=&ec_buy_car_list_ab=**"
????payload?=?{}
????headers?=?{
????????'authority':?'mapi.**.com',
????????'accept':?'application/json,?text/plain,?*/*',
????????'origin':?'https://www.**.com',
????????'platform':?'5',
????????'referer':?'https://www.**.com/',
????????'token':?'',
????????'user-agent':?'Mozilla/5.0?(Macintosh;?Intel?Mac?OS?X?10_15_7)?AppleWebKit/537.36?(KHTML,?like?Gecko)?Chrome/101.0.4951.64?Safari/537.36'
????}
????resp_str?=?requests.request("GET",?url,?headers=headers,?data=payload).text
????#?全局替換
????for?key,?value?in?new_font_map.items():
????????if?key?in?resp_str:
????????????resp_str?=?resp_str.replace(key,?str(value))最后就可以進(jìn)行數(shù)據(jù)提取了
...
#?數(shù)據(jù)解析
????resp?=?json.loads(resp_str)
????postList?=?resp.get("data").get("postList")
????for?item?in?postList:
????????title?=?item.get("title")
????????road_haul?=?item.get("road_haul")??#?公里
????????license_date?=?item.get("license_date")??#?購買時(shí)間
????????price?=?item.get("price")??#?價(jià)格
????????first_pay?=?item.get("first_pay")??#?首付
????????print(f'車型:{title},公里數(shù):{road_haul},購買時(shí)間:{license_date},價(jià)格:{price},首付:{first_pay}')
...
運(yùn)行爬蟲,我們發(fā)現(xiàn)爬取到的數(shù)據(jù)不存在亂碼,都正常顯示了

3、總結(jié)一下
在日常工作中,應(yīng)對(duì)字體反爬的常規(guī)步驟如下:
檢查網(wǎng)頁控件的字體名稱
通過網(wǎng)頁源碼、Network 面板(Font)、Source 面板(關(guān)鍵字搜索)及調(diào)試的方式獲取字體地址
在 PC 端通過軟件查看字體的映射關(guān)系
通過頁面刷新、下載查看字體,判斷字體文件是否動(dòng)態(tài)生成
使用依賴庫?fonttools 解析字體,根據(jù)元素內(nèi)容生成新的鍵值對(duì)
對(duì)響應(yīng)結(jié)果根據(jù)鍵值對(duì)進(jìn)行內(nèi)容替換
我已經(jīng)將文中所有源碼上傳到后臺(tái),回復(fù)關(guān)鍵字「 字體反爬 」即可以獲取完整源碼
如果你覺得文章還不錯(cuò),請(qǐng)大家?點(diǎn)贊、分享、留言?下,因?yàn)檫@將是我持續(xù)輸出更多優(yōu)質(zhì)文章的最強(qiáng)動(dòng)力!
END
