基於大衆點評字體庫的字體反爬案例

目標網址:http://shaoq.com/font
該頁面文章不是固定的,爲動態生成,並且字體做了反爬措施。
在這裏插入圖片描述
在這裏插入圖片描述
該頁面結果簡單,爬取提取數據基本上一行代碼就可以解決。但是爬取下來的爲字體加密後的字符。
在這裏插入圖片描述
所以我現在要做的就是,怎麼去將&#x…;字符轉爲對應的漢字。

先手動下載頁面中的字體文件。然後使用fontTools模塊,讀取字體文件,並將其保存爲xml格式(正常情況下,字體文件是無法打開查看的)
在這裏插入圖片描述
字體文件的xml結構爲:
在這裏插入圖片描述
字體編碼名稱表:
在這裏插入圖片描述
cmap映射表:
在這裏插入圖片描述
每個字體對應的筆畫座標表(這個是不會變的,但是編碼名稱會變):
在這裏插入圖片描述

fontTools模塊中有提取表格中數據的方法,這裏先不演示了,向將該字體文件導入百度字體在線識別看一看對應的文字
在這裏插入圖片描述
在這裏插入圖片描述
可以讀取出所有編碼名稱對應的數字及其漢字,總共601個。我們需要將其拿出來與字符編碼名稱對應起來,生成一個映射表

texts = [
        '','','1','2','3','4','5','6','7','8',
        '9','0','店','中','美','家','館','小','車','大',
        '市','公','酒','行','國','品','發','電','金','心',
        '業','商','司','超','生','裝','園','場','食','有',
        '新','限','天','面','工','服','海','華','水','房',
        '飾','城','樂','汽','香','部','利','子','老','藝',
        '花','專','東','肉','菜','學','福','飯','人','百',
        '餐','茶','務','通','味','所','山','區','門','藥',
        '銀','農','龍','停','尚','安','廣','鑫','一','容',
        '動','南','具','源','興','鮮','記','時','機','烤',
        '文','康','信','果','陽','理','鍋','寶','達','地',
        '兒','衣','特','產','西','批','坊','州','牛','佳',
        '化','五','米','修','愛','北','養','賣','建','材',
        '三','會','雞','室','紅','站','德','王','光','名',
        '麗','油','院','堂','燒','江','社','合','星','貨',
        '型','村','自','科','快','便','日','民','營','和',
        '活','童','明','器','煙','育','賓','精','屋','經',
        '居','莊','石','順','林','爾','縣','手','廳','銷',
        '用','好','客','火','雅','盛','體','旅','之','鞋',
        '辣','作','粉','包','樓','校','魚','平','彩','上',
        '吧','保','永','萬','物','教','喫','設','醫','正',
        '造','豐','健','點','湯','網','慶','技','斯','洗',
        '料','配','匯','木','緣','加','麻','聯','衛','川',
        '泰','色','世','方','寓','風','幼','羊','燙','來',
        '高','廠','蘭','阿','貝','皮','全','女','拉','成',
        '雲','維','貿','道','術','運','都','口','博','河',
        '瑞','宏','京','際','路','祥','青','鎮','廚','培',
        '力','惠','連','馬','鴻','鋼','訓','影','甲','助',
        '窗','布','富','牌','頭','四','多','妝','吉','苑',
        '沙','恆','隆','春','幹','餅','氏','裏','二','管',
        '誠','制','售','嘉','長','軒','雜','副','清','計',
        '黃','訊','太','鴨','號','街','交','與','叉','附',
        '近','層','旁','對','巷','棟','環','省','橋','湖',
        '段','鄉','廈','府','鋪','內','側','元','購','前',
        '幢','濱','處','向','座','下','県','鳳','港','開',
        '關','景','泉','塘','放','昌','線','灣','政','步',
        '寧','解','白','田','町','溪','十','八','古','雙',
        '勝','本','單','同','九','迎','第','臺','玉','錦',
        '底','後','七','斜','期','武','嶺','松','角','紀',
        '朝','峯','六','振','珠','局','崗','洲','橫','邊',
        '濟','井','辦','漢','代','臨','弄','團','外','塔',
        '楊','鐵','浦','字','年','島','陵','原','梅','進',
        '榮','友','虹','央','桂','沿','事','津','凱','蓮',
        '丁','秀','柳','集','紫','旗','張','谷','的','是',
        '不','了','很','還','個','也','這','我','就','在',
        '以','可','到','錯','沒','去','過','感','次','要',
        '比','覺','看','得','說','常','真','們','但','最',
        '喜','哈','麼','別','位','能','較','境','非','爲',
        '歡','然','他','挺','着','價','那','意','種','想',
        '出','員','兩','推','做','排','實','分','間','甜',
        '度','起','滿','給','熱','完','格','薦','喝','等',
        '其','再','幾','只','現','朋','候','樣','直','而',
        '買','於','般','豆','量','選','奶','打','每','評',
        '少','算','又','因','情','找','些','份','置','適',
        '什','蛋','師','氣','你','姐','棒','試','總','定',
        '啊','足','級','整','帶','蝦','如','態','且','嘗',
        '主','話','強','當','更','板','知','己','無','酸',
        '讓','入','啦','式','笑','贊','片','醬','差','像',
        '提','隊','走','嫩','才','剛','午','接','重','串',
        '回','晚','微','周','值','費','性','桌','拍','跟',
        '塊','調','糕'
    ]

