嚴重聲明:本文僅用於學習交流,不得用於商業用途,同時希望大家遵循網絡協議,維護網絡和諧。
- 上一篇文章我們分析了一下人人車的字體反爬,但是好像python進行逆向分析沒有很全,那麼今天我們來看一下樂居的字體反爬是怎麼做的,然後講一下這個代碼是怎麼走的,還是先看網站(樂居
):-
因爲我們需要的重要信息都在詳情頁裏面,所以,點開一個新房的詳情頁。按F12觀察網頁源碼裏面和正文的異同。如下圖所示,我們可以看到,和人人車一樣,都是利用css樣式渲染,讓源碼裏面的數字進行變換,讓我們看到真實數據。在源碼搜索new_font這個樣式一共有三個,兩個就是圖片裏裏面的,將指導價和開盤時間字體轉換。另一個,不是寫在樣式裏面,但是,我們也有用,後面說:
-
我們打開在F12的控制檯裏面選Network查看後臺給我們發的數據包,選擇font直接就能看到一堆字體文件(.woff結尾的),然後打開,最下面一行都是數字,但是,有個名字含有truetype的一看就不一樣,果然,裏面的數字順序是打亂的,別的文件數字順序都是1234567890。這裏面肯定存在着什麼貓膩,所以我們根據這個文件名字裏面有true認爲這個順序是正常順序,那麼源碼中的順序數字就按別的文件中的順序轉換,得到下圖中的這個對應關係,紅色數字表示源碼中的數字:
-
我們運用上面的對應看看主頁,按照上圖的規則剛好能得到我們看到的數據,可以看到,對應的上。那麼,再將這個套路運用到別的網頁,我自己看了十幾頁都對的上,我認爲的話應該就是這麼實現的反爬了:
-
- 那麼怎麼用代碼去實現這種反推呢,下面介紹:
-
用到python的第三方庫:fontTools,清華鏡像,我安裝的源碼是(你們可能是pip3):
pip install -i https://tuna.tsinghua.edu.cn/simple fontTools -
請求頁面,獲取字體變換的源數據,這裏我就只獲取了指導價格,裏面用到的scrapy.Selector()對象,和lxml差不多,但是我覺得會好一點,這段就是爲了獲取到指導價格那串迷惑文字:
# get response detail_url = 'https://house.leju.com/dl149612/' resp = requests.get(detail_url) # get guide price from scrapy import Selector html = Selector(text=resp.text) guide_price = html.xpath("normalize-space(//span[@class='t_l'])").extract_first() print(guide_price) # result: 指導價格: 約52777-59777 元/㎡
-
利用fontTools對字體文件進行解析,這裏我們不需要獲取字體文件,因爲,樂居將字體文件的base64編碼的數據直接放在網頁源碼裏面了,就是我剛開始說的new_font,這裏還有個原因就是字體文件其實應用的是哪一個是不固定的,在下面你們會看到,字體文件變了三四次,所以,我不推薦大家直接將字體文件下載下來就不變了,另外,多請求一次浪費資源:
所以,我們就不用再請求一次字體文件了,這裏直接利用python的字符串切割獲得,獲取的是base64字符串,直接編碼爲字節流,爲了之後用:# get font_file_bytes by split html source b64_str = resp.text.split("src: url(data:font/truetype;charset=utf-8;base64,")[1].split(") format('woff');")[0] font_file_io = base64.b64decode(b64_str) print(type(font_file_io)) # result: <class 'bytes'>
-
利用fonttools獲取字體文件,同時保存轉換的字體文件關係:
# parse font file by fontTools.TTFont from fontTools.ttLib import TTFont font = TTFont(io.BytesIO(font_file_io)) # this param also can be a path txt font.saveXML('leju.xml') # save the conversion map to the path you want
正常情況下,我們需要找GlyphOrder和cmap這兩種對應xml的,我也想解釋下對應關係的,但是,好像,他們的coder直接給我們打了註釋了,打開這個xml文件,第一個cmap有後臺程序員對我們深深的愛意:
-
在字體文件中獲取剛剛看到的第一個對應關係(cmap):
# get base conversion map font_map = font['cmap'].getBestCmap() print(type(font_map)) # result: <class 'dict'>
-
在cmap中獲取英文數字列表,同時構造真實對應關係字典,就是將正常順序的數字做鍵,上面的那個英文數字列表轉成阿拉伯數字後做值,組裝成一個字典:
# get conversion dict: key: html source num, value: true num base_eng_list = {'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4', 'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9'} mapping_list = [base_eng_list[_] for _ in list(font_map.values())[:10]] base_num_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] font_dict = dict(zip(base_num_list, mapping_list))
-
利用映射字典轉換網頁源碼中的數字:
# use the map dict converter the html source txt true_guide_price = ''.join([_ if not _.isdigit() else font_dict[_] for _ in guide_price]) font.close() print(true_guide_price) # 指導價格: 約15000-16000 元/㎡
最後,也希望大家的技術水平蒸蒸日上,如果喜歡,不妨關注啥的。
-