[爬虫]爬取猫眼电影票房信息(信息字体加密)
猫眼电影里面的实时票房, 票房占比等信息是字体加密的, 所以要爬取这些信息需要解决字体加密这个问题. 下面介绍一种解密字体的办法.
在橙色方块标注的地方我们可以看出字体是加密的, 我们就以这部分字体为例进行解析.
首先我面要找到加密的字体, 查看网页源代码, 搜索font-family, 找到base64后面的字体字符串, 下图中方框中的部分.
创建一个font_cat.py的文件, 并将这部分字符串粘贴出来, 用来生成字体文件,.
from io import BytesIO
from fontTools.ttLib import TTFont
import base64
def make_font_file(base64_string: str):
# 将base64编码的字体字符串解码成二进制流
bin_data = base64.decodebytes(base64_string.encode())
with open("maoyan.woff", "wb") as fp:
fp.write(bin_data)
return bin_data
def convert_font_to_xml(bin_data):
# BytesIO把一个二进制内存块当成文件来操作
font = TTFont(BytesIO(bin_data))
# 将解码字体保存为xml
font.saveXML("maoyan.xml")
base_str = """d09GRgABAAAAAAgoAAsAAAAAC7......j0DFdI30hPSPXol6k5vTV2iIps1a3Wi+KmnPqxVhjCxeBfasZUUiSrs+XY82sjqpbp46yGPTFpKr2ak0RkhazwtlR86i42RVfGn93nCip5t5gh/gPYnXLBAHicbYs7DoAgFATf4gdFvIsIorQo3MXGzsTjGx+t00yykyVBBUX/aAhUqNGghUSHHgoDNEbCI+/rTMlNn7NdEnvyG+8uW7Y3B+9+Lt25WHow7LjP5R9WohcavReI"""
convert_font_to_xml(make_font_file(base_str))
这段代码会会生成两个文件maoyan.woff和maoyan.xml, 将woff的文件用FontCreator软件打开(当然如果嫌下载软件麻烦你也可以用百度在线的FontEditor), 可以得到加密字体之间的对应关系表.
准备工作差不多了, 下面就可以开始具体的解析了
import requests
import lxml.html
import re
from io import BytesIO
from fontTools.ttLib import TTFont
import base64
def make_font_file(base64_string: str):
bin_data = base64.decodebytes(base64_string.encode())
return bin_data
base_str = """d09GRgABAAAAAAgoAAsAAAAAC7......j0DFdI30hPSPXol6k5vTV2iIps1a3Wi+KmnPqxVhjCxeBfasZUUiSrs+XY82sjqpbp46yGPTFpKr2ak0RkhazwtlR86i42RVfGn93nCip5t5gh/gPYnXLBAHicbYs7DoAgFATf4gdFvIsIorQo3MXGzsTjGx+t00yykyVBBUX/aAhUqNGghUSHHgoDNEbCI+/rTMlNn7NdEnvyG+8uW7Y3B+9+Lt25WHow7LjP5R9WohcavReI"""
url = "http://piaofang.maoyan.com/?ver=normal"
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'
}
result = requests.get(url=url, headers=headers)
tree = lxml.html.fromstring(result.content.decode("utf8"))
# 抓取当前页面的具体字体
text = tree.xpath('//*[@id="ticket_tbody"]/ul[1]/li[2]/b/i/text()')[0]
# 因为页面刷新字体会发生变化, 在这里动态获取当前的字体。
font_str = re.findall(r'base64,(.*?)\)', result.text)[0]
# 得到基础字体绘制的点阵关系
baseFont = TTFont(BytesIO(make_font_file(base_str)))
# print(baseFont["cmap"].tables[0].ttFont.getGlyphOrder())
def decode_font_advance(font_str):
match_font = TTFont(BytesIO(make_font_file(font_str)))
# 准备一个字典用于存放新的对应关系
numDic = {}
uniList = match_font["cmap"].tables[0].ttFont.getGlyphOrder()
# 手动写一个基础字体之间的对应关系(就是之前用FontCreator软件打开的字体对照关系)
base_num_list = [".", "9", "2", "4", "8", "6", "3", "1", "5", "0", "7"]
base_unicode = ["x", "uniEE40", "uniF35E", "uniF068", "uniE4F3", "uniE61D", "uniF62E", "uniF44B", "uniE491",
"uniEBC2", "uniF397"]
for i in range(1, 12):
# 找到新字体的绘制点阵
matchGlyph = match_font["glyf"][uniList[i]]
for j in range(11):
# 取出基础字体的绘制点阵, 与新的点阵依次进行对比, 如果绘制点阵相同说明是同一个字
baseGlyph = baseFont["glyf"][base_unicode[j]]
if matchGlyph == baseGlyph:
# 将新的对应关系存入字典
numDic[uniList[i]] = base_num_list[j]
break
return numDic
# 解析页面具体的文字
def decode_encrypt_text(text, font_str):
# font1 = TTFont(BytesIO(make_font_file(font_str)))
# c = font1.getBestCmap()
result = ""
for i in range(len(text)):
if text[i] == ".":
key_str = "x"
elif text[i] == '%':
key_str = "%"
else:
key_str = "uni" + (text[i].encode("unicode-escape")[-4:].decode()).upper()
if key_str == "%":
real_data_cat = "%"
else:
real_data_cat = decode_font_advance(font_str)[key_str]
result += real_data_cat
return result
print(decode_encrypt_text(text, font_str))