欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 游戏 > 字体反爬(一)

字体反爬(一)

2024/10/24 23:27:30 来源:https://blog.csdn.net/qq_44744457/article/details/141969504  浏览:    关键词:字体反爬(一)

网址

http://xxfb.mwr.cn/sq_djdh.html?v=1.0
获取相关数据

解决

  1. F12 先找接口吧, 搜索一下表格的数据
    在这里插入图片描述
    在这里插入图片描述
    直接从表格中复制
    复制过来乱码,基本锁定有字体反爬处理
    在这里插入图片描述
  2. 先点进去看看
{"addvnm": "#GkcERlldm4_1725629424756otltag㯼㢴#FontTag","alertValue": 0.0,"bsnm": "#GkcERlldm4_1725629424756otltag㱬㯼#FontTag","createTime": "2024-09-06 15:40:21","idNo": "#GkcERlldm4_1725629424756otltag㝺㝸㝴#FontTag","importantSection": "#GkcERlldm4_1725629424756otltag㣸#FontTag","lgtd": "","lttd": "","rvnm": "#GkcERlldm4_1725629424756otltag㰬㯼#FontTag","stType": "#GkcERlldm4_1725629424756otltag㞪㞜㞲㞘㞪#FontTag","stnm": "#GkcERlldm4_1725629424756otltag㩘㲘#FontTag","tm": "2024-09-06 08:00:00","z": "#GkcERlldm4_1725629424756otltag㝶㝺.㝶㝶#FontTag"},

定位一下表格信息内容,源码有
在这里插入图片描述
字段能对上,说明接口是这个,

  1. 寻找字体文件
    在这里插入图片描述
    一般.ttf 或者woff 文件,再结合
<td class="hidden-m" align="center" width="13%" style="font-family:cfg_GkcERlldm4_1725629424756">㱬㯼</td>

GkcERlldm4_1725629424756 并且返回的数据也是带有GkcERlldm4_1725629424756
应该是这个ttf文件
刷新一次
在这里插入图片描述
发现文件名变化了, 而且能在network中观察到, ttf是后出现的
而ttf的文件名字就在返回数据中

#GkcERlldm4_1725629424756otltag㰬㯼#FontTag

所以要解码 㰬㯼 为正常的汉字

思路:

  1. 先根据数据接口拿到数据
  2. 从数据中提取ttf文件的名字
  3. 根据ttf文件名字从ttf文件接口中获取ttf文件并保存
  4. 对数据进行提取把多余部分去除
def extract_data(obj):new_obj = {}for key, value in obj.items():part1 = Noneif isinstance(value, str) and "#FontTag" in value:# 方法1: 通过字符串查找和切片提取start_tag = "#"middle_tag = "otltag"end_tag = "#FontTag"# 提取第一个部分(mPgcfp7TXR_1725546532529)start_idx = value.find(start_tag) + len(start_tag)middle_idx = value.find(middle_tag)part1 = value[start_idx:middle_idx]# 提取第二个部分(㸫㫣)middle_idx += len(middle_tag)end_idx = value.find(end_tag)part2 = value[middle_idx:end_idx]# 输出结果# print("Part 1:", part1)  # 输出 mPgcfp7TXR_1725546532529# print("Part 2:", part2)  # 输出 㸫㫣new_obj[key] = part2else:new_obj[key] = valuereturn new_obj, part1response = requests.get('http://xxfb.mwr.cn/OTMpfiwozTexvsf/OTMnprjsvUahsv', cookies=cookies, headers=headers, verify=False)
datas = json.loads(response.text)['result']
print("=======数据获取成功======")
# print(datas)
ttf_url = ""
newDatas = []
for i, data in enumerate(datas):# ttf_status = Truedata, ttf_url = extract_data(data)newDatas.append(data)
  1. 用FontCreator打开分析真正字符与Name之间的对应关系
    由于动态变化先记录下

㷼㶌 对应 淮河
在这里插入图片描述
在这里插入图片描述
淮的Name是glyph639
河的Name是glygh583

  1. 将字体文件转换为xml,分析uncode编码与Name之间的关系
from fontTools.ttLib import TTFontdef ttf_to_xml(ttf_file, xml_file):# 打开TTF文件font = TTFont(ttf_file)# 保存为XML文件font.saveXML(xml_file)print(f"TTF file '{ttf_file}' has been successfully converted to XML as '{xml_file}'.")# 示例:将 'example.ttf' 转换为 'example.xml'
ttf_to_xml('./Temp/8UJgMNCr5u_1725630929271.ttf', './Temp/example.xml')

d
搜索cmap会发现

<map code="0x3900" name="glyph00001"/>

name和code有一个对应
我们找到glyph00639
在这里插入图片描述

