記一次css字體反爬


前段時間在看css反爬的時候,發現很多網站都做了css反爬,比如,設置字體反爬的(58同城租房版塊,實習僧招聘https://www.shixiseng.com/等)設置雪碧圖反爬的(自如租房http://gz.ziroom.com/)。

還有一個網站本身是沒有其他反爬措施的,只是設置了字體反爬,但是這個網站的反爬就有些扯淡,http://www.qiwen007.com/,我們隨便點開一個文章,並打開開發者工具

 

 

 其中的文字並不是像其他字體反爬一樣,是將某些文字轉為了Unicode顯示在源碼中的

首先來看一下破解流程及思路:

"""
流程及思路:
1. 通過requests請求獲取響應數據,得到的就是源碼中加密數據(雜亂的文章)
2. 將加密數據的每一個字符轉成Unicode編碼,得到字符的Unicode列表
3. 通過搜索源碼中font-face關鍵字,找到字體庫文件(ttf/woff文件)將當前域名拼接上/hansansjm.ttf,即 http://www.qiwen007.com/hansansjm.ttf,打開后即下載
4. 使用FontCreator或者百度字體編輯器打開ttf文件,會看到里面每個字符
5. 將ttf轉為xml文件
6. 同時使用pycharm打開xml文件,找到 glyf 標簽下面的 TTGlyph 標簽,里面的name 即是xml文件中的Unicode編碼,
里面contour下面的pt的x,y即是每個字符的坐標,計算坐標差(計算坐標差的時候,可以直接取第一個contour的前兩組pt即可)
(但是 TTGlyph name的順序 有可能和ttf文件里面的字符的Unicode順序對應不上,此時就要從GlyphOrder里面看是否對應,
如果對應,就從GlyphOrder中獲取每個xml文件中的每個字符的Unicode列表,然后遍歷列表,從 glyf 中找到對應的坐標)
7. 手動去組成漢字字符的列表,然后使用映射(dict(zip()))得到漢字和坐標差的映射
8. 在第6步中我們可以獲取Unicode和坐標差的映射
9. 然后回到第2步,拿到加密數據的Unicode編碼列表后,去Unicode和坐標差的映射中找到對應的坐標差,拿到坐標差后找到對應的漢字
至此,破解過程結束
"""

這里說明一下,為什么加密內容轉Unicode之后不能直接用的原因,因為加密內容的Unicode和ttf文件中的Unicode不對應

 

 可以看到在ttf文件中萬字的編碼為uni2F00,而在Unicode在線編碼中 為\u4e07,uni 和 \u 可忽略,直接看后四位,后面代碼中有做轉換

獲取文章加密內容,及將每個字符轉為Unicode編碼
def get_source_article():
    """獲取加密文章內容"""
    url = 'http://www.qiwen007.com/mb-db/pc-sg/zbwz/363609.html'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36',
    }
    resp = requests.get(url=url, headers=headers)

    html = etree.HTML(resp.text)

    p_list = html.xpath('//div[@class="article"]/p')
    for p in p_list:
        temp_article = p.xpath('./text()')
        if len(temp_article) > 0:
            print('temp_article', temp_article)
            result = to_unicode(temp_article[0])
            print('result', result)
            break


def to_unicode(temp_article):
    """將漢字全部轉為Unicode編碼"""
    bytes_article = temp_article.encode(encoding='unicode-escape')
    str_article_li = str(bytes_article)[2:-1].split('\\')
    uni_article_li = ['uni' + uni[1:].upper() for uni in str_article_li if uni != '']
print(uni_article_li)
 
        

 

將ttf文件或者woff文件轉為xml文件

# 將ttf/woff文件轉換為xml文件(ttf/woff文件pycharm打開是亂碼,xml可以打開)
from fontTools.ttLib import TTFont
font = TTFont('hansansjm.ttf')
# 此時就將ttf/woff文件轉換為了xml文件
font.saveXML('hansansjm.xml')

 

