python爬蟲 - js逆向之woff字體反爬破解

前言

本篇博文的主題就是處理字體反爬的,其實這種網上已經很多了,那爲什麼我還要寫呢?因爲無聊啊,最近是真沒啥事,並且我看了下,還是有點難度的,然後這個字體反爬系列會出兩到三篇博文,針對市面上主流的字體反爬,一一講清楚

 

不多bb,先看目標站

 

aHR0cDo{防查找,刪除我,包括花括號}vL3d3dy5kaWFucGluZy5jb20vbW{防查找,刪除我,包括花括號}VtYmVyLzc5Mzk5NTky{防查找,刪除我,包括花括號}L3Jldmlld3M=

 

 

分析

 

打開網站,如下:

 

發現,地址在源碼裏不顯示

 

 

 

再看下面的文字,網頁源碼裏面也沒有正常顯示

 

 

 

 

這種就很秀了啊,對於沒搞過字體反爬的朋友來說,估計就迷糊了,不用怕,跟着我的思路來

先看地址欄,點下那個標籤,看右邊的css樣式(對這個不理解的,看看html前端基礎吧,最多一週就懂了),或者看看我的之前的博文,https://www.cnblogs.com/Eeyhan/category/1339041.html

 

 

 

 

在看下面的內容:

 

 

 

這種啥意思呢,首先哈,看到這種源碼裏面看不到的,那一定是在css樣式裏,用的@font-face自定義的字體,所以,上面圈出來的兩個css就很重要了,點進去看看,點這個

 

 

 

進去之後,格式化一下,然後就看到如下:

 

 

 

 

果然有個@font-face,就看這個後面的url引入了啥樣式的字體文件,往後面拉下滾動條,果然看到一個woff的字體文件

補充一下,字體文件格式有哪幾種呢?常見的有woff,svg,ttf,其他的就不細說了,好的,先把這個字體下載下來,複製鏈接瀏覽器打開直接下載,不用補齊http協議直接下載:

 

 

 

 

這個字體先放着,目前這個是地址相關的,再看內容的字體文件,同樣的方式點擊那個css,進入裏面把鏈接複製出來下載:

 

 

 

 

因爲我之前分析的時候已經下載過了,所以,文件名會有個(1)。

 

好的,這兩個字體文件,梳理一下,f76的是地址的,924的是內容的,這種文件怎麼打開呢?用這個地址:點我 ,(百度的在線字體編輯器網址已經打不開了,另外找的一個)在線打開:

 

 

 

當然你也可以用fontcreator軟件打開:

 

 

 

 

果然哈,這裏面就是定義好的字體了,而可以看到,這種有編碼,有實際字體的,只要找到映射關係,就可以把我們要的內容給映射出來了,那麼,我們怎麼去找映射關係呢?

 

先看看規律哈,提前說下,這裏直接是中文字,而不是網上有些老哥針對字體反爬講解的數字,然後找到映射關係之後減2哈,所以還是要自己去找那套映射邏輯

 

怎麼找?直接用一個字來看吧,就找這個【廣】字

 

 

 

先看網頁源碼裏這個廣是啥編碼,好的,&#xe2c9,先放一放

 

 

看這邊woff字體裏這個廣是啥

在線網站看到的,還好,第一頁就有,是unie2c9

 

 unie2c9跟&#xe2c9,好像有點像,先不急,看下,fontCreator軟件裏是啥:

 

 

 看着有點不一樣哈,這不重要,接下來,我們用python的庫看看,python裏有一個大佬寫好的字體映射文件庫,fontTools(自己用pip安裝,不多介紹了)

 

 

打印結果如下,然後它生成了一個font的xml文件,打開看看:

 

 

裏面有兩個關鍵的節點就是GlyphOrder和cmap,而這兩個,剛纔的代碼裏已經打印出來了,結果:

 

 

那行,我們找下這個【廣】在哪,搜從在線字體文件編輯網裏拿到的unie2c9,發現有兩個:

 

 

 

 

哪個纔是呢?再搜下,字體文件拿到的glyph86,發現沒有

 

 

 

但是,目前感覺有點聯繫,&#xe2c9  ---  unie2c9 --- 86

這種是啥呀,就不多說了,unie2c9前面的uni就是unicode編碼的意思,姑且認定爲【&#xe2c9  =  unie2c9】,那86啥,怎麼映射出【廣】字的,大膽猜測,這個86就是索引位置,在那個woff文件裏數一下,看是不是第86個,先看這個,一行是10個,然後第一行是沒有任何編碼的,所以第一行只有9個,

 

 往下數,數到第8行倒數第四個,也就是87,但是第一行只有9個,那就是86了

 

 

 

哈哈哈,剛好對上,那現在就說得通了,那我們先拿到源碼,然後去找映射關係,找到索引位置,再從索引位置裏找到真實的文字內容就行了。

 