現在介紹一下fontTools獲取字體文件數據的一些方法:
getBestCmap()返回cmap表中對應的映射,只不過code字段返回的爲十進制數據,如果想轉爲十六進制,使用hex()內置方法即可
在這裏插入圖片描述

getGlyphOrder()方法返回所有字符編碼名稱,按表格順序提取,類型爲列表
在這裏插入圖片描述

字體編碼名稱和對應的解碼數據數量必須一樣,由於解碼數據是我從百度字體識別中抓取出來的,固定的,而且位置也是固定的;字體編碼名稱根據每次請求可能不一樣,但是對應位置是固定的。所以可以動態提取字體編碼名稱和解碼數據一一對應。
在這裏插入圖片描述

# 讀取字體文件,生成字體編碼與文字的映射表
def get_font_map():
    """
    讀取字體文件,生成字體編碼與文字的映射表
    :return: 返回編碼文字映射字典
    """
    # 這個字體文件需要先析網頁,找到這個url,然後下載下來到本地,然後使用TTFont()加載字體文件
    #       字體文件的名字
    font = TTFont('123.woff')
    # 得到cmap 字體對應代碼->字體名字
    # font_cmap = font.getBestCmap()
    # 得到所有的字體名字 類似:unif2ab
    font_names = font.getGlyphOrder()
    print('字體編碼名稱數量:',len(font_names))
    # 使用百度在線字體識別工具獲取的文字數據
    texts = [
        '','','1','2','3','4','5','6','7','8',
        '9','0','店','中','美','家','館','小','車','大',
        '市','公','酒','行','國','品','發','電','金','心',
        '業','商','司','超','生','裝','園','場','食','有',
        '新','限','天','面','工','服','海','華','水','房',
        '飾','城','樂','汽','香','部','利','子','老','藝',
        '花','專','東','肉','菜','學','福','飯','人','百',
        '餐','茶','務','通','味','所','山','區','門','藥',
        '銀','農','龍','停','尚','安','廣','鑫','一','容',
        '動','南','具','源','興','鮮','記','時','機','烤',
        '文','康','信','果','陽','理','鍋','寶','達','地',
        '兒','衣','特','產','西','批','坊','州','牛','佳',
        '化','五','米','修','愛','北','養','賣','建','材',
        '三','會','雞','室','紅','站','德','王','光','名',
        '麗','油','院','堂','燒','江','社','合','星','貨',
        '型','村','自','科','快','便','日','民','營','和',
        '活','童','明','器','煙','育','賓','精','屋','經',
        '居','莊','石','順','林','爾','縣','手','廳','銷',
        '用','好','客','火','雅','盛','體','旅','之','鞋',
        '辣','作','粉','包','樓','校','魚','平','彩','上',
        '吧','保','永','萬','物','教','喫','設','醫','正',
        '造','豐','健','點','湯','網','慶','技','斯','洗',
        '料','配','匯','木','緣','加','麻','聯','衛','川',
        '泰','色','世','方','寓','風','幼','羊','燙','來',
        '高','廠','蘭','阿','貝','皮','全','女','拉','成',
        '雲','維','貿','道','術','運','都','口','博','河',
        '瑞','宏','京','際','路','祥','青','鎮','廚','培',
        '力','惠','連','馬','鴻','鋼','訓','影','甲','助',
        '窗','布','富','牌','頭','四','多','妝','吉','苑',
        '沙','恆','隆','春','幹','餅','氏','裏','二','管',
        '誠','制','售','嘉','長','軒','雜','副','清','計',
        '黃','訊','太','鴨','號','街','交','與','叉','附',
        '近','層','旁','對','巷','棟','環','省','橋','湖',
        '段','鄉','廈','府','鋪','內','側','元','購','前',
        '幢','濱','處','向','座','下','県','鳳','港','開',
        '關','景','泉','塘','放','昌','線','灣','政','步',
        '寧','解','白','田','町','溪','十','八','古','雙',
        '勝','本','單','同','九','迎','第','臺','玉','錦',
        '底','後','七','斜','期','武','嶺','松','角','紀',
        '朝','峯','六','振','珠','局','崗','洲','橫','邊',
        '濟','井','辦','漢','代','臨','弄','團','外','塔',
        '楊','鐵','浦','字','年','島','陵','原','梅','進',
        '榮','友','虹','央','桂','沿','事','津','凱','蓮',
        '丁','秀','柳','集','紫','旗','張','谷','的','是',
        '不','了','很','還','個','也','這','我','就','在',
        '以','可','到','錯','沒','去','過','感','次','要',
        '比','覺','看','得','說','常','真','們','但','最',
        '喜','哈','麼','別','位','能','較','境','非','爲',
        '歡','然','他','挺','着','價','那','意','種','想',
        '出','員','兩','推','做','排','實','分','間','甜',
        '度','起','滿','給','熱','完','格','薦','喝','等',
        '其','再','幾','只','現','朋','候','樣','直','而',
        '買','於','般','豆','量','選','奶','打','每','評',
        '少','算','又','因','情','找','些','份','置','適',
        '什','蛋','師','氣','你','姐','棒','試','總','定',
        '啊','足','級','整','帶','蝦','如','態','且','嘗',
        '主','話','強','當','更','板','知','己','無','酸',
        '讓','入','啦','式','笑','贊','片','醬','差','像',
        '提','隊','走','嫩','才','剛','午','接','重','串',
        '回','晚','微','周','值','費','性','桌','拍','跟',
        '塊','調','糕'
    ]
    print('字體編碼對應解碼字符數量:', len(texts))

    # 將 字體名字 和 我們查看到的值 組成一個字典 如:'unif2ab': '副'
    font_name_map = {}
    for index,value in enumerate(texts):
        font_name_map[font_names[index]] = value

    # 轉爲頁面抓取的字符格式映射表,如:'': '美'
    mappings = {}
    for k,v in font_name_map.items():
        if k.startswith('uni'):
            key_ = k.replace('uni','&#x')
            mappings[key_] = v

        else:
            mappings[k] = v
    return mappings

