前言:字體反爬,也是一種常見的反爬技術,例如貓眼電影票房,汽車之家,天眼查等網站。這些網站采用了自定義的字體文件,在瀏覽器上正常顯示,但是爬蟲抓取下來的數據要么就是亂碼,要么就是變成其他字符。采用自定義字體文件是CSS3的新特性,詳情參考:
http://www.w3school.com.cn/css3/css3_font.asp
一、貓眼電影
打開貓眼電影票房 https://piaofang.maoyan.com/?ver=normal ,打開瀏覽器開發者模式,可以看到這些票房數據在HTML代碼中是顯示不了的。
我們來找一下這個字體文件 ,在html頁面中搜索關鍵字:font-face,找到如下內容。一大串字符串,從base64后面開始一直到后面format前面的括號中的內容,應該是字體文件的內容。是經過了base64編碼后的形式。把這一段字符串考出來,用base64解碼后再保存成本地ttf文件(ttf是字體的一種類型)。
處理代碼如下,先解碼,再保存成本地文件 zt02.ttf:
接下來我們要查看和處理這個字體文件,這里要用到兩個工具。一個是軟件 FontCreator,可以直接打開ttf字體文件,查看每一個字符對應的編碼。還有一個是python第三方庫fontTools,借助這個庫可以用python代碼來操作ttf文件。
FontCreator安裝:
安裝包下載地址 :https://pan.baidu.com/s/1zKIr7EcGlMSSF6e9Z6IZmw 提取碼:d4gm (如果無效的話自己百度下)
安裝好后,不用激活也能免費試用30天,Use Evaluation Version 。然后點擊左上角打開文件,打開我們上面保存的zt02.ttf文件。
打開后看到如下界面,可以看到數字2 9 6上面的編碼和我們之前html中看到的編碼是一致的。
思路分析
這里補充一點就是你每次訪問加載的字體文件中的字符的編碼可能是變化的,就是說網站有多套的字體文件。
既然編碼是不固定的,那就不能用編碼的一一對應關系來處理字體反爬。這里要用到上面說的三方庫fontTools,利用fontTools可以獲取每一個字符對象,這個對象你可以簡單的理解為保存著這個字符的形狀信息。而且編碼可以作為這個對象的id,具有一一對應的關系。像貓眼電影,雖然字符的編碼是變化的,但是字符的形狀是不變的,也就是說這個對象是不變的。
基本思路:先下載一個字體文件保存到本地(比如叫01.ttf),人工的找出每一個數字對應的編碼。當我們重新訪問網頁時,同樣也可以把新的字體文件下載下來保存到本地ttf(比如叫02.ttf)。網頁中的一個數字的編碼比如為AAAA,如何確定AAAA對應的數字。我們先通過編碼AAAA找到這個字符在02.ttf中的對象,并且把它和01.ttf中的對象逐個對比,直到找到相同的對象,然后獲取這個對象在01.ttf中的編碼,再通過編碼確認是哪個數字。
具體實現:先安裝 fontTools,應該是直接 pip install fonttools 就可以
基本命令介紹:
先把字體文件轉化成xml格式,以便打開查看里面的數據結構。打開xml文件可以看到類似html標簽的結構。這里我們用到的標簽是和 。
點開標簽內部,內包含著所有編碼信息,注意前兩個是不是0-9的編碼,需要去除
標簽內包含著每一個字符的對象,同樣第一個和最后一個不是0-9的字符,需要祛除。
點開對象,里面的信息如下,是一些坐標點的信息,可以聯想到這些點應該是描繪字體形狀的,后面再講。
實現步驟:先在本地保存字體文件01.ttf,并手動確認編碼和數字的對應關系,保存到字典中。然后重新訪問網頁的時候,把網頁中新的字體文件也下載保存到本地02.ttf。對于02中的編碼uni2,先獲取uni2的對象obj2,與01中的每一個對象注逐一對比,直到找到相同的對象obj1,再根據obj1的編碼,在字典中找到對應的數字。代碼如下:
-----------分割線-------------
二、汽車之家
上面講的貓眼電影例子,是編碼變化,但是字體形狀不變,網上也有很多介紹的文章。而汽車之家的字體反爬,不僅是編碼變化,而且是字體形狀也有變化。就是說對象本身變化,不能再直接用比較對象的方法處理。網上搜也是基本沒什么好的解決辦法,有一種是用OCR識別,這個當然可以。下面介紹一種博主自己摸索的方法,簡單試了下應該是ok的。
先看問題,打開汽車之家論壇的一篇文章,https://club.autohome.com.cn/bbs/thread/1f05b4da4448439b/76044817-1.html###
右鍵檢查,可以看到文章中的某些字符是顯示不了的,如下圖。
查找字體文件,直接html中搜索font-face,如下圖,直接把url復制到瀏覽器中就能下載字體文件
再用FontCreator打開,一共有38個漢字采用了自定義字體。
上面說到汽車之家的字體形狀也是變動的。直接打開字體文件看看是哪些數據發生了改變。其實主要有兩個信息,一個是x,y坐標信息,還有一個是對應點0、1值。下幾個字體文件就可以發現,同一個字符在不同的字體文件中x,y坐標是變化的,0,1值不變,還有坐標的數量也不變。
很容易可以聯想到這些x,y坐標應該是用來描繪字體形狀的,用pylab畫個圖可以看出,如下圖。0,1估計是描述連線的參數。
這里猜想就是x,y坐標的變化是基于一個標準值做一定幅度的隨機加減,而且這個范圍相對于坐標系來說不會太大。因為變化太大的話字體顯示就會有較大的差異,一個網站上的字符顯示應該是保持一致的。實際通過幾個字體文件計算得出的差值變化在40以內。
總結一下,這里我們要比較兩個對象表示的字符是否相同,不能通過直接對比對象是否相等判斷,而是要具體比較對象的坐標來判斷。首先判斷坐標的數量是否相同,如果相同的話,再比較每一個坐標是否相同,只要x,y相差40以內,我們就認為這2個坐標是相同的。如果兩個對象的所有坐標都相同,則認為這兩個對象表示同一個字符。這里有個前提就是同一個字符在不同字體文件中包含的x,y坐標數量是不變的,且x,y波動是在一定范圍內的。實際測試幾個樣本來看這兩個前提是滿足的。還有就是兩個不同字符誤判成同一字符的概率,首先這38個字符坐標有29種數量,相同坐標數量的就沒幾個,還有每個坐標都差40以內是比較難碰撞的。還有為什么不用0,1來判斷,這個值不同字符也有相同的。
具體實現:首先還是要在本地下載一個字體文件01.ttf,并手動確認好編碼和字符的對應關系。然后重新訪問網站時新加載的字體文件也下載到本地02.ttf,然后還是和上面的一樣找到01中相同的對象并確認表示的字符。代碼如下:
運行結果:根據01找出02中編碼和字符的對應關系,再用FontCreator打開02進行對比確認,結果OK 。
PS:博主實際測試了幾個字體文件,都是OK的,但是也不保證全都OK。
【關于投稿】
如果大家有原創好文投稿,請直接給公號發送留言。
① 留言格式:
【投稿】+《 文章標題》+ 文章鏈接
② 示例:
【投稿】《不要自稱是程序員,我十多年的 IT 職場總結》:
http://blog.jobbole.com/94148/
③ 最后請附上您的個人簡介哈~
看完本文有收獲?請轉發分享給更多人
關注「Python開發者」,提升Python技能