<map code="0x3dfc" name="glyph00639"/><!-- CJK UNIFIED IDEOGRAPH-3DFC -->

在这里插入图片描述
0x3dfc -> 㷼
0x3dfc ->name : glyph00639 -> 淮

 <GlyphOrder><!-- The 'id' attribute is only for humans; it is ignored when parsed. --><GlyphID id="0" name="glyph00000"/><GlyphID id="1" name="glyph00001"/><GlyphID id="2" name="glyph00002"/><GlyphID id="3" name="glyph00003"/><GlyphID id="4" name="glyph00004"/><GlyphID id="5" name="glyph00005"/><GlyphID id="6" name="glyph00006"/><GlyphID id="7" name="glyph00007"/><GlyphID id="8" name="glyph00008"/>

id和name也有对应关系, 有时候会用到这里用不到

和前面记录的一致
所以大概就是
乱码的Unicode-----Unicode码------码对应的name----[name对应的id]
7. 建立乱码与真正字符之间的映射关系
包括:
乱码与Name
根据Name,绘图OCR识别,返回真正字符

所以要从ttf文件中提取两者之间的关系,

def get_char_list_from_ttf(font_file):""" 给定font_file,获取它的中文字符 """font = TTFont(font_file)res = []for cmap_table in font['cmap'].tables:# if cmap_table.platformID == 3:# print(f"Windows Platform, Encoding ID: {cmap_table.encodingID}")# 打印当前 cmap 表中的字符映射for unicode_val, glyph_name in cmap_table.cmap.items():char = chr(unicode_val)# print(f"Unicode: {hex(unicode_val)}, Character: {char}, Glyph: {glyph_name}")res.append([hex(unicode_val), char, glyph_name])return res

输出:[[‘0x42d0’, ‘䋐’, ‘glyph01117’],]

在这里插入图片描述
但是怎么映射为正常字符呢?
因为这里的unicode和并不是正常汉字一一对应,

渲染+OCR文字识别
参考博文 ttf解析
直接copy
功能是给定义一个
乱码的字符对应的glyphName和对应的ttf文件,返回绘制并OCR识别的文字字符

