目標網址: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)
已解碼成功: