爬虫之字体解密

一、背景

待破解网站

问题:xpath 提取章节发现字体加密

待破解的 HTML

<dd class="col-sm-3">
    <a href="/books/34/34652/18381474.html">
        <i></i><i></i>章 醒
    </a>
</dd>

二、破解

破解方案

  • 正则提取章节部分,获取加密部分内容(xpath 提取后是加密后的而不是原始数据,所以这里采用正则)
  • 下载字体文件,使用 百度字体编辑器 解析字体文件,获取对应关系
  • 将字体文件转换为 xml,解析 xml 获取字体对应关系

正则提取

chapter_list = re.findall("<dd class=\"col-sm-3\"><a href=.*?>(.*?)</a>", html)

提取后的结果:

# &#xe848; 就是需要破解的、&#xe801;
[
    '<i>&#xe848;</i><i>&#xe801;</i>章&#8192;醒',
    '<i>&#xe848;</i>二章&#8192;谁'
]

百度字体编辑器解密

f12 打开刷新浏览器获取 font.ttf 字体文件,上传到 百度字体编辑器 就会得到一个字体对应关系:

{'uniE800': '的', 'uniE801': '一', 'uniE802': '是', 'uniE803': '了',
 'uniE804': '我', 'uniE805': '不', 'uniE806': '人', 'uniE807': '在',
 'uniE808': '他', 'uniE809': '有', 'uniE80A': '这', 'uniE80B': '个',
 'uniE80C': '上', 'uniE80D': '们', 'uniE80E': '来', 'uniE80F': '到',
 'uniE810': '时', 'uniE811': '大', 'uniE812': '地', 'uniE813': '为',
 'uniE814': '子', 'uniE815': '中', 'uniE816': '你', 'uniE817': '说',
 'uniE818': '生', 'uniE819': '国', 'uniE81A': '年', 'uniE81B': '着',
 'uniE81C': '就', 'uniE81D': '那', 'uniE81E': '和', 'uniE81F': '要',
 'uniE820': '她', 'uniE821': '出', 'uniE822': '也', 'uniE823': '得',
 'uniE824': '里', 'uniE825': '后', 'uniE826': '自', 'uniE827': '以',
 'uniE828': '会', 'uniE829': '家', 'uniE82A': '可', 'uniE82B': '下',
 'uniE82C': '而', 'uniE82D': '过', 'uniE82E': '天', 'uniE82F': '去',
 'uniE830': '能', 'uniE831': '对', 'uniE832': '小', 'uniE833': '多',
 'uniE834': '然', 'uniE835': '于', 'uniE836': '心', 'uniE837': '学',
 'uniE838': '么', 'uniE839': '之', 'uniE83A': '都', 'uniE83B': '好',
 'uniE83C': '看', 'uniE83D': '起', 'uniE83E': '发', 'uniE83F': '当',
 'uniE840': '没', 'uniE841': '成', 'uniE842': '只', 'uniE843': '如',
 'uniE844': '事', 'uniE845': '把', 'uniE846': '还', 'uniE847': '用',
 'uniE848': '第', 'uniE849': '样', 'uniE84A': '道', 'uniE84B': '想',
 'uniE84C': '作', 'uniE84D': '种', 'uniE84E': '开', 'uniE84F': '美',
 'uniE850': '总', 'uniE851': '从', 'uniE852': '无', 'uniE853': '情',
 'uniE854': '己', 'uniE855': '面', 'uniE856': '最', 'uniE857': '女',
 'uniE858': '但', 'uniE859': '现', 'uniE85A': '前', 'uniE85B': '些',
 'uniE85C': '所', 'uniE85D': '同', 'uniE85E': '日', 'uniE85F': '手',
 'uniE860': '又', 'uniE861': '行', 'uniE862': '意', 'uniE863': '动'}

提取 xml

font.ttf 转换为 xml 文件,提取其中的字体对应关系:

# pip install fontTools
from fontTools.ttLib import TTFont

def process_font():
    font = TTFont("fonteditor.ttf")
    font.saveXML("fonteditor.xml")

font.xml

<!-- 提取其中关键部分 -->
<cmap>
    <tableVersion version="0"/>
    <cmap_format_4 platformID="0" platEncID="3" language="0">
      <map code="0xe800" name="uniE800"/><!-- ???? -->
      <map code="0xe801" name="uniE801"/><!-- ???? -->
      <map code="0xe802" name="uniE802"/><!-- ???? -->
      <map code="0xe803" name="uniE803"/><!-- ???? -->
      <map code="0xe804" name="uniE804"/><!-- ???? -->
      <map code="0xe805" name="uniE805"/><!-- ???? -->
      <map code="0xe806" name="uniE806"/><!-- ???? -->
        ....
    </cmap_format_4>
</cmap>

解析:

def parse_xml():
    from xml.dom.minidom import parse
    import xml.dom.minidom

    # 使用minidom解析器打开 XML 文档
    DOMTree = xml.dom.minidom.parse("fonteditor.xml")
    collection = DOMTree.documentElement

    font_info_dict = {}

    # 在集合中获取所有 cmap
    cmap = collection.getElementsByTagName("cmap")
    for item in cmap:
        cmap_format_4 = item.getElementsByTagName("cmap_format_4")
        for cmp_ in cmap_format_4:
            if cmp_.getAttribute("platformID") == "0":
                map_list = cmp_.getElementsByTagName("map")
                for map in map_list:
                    code = map.getAttribute("code")
                    name = map.getAttribute("name")
                    
                    # 0xe806 转换为 &#xe806;
                    code = f'{re.sub("0xe", "&#xe", code)};'
                    # print(code, name)
                    font_info_dict[code] = name

    print(font_info_dict)

对应关系:

{'&#xe800;': 'uniE800', '&#xe801;': 'uniE801'...}

以上三步走完之后差不多就可以完整解析了,完整代码

posted @ 2021-12-12 13:53  Hubery_Jun  阅读(482)  评论(0编辑  收藏  举报