import ddddocrfrom fontTools.ttLib.ttFont import TTFont
from fontTools.pens.svgPathPen import SVGPathPen
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.path import Path
import matplotlib._color_data as mcd
def ttf_to_char(font_file, glyph_set_name="glyph00583", temp_png_path="./"):# %matplotlib inline#加载字体font = TTFont(font_file)#7.1 生成PNG图片#7.1.1 第一步提取绘制命令语句#获取包含字形名称和字形对象的--字形集对象glyphsetglyphset = font.getGlyphSet()# print(glyphset.keys())#获取pen的基类pen = SVGPathPen(glyphset)#查找"马"的字形对象glyph = glyphset[glyph_set_name]#绘制"马"的字形对象glyph.draw(pen)#提取"马"的绘制语句commands = pen._commands# print(commands)total_commands = []command = []for i in commands:#每一个命令语句if  i == 'Z':#以闭合路径指令Z区分不同轮廓线command.append(i)total_commands.append(command)command = []else:command.append(i)#从'head'表中提取所有字形的边界框xMin = font['head'].xMinyMin = font['head'].yMinxMax = font['head'].xMaxyMax = font['head'].yMax# print("所有字形的边界框: xMin = {}, xMax = {}, yMin = {}, yMax = {}".format(xMin, xMax, yMin, yMax))#所有字形的边界框: xMin = -12, xMax = 264, yMin = -47, yMax = 220preX = 0.0preY = 0.0#笔的起始位置startX = 0.0startY = 0.0#所有轮廓点total_verts = []#所有指令total_codes = []#转换命令for i in total_commands:#每一条轮廓线verts = []codes = []for command in i:#每一条轮廓线中的每一个命令code = command[0] #第一个字符是指令vert = command[1:].split(' ') #其余字符是坐标点,以空格分隔# M = 路径起始 - 参数 - 起始点坐标 (x y)+if code == 'M':codes.append(Path.MOVETO)  #转换指令verts.append((float(vert[0]), float(vert[1])))  #提取x和y坐标#保存笔的起始位置startX = float(vert[0])startY = float(vert[1])#保存笔的当前位置(由于是起笔,所以当前位置就是起始位置)preX = float(vert[0])preY = float(vert[1])# Q = 绘制二次贝塞尔曲线 - 参数 - 曲线控制点和终点坐标(x1 y1 x y)+elif code == 'Q':codes.append(Path.CURVE3)  #转换指令verts.append((float(vert[0]), float(vert[1]))) #提取曲线控制点坐标codes.append(Path.CURVE3) #转换指令verts.append((float(vert[2]), float(vert[3]))) #提取曲线终点坐标#保存笔的当前位置--曲线终点坐标x和ypreX = float(vert[2])preY = float(vert[3])# C = 绘制三次贝塞尔曲线 - 参数 - 曲线控制点1,控制点2和终点坐标(x1 y1 x2 y2 x y)+elif code == 'C':codes.append(Path.CURVE4)  #转换指令verts.append((float(vert[0]), float(vert[1]))) #提取曲线控制点1坐标codes.append(Path.CURVE4) #转换指令verts.append((float(vert[2]), float(vert[3]))) #提取曲线控制点2坐标codes.append(Path.CURVE4) #转换指令verts.append((float(vert[4]), float(vert[5]))) #提取曲线终点坐标#保存笔的当前位置--曲线终点坐标x和ypreX = float(vert[4])preY = float(vert[5])# L = 绘制直线 - 参数 - 直线终点(x, y)+elif code == 'L':codes.append(Path.LINETO)  #转换指令verts.append((float(vert[0]), float(vert[1]))) #提取直线终点坐标#保存笔的当前位置--直线终点坐标x和ypreX = float(vert[0])preY = float(vert[1])# V = 绘制垂直线 - 参数 - 直线y坐标 (y)+elif code == 'V':#由于是垂直线,x坐标不变,提取y坐标x = preXy = float(vert[0])codes.append(Path.LINETO)  #转换指令verts.append((x, y)) #提取直线终点坐标#保存笔的当前位置--直线终点坐标x和ypreX = xpreY = y# H = 绘制水平线 - 参数 - 直线x坐标 (x)+elif code == 'H':#由于是水平线,y坐标不变,提取x坐标x = float(vert[0])y = preYcodes.append(Path.LINETO)  #转换指令verts.append((x, y)) #提取直线终点坐标#保存笔的当前位置--直线终点坐标x和ypreX = xpreY = y# Z = 路径结束,无参数elif code == 'Z':codes.append(Path.CLOSEPOLY)  #转换指令verts.append((startX, startY)) #终点坐标就是路径起点坐标#保存笔的当前位置--起点坐标x和ypreX = startXpreY = startY#有一些语句指令为空,当作直线处理else:codes.append(Path.LINETO)  #转换指令verts.append((float(vert[0]), float(vert[1]))) #提取直线终点坐标#保存笔的当前位置--直线终点坐标x和ypreX = float(vert[0])preY = float(vert[1])#整合所有指令和坐标total_verts.append(verts)total_codes.append(codes)color_list = list(mcd.CSS4_COLORS)#获取所有的轮廓坐标点total_x = []total_y = []for contour in total_verts:#每一条轮廓曲线x = []y = []for i in contour:#轮廓线上每一个点的坐标(x,y)x.append(i[0])y.append(i[1])total_x.append(x)total_y.append(y)#创建画布窗口fig, ax = plt.subplots()#按照'head'表中所有字形的边界框设定x和y轴上下限ax.set_xlim(xMin, xMax)ax.set_ylim(yMin, yMax)#设置画布1:1显示ax.set_aspect(1)#添加网格线# ax.grid(alpha=0.8,linestyle='--')#画图# print(f"{glyph_set_name}======绘制图片=======")for i in range(len(total_codes)):#(1)绘制轮廓线#定义路径path = Path(total_verts[i], total_codes[i])#创建形状,无填充,边缘线颜色为color_list中的颜色,边缘线宽度为2patch = patches.PathPatch(path, facecolor = 'none', edgecolor = color_list[i+10], lw=2)#将形状添到图中ax.add_patch(patch)#(2)绘制轮廓点--黑色,点大小为10# ax.scatter(total_x[i], total_y[i], color='black',s=10)#保存图片temp_path = f"{temp_png_path}/temp.png"plt.savefig(temp_path)# print(f"{glyph_set_name}======保存图片=======")with open(temp_path, "rb") as f:content = f.read()# print(f"{glyph_set_name}=====DdddOcr开始识别文字=======")dddd = ddddocr.DdddOcr(show_ad=False)text = dddd.classification(content)return text
最后

DdddOcr 用pyinstaller打包存在的问题解决方法:
存在的问题xxx.onnx 找不到
把ddddocr包下面的

common.onnx
common_old.onnx
common_det.onnx

复制到和要打包那个脚本统一目录下
在这里插入图片描述
在这之前先打包一次,生成.spec文件,
在修改

datas=[('./common.onnx','ddddocr'),('./common_old.onnx','ddddocr'), ('./common_det.onnx','ddddocr')],

在这里插入图片描述
最后删除build, dist文件夹
重新打包
命令

pyinstaller main.spec

就OK了

我用的anaconda, 如果需要打包最好新建一个虚拟环境,安装好项目对应的包

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com