岁月匆匆,往事如年。经历了前面几篇的基本爬虫(静态与动态),相信看过的小伙伴已经对爬虫的流程有一定的了解。这次小编带大家来解决起点中文网的字体反爬,希望这篇文章能带给你新的知识点,解决你所遇到的难题。
操作环境: Windows10、Python3.6、pycharm2019.3、谷歌浏览器
目标网址: https://book.qidian.com/info/1009704712(牧神记)
相关文章: 爬虫(豆瓣电影、拉钩网、腾讯zp、新笔趣阁、链家网),豆瓣分析
==============================================
目录
一、分析网页
1.1、静态数据
这里以起点中文网的一篇小说为例:宅猪著作《牧神记》。复制需要获取的字段,打开查看网页源码,快捷键Ctrl+F查看字段数据是否存在,存在即静态数据,反之为ajax动态数据。
但是由分析得出,汉字字段是正常的数据,而数字字段却被字体文件所加密混淆。如下图可看出,总字数中的数字5为:𘡝;
1.2、基本流程
1、请求页面
2、获取加密的字体库
3、解析字体库,获取字体间的映射关系
4、获取加密的字体,获取字体间映射关系,一一对应
二、请求解析
2.1、请求目标网址
导入相关库,设置好请求头信息。
# 导入相关库
import requests
headers = { # 请求头 伪造身份
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36',
}
if __name__ == '__main__':
url = 'https://book.qidian.com/info/1009704712'
# 请求目标网址并编码
response = requests.get(url=url,headers=headers).text
print(response)
代码展示部分运行结果,数据无误,请求成功。
2.2、字体加密
2.2.1、查看字体加密文件
1、打开检查
2、鼠标点击查看数字加密位置
3、class属性KvrLqLSs查看css规则
4、源码查看font-family
一般来说KvrLqLSs属性最有可能是字体加密文件的关键参数。由图片可看出.woff的结尾的链接包含KvrLqLSs属性,最有可能是字体加密文件。
2.2.2、正则提取
导入re正则模块import re,定义一个get_font函数用于提取源码中的字体加密文件.woff。
html参数为传入的目标网页源码。
# 获取加密字体文件url
font_link = re.findall(r"format\('eot'\); src: url\('(.*?)'\) format\('woff'\)",html)[0]
print(font_link)
输出结果:
点击输出获取到的字体加密链接,或者复制链接到浏览器中单独打开,即可在网页中下载字体文件。使用 High-Logic FontCreator 软件打开查看字体加密规则。
2.2.3、保存字体文件
split(’’):以 \ 分割。
response_woff = requests.get(url=font_link) #请求加密字体文件url
filename = font_link.split('/')[-1] #定义文件名字
with open(filename,'wb') as f:
f.write(response_woff.content)
2.3、字体映射
导入fontTools模块中的TTFont函数方法,即from fontTools.ttLib import TTFont。将字节文件转化为xml格式。
font_name: 为前面所保存的字体文件名字。
# 把字体文件读取为Python能理解的对象
base_font = TTFont(font_name)
# 保存为XML格式
base_font.saveXML('font.xml')
查看保存好的xml文件的cmap标签里的字体映射规则:(与之前打开的字体文件链接查看的规则十分相似,只是阿拉伯数字变成英文数字)
自定义规则与映射规则
# 自定义匹配规则
eng_2_num = {
'period': ".", 'two': '2', 'zero': '0', 'five': '5', 'nine': "9", 'seven': '7', 'one': '1', 'three': '3',
'six': '6', 'four': '4', 'eight': '8'
}
# 把字体文件读取为Python能理解的对象
base_font = TTFont(font_name)
# 保存为XML格式
base_font.saveXML('font.xml')
# 获取映射规则(在cmap标签内)
map_list = base_font.getBestCmap()
print('映射前的规则:',map_list)
for key in map_list.keys():
map_list[key] = eng_2_num[map_list[key]]
print('映射后的规则:',map_list)
代码输出结果为:
映射前的规则: {100543: 'eight', 100545: 'four', 100546: 'seven', 100547: 'nine', 100548: 'three', 100549: 'five', 100550: 'two', 100551: 'one', 100552: 'six', 100553: 'zero', 100554: 'period'}
映射后的规则: {100543: '8', 100545: '4', 100546: '7', 100547: '9', 100548: '3', 100549: '5', 100550: '2', 100551: '1', 100552: '6', 100553: '0', 100554: '.'}
2.4、终极替换
定义一个new_replace函数,传入字体映射规则map_list,以及网页源码old_html。
将加密规则替换为页面正确数字。注意网页源码中的加密字体是𘣅;而字体映射规则map_list中的却是100549,漏缺的&#;需要自行填补上。
# 终极替换正确字体
def new_replace(map_list,old_html):
new_html = old_html
for key,value in map_list.items():
# print(key,value)
# 𘣅 5
# 将加密规则替换为页面正确数字
new_html = new_html.replace('&#' + str(key) + ';',value)
new = new_html
print(new)
查看替换后的网页源码,代码输出结果为:(与网页展示数据一致,字体反爬解决,替换成功。)
三、提取数据
导入parsel解析库,将网页源码转换为Selector对象,以便使用xpath或者css语法提取数据。
new: 替换后的正确网页源码
import parsel
selector = parsel.Selector(new)
使用css语法提取各字段数据。
title = selector.css('div.book-info h1 em::text').extract_first() #获取小说标题
author = selector.css('div.book-info h1 span a::text').get() #获取作者
# 获取小说标签
label1 = ','.join(selector.css('p.tag span::text').getall())
label2 = ','.join(selector.css('p.tag a::text').getall())
label = label1 + ',' +label2
tiny = selector.css('p.intro::text').get()
# 获取小说总字数
number1 = selector.css('div.book-info p:nth-child(4) em:nth-child(1) span::text').get()
number2 = selector.css('p cite:nth-child(2)::text').get()
number = number1 + str(number2)
# 获取总推荐
recom1 = selector.css('div.book-info p:nth-child(4) em:nth-child(4) span::text').get()
recom2 = selector.css('p cite:nth-child(5)::text').get()
recommend = recom1 + str(recom2)
# 获取总推荐
wkmend1 = selector.css('div.book-info p:nth-child(4) em:nth-child(7) span::text').get()
wkmend2 = selector.css('p cite:nth-child(8)::text').get()
wkmend = wkmend1 + str(wkmend2)
intro = ','.join(selector.css('div.book-intro p::text').extract()).strip() #获取小说简介
print(title,author,'\n',label,'\n',tiny,'\n',number,recommend,wkmend,'\n',intro)
代码输出结果为:(若爬取多个的,即先获取到每个小说的详情页简介链接,传入之前的get_font获取字体映射规则函数即可。)
四、项目总结
本次项目重在解决字体反爬,没有写上持久化保存,若想保存数据,参考之前的文章即可。
字体反爬解决在于找到正确的字体映射规则,然而起点中文网只是静态的字体加密文件,换句话说是规则“写死了”,分析自行定义字体规则即可。若遇到每请求便更新字体文件规则的才是恶心,后续小编会带大家解决这个问题。敬请期待!
“赠人玫瑰,手有余香”,看完的小伙伴记得点赞收藏,感谢!
若有小伙伴有疑惑的地方,可在评论区留言,小编看到会尽快一一回复;此项目有需要改进的地方,也请大佬不吝赐教,感谢!
注:本项目仅用于学习用途,若用于商业用途,请自行负责!!