Python爬虫实例之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

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

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

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

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)

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

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

因此就得到了一个思路 获得基础字体映射关系表爬取页面下载所加载的字体获得需要对比的字体映射关系表计算每套字体每个字形的线段斜率,并进行差值计算循环匹配,从基础字形的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-02-22

python解决网站的反爬虫策略总结

本文详细介绍了网站的反爬虫策略,在这里把我写爬虫以来遇到的各种反爬虫策略和应对的方法总结一下. 从功能上来讲,爬虫一般分为数据采集,处理,储存三个部分.这里我们只讨论数据采集部分. 一般网站从三个方面反爬虫:用户请求的Headers,用户行为,网站目录和数据加载方式.前两种比较容易遇到,大多数网站都从这些角度来反爬虫.第三种一些应用ajax的网站会采用,这样增大了爬取的难度(防止静态爬虫使用ajax技术动态加载页面). 1.从用户请求的Headers反爬虫是最常见的反爬虫策略. 伪装header

Python反爬虫伪装浏览器进行爬虫

对于爬虫中部分网站设置了请求次数过多后会封杀ip,现在模拟浏览器进行爬虫,也就是说让服务器认识到访问他的是真正的浏览器而不是机器操作 简单的直接添加请求头,将浏览器的信息在请求数据时传入: 打开浏览器--打开开发者模式--请求任意网站 如下图:找到请求的的名字,打开后查看headers栏,找到User-Agent,复制.然后添加到请求头中 代码如下: import requests url = 'https://www.baidu.com' headers ={ 'User-Agent':'Mo

python爬虫的一个常见简单js反爬详解

前言 我们在写爬虫是遇到最多的应该就是js反爬了,今天分享一个比较常见的js反爬,这个我已经在多个网站上见到过了. 我把js反爬分为参数由js加密生成和js生成cookie等来操作浏览器这两部分,今天说的是第二种情况. 目标网站 列表页url:  http://www.hnrexian.com/archives/category/jk. 正常网站我们请求url会返回给我们网页数据内容等,看看这个网站返回给我们的是什么呢? 我们把相应中返回的js代码格式化一下,方便查看. < script typ

Python反爬虫技术之防止IP地址被封杀的讲解

在使用爬虫爬取别的网站的数据的时候,如果爬取频次过快,或者因为一些别的原因,被对方网站识别出爬虫后,自己的IP地址就面临着被封杀的风险.一旦IP被封杀,那么爬虫就再也爬取不到数据了. 那么常见的更改爬虫IP的方法有哪些呢? 1,使用动态IP拨号器服务器. 动态IP拨号服务器的IP地址是可以动态修改的.其实动态IP拨号服务器并不是什么高大上的服务器,相反,属于配置很低的一种服务器.我们之所以使用动态IP拨号服务器,不是看中了它的计算能力,而是能够实现秒换IP. 动态IP拨号服务器有一个特点,就是每

python网络爬虫之如何伪装逃过反爬虫程序的方法

有的时候,我们本来写得好好的爬虫代码,之前还运行得Ok, 一下子突然报错了. 报错信息如下: Http 800 Internal internet error 这是因为你的对象网站设置了反爬虫程序,如果用现有的爬虫代码,会被拒绝. 之前正常的爬虫代码如下: from urllib.request import urlopen ... html = urlopen(scrapeUrl) bsObj = BeautifulSoup(html.read(), "html.parser") 这

用python3 urllib破解有道翻译反爬虫机制详解

前言 最近在学习python 爬虫方面的知识,网上有一博客专栏专门写爬虫方面的,看到用urllib请求有道翻译接口获取翻译结果.发现接口变化很大,用md5加了密,于是自己开始破解.加上网上的其他文章找源码方式并不是通用的,所有重新写一篇记录下. 爬取条件 要实现爬取的目标,首先要知道它的地址,请求参数,请求头,响应结果. 进行抓包分析 打开有道翻译的链接:http://fanyi.youdao.com/.然后在按f12 点击Network项.这时候就来到了网络监听窗口,在这个页面中发送的所有网络

详解python 破解网站反爬虫的两种简单方法

最近在学爬虫时发现许多网站都有自己的反爬虫机制,这让我们没法直接对想要的数据进行爬取,于是了解这种反爬虫机制就会帮助我们找到解决方法. 常见的反爬虫机制有判别身份和IP限制两种,下面我们将一一来进行介绍. (一) 判别身份 首先我们看一个例子,看看到底什么时反爬虫. 我们还是以 豆瓣电影榜top250(https://movie.douban.com/top250) 为例.` import requests # 豆瓣电影榜top250的网址 url = 'https://movie.douban

详解Python修复遥感影像条带的两种方式

GDAL修复Landsat ETM+影像条带 Landsat7 ETM+卫星影像由于卫星传感器故障,导致此后获取的影像出现了条带.如下图所示, 影像中均匀的布满条带. 使用GDAL修复影像条带的代码如下: def gdal_repair(tif_name, out_name, bands): """ tif_name(string): 源影像名 out_name(string): 输出影像名 bands(integer): 影像波段数 """ #

详解Python列表赋值复制深拷贝及5种浅拷贝

概述 在列表复制这个问题,看似简单的复制却有着许多的学问,尤其是对新手来说,理所当然的事情却并不如意,比如列表的赋值.复制.浅拷贝.深拷贝等绕口的名词到底有什么区别和作用呢? 列表赋值 # 定义一个新列表 l1 = [1, 2, 3, 4, 5] # 对l2赋值 l2 = l1 print(l1) l2[0] = 100 print(l1) 示例结果: [1, 2, 3, 4, 5] [100, 2, 3, 4, 5] 可以看到,更改赋值后的L2后L1同样也会被更改,看似简单的"复制"

详解PyQt5中textBrowser显示print语句输出的简单方法

开发python程序处理大数据量的时候,少不了使用print语句看看输出结果:长时间处理数据时用print输出处理进展情况.使用PyQt5开发了UI界面后,本能地想让已自己调试好的py代码中的print输出到UI的textBrowser中显示出来.在CSDN上查了不少结果,一般都是使用多线程.我对多线程研究不多,就采用了变通办法,效果还挺好. 在Ui界面程序(Ui_startaml.py)中设置textBrowser用于显示程序输出信息,并自己定义代码(def printf ),以后将.py程序

详解git的分支与合并的两种方法

如何将两个分支合并到一起.就是说我们新建一个分支,在其上开发某个新功能,开发完成后再合并回主线. 1.   git merge 咱们先来看一下第一种方法 -- git merge 在 Git 中合并两个分支时会产生一个特殊的提交记录,它有两个父节点.翻译成自然语言相当于:"我要把这两个父节点本身及它们所有的祖先都包含进来."下面具体解释. # 创建新分支 bugFix git branch bugFix # 切换到该分支 git checkout bugFix # 提交一次 git c

Python with语句上下文管理器两种实现方法分析

本文实例讲述了Python with语句上下文管理器.分享给大家供大家参考,具体如下: 在编程中会经常碰到这种情况:有一个特殊的语句块,在执行这个语句块之前需要先执行一些准备动作:当语句块执行完成后,需要继续执行一些收尾动作.例如,文件读写后需要关闭,数据库读写完毕需要关闭连接,资源的加锁和解锁等情况. 对于这种情况python提供了上下文管理器(Context Manager)的概念,可以通过上下文管理器来定义/控制代码块执行前的准备动作,以及执行后的收尾动作. 一.为何使用上下文管理器 1.

详解python破解zip文件密码的方法

1.单线程破解纯数字密码 注意: 不包括数字0开头的密码 import zipfile,time,sys start_time = time.time() def extract(): zfile = zipfile.ZipFile('IdonKnow.zip')#读取压缩包,如果用必要可以加上'r' for num in range(1,99999,1): try: pwd = str(num) zfile.extractall(path='.',pwd=pwd.encode('utf-8')

详解Python 数据库的Connection、Cursor两大对象

Python 数据库图解流程 Connection.Cursor比喻 Connection()的参数列表 host,连接的数据库服务器主机名,默认为本地主机(localhost).user,连接数据库的用户名,默认为当前用户.passwd,连接密码,没有默认值.db,连接的数据库名,没有默认值.conv,将文字映射到Python类型的字典. MySQLdb.converters.conversionscursorclass,cursor()使用的种类,默认值为MySQLdb.cursors.Cu

详解python中自定义超时异常的几种方法

最近在项目中调用第三方接口时候,经常会出现请求超时的情况,或者参数的问题导致调用异代码异常.针对超时异常,查询了python 相关文档,没有并发现完善的包来根据用户自定义的时间来抛出超时异常的模块.所以自己干脆自己来实现一个自定义的超时异常.目前找到了两种方式来实现超时异常的功能(signal.alarm().threading实现超时异常) 方法1 thread + time 原理:将要调用的功能函数放入子线程,通过设定子线程的阻塞时间,超时则主线程并不会等待子线程的执行.主线程退出,子线程就

Python实现栈的方法详解【基于数组和单链表两种方法】

本文实例讲述了Python实现栈的方法.分享给大家供大家参考,具体如下: 前言 使用Python 实现栈. 两种实现方式: 基于数组 - 数组同时基于链表实现 基于单链表 - 单链表的节点时一个实例化的node 对象 完整代码可见GitHub: https://github.com/GYT0313/Python-DataStructure/tree/master/5-stack 目录结构: 注:一个完整的代码并不是使用一个py文件,而使用了多个文件通过继承方式实现. 1. 超类接口代码 arra