字體反爬之博X網(wǎng)實(shí)戰(zhàn)
作者 | 老肥
來(lái)源 |?老肥碼碼碼
今天的目標(biāo)網(wǎng)站是某彩票網(wǎng)站博X網(wǎng)。其主要的反爬技術(shù)為字體反爬,話不多說(shuō),我們直接開(kāi)始!

我們想要獲取的是具體的開(kāi)獎(jiǎng)號(hào)碼,此號(hào)碼是通過(guò)藍(lán)色的小球表示的,如何獲取呢?
觀察NetWork后,我們很容易發(fā)現(xiàn)這些號(hào)碼數(shù)據(jù)等信息都以json的形式放在下圖紅色圈出的html頁(yè)面中。

而這些號(hào)碼數(shù)據(jù)是以類似于這種形式表示的(上圖中藍(lán)色框部分)。
因此,我們的爬取路線可以分兩步走,先設(shè)法獲取該json數(shù)據(jù),繼而通過(guò)某種方式將數(shù)據(jù)中的編碼轉(zhuǎn)換為正常的數(shù)字。
JSON獲取
觀察該頁(yè)面的Headers數(shù)據(jù),可知為POST請(qǐng)求,其表單數(shù)據(jù)格式如下圖所示,

反復(fù)刷新頁(yè)面發(fā)現(xiàn)code和limit是固定的參數(shù),而path是不斷變化的,因而我們需要找到path參數(shù)才能夠構(gòu)造POST請(qǐng)求訪問(wèn)頁(yè)面并獲取數(shù)據(jù)。
我們復(fù)制當(dāng)前的path參數(shù)到搜索框搜索,發(fā)現(xiàn)有兩個(gè)頁(yè)面包含該path參數(shù)。

txffc.html這個(gè)頁(yè)面正是我們爬取的目標(biāo)頁(yè)面,我們只需要從該頁(yè)面的網(wǎng)頁(yè)源代碼中通過(guò)正則表達(dá)式獲取該path參數(shù)即可,也就是代碼中的woff_id。

從另外一個(gè)頁(yè)面,我們可以下載對(duì)應(yīng)path的woff字體文件,并借此解碼出這些數(shù)據(jù),后面會(huì)詳細(xì)講。

得到path參數(shù)之后,我們可以輕松的構(gòu)建POST請(qǐng)求,并獲取到相應(yīng)的json數(shù)據(jù),這里建議選擇Headers參數(shù)的時(shí)候,除了User-Agent,記得將網(wǎng)頁(yè)請(qǐng)求攜帶的所有參數(shù)帶上,以防無(wú)法正常獲取數(shù)據(jù)。
字體反爬
上面講到,根據(jù)path我們已經(jīng)可以獲取相應(yīng)的woff文件,用FontCreator打開(kāi)看看。

我們可以看到0-9一共十個(gè)數(shù)字,json數(shù)據(jù)中的正是通過(guò)這樣的字體文件轉(zhuǎn)換的。
這里出現(xiàn)了幾個(gè)難點(diǎn)。
字體文件實(shí)時(shí)變化,人工的方法只能每爬取一次,根據(jù)woff文件做一次轉(zhuǎn)換,來(lái)得到想要的數(shù)據(jù);
不同時(shí)刻字體文件有些出入,比如上圖包含很多問(wèn)號(hào)也就是無(wú)效的字符,而有些則不包含,比如下圖;?

這些字體對(duì)象的name和code不一致(與貓眼等網(wǎng)站不同),需要構(gòu)建新的映射關(guān)系;
?

數(shù)字9的字體坐標(biāo)位置并不完全相同。?

這些問(wèn)題自然是需要一個(gè)一個(gè)來(lái)解決。首先我們需要一個(gè)參照系,人工標(biāo)識(shí)出字體的對(duì)應(yīng)關(guān)系(base_dict),從而當(dāng)新字體文件引入的時(shí)候,我們可以根據(jù)這個(gè)參照獲得新的映射關(guān)系。
base_dict={'glyph00009': 7, 'glyph00013': 2, 'glyph00018': 1, 'glyph00023': 6, 'glyph00028': 9, 'glyph00030': 8,'glyph00034': 4, 'glyph00039': 5, 'glyph00044': 3, 'glyph00048': 0}
通過(guò)get_new_name_list函數(shù)來(lái)篩選出有效的字體,上述的問(wèn)號(hào)字體是不存在coordinates屬性的。?

由于該網(wǎng)站不同的字體文件數(shù)字9的坐標(biāo)位置有些許不同,這里采用對(duì)比字體前十個(gè)坐標(biāo)來(lái)做判斷,如果完全相同,則認(rèn)為對(duì)應(yīng)的字體是相同的數(shù)字。

我們由此可以得到新的字典new_dict——它的鍵為新字體文件的字體對(duì)象的name,值為字體對(duì)應(yīng)的數(shù)字。
{'glyph00002': 6, 'glyph00003': 1, 'glyph00004': 0, 'glyph00005': 4, 'glyph00006': 7, 'glyph00007': 5, 'glyph00008': 8, 'glyph00009': 9, 'glyph00010': 3, 'glyph00011': 2}
接著為了對(duì)應(yīng)編碼與數(shù)字,我們需要重新建立新的字體映射表。通過(guò)getBestCmap函數(shù),獲取對(duì)應(yīng)字體的name與code之間的關(guān)系,由此我們可以得到映射表——鍵為編碼,值為數(shù)字,需要注意的是由于getBestCmap()會(huì)將內(nèi)容轉(zhuǎn)化為10進(jìn)制,因此在后面存入字典的時(shí)候需要轉(zhuǎn)化為16進(jìn)制。

通過(guò)該映射表,我們可以輕松將獲取地json數(shù)據(jù)相關(guān)部分轉(zhuǎn)換為可讀的數(shù)字。至此,我們就成功地解決了該網(wǎng)站的字體反爬。關(guān)于其他幾個(gè)經(jīng)典的字體反爬網(wǎng)站,可以參考專輯里面的文章~
——END——
