猫眼电影字体反爬
1.解决思路
1.1 获取对照组字体文件,按照不同字形字体的unicode得到文字与其坐标的对应关系形成可遍历的数据结构,然后处理成可计算的数组或者直接遍历出所有的坐标数据;
1.2 再提取当前访问页面的自定义字体文件,取出其各个文字对应的坐标信息,然后也处理成数组或者遍历出所有的坐标;
1.3 将最新的坐标数据与对照字体的坐标数据进行计算,得出最小的差别值的索引值,再根据这个索引取出匹配度最高的文字;
1.4 再根据匹配到的文字对应的unicode找出html源码中加密的位置将文字替换上去。
2.网页解析
2.1 猫眼电影加密字体为数字类型的,并且每次刷新都是不同字形的字体,所以字体文件需实时获取;
2.2 下载字体文件后用分析工具打开对照源码查看编码对应关系,源码中代替数字的编码后四位与字体文件中数字对应的unicode的后四位相同;
2.3 字体文件还包含了了这种字体每个数字形成的坐标数据,可用fonttools模块中的TTFont打开字体文件获取;
# 打开字体文件
new_font = TTFont('./fonts/current.woff')
# 以xml格式保存,查看各种对应关系
new_font.savexml('./fonts/fong.xml')
# 可获取各个数字对应的unicode,索引对应数字的索引
unicodes = new_font.getGlyphOrder()[2:]
# 根据unicode获取对应数字的坐标信息
contour=new_font['glyf'}[unicode].coordinates
3.重要代码实现
3.1 获取5组字体数据,手动创建成对照表;
from fontTools.ttLib import TTFont
def get_font_infos():
"""
获取下载的各种字体信息,作为样例进行训练
:return: 返回全部字体的坐标信息
"""
font1=TTFont('./fonts/1.woff')
order1=[3,8,2,6,5,9,7,1,0,4]
contour1=get_contour(font1,order1)
font2=TTFont('./fonts/2.woff')
order2=[9,4,0,7,1,5,2,8,6,3]
contour2=get_contour(font2,order2)
font3 = TTFont('./fonts/3.woff')
order3 = [9,5,6,0,7,3,2,1,8,4]
contour3= get_contour(font3, order3)
font4 = TTFont('./fonts/4.woff')
order4 = [6,1,3,2,5,7,0,8,9,4]
contour4 = get_contour(font4, order4)
font5 = TTFont('./fonts/5.woff')
order5 = [6,1,4,0,3,7,2,9,8,5]
contour5 = get_contour(font5, order5)
contours=contour1+contour2+contour3+contour4+contour5
return contours
def get_contour(font,order):
"""
获取下载好的各个字体的坐标信息
:param font: 不同的字体文件
:param order: 不同顺序(字体)的实例
:return: 返回各个字体的坐标信息
"""
unicodes=font.getGlyphOrder()[2:]
infos=[]
font_dict={}
# 用枚举函数遍历出字体的Unicode及对应的索引
for index,unicode in enumerate(unicodes):
contour=font['glyf'][unicode].coordinates
contour_list=[i for items in contour for i in items]
# 根据索引得出各个文字对应的坐标信息,并将文字添加到对应的坐标信息列表中
contour_list.insert(0,order[index])
infos.append(contour_list)
# 打印出需要的对照表,新建font_data存储对照表
print(font_dict)
return infos
if __name__=='__main__':
get_font_infos()
3.2 获取对照表中坐标及数字顺序的标签,转化成数组以备后续与实时获取的字体信息进行运算比较;
import numpy as np
from . import font_data
def base_array():
"""
根据制作好的字体对照表创建可以进行比较的数组
:return: 创建好的训练数组及各种字体的存在形式
"""
# 获取对照表中存储的各种字体的数字顺序
orders=[data[0] for data in font_data]
# 坐标
contour=[data[1] for data in font_data]
# 创建50行,150个数位0的数组
base_array=np.zeros([50,150])
# print(base_array)
for index,array in enumerate(contour):
# base_array中第index+1行且1到len(array)的数据等于array的全部数据(将array替换到base_array[index,:len(array)]索引指示的位置)
base_array[index,:len(array)]=array
return base_array,orders
3.3 获取当前访问时的字体数据,转化成数组与对照组进行运算匹配,获取匹配度最高的数字;
def get_num(new_array,base_array,orders,k=5):
"""
得到自定义字体的坐标数组后与训练数组进行knn计算,得到knn系数后取出最相似的5个对应的索引,
再根据索引得到对应的数字
:param new_array: 新的自定字体坐标数组
:param base_array: 训练数组
:param orders: 不同训练字体的样式标签
:param k: 获取的相似字体数目
:return: 返回匹配度最高的数字
"""
# 根据knn公式获取knn值
diff=(((np.tile(new_array,(50,1))-base_array)**2).sum(axis=1))**0.5
# 将对比后的knn系数进行排序并返回其索引值
diff_index=diff.argsort()
count_num={}
for i in range(k):
# 根据diff值的索引获取相应位置的数字
num=lables[diff_index[i]]
# 用字典标记某个字匹配的次数,记录在value上,出现就加1,没有就为0
count_num[num]=count_num.get(num,0)+1
# 取出字典中某个字的匹配次数(可能有多个字)再按照字的出现频率进行排序
count_list=sorted(count_num.items(),key=lambda x:x[1],reverse=True)
return count_list[0][0]
def knn_main(new_contour_list):
"""执行knn操作,获取到匹配度最高的数字"""
base_array, orders = train_array()
new_array = np.zeros([150])
new_array[:len(new_contour_list)] = new_contour_list
num=get_num(new_array,base_array,orders,5)
return num
3.4 最后的到当前字体的unicode与数字的对应关系,再根据unicode与源码中替代数字的编码关系对其进行替换,然后进行解析数据(1-3点的代码即可完成字体反爬,这步可根据自己的要求进行定制补充)。