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实现电子词典
Apr 23 Python
跟老齐学Python之从if开始语句的征程
Sep 14 Python
跟老齐学Python之编写类之三子类
Oct 11 Python
Django查询数据库的性能优化示例代码
Sep 24 Python
python 连接各类主流数据库的实例代码
Jan 30 Python
Python爬虫常用库的安装及其环境配置
Sep 19 Python
详解Python并发编程之从性能角度来初探并发编程
Aug 23 Python
Python内置加密模块用法解析
Nov 25 Python
Python 实现try重新执行
Dec 21 Python
用python3读取python2的pickle数据方式
Dec 25 Python
python使用paramiko实现ssh的功能详解
Mar 06 Python
如何配置关联Python 解释器 Anaconda的教程(图解)
Apr 30 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+mysql实现无限级分类 | 树型显示分类关系
2006/11/19 PHP
setcookie中Cannot modify header information-headers already sent by错误的解决方法详解
2013/05/08 PHP
CURL状态码列表(详细)
2013/06/27 PHP
JoshChen_php新手进阶高手不可或缺的规范介绍
2013/08/16 PHP
PHP抽奖算法程序代码分享
2015/10/08 PHP
PHP实现基于图的深度优先遍历输出1,2,3...n的全排列功能
2017/11/10 PHP
JavaScript 获得选中文本内容的方法
2009/02/15 Javascript
在一个浏览器里呈现所有浏览器测试结果的前端测试工具的思路
2010/03/02 Javascript
获取焦点时,利用js定时器设定时间执行动作
2010/04/02 Javascript
基于jquery的防止大图片撑破页面的实现代码(立即缩放)
2011/10/24 Javascript
调用jQuery滑出效果时闪烁的解决方法
2014/03/27 Javascript
jquery实现类似淘宝星星评分功能有截图
2014/09/15 Javascript
JavaScript 节流函数 Throttle 详解
2016/07/04 Javascript
AngularGauge 属性解析详解
2016/09/06 Javascript
AngularJS ionic手势事件的使用总结
2017/08/09 Javascript
vue 登录滑动验证实现代码
2018/08/24 Javascript
详解js中Array的方法及技巧
2018/09/12 Javascript
Vue 3.x+axios跨域方案的踩坑指南
2019/07/04 Javascript
Vue实现可移动水平时间轴
2020/06/29 Javascript
vue监听浏览器原生返回按钮,进行路由转跳操作
2020/09/09 Javascript
Vue.extend 登录注册模态框的实现
2020/12/29 Vue.js
利用python爬取斗鱼app中照片方法实例
2017/12/03 Python
详解Python数据分析--Pandas知识点
2019/03/23 Python
Python3中_(下划线)和__(双下划线)的用途和区别
2019/04/26 Python
PyQt QListWidget修改列表项item的行高方法
2019/06/20 Python
Python3使用腾讯云文字识别(腾讯OCR)提取图片中的文字内容实例详解
2020/02/18 Python
找Python安装目录,设置环境路径以及在命令行运行python脚本实例
2020/03/09 Python
python+flask编写一个简单的登录接口
2020/11/13 Python
利用python 下载bilibili视频
2020/11/13 Python
python 基于Apscheduler实现定时任务
2020/12/15 Python
深入CSS3 动画效果的总结详解
2013/05/09 HTML / CSS
雅虎笔试题(字符串操作)
2015/03/24 面试题
一年级班主任寄语
2014/01/19 职场文书
大学生简短的自我评价分享
2014/02/20 职场文书
2015年销售人员工作总结
2015/04/07 职场文书
有关水浒传的读书笔记
2015/06/25 职场文书