58 字體反爬攻略 python3


1、下載安裝包

pip install fontTools

2、下載查看工具FontCreator 

百度后一路傻瓜式安裝即可

3、反爬蟲機制

網頁上看見的

后台源代碼里面的

從上面可以看出,生這個字變成了亂碼,請大家特別注意箭頭所指的數字。

 

3、解決

1、確定反爬方法

在看了別人的解析文章之后,確定采取的是字體反爬機制,即網站定義了字體文件,然后進行相應的查找替換,在前端看起來,是沒有任何差異的。其實從審查元素的也是可以看到的:

和大眾點評的反爬差不多,都是通過css搞得。

2、尋找字體文件

以上面方框里的”customfont“為關鍵詞搜了一下,發現就在源代碼里面:

而且還有base64,直接進行解密,但是解密出來的其實是亂碼,這個時候其實要做的很簡單,把解密后的內容保存為.ttf格式即可。

ttf文件: *.ttf是字體文件格式。TTF(TrueTypeFont)是Apple公司和Microsoft公司共同推出的字體文件格式,隨着windows的流行,已經變成最常用的一種字體文件表示方式。

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

因為我們要對字體進行研究,所以必須將它打開,這里我是用的是FontCreator,打開以后是這個樣子(其實很多字,在這里為了看的清楚,所以只截了下面的圖):

很明顯,每個字可以看到字形和字形編碼。

觀察現在箭頭指的地方和前面箭頭指的地方的數字是不是一樣啊,沒錯,就是通過這種方法進行映射的。

所以我們現在的思路似乎就是在源代碼里找到箭頭指的數字,然后再來字體里找到后替換就行了。

恭喜你,如果你也是這么想的,那你就掉坑里了。

因為每次訪問,字體字形是不變的,但字符的編碼確是變化的。因此,我們需要根據每次訪問,動態解析字體文件。

字體1:

字體2:

所以想通過寫死的方式也是行不通的。

這個時候我們就要對字體文件進行更深一步的研究了。

3、研究字體文件

剛剛的.ttf文件我們是看不到內部的東西的,所以這個時候我們要對字體文件進行轉換格式,將其轉換為xml格式,然后來查看:

具體操作如下:

xml的格式如下:

今天,我終於弄懂了字體反爬是個啥玩意!

文件很長,我只截取了一部分。

仔細的觀察一下,你會發現~這倆下面的x,y,on值都是一毛一樣的。所以我們的思路就是以一個已知的字體文件為基本,然后將獲取到的新的字體文件的每個文字對應的x,y,on值進行比較,如果相同,那么說明新的文字對就 可以在基礎字體那里找到對應的文字,有點繞,下面舉個小例子。

假設: “我” 在基本字體中的名為uni1,對應的x=1,y=1,n=1新的字體文件中,一個名為uni2對應的x,y, n分別於上面的相等,那么這個時候就可以確定uni2 對應的文字為”我”。

查資料的時候,發現在特殊情況下,有時候兩個字體中的文字對應的x,y不相等,但是差距都是在某一個閾值之內,處理方法差不多,只不過上面是相等,這種情況下就是要比較一下。

其實,如果你用畫圖工具按照上面的x與y值把點給連起來,你會發現,就是漢字的字形~

所以,到此總結一下:

一、將某次請求獲取到的字體文件保存到本地[基本字體];
二、用軟件打開后,人工的找出每一個數字對應的編碼[
一定要保證順序的正確,要不然會出事];
三、我們以后訪問網頁時,需要保存新字體文件;
四、用Fonttools庫對基本字體與新字體進行處理,找
到新的字體與基本字體之間的映射;
五、替換;

4、代碼

# coding=utf-8
import requests
import re
import time
import  lxml.html as H
import base64
from fontTools.ttLib import TTFont
import requests
from lxml import etree
def get_data(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
    }
    response = requests.get(url, headers=headers)
    font_data_origin = re.search(r'base64,(.*?)\)', response.text, re.S).group(1)
    font_data_after_decode = base64.b64decode(font_data_origin)
    new_font_name = "font_new.ttf"
    with open(new_font_name, 'wb') as f:
        f.write(font_data_after_decode)
    # font_base = TTFont('font_base.ttf').saveXML('font_base.xml')
    # font_base = TTFont('font_new.ttf').saveXML('font_new.xml')
    map_data = tff_parse(new_font_name)
    names = etree.HTML(response.text).xpath('//span[@class="infocardName fl stonefont resumeName"]/text()')
    # 有的時候會找不到,可以多執行幾次;
    if names:
        for name in names:
            print('name in page source', name)
            for j in map_data.keys():
                    name = name.replace(j, map_data[j])
            print('name actual', name)

def tff_parse(font_parse_name):
    # 我這里的字體的順序,如果你的不同,一定要修改
    font_dict = [u'', u'', u'', u'', u'', u'', u'8', u'1', u'', u'E', u'2', u'6', u'',
                 u'M', u'', u'5', u'', u'', u'', u'', u'', u'', u'4', u'', u'', u'', u'',
                 u'', u'', u'', u'A', u'', u'', u'3', u'', u'7', u'0', u'9', u'', u'', u'',
                 u'', u'', u'', u'B']
    font_base = TTFont('font_base.ttf')
    font_base_order = font_base.getGlyphOrder()[1:]
    # font_base.saveXML('font_base.xml')  #調試用

    font_parse = TTFont(font_parse_name)
    # font_parse.saveXML('font_parse_2.xml')調試用
    font_parse_order = font_parse.getGlyphOrder()[1:]
    f_base_flag = []
    for i in font_base_order:
        flags = font_base['glyf'][i].flags
        f_base_flag.append(list(flags))
    f_flag = []
    for i in font_parse_order:
        flags = font_parse['glyf'][i].flags
        f_flag.append(list(flags))
    result_dict = {}
    for a, i in enumerate(f_base_flag):
        for b, j in enumerate(f_flag):
            if comp(i, j):
                key = font_parse_order[b].replace('uni', '')
                key = eval(r'u"\u' + str(key) + '"').lower()
                result_dict[key] = font_dict[a]
    return result_dict

def comp(L1, L2):
    if len(L1) != len(L2):
        return 0
    for i in range(len(L2)):
        if L1[i] == L2[i]:
            pass
        else:
            return 0
    return 1

if __name__ == '__main__':
    url = "https://su.58.com/qztech/"
    get_data(url)

 

看一下成果

 

參考鏈接:

https://cuiqingcai.com/6431.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM