300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Python—猫眼电影票房爬虫实战 轻松弄懂字体反爬!

Python—猫眼电影票房爬虫实战 轻松弄懂字体反爬!

时间:2020-02-07 02:38:13

相关推荐

Python—猫眼电影票房爬虫实战 轻松弄懂字体反爬!

目录

目标爬取网址分析结论前期准备代码

目标

1.爬取猫眼票房信息

2.将信息保存起来(MySQL、Redis、MongoDB或者Excel等等),我们保存的是Excel

爬取网址

/?ver=normal&isid_key=2d9aacd2f26d4d0dba63

分析

1.打开chrome,输入网址我们看到的效果如下

2.我们要获取的信息如下图

3.右击检查打开开发者工具,选择左下角鼠标按钮,然后点击一个电影的标题

可以看到这个标签里面就是我们想要获取的电影标题信息,别急,我们在接着往下看,再次点击标题下面的7.86亿和实时票房,可以发现开发者工具里面显示的并不是我们看见的数字,这就是我们本次爬虫的重点字体反爬

字体反爬顾名思义,就是通过css加载本地的字体文件,在网页上展示特定的字体,我们想要获取信息,就必须要弄到这个字体文件,然后解析得到我们想要的数据

4.开发者工具上无法显示这个特殊的字体,这时候我们右击点查看源代码,就可以看见一串编码,这每个编码就表示一个对应的数字

5.想要破解字体,我们要先得到他的字体文件,在源代码页面Ctrl+F搜索font,可以看到这个就是加密的字体

通过分析这段代码可以看出这个是这个是通过base64进行加密的,文件后缀为woff

通过上面的分析可以得出结论:我们想要得到数据,打开开发者工具发现我们想要的数据进行了字体反爬,查看网页原代码分析找到了字体文件是通过base64加密的,即我们只要吧字体文件下载下来,然后进行base64解密,保存到本地,在通过字体文件解析我们爬下来的网页数据就能得到我们想要的数据了

看到这里,肯定很多小伙伴就会想,这字体反爬好简单啊,就分析出他的字体文件,然后下载下来进行解析就能得到我们的数据了,确实,简单的字体反爬分析到这里就已经结束了,但我只能说,你还是太年轻了,下面我们在接着往下分析

6.点击左上角重新请求这个网站,数据还是这些数据,这时候我们在右击查看源代码,找到一个有反爬字体的地方和font,和前一个网页源代码进行比较,可以发现同样的数据,但编码号变了,base64加密后的font也变了,这就是本次猫眼爬虫的另外一个难点,即每次重新请求都会有一套新的编码和字体文件

结论

我们想要得到数据,就需要解决以下这些问题

1.先解码数据

2.需要字体文件

3.每次请求字体文件会产生变化

前期准备

为了完成下面的代码,我们需要先做一些准备

首先我们先将源代码页面的加密字体复制下来,放到py中生成基础的字体文件

# 使用bs64进行解码import base64# 保存加密字体font_s = "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"# base64解密b = base64.b64decode(font_s)# 打开文件保存字体文件with open('maoyan_pf.woff', 'wb') as f:f.write(b)

使用TTFont模块读取字体文件,并转换成pycharm可以打开的xml格式

from fontTools.ttLib import TTFont# 建立字体文件对象base_font = TTFont("maoyan_pf.woff")# 转换成xml格式base_font.saveXML("maoyan_pf.xml")

打开字体xml文件就可以看见解密后保存的字体文件了,code对应的是每个字体的形状名,即name,而name对应的就是每个字体的形状,即坐标,对的,这个字体文件里的每个字都是通过坐标画出来的

下面是一个字体对应的一部分坐标

是不是根本看不明白?别急,下面我们可以借助一个软件High-Logic FontCreator来打开字体文件 打开软件后,将刚刚保存的woff字体文件拖到软件中

