Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)


Posted in Python onFebruary 22, 2021

前言:

猫眼票房页面的字体加密是动态的,每次或者每天加载页面的字体文件都会有所变化,本篇内容针对这种加密方式进行分析

字体加密原理:简单来说就是程序员在设计网站的时候使用了自己设计的字体代码对关键字进行编码,在浏览器加载的时会根据这个字体文件对这些字体进行编码,从而显示出正确的字体。

已知的使用了字体加密的一些网站:
58同城,起点,猫眼,大众点评,启信宝,天眼查,实习僧,汽车之家
本篇内容不过多解释字体文件的映射关系,不了解的请自行查找其他资料。
如若还未入门爬虫,请往这走 简单粗暴入门法——Python爬虫入门篇

import requests
import urllib.request as down
import json
from fontTools.ttLib import TTFont
import re
#分析用
import matplotlib.pyplot as plt #绘图
import numpy as np # 科学计算库

安装:
pip install matplotlib
pip install requests
pip install numpy
pip install fonttools

首先我们对猫眼票房页面进行简单分析

Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)

可以看到票房数字在审查中显示的是乱码,类似与这种情况的就可能是使用了字体加密,因此我们需要找到字体文件(字体文件会以链接方式存放在页面中)

Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)

找到了字体文件,下载并对映射关系进行分析,可以得到我们需要的一组基础字形映射表;并且可以通过映射关系得到每个字形的所有坐标

baseFont=TTFont('maoyan.woff')
# 获取相应数字的namecode和形状坐标的关系,可用来获取坐标
glyf=baseFont['glyf']
#通过对一份字体样本分析得出的字体映射
baseNumberMaps={
 0:glyf['uniF632'],
 1:glyf['uniF2F1'],
 2:glyf['uniF0A4'],
 3:glyf['uniF7B7'],
 4:glyf['uniE82D'],
 5:glyf['uniF653'],
 6:glyf['uniE756'],
 7:glyf['uniF41A'],
 8:glyf['uniE79B'],
 9:glyf['uniE81E']
}
for num,name in baseNumberMaps.items():
 print(name.coordinates)

Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)

我们将坐标绘图成图形,在进行不同组字形图形对比可以发现每套字形的坐标不同,大小比例不同,而字形是不变的,也就是相似

Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)

对比坐标发现每套字形坐标都会有所改变,但是整体图形还是同一个,所以我想到了斜率对比,我们计算每个字形部分线段的斜率,如果斜率之差小于一个数值,就说明这两个是相同的数字。

Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)

因此就得到了一个思路 获得基础字体映射关系表爬取页面下载所加载的字体获得需要对比的字体映射关系表计算每套字体每个字形的线段斜率,并进行差值计算循环匹配,从基础字形的0-9开始去匹配新字形的斜率,如果斜率之差小于0.5并且样本数>=9我们则认为两个图形为同一个数字,获得正确的字体映射关系对加密字体进行替换得到正确内容

程序实现

import requests
import urllib.request as down
import json
from fontTools.ttLib import TTFont
import re
import MyPyClass

# 得到字体斜率列表(部分)
def font_Kdict(mapstype,maps=None):
 '''
 得到字体斜率字典(部分)
 参数:
 mapstype:str->maps类型,判断是是base/new
 maps:映射字典

 return kdict
 kdict字典关系:
 num:Klist 数字对应每条线段的斜率列表
 '''
 kdict={}
 # 遍历maps字典,找到对应的num和namecode
 for num, namecode in maps.items():
 # 跳过无用数据
 if namecode == 'x': continue
 # 判断类型,并从.coordinates得到对应num的所有坐标
 if mapstype=='base':coordinates = namecode.coordinates
 elif mapstype=='new':coordinates=glyf[namecode].coordinates
 # 得到坐标 X列表和坐标 Y列表
 x = [i[0] for i in coordinates]
 y = [i[1] for i in coordinates]
 Klist = []
 # 遍历X列表并切片为前10个数据进行斜率计算,即代表绘图的前10条线段的斜率
 for index, absx in enumerate(x[:10]):
  # 当斜率为0/1时,认为斜率为1计算
  if x[index + 1] == x[index] or y[index + 1] == y[index]:
  absxy = 1
  else:
  absxy = (y[index + 1] - y[index]) / (x[index + 1] - x[index])
  # 将斜率加入到列表
  Klist.append(-absxy if absxy < 0 else absxy)
 kdict[num]=Klist
 #print('base:', code, Klist, name)
 return kdict
