JS逆向03:字体反爬
jsvmp : 代码虚拟化保护方案
ast : 抽象语法树
1.字体文件、字体反爬
### 1 字体文件 .ttf .woff 或 .eot
在计算机内存中,文字就是一堆二进制(unicode),要以文字图形的样子,展示给用户看
就要指定 它们之间的对应关系,就是字体文件
书法字体、火星文字体,不同的字体文件,对同样的文字unicode,显示出来的文字图形不一样
文字unicode <== 字体文件 ==> 显示的文字图形(按照字体文件的规则,对应显示)
eg: 帅(101011二进制) <--- 微软雅黑字体 ---> 帅(汉字的图形)
# 文字图形样子的核心:取决于 文字的unicode 和 字体文件
# windows中的font-creator软件,可以打开查看字体文件 (只有windows)
### 2 字体反爬原理
unicode的字节范围比较大(包含世界所有文字)
网站自定义了一套字体规则,把文字unicode中 用不上的unicode码,重新进行编排,对应上文字图形
而且有可能 编排对应后的文字图形,也做过一些微处理
例如:人字,细节处多一个小点,或者 一部分很长之类的
并在网页中显示时,不是直接显示 正常的现成文字图形
而是混杂着,重新编排的一些 特定unicode码 和 正常文字图形
所以正常爬取时,爬到这些特定的文字unicode码,并按照自己的现有字体文件 加载的话,就是乱的
### 3 解决字体反爬的实现步骤:
1.从网站中,获取字体文件
2.从字体文件中,获取所有的文字unicode码
3.先将文字unicode码,照着字体文件里面的样子,把所有对应的文字图形,画到同一张图片上去
4.再用人工智能-图片识别文字【AI_OCR】,去识别这个图片 # 识别没有百分之百的准确率
就知道了网页中 所有该文字unicode码,对应的正确文字是什么
# 3、4步是 对字体文件进行解析 # unicode ---> 正确的文字 (难)
5.在获取到页面源代码后
把unicode码的文字,进行正确文字的替换 # 后续就可以正常处理了
### 4 第三方图片识别文字 ---> 也可以用于识别 验证码
1.ddddocr # python的第三方库 免费
2.Tesseract-OCR # python的第三方库 免费
3.火山(字节) # 第三方 收费
4.百度AI(比上面这几个都好一些)
2.案例
2.1 获取字体文件
### 1 从网站中,获取到字体文件
import requests
from lxml import etree
from urllib.parse import urljoin
import re
# 1.先加载 index.html
url = "http://bikongge.com/chapter_1/font_1/index.html"
resp = requests.get(url)
resp.encoding = 'utf-8'
# print(resp.text)
# 2.从index.html中,获取style.css的url
tree = etree.HTML(resp.text)
href = tree.xpath("//link/@href")[0]
href = urljoin(url, href)
# print(href)
# 3.从style.css中,获取字体文件的url
font_resp = requests.get(href)
font_url_re = re.compile(r'src: url\("(?P<font_url>.*?)"\);', re.S)
font_url = font_url_re.search(font_resp.text).group("font_url")
# print(font_url)
# url拼接 这个url应该和谁拼接,是相对应style.css的,和style.css的url拼接
font_url = urljoin(href, font_url)
print(font_url)
# 4.下载字体文件
font_resp = requests.get(font_url)
with open("font.woff", mode="wb") as f:
f.write(font_resp.content)
# 对比查看:
s = "\ue0df" # 文字unicode码
print(s) # 直接打印(默认unicode编码打印):一个奇怪的乱字
# 但实际是 windows软件font-creator打开看
'\ue0df' => 店 (字体文件)
2.2 获取文字unicode码
# 2 从字体文件中,获取所有的文字unicode码 需要font-tools库
# pip install fontTools
# TTFont('字体文件')的方法
.getGlyphOrder() # 获取所有文字的unicode码 有顺序 推荐使用
.getGlyphNames() # 获取所有文字的unicode码 无顺序
from fontTools.ttLib import ttFont
ttf = ttFont.TTFont("font.woff")
uni_list = ttf.getGlyphOrder()[2:] # 推荐使用, 有顺序 ['unie467', 'uniee7e'...]
# 将uni字符串,转化为 python的unicode格式 \u:unicode码
uni_ok_list = []
for uni in uni_list:
uni = uni.replace("uni", "\\u") # 不能直接 \u,会被直接识别为unicode,再加个\ 防止转义
uni_ok_list.append(uni)
print(uni_ok_list)
print(len(uni_ok_list)) # 601
2.3 根据字体文件画图
# 3 按照字体文件,把文字unicode码的文字图形,画出来
# 安装 pip install pillow
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
# 1.创建画布 参数:模式-RGB彩色、大小、颜色
img = Image.new(mode="RGB", size=(2000, 1000), color=(255, 255, 255))
# 2.准备一只可以在图上画画的笔
img_draw = ImageDraw.Draw(img)
# 3.准备好画图的字体 画的字体、字体每个字的像素大小
img_font = ImageFont.truetype("font.woff", size=50)
# 4.准备开始画图,需要画图规划 不能全在一行,需要换行
# 例如:一行40个字 (50*40=2000,画布宽2000,正好够)
# 换行逻辑:
# 根据所有文字unicode码列表的索引号, 若 索引 % 40 == 0,(取余) 就该换行了
# 行号: 索引 // 40 # 整除,向下取整 从第0行开始
# 目的是为了计算,每行画笔的高度 y ---> (索引 // 40) * 每行的高度
# 指定每行字符的个数
line_length = 40
# 指定每行的高度 像素
line_heigth = 60
# 把一行需要画的字符,放进一个列表
char_line = []
for i in range(len(uni_ok_list)):
uni = uni_ok_list[i]
# 把 '\\uxxxx' 修改成unicode码,需要 用编码的转换 来进行调整 先编码成字节,再按照unicode解码
uni = uni.encode().decode("unicode-escape")
if i % line_length == 0 and i != 0: # 遍历到需要换行的字符了
# 1.先把上一行的字符,同时画到画布上
char_line_s = "".join(char_line)
# 画笔写字(.text)的参数:画的坐标(x,y)、内容、fill(填充)、参照的字体
img_draw.text((0, (i // line_length - 1) * line_heigth), char_line_s, fill=1, font=img_font)
# 2.本次遍历到字符 需要换行,直接覆盖上一次的行列表
char_line = [uni]
else:
# 正常需画在同一行的字符,追加行列表
char_line.append(uni)
# 再画,最后不足一整行的字符
if char_line:
char_line_s = "".join(char_line)
img_draw.text((0, (len(uni_ok_list) // line_length) * line_heigth), char_line_s, fill=1, font=img_font)
# 完成上述操作,你只是在内存中画了一张图
# 保存到硬盘上
img.save("tu.jpg")
2.4 图片识别文字
# 4 接下来就是识别上面这张图,获取到所有文字 ---> 百度AI
pip install baidu-aip
# 该库 还需要其他库
pip install chardet
from aip import AipOcr
"""刚才创建的那个应用里. 有下面这三个东西"""
APP_ID = '27643903'
API_KEY = 'atSZosOQ1xOQmBOmGSgLnbNA'
SECRET_KEY = 'znj959V9ZN0zlGVhZexABFT4ek57Kom2'
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
f = open("tu.jpg", mode="rb")
r = client.basicGeneral(f.read())
# 所有文字
result_list = []
for item in r['words_result']:
result_list.extend(item['words'])
print(len(result_list)) # 注意:调整识别后的文字数量 是否与 unicode码字数量 一致
2.5 解析替换
### 5 替换--反向
# 5.1 把unicde对应的word,进行映射 ---> 字典
# zip() 有水桶效应(以短的一方,为边界)
# 故:uni_ok_list和result_list必须长度保持一致.
dic = dict(zip(uni_ok_list, result_list))
# 结果:{"\\uxxxx": 天}
# 5.2 获取页面源代码,并把字典中'\\u' ,按照页面的形式,换成 '&#X'
# 5.3 再页面源码的'&#X',替换成 识别后的文字
url = "http://bikongge.com/chapter_1/font_1/index.html"
resp = requests.get(url)
resp.encoding = 'utf-8'
page_source = resp.text
for k in dic:
# k: \\uxxxxx
# v: 天
v = dic[k]
kk = k.replace("\\u", "&#x") + ";"
# 字符串不可变. 需要重新赋值
page_source = page_source.replace(kk, v)
print(page_source)
标签:文字,03,07,--,url,字体,unicode,uni,font
From: https://www.cnblogs.com/Edmondhui/p/18298496