通过软件我们一眼就能看出每个编码对应的数字了,有了这个我们就能建立一个基础的字体映射字典,通过刚刚的字体对象里面的glyf可以取出所有name对应的字体形状,在参照软件上打开的字体文件信息,用数字当做基础映射字典的key,用取出来的形状当基础映射字典的value,这个基础映射字典只能看着软件手动组成

from fontTools.ttLib import TTFont# 建立字体文件对象base_font = TTFont("maoyan_pf.woff")# 转换成xml格式base_font.saveXML("maoyan_pf.xml")# 获取name和形状之间的关系base_glyph = base_font["glyf"]# 组装出基础映射字典base_num_glyph_map = {0: base_glyph["uniF1DD"],1: base_glyph["uniE09F"],2: base_glyph["uniEF13"],3: base_glyph["uniED29"],4: base_glyph["uniEE6F"],5: base_glyph["uniE853"],6: base_glyph["uniF44B"],7: base_glyph["uniEF72"],8: base_glyph["uniF814"],9: base_glyph["uniF466"],}

代码

准备完成之后,下面我们开始创建项目,开始编写爬虫,这次我们使用requests模块来进行数据爬取

1.导入requests模块,创建类,将前面准备的基础映射字典、请求url和请求体放到__init__初始化函数,定义run方法作为开启爬虫的函数,在run方法中请求url,打印获取到的HTML

import requestsclass MYSpider(object):"""爬取猫眼即将上映页面"""def __init__(self):# 请求urlself.url = "/?ver=normal&isid_key=2d9aacd2f26d4d0dba63"# 请求头self.headers = {"User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1",}# 建立字体文件对象base_font = TTFont("maoyan_pf.woff")# 获取name和形状之间的关系base_glyph = base_font["glyf"]# 组装出基础映射字典self.base_num_glyph_map = {0: base_glyph["uniF1DD"],1: base_glyph["uniE09F"],2: base_glyph["uniEF13"],3: base_glyph["uniED29"],4: base_glyph["uniEE6F"],5: base_glyph["uniE853"],6: base_glyph["uniF44B"],7: base_glyph["uniEF72"],8: base_glyph["uniF814"],9: base_glyph["uniF466"],}def run(self):"""开始爬取"""# 发送请求response = requests.get(url=self.url, headers=self.headers)# 打印获取的HTMLprint(response.text)if __name__ == '__main__':my = MYSpider() # 创建爬虫对象my.run() # 运行爬虫

运行之后可以发现确实是我们想要的数据

2.下面开始提取加密字体文件,进行base64解密

观察源代码里面的加密字体是从base64,开始到结束的这一段就是加密字体

将获取到的HTML转换成lxml对象进行xpath提取这段加密字体,并调用方法进行解密

def run(self):"""开始爬取"""# 发送请求response = requests.get(url=self.url, headers=self.headers)# print(response.text)# 转换成lxml对象进行xpath提取html = etree.HTML(response.text)# 获取字体加密# 提取style标签里面的内容encryption_font = html.xpath("//style[@id='js-nuwa']/text()")[0].split()[0]# 使用正则提取想要的加密字体encryption_font = re.search(r";base64,(.+)\)$", encryption_font).group(1)# 调用方法对字体进行解密self.decryption_font(encryption_font)

定义新方法decryption_font将获取到的新加密字体进行base64解密,并保存到新字体文件中

def decryption_font(self, encryption_font):"""字体解密保存"""# 使用bs64进行解码b = base64.b64decode(encryption_font)# 打开文件保存字体文件with open('maoyan_pf_new.woff', 'wb') as f:f.write(b)

定义新方法font_map对新字体进行映射,这里用到了一个 knn 算法(K近邻)的思想,因为每次请求得到的都是新的字体,所以本次请求的字体与上次请求的字体坐标会有微差,但不管怎么变,这个字肯定不可能变成别的字,所以我们只有得到本次请求的字体坐标与基础的映射字典里的字体坐标进行对比就能,然后取出最接近的值,那么肯定就是我们想要的值了