坑:TTGlyph name的順序 有可能和ttf文件里面的字符的Unicode順序對應不上

 

 

 

 

此時就要找 GlyphOrder 中的name 

 

 

組成 所有漢字和坐標差的映射,和 Unicode和坐標差的映射

# 此漢字列表,需要通過百度字體編輯器(在線)打開ttf/woff文件,或者FontCreator,將全部漢字按照順序寫到列表中
hans_list = ['', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '穿', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '西', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '',
             ]


def get_coordinate_hans_and__unicode_coordinate_map():
    """
    獲取所有漢字和坐標差的映射,和 Unicode和坐標差的映射,
    接下來便可以通過映射找到Unicode所對應的坐標差,
    拿到坐標差之后,就可以再通過coordinate_hans_map,找到具體的漢字
    """
    # 存儲該hansansjm.xml文件中所有的字符坐標差
    coordinate_diff_list = []

    # Unicode和坐標差的映射
    _unicode_coordinate_map = {}

    print('>>> 開始計算xml文件中所有字符的坐標差')
    content = parse('hansansjm.xml')
    # 此處有個坑,應當先獲取GlyphOrder中的GlyphID對應的name即Unicode,這里的Unicode是和ttf文件中一一對應的
    # 而glyf中的TTGlyph對應的name(Unicode)不是和ttf文件中一一對應的
    # 應該拿到GlyphOrder 下面所有的Unicode編碼后,再去 glyf中根據Unicode找對應的坐標
    GlyphID_list = content.getElementsByTagName('GlyphID')
    TTGlyph_list = content.getElementsByTagName('TTGlyph')
    # 由於xml文件中glyf下面的第一個TTGlyph 所對應的Unicode為.notdef ,要剔除掉,因此TTGlyph_list不能包含第一個元素
    # 注意:如果最后一個元素也不是Unicode編碼的,應當也要剔除掉

    # 獲取GlyphID下面所有的Unicode編碼
    GlyphID_uni_list = []
    for GlyphID in GlyphID_list[1:]:
        # 獲取Unicode
        _unicode = GlyphID.getAttribute('name')
        GlyphID_uni_list.append(_unicode)
    for uni in GlyphID_uni_list:
        for TTGlyph in TTGlyph_list[1:]:
            if uni == TTGlyph.getAttribute('name'):
                # 獲取第一個contour
                # print(TTGlyph.getElementsByTagName('contour')[0])
                first_contour = TTGlyph.getElementsByTagName('contour')[0]

                # 獲取第一個contour中的前兩個pt元素,進一步獲取這兩個元素的x,y屬性,便於計算坐標差
                first_pt = first_contour.getElementsByTagName('pt')[0]
                first_pt_x = int(first_pt.getAttribute('x'))
                first_pt_y = int(first_pt.getAttribute('y'))
                # print(first_pt_x, first_pt_y)

                second_pt = first_contour.getElementsByTagName('pt')[1]
                second_pt_x = int(second_pt.getAttribute('x'))
                second_pt_y = int(second_pt.getAttribute('y'))
                # print(second_pt_x, second_pt_y)

                # 計算坐標差
                coordinate_diff = (second_pt_x - first_pt_x, second_pt_y - first_pt_y)
                # print(coordinate_diff)
                coordinate_diff_list.append(coordinate_diff)
                # break
                _unicode_coordinate_map[uni] = coordinate_diff

            # print(coordinate_diff_list)

    # 將坐標差和漢字組成字典,完成映射
    coordinate_hans_map = dict(zip(coordinate_diff_list, hans_list))
    print(coordinate_hans_map)
    print(_unicode_coordinate_map)
    return coordinate_hans_map, _unicode_coordinate_map

 

完整代碼如下:

import requests
from lxml import etree
from fontTools.ttLib import TTFont
from xml.dom.minidom import parse

