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 相关文章推荐
linux系统使用python监控apache服务器进程脚本分享
Jan 15 Python
Django中对数据查询结果进行排序的方法
Jul 17 Python
Python实现自动上京东抢手机
Feb 06 Python
python 将列表中的字符串连接成一个长路径的方法
Oct 23 Python
用pycharm开发django项目示例代码
Jun 13 Python
使用python os模块复制文件到指定文件夹的方法
Aug 22 Python
python实现的生成word文档功能示例
Aug 23 Python
Django之路由层的实现
Sep 09 Python
简单了解Java Netty Reactor三种线程模型
Apr 26 Python
Keras 切换后端方式(Theano和TensorFlow)
Jun 19 Python
python中plt.imshow与cv2.imshow显示颜色问题
Jul 16 Python
Python爬虫制作翻译程序的示例代码
Feb 22 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下实现在指定目录搜索指定类型文件的函数
2008/10/03 PHP
PHP中的str_repeat函数在JavaScript中的实现
2013/09/16 PHP
解决Laravel无法使用COOKIE和SESSION的问题
2019/10/16 PHP
thinkphp5 + ajax 使用formdata提交数据(包括文件上传) 后台返回json完整实例
2020/03/02 PHP
jQuery 技巧大全(新手入门篇)
2009/05/12 Javascript
图片上传即时显示缩略图的js代码
2009/05/27 Javascript
在网页中使用document.write时遭遇的奇怪问题
2010/08/24 Javascript
jquery移动listbox的值原理及代码
2013/05/03 Javascript
js加载之使用DOM方法动态加载Javascript文件
2013/11/08 Javascript
Jquery的Tabs内容轮换效果实现代码,几行搞定
2014/02/12 Javascript
原生js实现日期联动
2015/01/12 Javascript
javascript日期比较方法实例分析
2016/06/17 Javascript
NodeJS与HTML5相结合实现拖拽多个文件上传到服务器的实现方法
2016/07/26 NodeJs
深入学习js瀑布流布局
2016/10/14 Javascript
AngulaJS路由 ui-router 传参实例
2017/04/28 Javascript
Vue2.0表单校验组件vee-validate的使用详解
2017/05/02 Javascript
第一次记录Bootstrap table学习笔记(1)
2017/05/18 Javascript
详解vue嵌套路由-query传递参数
2017/05/23 Javascript
jQuery 防止相同的事件快速重复触发方法
2018/02/08 jQuery
vue2 前端搜索实现示例
2018/02/26 Javascript
11行JS代码制作二维码生成功能
2018/03/09 Javascript
Vue+Node服务器查询Mongo数据库及页面数据传递操作实例分析
2019/12/20 Javascript
[56:18]DOTA2上海特级锦标赛主赛事日 - 4 败者组第四轮#2 MVP.Phx VS Fnatic第二局
2016/03/05 DOTA
Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法
2016/04/12 Python
python用户评论标签匹配的解决方法
2018/05/31 Python
mac下如何将python2.7改为python3
2018/07/13 Python
python针对不定分隔符切割提取字符串的方法
2018/10/26 Python
Python中面向对象你应该知道的一下知识
2019/07/10 Python
Linux下通过python获取本机ip方法示例
2019/09/06 Python
pytorch 利用lstm做mnist手写数字识别分类的实例
2020/01/10 Python
Windows10+anacond+GPU+pytorch安装详细过程
2020/03/24 Python
python输出结果刷新及进度条的实现操作
2020/07/13 Python
python opencv图像处理(素描、怀旧、光照、流年、滤镜 原理及实现)
2020/12/10 Python
物流合作计划书
2014/01/10 职场文书
销售团队激励口号
2014/06/06 职场文书
教育项目合作协议书格式
2014/10/17 职场文书