记录爬取含有.ttf自定义字体的爬虫

记录爬取含有.ttf自定义字体的爬虫

示例网站中国供应商网
完整代码:


# 总体思想:保存一份xml文件为模板,找出name和字体之间的映射关系从而找出数字和字体之间的映射关系
# 寻找@font-face字段,这是一种css3使用自定义字体的方式,字体文件加载方式为data-url
# 使用base64.b64decode将文件解码保存为.ttf文件
# 使用第一个网站将.ttf文件转换为svg文件,然后使用第二个网站查看svg图片找到字符和编码的映射关系
# https://everythingfonts.com/ttf-to-svg
# https://icomoon.io/app/#/select
# 由于每次请求网页返回的ttf字体文件的base64 code都不同所以每次都要解析新的映射关系
# from fontTools.ttLib import TTFont 使用TTFont将base64 code保存为xml文件
# 使用xpath解析出每种字体的写法,通过对比找到每次请求返回时的字体与模板字体是否一致从而根据模板得到数字结果
def ttfExample():
    order = ['uni100ff', 'uni10100', 'uni10101', 'uni10102', 'uni10103', 'uni10104', 'uni10105', 'uni10106',
             'uni10107', 'uni10108', 'uni1009f']  # 模板的编码顺序
    number = ['1', '5', '4', '2', '3', '6', '0', '9', "-", '8', '7']  # 模板对应的数字顺序
    with open("./ttf.xml", "rb") as fp:
        selector = etree.HTML(fp.read(), parser=etree.XMLParser(encoding="utf-8"))  # 模板的选择器
    match_char = [selector.xpath(f'//CFFFont/CharStrings/CharString[@name="{name}"]//text()')[0] for name in
                  order]  # 模板的CharString
    match_dict = {key: value for key, value in zip(match_char, number)}
    return match_dict  # 字体和数字的映射

match_dict = ttfExample()
# 缺点是每次都需要保存为xml文件再打开使用etree解析,因为尝试直接用etree解析ttf_code会报错
# 不能再内存中处理直接导致第二个缺点就是多线程下文件命名的问题每个线程不能使用同一个文件名称,所以随机生成文件名称
# 使用完需要及时删除
def parse_code(font, number_list):
    ttf_code = base64.b64decode(font)  # 新的ttf文件
    ttf_file = BytesIO(ttf_code)
    ttf = TTFont(ttf_file)
    ttf_file.close()
    random_file_name = md5().update(time.time()).hexdigest()
    ttf.saveXML(f"./{random_file_name}.xml")  # 保存为xml文件
    with open(f"./{random_file_name}.xml", "rb") as fp:
        second_selector = etree.HTML(fp.read(), parser=etree.XMLParser(encoding="utf-8"))
    os.remove(f"./{random_file_name}.xml")
    names = ttf.getGlyphOrder()  # 获取字体编码->list
    charstrings = [second_selector.xpath(f'//CFFFont/CharStrings/CharString[@name="{name}"]//text()')[0] for name in names]
    phone = {name: match_dict.get(char) for name, char in zip(names, charstrings) if match_dict.get(char) is not None}
    return "".join([phone.get(num.strip()) for num in number_list if num is not ""])


# 同样需要保存新的xml文件,找到name和字体之间的映射关系从而找到name和数字之间的关系
# 使用正则提取目标字段,使用xpath可能是因为编码原因只能提取到“??? ???? ????“这样的结果,未能找到解决方法
def decodeTTf(selector, txt):
    item = {}
    style = selector.xpath('string(//style[contains(text(),"@font-face")]/text())')
    font = re.search(r"base64,(.*?)'\)", style)  # 字体base64编码
    if font:
        font = font.group(1)
    mobile_phone_code = re.search(r'<span class="secret">手机:(.*?)</span>', txt)
    phone_code = re.search(r'<span class="secret">电话:(.*?)</span>', txt)
    if phone_code:
        phone_code = phone_code.group(1)
        phone_num_list = phone_code.replace("&#x", "uni").split(";")
        phone = parse_code(font, phone_num_list)
        item["phone"] = phone
    if mobile_phone_code:
        mobile_phone_code = mobile_phone_code.group(1)
        mobile_phone_num_list = mobile_phone_code.replace("&#x", "uni").split(";")[0:-1]
        mobile_phone = parse_code(font, mobile_phone_num_list)
        item["mobile_phone"] = mobile_phone
        return item



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值