"""
此案例是破解css反爬 網站:http://www.qiwen007.com
經過查看網站頁面源碼后發現,tff文字庫是 '/hansansjm.ttf',因此判定該網站的文字庫不會自動變換

woff/ttf文件樣式查看(在線) http://fontstore.baidu.com/static/editor/index.html
也可以使用FontCreator(下載地址) https://www.onlinedown.net/soft/88758.htm
"""

"""
流程及思路:
1. 通過requests請求獲取響應數據,得到的就是源碼中加密數據(雜亂的文章)
2. 將加密數據的每一個字符轉成Unicode編碼,得到字符的Unicode列表
3. 通過搜索源碼中font-face關鍵字,找到字體庫文件(ttf/woff文件)將當前域名拼接上/hansansjm.ttf,即 http://www.qiwen007.com/hansansjm.ttf,打開后即下載
4. 使用FontCreator或者百度字體編輯器打開ttf文件,會看到里面每個字符
5. 將ttf轉為xml文件
6. 同時使用pycharm打開xml文件,找到 glyf 標簽下面的 TTGlyph 標簽,里面的name 即是xml文件中的Unicode編碼,
   里面contour下面的pt的x,y即是每個字符的坐標,計算坐標差(計算坐標差的時候,可以直接取第一個contour的前兩組pt即可)
 (但是 TTGlyph name的順序 有可能和ttf文件里面的字符的Unicode順序對應不上,此時就要從GlyphOrder里面看是否對應,
  如果對應,就從GlyphOrder中獲取每個xml文件中的每個字符的Unicode列表,然后遍歷列表,從 glyf 中找到對應的坐標)
7. 手動去組成漢字字符的列表,然后使用映射(dict(zip()))得到漢字和坐標差的映射
8. 在第6步中我們可以獲取Unicode和坐標差的映射
9. 然后回到第2步,拿到加密數據的Unicode編碼列表后,去Unicode和坐標差的映射中找到對應的坐標差,拿到坐標差后找到對應的漢字
至此,破解過程結束
"""

# 此漢字列表,需要通過百度字體編輯器(在線)打開ttf/woff文件,或者FontCreator,將全部漢字按照順序寫到列表中
hans_list = ['', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '穿', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '西', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '', '',
             '', '', '', '', '', '', '', '', '', '', '', '', '',
             ]


def get_result_article():
    """獲取加密文章內容"""
    url = 'http://www.qiwen007.com/mb-db/pc-sg/zbwz/363609.html'
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36',
    }
    resp = requests.get(url=url, headers=headers)

    html = etree.HTML(resp.text)

    p_list = html.xpath('//div[@class="article"]/p')
    for p in p_list:
        temp_article = p.xpath('./text()')
        if len(temp_article) > 0:
            print('temp_article', temp_article)
            result = to_unicode(temp_article[0])
            print('result', result)
            break


def to_unicode(temp_article):
    """將漢字全部轉為Unicode編碼"""
    bytes_article = temp_article.encode(encoding='unicode-escape')
    str_article_li = str(bytes_article)[2:-1].split('\\')
    uni_article_li = ['uni' + uni[1:].upper() for uni in str_article_li if uni != '']
    # print(uni_article_li)
    return parse_uni(uni_article_li)


def parse_uni(uni_article_li):
    # 獲取坐標差漢字映射,和 Unicode 坐標差的映射
    get_map = get_coordinate_hans_and__unicode_coordinate_map
    coordinate_hans_map, _unicode_coordinate_map = get_map()
    result_article_content = ''
    for uni in uni_article_li:
        if uni in list(_unicode_coordinate_map.keys()):
            coordinate = _unicode_coordinate_map.get(uni)
            hans = coordinate_hans_map.get(coordinate)
            # print('1', hans)
            result_article_content += hans
        else:
            hans = chr(int(uni[3:], 16))
            # print('2', hans)
            result_article_content += hans

    return result_article_content