但有個很繁瑣的,這些實際的文字內容,我們要一個一個的手寫映射關係(哭了),沒法啊,找好之後,寫成一個json,然後load吧

 

 

調試

先把剛纔打開網頁源碼,直接copy到本地保存成html文件測試吧,免得一改什麼就請求下,因爲這個站的風控還挺強的

 

廢話不多說,直接處理保存在本地的html,然後我只打印了地址信息

 

 

 

感覺跟在源碼裏看到的&#開頭的有點不一樣,好像給處理成了【\u】,先看看能不能處理吧:

 

複製一個['\ue2c9', '\uef20', '\ue801', '5', '\ued77', '\ue150', '42'],拿來處理下,

 

 

 

臥槽,這咋回事,打斷點一看,這個參數並不是我們預期的,

 

 

那多半就是那個被轉義成【\u】的問題了,那我們直接在讀取內容的時候,直接就替換一下:

 

 

 

執行下:

 

 

 

 然後同樣的,拿第一個來處理:

 

 

完美,跟原網站的數據對上

 

 

 接着再處理內容的,這個內容原理一樣,只是把woff文件替換下即可

 

打印下內容的:

 

 

 

選第一個,然後執行:

 

 

對比原網站:

 

 

然後,有朋友要問了,那後面的emoji怎麼沒有搞出來,看看源碼哈:

 

 

這個emoji,是個圖片資源,你要處理肯定是可以的,拼接一下就可以了

 

python實現

提一句,那兩個字體文件經過我的發現,是會不定期變的,所以你需要去請求源碼,用正則匹配指定位置,然後請求css文件,再去把woff文件url匹配出來,單獨請求,下載下來,接着完成後續的工作即可

 

 最後用python完整實現,完整的代碼就不貼出來了,後續的都是一些常規且簡單的操作了,再一個就是,我根本就沒寫完整的代碼(哈哈哈哈哈),只貼出部分:

from fontTools.ttLib import TTFont
import re
import requests
from lxml import etree
import json


def parser_woff_font(font='4375cf76.woff', something=None):
    font = TTFont(font)
    glyph = font.getReverseGlyphMap()
    f = open('font_template.json', encoding='utf-8')
    font_template = json.load(f)
    f.close()
    new_str = ''
    for item in something:
        if not item:
            continue
        if item.endswith(';'):
            item = item.replace(';', '')
        if item in glyph:
            index = glyph.get(item)
            if index:
                real = font_template.get(str(index))
                if real:
                    new_str += real
        else:
            new_str += item
    print(12312312, new_str)
    return new_str


def get_real_data():
    f = open('content.html', encoding='utf-8')
    source_data = f.read()
    source_data = source_data.replace('&#x', 'uni')
    f.close()
    html = etree.HTML(source_data)
    data = html.xpath('//div[@class="txt J_rptlist"]')
    for item in data:
        temp_dict = dict()
        shop_name = item.xpath('./div[1]/h6//text()')
        shop_addr = item.xpath('.//div[@class="mode-tc addres"]/p//text()')
        shop_score = item.xpath('.//div[@class="mode-tc comm-rst"]/span/@class')
        shop_comment = item.xpath('.//div[@class="mode-tc comm-entry"]//text()')
        comment_photo_url = item.xpath('.//div[@class="mode-tc comm-photo"]/a/@href')
        comment_photo_url = ''.join(comment_photo_url) if comment_photo_url else ''
        create_time = item.xpath('.//div[@class="mode-tc info"]/span[1]/text()')
        create_time = ''.join(create_time) if create_time else ''
        if create_time:
            create_time = create_time.replace('發表於', '')
        temp_dict['shop_name'] = shop_name
        temp_dict['shop_addr'] = shop_addr
        temp_dict['shop_score'] = shop_score
        temp_dict['shop_comment'] = shop_comment
        temp_dict['comment_photo_url'] = comment_photo_url
        temp_dict['create_time'] = create_time
        print(123123, temp_dict['shop_comment'])


# get_real_data()




s = ['unif1af;', 'unif147;', 'uniecc0;', 'unie635;', 'unif083;', 'unie3c5;', 'unif802;', ' ', 'unie931;', 'uniea55;', 'unif534;', 'unied79;', 'unie1bd;', ' ', 'unie1e4;', 'unie7b0;', 'unie65d;', 'unif534;', 'unie3c5;', 'unie66f;', 'unif52d;', ' ', 'unif765;', 'unif49d;', 'unieb19;', 'unie2de;', 'unie66f;', '', 'unie8ee;', 'unie3a4;', 'unif759;', ' ', 'unif195;', 'unif195;', 'unif195;', 'unif195;']

parser_woff_font('2f66e924.woff', s)

 

那個映射的font_template.json文件,點我

說明一下,這個json映射關係是隻針對這一個站,並不通用網上所有的字體反爬哈,而且,這個站的映射,說不定以後還會改變,所以,你懂我意思吧

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章