# 对比斜率字典
def contrast_K(kbase,knew):
 '''
 对比斜率映射差距
 参数:
 kbase:基础字体映射表的斜率字典
 knew:当前链接的字体映射表的斜率字典

 return:dict
 fontMaps:根据对比得出正确的字体映射关系字典

 '''
 fontMaps = {}
 # 遍历kbase字典
 for base in kbase.items():
 n = 0 # 成功匹配的斜率个数
 # 遍历knew字典
 for new in knew.items():
  # 遍历kbase>knew>下的两组斜率,进行大小匹配,
  # 如果斜率k的差值小于0.5,并且样本数>=9时,认为两个坐标图形相识只是大小比例不同
  # 即k<=0.5 n>=9
  for (k1,k2) in zip(base[1],new[1]):
  # k取正数
  k=k1-k2 if k1>k2 else k2-k1
  if k<=0.5:
   n+=1
   continue
  else:
   break
  if n>=9:
  # 匹配正确则添加进字典中 此时的字典关系是:code:num 代码对应数字的关系
  fontMaps[str(hex(new[0]).replace('0x','&#x'))]=str(base[0])
  break
  n=0
 #print(fontMaps)
 return fontMaps

# 建立基础字体对象
baseFont=TTFont('maoyan.woff')
# 获取相应数字的namecode和形状坐标的关系,可用来获取坐标
glyf=baseFont['glyf']
#通过对一份字体样本分析得出的字体映射
baseNumberMaps={
 0:glyf['uniF632'],
 1:glyf['uniF2F1'],
 2:glyf['uniF0A4'],
 3:glyf['uniF7B7'],
 4:glyf['uniE82D'],
 5:glyf['uniF653'],
 6:glyf['uniE756'],
 7:glyf['uniF41A'],
 8:glyf['uniE79B'],
 9:glyf['uniE81E']
}
url='https://piaofang.maoyan.com/dashboard-ajax?orderType=0&uuid=1778ad877f8c8-0b23bf32a2bb26-c7d6957-1fa400-1778ad877f8c8&riskLevel=71&optimusCode=10'
ua=MyPyClass.GetUserAgent()#获得ua
# 爬取内容
with requests.get(url,headers={'user-agent':ua}) as response:
 # 获取存放字典的json字段,并提取字体url
 fontStyle=json.loads(response.content)['fontStyle']
 fontStyle=re.findall('\"([\s\S]*?)\"',fontStyle[::-1])
 fonturl='http:'+fontStyle[0][::-1]# 字体url链接
 # 将加载的字体下载保存到本地,并对其进行分析
 down.urlretrieve(fonturl,'newfont.woff')
 # 爬取的电影数据内容
 content = json.loads(response.content)['movieList']['data']['list']
# 信息字典
movieNum={}#综合票房数字典
movieDayOne= {}#上映首日数量
movieRate={}#票房占比
movieshowCount={}#排片场次
movieViewerAvg={}#场均人数
movieInfos={}
# 页面内容
for i in content:
 moviename=i['movieInfo']['movieName']
 movieNum[moviename]=i['boxSplitUnit']['num']
 movieDayOne[moviename]=i['sumBoxDesc']
 movieRate[moviename]=i['splitBoxRate']
 movieshowCount[moviename]=i['showCount']
 movieViewerAvg[moviename]=i['avgShowView']

# 新字体对象
fontnew=TTFont('newfont.woff')
# 得到当前字体的映射关系表
newNumberMaps=fontnew.getBestCmap()
# 获取字形
glyf=fontnew['glyf']
# 基础字体斜率字典
k_base_dict=font_Kdict(maps=baseNumberMaps,mapstype='base')
# 新字体斜率字典
k_new_dict=font_Kdict(maps=fontnew.getBestCmap(),mapstype='new')
# 得到字体映射字典
fontcodes=contrast_K(k_base_dict,k_new_dict)
# 对加密的字体遍历分组,并去除无用字符
for name,numbercode in movieNum.items():
 movieNum[name]=re.findall('([\S]*?);', numbercode)
# 根据得到的fontcodes映射对加密字体进行替换,得到正确数值
for index,(name,numbercodelist) in enumerate(movieNum.items()):
 num=[]
 # 替换操作
 for code in numbercodelist:
 if '.' in code:
  code=code.replace('.','')
  num.append('.'+fontcodes[code])
 else:
  num.append(fontcodes[code])
 infos=['排行:'+str(index+1),
 '片名',name,
 '上映首日',movieDayOne[name],
 '票房',''.join(num)+'万',
 '票房占比',movieRate[name],
 '场均人数',movieViewerAvg[name]+'人',
 '排片场次',movieshowCount[name]]
 print(infos)