# 將ttf/woff文件轉換為xml文件(ttf/woff文件pycharm打開是亂碼,xml可以打開)
def ttf_to_xml():
    font = TTFont('hansansjm.ttf')
    # 此時就將ttf/woff文件轉換為了xml文件
    font.saveXML('hansansjm.xml')


def get_coordinate_hans_and__unicode_coordinate_map():
    """
    獲取所有漢字和坐標差的映射,和 Unicode和坐標差的映射,
    接下來便可以通過映射找到Unicode所對應的坐標差,
    拿到坐標差之后,就可以再通過coordinate_hans_map,找到具體的漢字
    """
    # 存儲該hansansjm.xml文件中所有的字符坐標差
    coordinate_diff_list = []

    # Unicode和坐標差的映射
    _unicode_coordinate_map = {}

    print('>>> 開始計算xml文件中所有字符的坐標差')
    content = parse('hansansjm.xml')
    # 此處有個坑,應當先獲取GlyphOrder中的GlyphID對應的name即Unicode,這里的Unicode是和ttf文件中一一對應的
    # 而glyf中的TTGlyph對應的name(Unicode)不是和ttf文件中一一對應的
    # 應該拿到GlyphOrder 下面所有的Unicode編碼后,再去 glyf中根據Unicode找對應的坐標
    GlyphID_list = content.getElementsByTagName('GlyphID')
    TTGlyph_list = content.getElementsByTagName('TTGlyph')
    # 由於xml文件中glyf下面的第一個TTGlyph 所對應的Unicode為.notdef ,要剔除掉,因此TTGlyph_list不能包含第一個元素
    # 注意:如果最后一個元素也不是Unicode編碼的,應當也要剔除掉

    # 獲取GlyphID下面所有的Unicode編碼
    GlyphID_uni_list = []
    for GlyphID in GlyphID_list[1:]:
        # 獲取Unicode
        _unicode = GlyphID.getAttribute('name')
        GlyphID_uni_list.append(_unicode)
    for uni in GlyphID_uni_list:
        for TTGlyph in TTGlyph_list[1:]:
            if uni == TTGlyph.getAttribute('name'):
                # 獲取第一個contour
                # print(TTGlyph.getElementsByTagName('contour')[0])
                first_contour = TTGlyph.getElementsByTagName('contour')[0]

                # 獲取第一個contour中的前兩個pt元素,進一步獲取這兩個元素的x,y屬性,便於計算坐標差
                first_pt = first_contour.getElementsByTagName('pt')[0]
                first_pt_x = int(first_pt.getAttribute('x'))
                first_pt_y = int(first_pt.getAttribute('y'))
                # print(first_pt_x, first_pt_y)

                second_pt = first_contour.getElementsByTagName('pt')[1]
                second_pt_x = int(second_pt.getAttribute('x'))
                second_pt_y = int(second_pt.getAttribute('y'))
                # print(second_pt_x, second_pt_y)

                # 計算坐標差
                coordinate_diff = (second_pt_x - first_pt_x, second_pt_y - first_pt_y)
                # print(coordinate_diff)
                coordinate_diff_list.append(coordinate_diff)
                # break
                _unicode_coordinate_map[uni] = coordinate_diff

            # print(coordinate_diff_list)

    # 將坐標差和漢字組成字典,完成映射
    coordinate_hans_map = dict(zip(coordinate_diff_list, hans_list))
    print(coordinate_hans_map)
    print(_unicode_coordinate_map)
    return coordinate_hans_map, _unicode_coordinate_map


# ttf_to_xml()
get_result_article()

 

最后說明:在我測試的時候,發現最終結果還是有一些問題,通過解密,數據還是和頁面上顯示的不太一樣,個別字符還是不對,搞不懂問題出在哪里,歡迎大佬們指正

 


免責聲明!

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



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