def font_map(self):"""对字体生成新的映射"""# 生成当前字体文件的对象font = TTFont('maoyan_pf_new.woff')font.saveXML('maoyan_pf_new.xml') # 将ttf文件生成xml文件并保存到本地# 获取当前字体的code和name映射code_name_cmap2 = font.getBestCmap()# 获取当前字体的字形name_glyph_map = font['glyf']# 遍历当前字体的code和namefor code, name in code_name_cmap2.items():# 判断是否是无用的数据if name == "x":continue# 通过name取出当前字体的所有字形坐标current_glyph = name_glyph_map[name]num_diff = dict()# 遍历基础字形字典,取出对应的映射数字和坐标for num, glyph in self.base_num_glyph_map.items():# 定义一个变量用来记录当前所有坐标的最小差值diff = 0# 遍历当前字形字典,取出所有坐标for coor1 in current_glyph.coordinates:# 定义一个列表用来保存当前最小差值的所有差值coor_diff_list = list()for coor2 in glyph.coordinates:coor_diff_list.append(abs(coor1[0] - coor2[0]) + abs(coor1[1] - coor2[1]))diff += min(coor_diff_list)# 组成当前字体的映射字典num_diff[num] = diff# 取出对应的映射num = min(num_diff, key=num_diff.get)# code = str(hex(code)).replace("0", "&#", 1) + ";"# 将默认映射替换成想要的样式code = str(hex(code)).replace("0x", r"\u", 1)# print(code, num)# 将新字体的映射组成字典保存self.new_num_glyph_map[code] = num

获取详情信息,定义新方法get_info获取想要的信息,组成字典,保存到列表中

def run(self):"""开始爬取"""# 发送请求response = requests.get(url=self.url, headers=self.headers)# print(response.text)# 转换成lxml对象进行xpath提取html = etree.HTML(response.text)# 获取字体加密# 提取style标签里面的内容encryption_font = html.xpath("//style[@id='js-nuwa']/text()")[0].split()[0]# 使用正则提取想要的加密字体encryption_font = re.search(r";base64,(.+)\)$", encryption_font).group(1)# 调用方法对字体进行解密self.decryption_font(encryption_font)# 对新字体进行映射self.font_map()# 获取详情ul_list = html.xpath("*//div[@class='content strip']/ul")# print(ul_list)# 调用方法self.get_info(ul_list)def get_info(self, ul_list):"""获取详情信息"""for ul in ul_list:# print(ul)# 定义字典保存当前信息info_dict = dict()temp = ul.xpath(".//li[@class='c1']//text()")# print(temp)info_dict["片名"] = temp[1]info_dict["上映首日"] = temp[4]info_dict["实时票房(万元)"] = ul.xpath(".//li[@class='c2 ']/b/i/text()")[0]info_dict["票房占比"] = ul.xpath(".//li[@class='c3 ']/i/text()")[0]info_dict["排片占比"] = ul.xpath(".//li[@class='c4 ']/i/text()")[0]info_dict["上座率"] = ul.xpath(".//li[@class='c5 ']//i/text()")[0]# print(info_dict)# 调用方法进行解密info_dict = self.decryption_info(info_dict)# 保存进列表self.info_list.append(info_dict)# print(self.info_list)

定义新方法save_excel将数据保存到Execl,就大功告成了

def save_excel(self):"""将数据保存到Excel"""# 创建一个workbook 设置编码workbook = xlwt.Workbook(encoding='utf-8')# 创建一个worksheetworksheet = workbook.add_sheet("Arknights")# 写入excel# 参数对应 行, 列, 值for i, content in enumerate(self.info_list):for x, info in enumerate(content.values()):worksheet.write(i, x, label=info) # 将数据存入excel# 保存workbook.save('猫眼票房.xls')

爬取到的Excel

到这里我们本次的猫眼爬虫就结束了,如果对你有帮助,不妨点个赞~

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。