基于大众点评字体库的字体反爬案例

目标网址: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)

已解码成功:
《基于大众点评字体库的字体反爬案例》

    原文作者:没了对象省了流量ii
    原文地址: https://blog.csdn.net/z_ipython/article/details/101291500
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