本帖最后由 DreamMark 于 2023-11-23 17:52 编辑
前言
字体反爬, 其实就是在网页端加载一种字体文件,通过映射,在页面上显示正常。传统的方式就是找到目标ttf 文件,人为得映射乱码和真实字符。但是当ttf 更新频繁时,人为对照成本就很高了。针对上面的痛点,想了一种成本较低得方法来解决这个问题。通过调研发现Python 的image 类是可以加载字体文件的,那结合机器学习文字识别那就可以很轻松的拿到 乱码和字体的映射。现在问题是找到一个靠谱的汉字识别模型。对比市面上的汉字识别模型, 最终采用了cnocr 模型。当然使用模型是有准确率的,最终的结果最好还是自己验证一次。
环境安装
python : 3.8.8
win10 系统
安装cnocr
pip install cnocr
如果报错 :
1. ERROR: Failed building wheel for Polygon3
直接通过whl 安装Polygon3
https://www.lfd.uci.edu/~gohlke/pythonlibs/#polygon
即在上述链接中 选择 Polygon3-3.0.9.1-cp38-cp38-win_amd64.whl 下载安装即可
2, 在实际执行中发现 cv2找不到 某个函数,不要犹豫,大概率是opencv-python 的差异,建议直接升级到最高版本
pip install --user opencv-python --upgrade
代码&效果展示
[Python] 纯文本查看 复制代码 import os
from PIL import ImageFont, Image, ImageDraw
from cnocr import CnOcr
import numpy as np
from fontTools.ttLib import TTFont
import requests
from io import BytesIO
def font_to_img(code_list, filename, ignore_flag=True, score=0.95):
normal_dict = {}
be_padding_dict = {}
ocr = CnOcr()
"""
将字体画成图片
code_list: 加密字符列表
filename: 字体文件
ignore_flag:是否忽视 sorce 返回结果
score: 识别准确率默认 95%以上
"""
for char_list in code_list:
char_code = char_list.encode().decode()
img_size = 1024
img = Image.new('1', (img_size, img_size), 255)
draw = ImageDraw.Draw(img)
font = ImageFont.truetype(filename, int(img_size * 0.7))
x, y = draw.textsize(char_code, font=font)
draw.text(((img_size - x) // 2, (img_size - y) // 2), char_code, font=font, fill=0)
# 将单通道 转为 三通道
img = img.convert("RGB")
# word = ocr.ocr_for_single_line("%s.jpg" % mame_ocr)
word = ocr.ocr_for_single_line(np.array(img))
if word["score"] >= score:
# 处理重复名字
# img.save("%s_%s.jpg" % (char_code, word["text"]))
normal_dict[char_code] = word["text"]
else:
be_padding_dict[char_code] = word
img.save("%s_%s_be_padding.jpg" % (char_code, word["text"]))
if ignore_flag:
normal_dict[char_code] = word["text"]
return normal_dict, be_padding_dict
def ttf_parse(url, ttf_name):
"""
根据url 获取返回值
"""
response = requests.get(url)
font_parse = TTFont(BytesIO(response.content))
font_parse.save(ttf_name)
m_dict = font_parse.getBestCmap()
unicode_list = []
for key, _ in m_dict.items():
unicode_list.append(key)
# 获取需要判断的字符
char_list = [chr(ch_unicode) for ch_unicode in unicode_list]
normal_dict, error_dict = font_to_img(char_list, ttf_name)
print(normal_dict)
print(error_dict)
# 删除字体文件
os.remove(ttf_name)
效果
{'\uec20': '公', '\uec29': '是', '\uec2a': '当', '\uec31': '矮', '\uec33': '了', '\uec3a': '保', '\uec43': '近', '\uec44': '味', '\uec4c': '七', '\uec4d': '的', '\uec56': '量', '\uec5f': '坐', '\uec60': '地', '\uec66': '盘', '\uec67': '只', '\uec70': '身', '\uec79': '不', '\uec7a': '八', '\uec82': '三', '\uec8c': '养', '\uec93': '雨', '\uec95': '二', '\uec9c': '很', '\uec9d': '内', '\ueca6': '硬', '\uecad': '过', '\uecaf': '下', '\uecb8': '控', '\uecc0': '空', '\uecc2': '长', '\uecc9': '开', '\ueccb': '低', '\uecda': '真', '\uecdc': '副', '\uece3': '动', '\uece5': '实', '\ueced': '自', '\uecf6': '少', '\uecf8': '启', '\uecfd': '右', '\uecff': '和', '\ued08': '十', '\ued09': '多', '\ued10': '比', '\ued12': '五', '\ued19': '门', '\ued23': '着', '\ued2b': '油', '\ued2c': '音', '\ued35': '手', '\ued3e': '软', '\ued3f': '小', '\ued45': '无', '\ued46': '六', '\ued4f': '泥', '\ued58': '中', '\ued59': '路', '\ued61': '大', '\ued62': '响', '\ued6b': '加', '\ued72': '排', '\ued74': '机', '\ued7b': '高', '\ued7c': '级', '\ued85': '冷', '\ued8e': '灯', '\ued8f': '耗', '\ued97': '呢', '\ued9f': '问', '\ueda1': '上', '\ueda8': '四', '\uedaa': '九', '\uedb1': '一', '\uedbb': '档', '\uedbd': '皮', '\uedc2': '性', '\uedc4': '左', '\uedcc': '里', '\uedcd': '更', '\uedd5': '短', '\uedd7': '光', '\ueddc': '好', '\uedde': '外', '\uedef': '来', '\uedf1': '远', '\uedf8': '有', '\uedfa': '坏', '\uee02': '孩', '\uee0a': '得', '\uee0b': '电'}
以评分95% 做分割 ,下面是可能不对的,经过对比图片发现没有错误。只是评分较低。
{'\uec33': {'text': '了', 'score': 0.8509207963943481}, '\uec7a': {'text': '八', 'score': 0.8456708192825317}, '\uec82': {'text': '三', 'score': 0.8965854644775391}, '\uec95': {'text': '二', 'score': 0.9056768417358398}, '\uecaf': {'text': '下', 'score': 0.8665554523468018}, '\uece3': {'text': '动', 'score': 0.9179448485374451}, '\ueced': {'text': '自', 'score': 0.9478009343147278}, '\uecfd': {'text': '右', 'score': 0.8674691319465637}, '\uedb1': {'text': '一', 'score': 0.8194031715393066}, '\uedde': {'text': '外', 'score': 0.9190731048583984}, '\uee02': {'text': '孩', 'score': 0.6236460208892822}}
|