深刻細枝末節,字體反爬蟲到底怎麼一回事

內容選自即將出版的《Python3 反爬蟲原理與繞過實戰》,本次公開書稿範圍爲第 6 章——文本混淆反爬蟲。本篇爲第 6 章中的第 4 小節,其他小節將逐步放送css

字體反爬蟲開篇概述

在 CSS3 以前,Web 開發者必須使用用戶計算機上已有的字體。可是在 CSS3 時代,開發者可使用@font-face 爲網頁指定字體,對用戶計算機字體的依賴。開發者可將心儀的字體文件放在 Web 服務器上,並在 CSS 樣式中使用它。用戶使用瀏覽器訪問 Web 應用時,對應的字體會被瀏覽器下載到用戶的計算機上。html

在學習瀏覽器和頁面渲染的相關知識時,咱們瞭解到 CSS 的做用是修飾 HTML ,因此在頁面渲染的時候不會改變 HTML 文檔內容。因爲字體的加載和映射工做是由 CSS 完成的,因此即便咱們藉助 Splash、Selenium 和 Puppeteer 工具也沒法得到對應的文字內容。字體反爬蟲正是利用了這個特色,將自定義字體應用到網頁中重要的數據上,使得爬蟲程序沒法得到正確的數據。web

6.4.1 字體反爬蟲示例

示例 7:字體反爬蟲示例。算法

網址:http://www.porters.vip/confus...瀏覽器

任務:爬取影片信息展現頁中的影片評分、評價人數和票房數據,頁面內容如圖 6-32 所示。服務器

圖 6-32 示例 7 頁面網絡

在編寫代碼以前,咱們須要肯定目標數據的元素定位。定位時,咱們在 HTML 中發現了一些奇怪的符號,HTML 代碼以下:異步

<div class="movie-index">

頁面中重要的數據都是一些奇怪的字符,本應該顯示「9.7」的地方在 HTML 中顯示的是「☒.☒」,而本應該顯示「56.83」的地方在 HTML 中顯示的是「☒☒.☒☒」。與 6.3 節中的映射反爬蟲不一樣,案例中的文字都被「☒」符號代替了,根本沒法分辨。這就很奇怪了,「☒」能表明這麼多種數字嗎?編輯器

要注意的是,Chrome 開發者工具的元素面板中顯示的內容不必定是相應正文的原文,要想知道「☒」符號是什麼,還須要到網頁源代碼中確認。對應的網頁源代碼以下:工具

<div class="movie-index">

從網頁源代碼中看到的並非符號,而是由 開頭的一些字符,這與示例 6 中的 SVG 映射反爬蟲很是類似。咱們將頁面顯示的數字與網頁源代碼中的字符進行比較,映射關係如圖 6-33 所示。

圖 6-33 字符與數字的映射關係

字符與數字是一一對應的,咱們只須要多找一些頁面,將 0 ~ 9 數字對應的字符湊齊便可。但若是目標網站的字體是動態變化的呢?映射關係也是變化的呢?

根據 6.3 節的學習和分析,咱們知道人爲映射並不能解決這些問題,必須找到映射關係的規律,並使用 Python 代碼實現映射算法才行。繼續往下分析,難道字符映射是先異步加載數據再使用 JavaScript 渲染的?

圖 6-34 請求記錄

網絡請求記錄如圖 6-34 所示,請求記錄中並無發現異步請求,這個猜想並無獲得證明。CSS 樣式方面有沒有線索呢?頁面中包裹符號的標籤的 class 屬性值都是 stonefont:

<span class="stonefont">.</span>

但對應的 CSS 樣式中僅設置了字體:

.stonefont {

既然是自定義字體,就意味着會加載字體文件,咱們能夠在網絡請求中找到加載的字體文件 movie.woff,並將其下載到本地,接着使用百度字體編輯器看一看裏面的內容。

百度字體編輯器 FontEditor (詳見 http://fontstore.baidu.com/st...)是一款在線字體編輯軟件,可以打開本地或者遠程的 ttf、woff、eot、otf 格式的字體文件,具有這些格式字體文件的導入和導出功能,而且提供字形編輯、輪廓編輯和字體實時預覽功能,界面如圖 6-35 所示。

圖 6-35 百度字體編輯器界面

打開頁面後,將 movie.woff 文件拖曳到百度字體編輯器的灰色區域便可,字體文件內容如圖 6-36 所示。

圖 6-36 字體文件 movie.woff 預覽

該字體文件中共有 12 個字體塊,其中包括 2 個空白字體塊和 0 ~ 9 的數字字體塊。咱們能夠大膽地猜想,評分數據和票房數據中使用的數字正是今後而來。

由此看來,咱們還須要瞭解一些字體文件格式相關的知識,在瞭解文件格式和規律後,纔可以找到更合理的解決辦法。

6.4.2 字體文件 WOFF

WOFF(Web Open Font Format,Web 開放字體格式)是一種網頁所採用的字體格式標準。本質上基於 SFNT 字體(如 TrueType),因此它具有 TrueType 的字體結構,咱們只須要了解 TrueType 字體的相關知識便可。

TrueType 字體是蘋果公司與微軟公司聯合開發的一種計算機輪廓字體,TrueType 字體中的每一個字形由網格上的一系列點描述,點是字體中的最小單位,字形與點的關係如圖 6-37 所示。

圖 6-37 字形與點的關係

字體文件中不只包含字形數據和點信息,還包括字符到字形映射、字體標題、命名和水平指標等,這些信息存在對應的表中,因此咱們也能夠認爲 TrueType 字體文件由一系列的表組成,其中經常使用的表

及其做用如圖 6-38 所示。

圖 6-38 構成字體文件的經常使用表及其做用

如何查看這些表的結構和所包含的信息呢?咱們能夠藉助第三方 Python 庫 fonttools 將 WOFF 等字體文件轉換成 XML 文件,這樣就能查看字體文件的結構和表信息了。首先咱們要安裝 fonttools 庫, 安裝命令爲:

$ pip install fonttools

安裝完畢後就能夠利用該庫轉換文件類型,對應的 Python 代碼爲:

from fontTools.ttLib import TTFont

代碼運行後就會在當前目錄生成名爲 movie 的 XML 文件。文件中字符到字形映射表 cmap 的內容以下:

<cmap_format_4 platformID="0" platEncID="3" language="0">

map 標籤中的 code 表明字符,name 表明字形名稱,關係如圖 6-39 所示。

圖 6-39 字符到字形映射關係示例

XML 中的字符 0xe339 與網頁源代碼中的字符 對應,這樣咱們就肯定了 HTML 中的字符碼與 movie.woff 字體文件中對應的字形關係。字形數據存儲在 glyf 表中,每一個字形的數據都是獨立的,例如字形 uniE339 的字形數據以下:

<TTGlyph name="uniE339" xMin="0" yMin="-12" xMax="510" yMax="719">

TTGlyph 標籤中記錄着字形的名稱、

x

軸座標和

y

軸座標(座標也能夠理解爲字形的寬高)。contour 標籤記錄的是字形的輪廓信息,也就是多個點的座標位置,正是這些點構成了如圖 6-40 所示的字形。

圖 6-40 字形 uniE339 的輪廓

咱們能夠在百度字體編輯器中調整點的位置,而後保存字體文件並將新字體文件轉換爲 XML 格式,相同名稱的字形數據以下:

<TTGlyph name="uniE339" xMin="115" yMin="6" xMax="430" yMax="495">

接着將調整前的字形數據和調整後的字形數據進行對比。

如圖 6-41 所示,點的位置調整後,字形數據也會發生相應的變化,如 xMin、xMax、yMin、yMax 還有 pt 標籤中的 x 座標 y 座標都與以前的不一樣了。

圖 6-41 字形數據對比

XML 文件中記錄的是字形座標信息,實際上,咱們沒有辦法直接經過字形數據得到文字,只能從其餘方面想辦法。雖然目標網站使用多套字體,但相同文字的字形也是相同的。好比如今有 movie.woff 和 food.woff 這兩套字體,它們包含的字形以下:

# movie.woff

要實現自動識別文字,須要先準備參照字形,也就是人爲地準備數字 0 ~ 9 的字形映射關係和字形數據,如:

# 0 和 7 與字形名稱的映射僞代碼,data 鍵對應的值是字形數據

當咱們遇到目標網站上其餘字體文件時,就可使用參照字形中的字形數據與目標字形進行匹配,若是字形數據很是接近,就認爲這兩個字形描述的是相同的文字。字形數據包含記錄字形名稱和字形起止座標的 TTGlyph 標籤以及記錄點座標的 pt 標籤,起止座標表明的是字形在畫布上的位置,點座標表明字形中每一個點在畫布上的位置。在起止座標中,

x

軸差值表明字形寬度,

y

軸差值表明字形高度。

如圖 6-42 所示,兩個字形的起止座標和寬高都有很大的差異,可是卻可以描述相同的文字,因此字形在畫布中的位置並不會影響描述的文字,字形寬度和字形高度也不會影響描述的文字。

圖 6-42 描述相同文字的兩個字形

點座標的數量和座標值能夠做爲比較條件嗎?

如圖 6-43 所示,兩個不一樣文字的字形數據是不同的。雖然這兩種字形的 name 都是 uniE9C7,可是字形數據中大部分 pt 標籤 x 和 y 的差距都很大,因此咱們能夠斷定這兩個字形描述的並非

同一個文字。你可能會想到點的數量也能夠做爲排除條件,也就是說若是點的數量不相同,那麼這個

兩個字形描述的就不是同一個文字。真的是這樣嗎?

圖 6-43 描述不一樣文字的字形數據對比

在圖 6-44 中,左側描述文字 7 的字形有 17 個點,而右側描述文字 7 的字形卻有 20 個點。對應的字形信息如圖 6-45 所示。

圖 6-44 描述相同文字的字形

圖 6-45 描述相同文字的字形信息

雖然點的數量不同,可是它們的字形並無太大的變化,也不會形成用戶誤讀,因此點的數量並不能做爲排除不一樣字形的條件。所以,只有起止座標和點座標數據徹底相同的字形,描述的纔是相同字符。

6.4.3 字體反爬蟲繞過實戰

要肯定兩組字形數據描述的是否爲相同字符,咱們必須取出 HTML 中對應的字形數據,而後將待確認的字形與咱們準備好的基準字形數據進行對比。如今咱們來整理一下這一系列工做的步驟。

(1) 準備基準字形描述信息。

(2) 訪問目標網頁。

(3) 從目標網頁中讀取字體編碼字符。

(4) 下載 WOFF 文件並用 Python 代碼打開。

(5) 根據字體編碼字符找到 WOFF 文件中的字形輪廓信息。

(6) 將該字形輪廓信息與基準字形輪廓信息進行對比。

(7) 得出對比結果。

咱們先完成前 4 個步驟的代碼。下載 WOFF 文件並將其中字形描述的文字與人類認知的文字進行映射。因爲字形數據比較龐大,因此咱們能夠將字形數據進行散列計算,這樣獲得的結果既簡短又惟一,不會影響對比結果。這裏以數字 0 ~ 9 爲例:

base_font = {

字典中的 name 表明該字形的名稱,value 表明該字形描述的文字,hex 表明字形信息的 MD5 值。

考慮到網絡請求記錄中的字體文件路徑有可能會變化,咱們必須找到 CSS 中設定的字體文件路徑,引入 CSS 的 HTML 代碼爲:

<link href="./css/movie.css" rel="stylesheet">

由引入代碼得知該 CSS 文件的路徑爲 http://www.porters.vip/confus...,文件中 @font-face 處就是設置字體的代碼:

@font-face {

字體文件路徑爲 http://www.porters.vip/confus...。找到文件後,咱們就能夠開始編寫代碼了,對應的 Python 代碼以下:

import re

由於 TTFont 能夠直接讀取 woff 文件的結構,因此這裏不須要將 woff 保存爲 XML 文件。接着以評分數據 9.7 對應的編碼 #xe624.#xe9c7 進行測試,在原來的代碼中引入基準字體數據 base_font,而後新增如下代碼:

web_code = '.'

以上代碼運行結果爲:

['9', '7']

運行結果說明可以正確映射字體文件中字形描述的文字。

6.4.4 小結

字體反爬能給爬蟲工程師帶來很大的麻煩。雖然爬蟲工程師找到了應對方法,但這種方法依賴的條件比較嚴苛,若是開發者頻繁改動字體文件或準備多套字體文件並隨機切換,那真是一件令爬蟲工程師頭疼的事。不過,這些工做對於開發者來講也不是輕鬆的事。

轉載說明

本篇內容摘自出版圖書《Python3 反爬蟲原理與繞過實戰》,歡迎各位好友與同行轉載!

記得帶上相關的版權信息哦😊。

相關文章
相關標籤/搜索