新的挑战
昨天,一个小伙伴跟我说,“马上就要交期末作业了,我想做一个小说榜单的爬取,可是这个功能我不会做,仙草哥哥你能不能教教我,要怎么做呢?”
我说,“爬取榜单,这有什么困难的,首先发起requests请求,然后通过lxml解析,实在不行就上selenium呗,你在学校没学过吗?”
小伙伴说,“不行啊,你这三招对于没有反爬的网站还好说,对于有反爬的网站不管用啊,你去看看呗?”
什么?反爬?上当了啊,兄弟们,这有反爬的那我哪会啊!不过嘛,也还是先观察观察再说吧
页面分析
看这个页面呢,其实不太难,只要简单的爬取一下榜单上的信息就可以了,比如说,这是一个月票榜

但是问题在于什么呢?问题在于,虽然页面上显示的挺正常的,但是打开浏览器的代码的时候,发现这个月票数看不到,全都是框框

这怎么回事?怎么看不到呢?我浏览器坏掉了?我当时就傻住了,我再去查看源代码,发现,其他的都挺正常的,就是数字这里不正常

这怎么回事啊?这是什么意思?这本来应该放数字的位置怎么都是些看不懂的文字呢?这什么编码啊?通过decode()能解码吗?
好吧,其实说白了,这也没什么稀奇的,这其实是css3以后的特性,页面中可以通过css自己定义一套字体,这套字体虽然在浏览器中能够正常查看,但是无论在源代码中,还是在浏览器中都不能正常查看,而且由于字体是自己定义的,因此也没有办法直接转换,只能够去想办法解析它是怎么定义的,它的css代码应该是这样写的
@font-face{
font-family: 字体名;
src: url('xxxxxx.xxxxx.xxx/xx_xxxx_spider/xxxxxxxx.eot?') format('eot');
src: url('xxxxxx.xxxxx.xxx/xx_xxxx_spider/xxxxxxxx.woff') format('woff'), url('xxxxxx.xxxxx.xxx/xx_xxxx_spider/xxxxxxxx.ttf') format('truetype');
}
.使用字体的标签{
font-family: '字体名' !important;
display: initial !important;
color: inherit !important;
vertical-align: initial !important;
}
在这种情况下,页面上的数字就仿佛传送了一个图片那样,只能看到,但是没办法解析。因此无论是通过requests发送请求,还是通过selenium得到动态页面,都不行
实现分析
那怎么办呢?毫无疑问现在我们最重要的任务就是必须要解析这个字体才行。但是解析字体呢,太困难,不容易实现,还有一个没有办法的办法,也是最容易的办法,就是其实总共只有10个数字,我们将0-9的全部数字的对应编码规则给解析出来,不就能够转换了吗?
事实上,这个想法虽然可行,但是现在的网站显然也有应对的措施,那就是它们准备了几十套,几百套甚至更多的字体,你根本没有办法把全部的规则都解析出来。不过好在,如果我们需求不大的情况下,也可以先爬了再说,爬到了哪套字体,就解析哪套字体
那么这个字体到底要怎么解析呢?首先我们写一个简单的html页面,把字体引入进来
<!-- 首先,我们把css的字体定义放到style标签里 -->
<style>
@font-face{
font-family: 字体;
src: url('xxxxxx.xxxxx.xxx/xx_xxxx_spider/xxxxxxxx.eot?') format('eot');
src: url('xxxxxx.xxxxx.xxx/xx_xxxx_spider/xxxxxxxx.woff') format('woff'), url('xxxxxx.xxxxx.xxx/xx_xxxx_spider/xxxxxxxx.ttf') format('truetype');
}
.选择器{
font-family: '字体' !important;
display: initial !important;
color: inherit !important;
vertical-align: initial !important;
}
</style>
<!-- 然后,我们把对应的字体编码放到一个div标签里,然后使用刚刚定义的css样式 -->
<div class="选择器">𘚡𘚢</div>
这样,我们再打开这个页面,就能看到这段字体编码所对应的数字了,然后我们就立刻将其记录下来,凑齐了全部10个,就得到了转换规则,有了这个规则,我们就可以通过简单的字符串替换,来获取到我们最终所需的数字了
完整代码展示
第一部分
首先,我们需要爬取小说的榜单,提取出其中的数据,包括字体编码等,生成一个未完成的结果页,以及一个转换素材页
import requests
import re
from base64 import b64decode
from lxml import etree
url = b64decode("aHR0cHM6Ly93d3cucWlkaWFuLmNvbS9yYW5rL3l1ZXBpYW8v").decode()
headers = {"user-agent": "Moizlla/5.0"}
r = requests.get(url, headers=headers)
content = r.text
html = etree.HTML(content)
title = html.xpath("//ul/li//h2/a/text()")
author = html.xpath("//ul/li//p[@class='author']/a[1]/text()")
c_type = html.xpath("//ul/li//p[@class='author']/a[2]/text()")
u_time = html.xpath("//ul/li//p[@class='update']/span/text()")
t_style = html.xpath("//div[@class='total']//span/style/text()")
tickets = re.findall("}</style><span.*?>(.*?)</span>", content)
with open("未完成结果页.txt", "w") as f:
for i in range(len(title)):
f.write("标题:" + title[i] + "\n")
f.write("作者:" + author[i] + "\n")
f.write("类型:" + c_type[i] + "\n")
f.write("更新时间:" + u_time[i] + "\n")
f.write("月票数量:" + tickets[i] + "\n\n")
with open("转换素材页.txt", "w") as f:
for i in range(len(t_style)):
f.write(t_style[i] + "\n")
f.write(tickets[i] + "\n\n")
运行该程序,得到了未完成的结果页如下,可以看到此时的月票数量还是一些看不懂的编码

第二部分
此时,我们需要根据我们刚刚得到的转换素材,建立之前提到的转换页面,最终取得转换规则
# 此处需要填写上你找到的转换规则
# 比如,我找到某套字体的转换规则如下
c = {
0: "𘝭",
1: "𘝮",
2: "𘝣",
3: "𘝪",
4: "𘝩",
5: "𘝫",
6: "𘝬",
7: "𘝥",
8: "𘝧",
9: "𘝨"
}
第三部分
通过我们在第一部分中拿到的未完成的结果页,以及在第二部分中拿到的转换规则,运行转换程序,就可以得到最终的结果了
# 这里不能填写我得到的转换规则
# 必须填写你自己刚刚建立的转换规则
c = {
0: "",
1: "",
2: "",
3: "",
4: "",
5: "",
6: "",
7: "",
8: "",
9: ""
}
with open("未完成结果页.txt") as f:
content = f.read()
for i in c:
content = content.replace(c[i], str(i))
with open("最终结果.txt", "w") as f:
f.write(content)
最终完成了这一步以后,就可以看到我们得到的最终结果了。虽然过程有点波折,而且每次还需要手动操作,但至少结果是好的啦,可以看到最终,我们的数据已经可以正常显示了

众所周知,仙草哥哥只会三招,requests,lxml,selenium,本期这已经超纲了啊喂,所以,你不要来个关注吗?
本文介绍了如何应对网站使用自定义字体进行反爬的策略,通过创建HTML页面解析字体编码规则,从而转换并获取隐藏的数字信息。详细步骤包括爬取榜单数据、构建转换页面、应用转换规则,最终成功提取出原本不可见的月票数量。

2731

被折叠的 条评论
为什么被折叠?