实现效果如下

Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)

到此这篇关于Python爬虫实例之2021猫眼票房字体加密反爬策略(粗略版)的文章就介绍到这了,更多相关Python爬虫猫眼票房字体反爬内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python使用MYSQLDB实现从数据库中导出XML文件的方法
May 11 Python
python字符串对其居中显示的方法
Jul 11 Python
详解Django中间件执行顺序
Jul 16 Python
OpenCV2从摄像头获取帧并写入视频文件的方法
Aug 03 Python
详解如何将python3.6软件的py文件打包成exe程序
Oct 09 Python
PyQt5 实现字体大小自适应分辨率的方法
Jun 18 Python
Python3中的最大整数和最大浮点数实例
Jul 09 Python
python增加图像对比度的方法
Jul 12 Python
Python3 集合set入门基础
Feb 10 Python
django迁移文件migrations的实现
Mar 31 Python
PyCharm 2020.2 安装详细教程
Sep 25 Python
pytorch常用数据类型所占字节数对照表一览
May 17 Python
python实现计算图形面积
Feb 22 #Python
python实现银行账户系统
Feb 22 #Python
Django实现简单的分页功能
Feb 22 #Python
Python爬虫+tkinter界面实现历史天气查询的思路详解
Feb 22 #Python
Python爬虫设置Cookie解决网站拦截并爬取蚂蚁短租的问题
Feb 22 #Python
Python爬虫爬取微博热搜保存为 Markdown 文件的源码
Feb 22 #Python
Python爬虫制作翻译程序的示例代码
Feb 22 #Python
You might like
PHP中使用asort进行中文排序失效的问题处理
2014/08/18 PHP
PHP实现的自定义数组排序函数与排序类示例
2016/11/18 PHP
Centos7 Yum安装PHP7.2流程教程详解
2019/07/02 PHP
分享几种好用的PHP自定义加密函数(可逆/不可逆)
2020/09/15 PHP
jQuery中通过ajax的get()函数读取页面的方法
2016/02/29 Javascript
Javascript的表单验证长度
2016/03/16 Javascript
对象转换为原始值的实现方法
2016/06/06 Javascript
AngularJS基础 ng-options 指令详解
2016/08/02 Javascript
webpack实现热更新(实施同步刷新)
2017/07/28 Javascript
Bootstrap实现下拉菜单多级联动
2017/11/23 Javascript
vue 1.0 结合animate.css定义动画效果
2018/07/11 Javascript
Vue自定义指令结合阿里云OSS优化图片的实现方法
2019/11/12 Javascript
[02:01]2018完美盛典-开场舞《双子星》
2018/12/16 DOTA
Python实现爬取知乎神回复简单爬虫代码分享
2015/01/04 Python
在Mac OS系统上安装Python的Pillow库的教程
2015/11/20 Python
Python3中的json模块使用详解
2018/05/05 Python
python的pip安装以及使用教程
2018/09/18 Python
python中如何使用分步式进程计算详解
2019/03/22 Python
使用Python 自动生成 Word 文档的教程
2020/02/13 Python
Python读取JSON数据操作实例解析
2020/05/18 Python
python 实现两个npy档案合并
2020/07/01 Python
用于ETL的Python数据转换工具详解
2020/07/21 Python
利用Python的folium包绘制城市道路图的实现示例
2020/08/24 Python
python如何实时获取tcpdump输出
2020/09/16 Python
html5指南-4.使用Geolocation实现定位功能
2013/01/07 HTML / CSS
伦敦香水公司:The London Perfume Company
2019/11/13 全球购物
.net面试题
2015/12/22 面试题
高三自我鉴定怎么写
2013/10/19 职场文书
服务行业个人求职的自我评价
2013/12/12 职场文书
机关道德讲堂实施方案
2014/03/15 职场文书
运动会加油口号
2014/06/07 职场文书
2014年政风行风评议工作总结
2014/10/21 职场文书
小学四年级学生评语
2014/12/26 职场文书
护士年终工作总结不会写?各科护士模板总结
2020/01/02 职场文书
python实现简单的聊天小程序
2021/07/07 Python
利用Python读取微信朋友圈的多种方法总结
2021/08/23 Python