根據此處理過的映射表就可以替換網頁中的特殊字符,替換爲正常的文字。當然這種方法可能存在一定的問題,不可能100%真實還原。
最好的辦法還是根據字符筆畫的座標去解決。這裏能力有限,就不演示了。

完整字體解密代碼:

import requests
import re
from lxml import etree
from pyquery import PyQuery
from fontTools.ttLib import TTFont

# 下載動態字體文件
def save_font_file():
    """
    請求目標網站,下載動態字體文件,並返回頁面內容
    :return:
    """
    # 請求頭
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36'
    }
    # 目標頁面url
    url = 'http://shaoq.com/font'
    # 開始請求
    response = requests.get(url,headers=headers).content.decode('utf-8')
    # print(response)
    # 提取動態字體url鏈接
    sub_font_url = re.findall(r'src:url\("(.*?)"\)',response)[0]
    # 拼接完整的url鏈接
    full_font_url = 'http://shaoq.com/' + sub_font_url
    # 請求字體鏈接,保存字體
    res = requests.get(full_font_url,headers=headers).content
    with open('123.woff','wb')as fp:
        fp.write(res)

    return response

# 讀取字體
# font = TTFont('123.woff')
# font.saveXML('123.xml')
# 讀取字體文件,生成字體編碼與文字的映射表
def get_font_map():
    """
    讀取字體文件,生成字體編碼與文字的映射表
    :return: 返回編碼文字映射字典
    """
    # 這個字體文件需要先析網頁,找到這個url,然後下載下來到本地,然後使用TTFont()加載字體文件
    #       字體文件的名字
    font = TTFont('123.woff')
    # 得到cmap 字體對應代碼->字體名字
    # font_cmap = font.getBestCmap()
    # 得到所有的字體名字 類似:unif2ab
    font_names = font.getGlyphOrder()
    print('字體編碼名稱數量:',len(font_names))
    # 使用百度在線字體識別工具獲取的文字數據
    texts = [
        '','','1','2','3','4','5','6','7','8',
        '9','0','店','中','美','家','館','小','車','大',
        '市','公','酒','行','國','品','發','電','金','心',
        '業','商','司','超','生','裝','園','場','食','有',
        '新','限','天','面','工','服','海','華','水','房',
        '飾','城','樂','汽','香','部','利','子','老','藝',
        '花','專','東','肉','菜','學','福','飯','人','百',
        '餐','茶','務','通','味','所','山','區','門','藥',
        '銀','農','龍','停','尚','安','廣','鑫','一','容',
        '動','南','具','源','興','鮮','記','時','機','烤',
        '文','康','信','果','陽','理','鍋','寶','達','地',
        '兒','衣','特','產','西','批','坊','州','牛','佳',
        '化','五','米','修','愛','北','養','賣','建','材',
        '三','會','雞','室','紅','站','德','王','光','名',
        '麗','油','院','堂','燒','江','社','合','星','貨',
        '型','村','自','科','快','便','日','民','營','和',
        '活','童','明','器','煙','育','賓','精','屋','經',
        '居','莊','石','順','林','爾','縣','手','廳','銷',
        '用','好','客','火','雅','盛','體','旅','之','鞋',
        '辣','作','粉','包','樓','校','魚','平','彩','上',
        '吧','保','永','萬','物','教','喫','設','醫','正',
        '造','豐','健','點','湯','網','慶','技','斯','洗',
        '料','配','匯','木','緣','加','麻','聯','衛','川',
        '泰','色','世','方','寓','風','幼','羊','燙','來',
        '高','廠','蘭','阿','貝','皮','全','女','拉','成',
        '雲','維','貿','道','術','運','都','口','博','河',
        '瑞','宏','京','際','路','祥','青','鎮','廚','培',
        '力','惠','連','馬','鴻','鋼','訓','影','甲','助',
        '窗','布','富','牌','頭','四','多','妝','吉','苑',
        '沙','恆','隆','春','幹','餅','氏','裏','二','管',
        '誠','制','售','嘉','長','軒','雜','副','清','計',
        '黃','訊','太','鴨','號','街','交','與','叉','附',
        '近','層','旁','對','巷','棟','環','省','橋','湖',
        '段','鄉','廈','府','鋪','內','側','元','購','前',
        '幢','濱','處','向','座','下','県','鳳','港','開',
        '關','景','泉','塘','放','昌','線','灣','政','步',
        '寧','解','白','田','町','溪','十','八','古','雙',
        '勝','本','單','同','九','迎','第','臺','玉','錦',
        '底','後','七','斜','期','武','嶺','松','角','紀',
        '朝','峯','六','振','珠','局','崗','洲','橫','邊',
        '濟','井','辦','漢','代','臨','弄','團','外','塔',
        '楊','鐵','浦','字','年','島','陵','原','梅','進',
        '榮','友','虹','央','桂','沿','事','津','凱','蓮',
        '丁','秀','柳','集','紫','旗','張','谷','的','是',
        '不','了','很','還','個','也','這','我','就','在',
        '以','可','到','錯','沒','去','過','感','次','要',
        '比','覺','看','得','說','常','真','們','但','最',
        '喜','哈','麼','別','位','能','較','境','非','爲',
        '歡','然','他','挺','着','價','那','意','種','想',
        '出','員','兩','推','做','排','實','分','間','甜',
        '度','起','滿','給','熱','完','格','薦','喝','等',
        '其','再','幾','只','現','朋','候','樣','直','而',
        '買','於','般','豆','量','選','奶','打','每','評',
        '少','算','又','因','情','找','些','份','置','適',
        '什','蛋','師','氣','你','姐','棒','試','總','定',
        '啊','足','級','整','帶','蝦','如','態','且','嘗',
        '主','話','強','當','更','板','知','己','無','酸',
        '讓','入','啦','式','笑','贊','片','醬','差','像',
        '提','隊','走','嫩','才','剛','午','接','重','串',
        '回','晚','微','周','值','費','性','桌','拍','跟',
        '塊','調','糕'
    ]
    print('字體編碼對應解碼字符數量:', len(texts))

    # 將 字體名字 和 我們查看到的值 組成一個字典 如:'unif2ab': '副'
    font_name_map = {}
    for index,value in enumerate(texts):
        font_name_map[font_names[index]] = value

    # 轉爲頁面抓取的字符格式映射表,如:'': '美'
    mappings = {}
    for k,v in font_name_map.items():
        if k.startswith('uni'):
            key_ = k.replace('uni','&#x')
            mappings[key_] = v

        else:
            mappings[k] = v
    return mappings

# 根據處理後的映射表和網頁內容,進行解碼並提取文字內容
def html_font_parse(response,mappings):
    # 解碼後的響應內容變量
    response_ = response
    # 循環處理後的字體映射表
    for k,v in mappings.items():
        # 拼接頁面中完整的亂碼字符
        uni_font = k + ';'
        # 判斷亂碼字符是否存在抓取下來的網頁內容中
        if uni_font in response_:
            # 如果存在,將其替換爲解碼後的漢字
            response_ = response_.replace(uni_font,mappings[k])
    # print(response_)
    # 根據解碼後的網頁內容提取數據
    tree = etree.HTML(response_)
    text = tree.xpath('//body//text()')[3:]
    text = ''.join(text).replace('\n','')
    print(text)

if __name__ == '__main__':
    response = save_font_file()
    mappings = get_font_map()
    html_font_parse(response,mappings)

已解碼成功:
在這裏插入圖片描述

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