python爬虫-字体反爬全流程(woft文件-转换字体-字体图片-图片识别全流程)

python字体反爬

解决流程:

  1. 分析获得woff文件路径
  2. 将woff文件转换为svg格式文件
  3. 提取svg文件中的字体轮廓数据,画png图,此处首次转化的png图还需要上下镜像处理,和将背景色改为白色,方便识别
  4. 调用百度文字识别接口识别字体
  5. 人工检核,将字体图片打开与解析结果对比并更改

参考文章:

  1. 主要参考:爬虫技巧----字体反爬应对

  2. 字体编辑
    如果字体个数较少,可以借助百度的字体编辑进行人工建立解析字典
    打开:百度字体编辑器
    要将 woff格式 文件转换为 ttf格式 文件,点击左上角的 打开 按钮 读取 ttf格式 文件即可

  3. 字体反爬还可参考:
    反爬虫解析-字体替换(天眼查/猫眼电影)
    Spider-天眼查字体反爬

  4. svg文件转png图片参考:
    使用Python批量转换SVG文件为PNG或PDF文件
    中间遇到问题错误
    解决方案和gtk下载安装问题参考:
    cairosvg使用过程中需要注意的问题
    pygal输出png问题:dlopen() failed to load a library: cairo / cairo-2
    该处要下载gtk±bundle版本
    gtk下载链接
    下载gtk相关文件,并把目录下bin文件添加到环境变量的path值中,并安装pygal,之后重新启动你的python环境,

no library called "libcairo-2" was found
cannot load library 'libcairo.so': error 0x7e
cannot load library 'libcairo.2.dylib': error 0x7e
cannot load library 'libcairo-2.dll': error 0x7e
  1. 字体文件格式转换使用字客网:
    字客网

  2. unicode 的三种表现形式:&#、&#x、\u

  3. 使用百度通用文字识别接口:
    百度通用文字识别
    普通接口每天有50000次免费,高精度的有500次免费,但不支持并发

解决代码

代码使用前的配置

需要配置的参数
""" 你的 APPID AK SK """
APP_ID = '*****'
API_KEY = '*****'
SECRET_KEY = '*****'
# 字体下载链接
woff_url = 'https://static.tianyancha.com/fonts-styles/fonts/af/af55579f/tyc-num.woff'
# png图片保存路径
png_path = './pngpic'
# 原始字符 字体轨迹 提取正则表达式
pattern = r'''<glyph .*? unicode="(.*?)" .*?
    d?=?"?(.*?)"? />'''

整体代码

import os
import re
import time
import requests
import cairosvg
import shutil
from PIL import Image
from aip import AipOcr
import zipfile
import hashlib
import json
# 百度通用文字识别接口配置参数
APP_ID = '*****'
API_KEY = '*****'
SECRET_KEY = '*****'
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)

# 下载字体文件
def down_woff(woff_url):
    header = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
    }
    res = requests.get(woff_url, headers=header)
    # print(res.text)
    file_name = woff_url.split('/')[-1]
    with open(file_name, 'wb') as fw:
        fw.write(res.content)
    return file_name

# 从字客网将woff文件转换为svg文件
def get_svg(file_path):
    m = hashlib.md5()
    with open(file_path, 'rb') as fr:
        content = fr.read()
    header = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36',
    }
    m.update(content)
    md5_text = m.hexdigest()
    # 字客网
    url = f'https://www.fontke.com/?g=Font&m=Tool&a=exists&toolId=convfont&md5={md5_text}'
    res = requests.get(url)
    res = json.loads(res.text)
    if res['exists'] == 'yes':
        url2 = 'https://www.fontke.com/tool/convfont/'
        # 并没有上传文件 而是文件的md5值,因此如果返回不存在,人工上传一次即可
        data = {
            'name': file_path.split('/')[-1].split('.')[0],
            'filemd5': md5_text,
            'oldFormat': 'woff',
            'newformats': 'svg',
        }
        res2 = requests.post(url2, data=data, headers=header)
        res2 = json.loads(res2.text)
        down_url = 'https://www.fontke.com' + res2['downloadUrl']
        zip_res = requests.get(down_url, headers=header)
        with open('tmp.zip', 'wb') as fw:
            fw.write(zip_res.content)
        z = zipfile.ZipFile('./tmp.zip', 'r') 
        for f in z.namelist():
            if 'svg' in f:
                return z.read(f).decode('utf-8')
    else:
        print(res['exists'])

