一个python小菜鸟,在起点中文网上爬取月票数据,遇到了字体反爬,弄了好久才勉强弄出来了,麻烦大佬们给点建议,有更简单的方法分享下,救救孩子啊啊啊
[Python] 纯文本查看 复制代码 import re
import requests
from fontTools.ttLib import TTFont
from lxml import etree
if __name__ == '__main__':
url_ = 'https://www.qidian.com/rank/yuepiao/'
headers_ = {
'Cookie': 'e1=%7B%22pid%22%3A%22qd_P_rank_01%22%2C%22eid%22%3A%22qd_C45%22%2C%22l1%22%3A5%7D; e2=%7B%22pid%22%3A%22qd_P_rank_01%22%2C%22eid%22%3A%22qd_C46%22%2C%22l1%22%3A5%7D; newstatisticUUID=1653031883_243064998; _csrfToken=SqNQc9cnJezKvfEbUcq8hUVBBpkKKZnStUjihiGS; fu=317266460; _gid=GA1.2.102240006.1653031887; e1=%7B%22pid%22%3A%22qd_p_qidian%22%2C%22eid%22%3A%22qd_A16%22%2C%22l1%22%3A3%7D; e2=%7B%22pid%22%3A%22qd_p_qidian%22%2C%22eid%22%3A%22qd_A16%22%2C%22l1%22%3A3%7D; _yep_uuid=437ba5a1-9bf2-cf11-e031-4198b9b208d2; _gat_gtag_UA_199934072_2=1; _ga_FZMMH98S83=GS1.1.1653183720.5.1.1653183739.0; _ga_PFYW0QLV3P=GS1.1.1653183720.5.1.1653183739.0; _ga=GA1.2.1828961077.1653031887',
'Referer': 'https://www.qidian.com/rank/',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.67 Safari/537.36',
'connection': 'close'
}
response_ = requests.get(url_,headers=headers_)
str_data = response_.text
# 把拿到的数据转换为xpath能够转化的数据
html_obj = etree.HTML(str_data)
# 解析书名
title_list = html_obj.xpath('//h2/a/text()')
# 解析月票数据,用xpath无法解析,考虑使用正则
ticket_list = re.findall(r'</style><span class=".+?">(.*?)</span></span>月票</p>',str_data)
# print(ticket_list)
# 提取字体的url,用xapth不能直接获取,需要进一步用正则
res_ = html_obj.xpath('//style/text()')[0]
# print(res_)
font_url = re.findall("format\('eot'\); src: url\('(.*?)'\) format\('woff'\)",res_)[0]
# print(font_url)
# 发送请求,获取字体加密文件
font_response = requests.get(font_url,headers=headers_)
# 保存获取到的字体加密文件
with open('font_data.woff', 'wb') as f:
f.write(font_response.content)
# 解析字体加密文件
font_obj = TTFont('font_data.woff')
# 转换为能看懂的格式xml
font_obj.saveXML('font_data.xml')
# 获取其中的加密映射表
camp_dict = font_obj.getBestCmap()
# print(camp_dict)
# 创建一个英文和数字匹配的字典,与加密数字匹配
dict_number = {'one':'1','two':'2','three':'3','four':'4','five':'5','six':'6','seven':'7','eight':'8','nine':'9','zero':'0','preiod':'.',}
for i in camp_dict:
for j in dict_number:
if camp_dict[i] == j:
camp_dict[i] = dict_number[j]
# print(camp_dict)
# 因为我们在网页中拿到的加密有特殊字符混在其中,先去掉特殊字符
for i in enumerate(ticket_list):
new_ticket_list = re.findall('\d+',i[1])
ticket_list[i[0]] = new_ticket_list
# print(ticket_list)
# 去掉特殊字符后与得到的映射表匹配出数字
for i in ticket_list:
for j in enumerate(i):
for n in camp_dict:
if j[1] == str(n):
i[j[0]] = camp_dict[n]
# print(ticket_list)
# 把拿到的列表中子列表的引号去掉
ultimately_ticket_list = []
for i in ticket_list:
j = ''
for k in i:
j += k
ultimately_ticket_list.append(j)
# print(ultimately_ticket_list)
rank_list = dict(zip(title_list,ultimately_ticket_list))
print(rank_list) |