汽車之家字體反爬破解實踐

一、概覽

爬蟲與反爬蟲一直是一對天生的對手,反爬手段多種多樣,破解手段也應運而生。

本文主要介紹一種利用前端頁面自定義字體的方式來實現反爬的技術手段,並實踐如何技術上破解。(期間多次掉坑,拼接頑強的毅力,仍然堅強的走出來。)

自定義字體:@font-face是CSS3中的一個模塊,主要是實現將自定義的Web字體嵌入到指定網頁中去。具體詳細定義見CSS @font-face

二、查找字體源

汽車之家論壇是廣大車友愛好者的集聚地,大家分享買車、選車、開車、自駕游等個人經歷。我們嘗試爬取一些用戶熱門精華帖子的內容,初始訪問似乎並沒有什麼特別,直到我們發現下圖所示。這裡頁面顯示很正常的文字,但是在網頁源碼中某些字卻是一段span包裹的不可見文本。

手動拷貝網頁文本到Noetepad++,也發現了異常。

上面其實就是自定義字體搞的鬼。根據網頁源碼中,

<span style="font-family: myfont;">?</span>

使用了自定義的myfont字體,我們在網頁中查找myfont,很快有了發現,這就是標準的@font-face定義方法。且每次訪問,字體文件訪問地址都會隨機變化。

我們訪問其中ttf文件的地址,可將ttf字體文件下載到本地。

字體文件博大精深,ttf文件是其中一種,為了解析該字體,在沒有找到fonttools之前,找的很多代碼都不能滿足我這簡單的解析需求,差點就準備看ttf規範定義自己編寫解析代碼了。此一坑。

三、字體解析

ttf就是我們常用的字體文件,可以使用系統自帶的字體查看器查看,但是難以看到更多有效的信息,我們使用一個專用工具Font Creator查看。

可以看到,這裡不再是簡單的數字混淆了,而是對一些中文文字進行了重新編碼處理。這個字體里有91個字(含一個空白字),每個字顯示其字形和其字形編碼。比如上文中第一個顯示的是,其編碼是0xEC35,我們利用Notepad++查看複製的該不可見字元的十六進位編碼是0xEEB0B5,

兩者不一樣,這是怎麼回事。此又一坑!一頓查找,這其實分別是unicode編碼和utf-8編碼,

這樣兩者的關係就對應了,我們也知道頁面查看正確但讀取網頁卻無法獲取文本的原因了。我們發現,論壇頁面每次訪問,字體是不變的,但字元編碼是變化的。因此,我們需要根據每次訪問動態解析字體文件。

接著一頓查找,好不容易找到一款專門解析font的python包,fonttools,

使用下面語句可以獲取順序的字元編碼值,

# 解析字體庫font文件font = TTFont("autohome.ttf")uniList = font["cmap"].tables[0].ttFont.getGlyphOrder()

四、內容替換

關鍵點攻破了,整個工作就好做了。先訪問需要爬取的頁面,獲取字體文件的動態訪問地址並下載字體,讀取用戶帖子文本內容,替換其中的自定義字體編碼為實際文本編碼,就可復原網頁為頁面所見內容了。

完整代碼如下:

# -*- coding:utf-8 -*-import requestsfrom lxml import htmlimport refrom fontTools.ttLib import TTFont#抓取autohome評論class AutoSpider: #頁面初始化 def __init__(self): self.headers = { "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8", "Accept-Encoding": "gzip, deflate, br", "Accept-Language": "zh-CN,zh;q=0.8", "Cache-Control": "max-age=0", "Connection": "keep-alive", "Upgrade-Insecure-Requests": "1", "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36" } # 獲取評論 def getNote(self): url = "https://club.autohome.com.cn/bbs/thread-c-2778-69436529-1.html" host = {"host":"club.autohome.com.cn", "cookie":"your cookie"} headers = dict(self.headers.items() + host.items()) # 獲取頁面內容 r = requests.get(url, headers=headers) response = html.fromstring(r.text) # 匹配ttf font cmp = re.compile(",url("(//.*.ttf)") format("woff")") rst = cmp.findall(r.text) ttf = requests.get("http:" + rst[0], stream=True) with open("autohome.ttf", "wb") as pdf: for chunk in ttf.iter_content(chunk_size=1024): if chunk: pdf.write(chunk) # 解析字體庫font文件 font = TTFont("autohome.ttf") uniList = font["cmap"].tables[0].ttFont.getGlyphOrder() utf8List = [eval("u"u" + uni[3:] + """).encode("utf-8") for uni in uniList[1:]] wordList = ["一", "七", "三", "上", "下", "不", "中", "檔", "比", "油", "泥", "燈", "九", "了", "二", "五", "低", "保", "光", "八", "公", "六", "養", "內", "冷", "副", "加", "動", "十", "電", "的", "皮", "盤", "真", "著", "路", "身", "軟", "過", "近", "遠", "里", "量", "長", "門", "問", "只", "右", "啟", "呢", "味", "和", "響", "四", "地", "壞", "坐", "外", "多", "大", "好", "孩", "實", "小", "少", "短", "矮", "硬", "空", "級", "耗", "雨", "音", "高", "左", "開", "當", "很", "得", "性", "自", "手", "排", "控", "無", "是", "更", "有", "機", "來"] # 獲取發帖內容 note = response.cssselect(".tz-paragraph")[0].text_content().encode("utf-8") print note print "---------------after-----------------" for i in range(len(utf8List)): note = note.replace(utf8List[i], wordList[i]) print note

注意,wordList直接寫"一",不需要寫u"一",因為note和utf8List[i]均是str類型,wordList也應是str類型,而不應是unicode類型,否則會報錯。此再一坑。

五、參考文章

1、爬蟲與詭異的字體 (zhuanlan.zhihu.com/p/28

2、fonttools源碼 (github.com/fonttools/fo)

3、ttf文件結構解析

(cnblogs.com/sjhrun2001/)

附錄

字體反爬破解實踐源代碼:點這裡,密碼:gxf7


推薦閱讀:

爬取張佳瑋138萬知乎關注者:數據可視化
python爬蟲之圖片下載APP1.0
Python3如何將圖片保存到本地?
自學python3的爬蟲,但是網上普遍是python2的書,想問下有沒有好的爬蟲的python3書?

TAG:python爬虫 | 汽车之家 | WebFont |