# 根据svg画png图
def draw_png(data,png_path):
    try:
        cc = "\\u" + data[0][3:7]
        print_key = cc.encode('utf-8').decode('unicode_escape')
        key = cc.encode('utf-8').decode('unicode_escape')
    except:
        print_key = data[0]
        key = data[0]
    # 构造svg文件内容
    org_text = f"""<?xml version="1.0" encoding="utf-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg id="_" width="128" height="128" style="width:192px;height:256px;" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="1 1 1024 1024" enable-background="new 1 1 1024 1024" xml:space="preserve"><path fill="#black" d="{data[1]}"/></svg>"""
    # svg转换为png图片
    exportPath = os.path.join(png_path, f'{data[0]}.png')
    with open(exportPath, 'w') as fw:
        cairosvg.svg2png(bytestring=org_text, write_to=exportPath)
    return exportPath,key,print_key

# 处理原始png图片
def deal_pic(exportPath):
    im = Image.open(exportPath)
    # 对图片镜像上下镜像处理
    im = im.transpose(Image.FLIP_TOP_BOTTOM)
    x, y = im.size
    try:
        # 对图片进行添加白色背景
        p = Image.new('RGBA', im.size, (255, 255, 255))
        p.paste(im, (0, 0, x, y), im)
        p.save(exportPath)
    except:
        print(f'{exportPath}图片处理失败')

# 从api接口获取解析结果
def get_word(file_path,accurate=True):
    # 读取图片
    with open(file_path, 'rb') as fp:
        image = fp.read()

    """ 如果有可选参数 """
    options = {}
    options["language_type"] = "CHN_ENG"
    options["detect_direction"] = "true"
    # options["detect_language"] = "true"
    options["probability"] = "true"
    if accurate:
        # 通用文字识别(高精度版)
        res = client.basicAccurate(image, options)
    else:
        # 通用文字识别
        res = client.basicGeneral(image, options)
    try:
        word_res= res['words_result'][0]['words']
        print_res = word_res
    except:
        print_res = res
        word_res= None
    return print_res, word_res

# 获得解析结果字典
def get_words_dict(pattern,png_path,svg_content,accurate=True):
    """
    :param png_path: 保存png图片的文件路径
    :param svg_content: svg内容
    :return: 返回解析字典 但还需要人工检查
    """
    if os.path.exists(png_path):
        # 删除文件夹
        shutil.rmtree(png_path, ignore_errors=True)
        os.mkdir(png_path)
    else:
        os.mkdir(png_path)
    svg_list = re.findall(pattern, svg_content, re.S)
    print('共有字数:',len(svg_list),'个')
    # 解析结果字典 key值为 被替换的字, value为替换字
    result = {}
    result_list=[]
    for i in svg_list:
        exportPath,key,print_key=draw_png(i,png_path)
        deal_pic(exportPath)
        print_res, word_res= get_word(exportPath,accurate)
        result[key] = word_res
        result_list.append(''.join([print_key, '*:* ', print_res]))
        print(print_key, '*:* ', print_res)
        exportPath2 = os.path.join(png_path, f'{i[0]}--{key}.png')
        # 重命名 方便检查
        os.rename(exportPath, exportPath2)
        time.sleep(1)
    print('解析完成:',len(result_list),'个')
    return result,result_list


def main():
    # 字体链接
    woff_url = 'https://static.tianyancha.com/fonts-styles/fonts/af/af55579f/tyc-num.woff'
    file_name = down_woff(woff_url)
    print('woff文件下载成功')
    svg_content = get_svg(file_name)
    if svg_content: print('svg转换成功')
    png_path = './tupian'
    # 提取svg数据的正则
    pattern = r'''<glyph .*? unicode="(.*?)" .*?
    d?=?"?(.*?)"? />'''
    # result={'被替换字':'替换字'}
    result,result_list = get_words_dict(pattern,png_path,svg_content)
    # 解析结果字典 key值为 被替换的字, value为替换字
    print(result)
    with open('result.txt','w',encoding='utf-8') as fw:
        fw.write('\n'.join(result_list))

if __name__ == '__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值