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使用rabbitmq实现网络爬虫示例
Feb 20 Python
pip 错误unused-command-line-argument-hard-error-in-future解决办法
Jun 01 Python
分析在Python中何种情况下需要使用断言
Apr 01 Python
Python代码实现KNN算法
Dec 20 Python
python语音识别实践之百度语音API
Aug 30 Python
Python 字符串换行的多种方式
Sep 06 Python
pygame游戏之旅 添加游戏介绍
Nov 20 Python
Python解析命令行读取参数之argparse模块
Jul 26 Python
python matplotlib包图像配色方案分享
Mar 14 Python
使用keras实现非线性回归(两种加激活函数的方式)
Jul 05 Python
使用Pytorch实现two-head(多输出)模型的操作
May 28 Python
用Python生成会跳舞的美女
Jan 18 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
JS与PHP向函数传递可变参数的区别实例代码
2011/05/18 PHP
PHP Beanstalkd消息队列的安装与使用方法实例详解
2020/02/21 PHP
tp5.1 框架数据库常见操作详解【添加、删除、更新、查询】
2020/05/26 PHP
PHP 出现 http500 错误的解决方法
2021/03/09 PHP
javascript应用:Iframe自适应其加载的内容高度
2007/04/10 Javascript
js的写法基础分析
2011/01/17 Javascript
浏览器解析js生成的html出现样式问题的解决方法
2012/04/16 Javascript
获取内联和链接中的样式(js代码)
2013/04/11 Javascript
JS使用parseInt解析数字实现求和的方法
2015/08/05 Javascript
C++中的string类的用法小结
2015/08/07 Javascript
Jquery代码实现图片轮播效果(一)
2015/08/12 Javascript
Vue.js实现一个SPA登录页面的过程【推荐】
2017/04/29 Javascript
AngularJS 支付倒计时功能实现思路
2017/06/05 Javascript
js实现文字列表无缝滚动效果
2017/06/23 Javascript
vue中倒计时组件的实例代码
2018/07/06 Javascript
AngularJS 监听变量变化的实现方法
2018/10/09 Javascript
vue实现学生信息管理系统
2020/05/30 Javascript
vue Cli 环境删除与重装教程 - 版本文档
2020/09/11 Javascript
[54:10]完美世界DOTA2联赛PWL S2 Magma vs FTD 第二场 11.29
2020/12/03 DOTA
Python实现把xml或xsl转换为html格式
2015/04/08 Python
一篇文章入门Python生态系统(Python新手入门指导)
2015/12/11 Python
Python+django实现简单的文件上传
2016/08/17 Python
Python pandas DataFrame操作的实现代码
2019/06/21 Python
使用APScheduler3.0.1 实现定时任务的方法
2019/07/22 Python
Django如何实现网站注册用户邮箱验证功能
2019/08/14 Python
如何利用python给图片添加半透明水印
2019/09/06 Python
PyInstaller运行原理及常用操作详解
2020/06/13 Python
html5使用canvas压缩图片的示例代码
2018/09/11 HTML / CSS
南非领先的在线旅行社:Travelstart南非
2016/09/04 全球购物
澳大利亚男士西服品牌:M.J.Bale
2018/02/06 全球购物
幼儿园教学随笔感言
2014/02/23 职场文书
质量标语大全
2014/06/12 职场文书
高中学校对照检查材料
2014/08/31 职场文书
写给纪委的违纪检讨书
2015/05/05 职场文书
政审证明范文
2015/06/19 职场文书
写作技巧:优秀文案必备的3种结构
2019/08/19 职场文书