严重声明:本文仅用于学习交流,不得用于商业用途,同时希望大家遵循网络协议,维护网络和谐。
- 上一篇文章我们分析了一下人人车的字体反爬,但是好像python进行逆向分析没有很全,那么今天我们来看一下乐居的字体反爬是怎么做的,然后讲一下这个代码是怎么走的,还是先看网站(乐居
):-
因为我们需要的重要信息都在详情页里面,所以,点开一个新房的详情页。按F12观察网页源码里面和正文的异同。如下图所示,我们可以看到,和人人车一样,都是利用css样式渲染,让源码里面的数字进行变换,让我们看到真实数据。在源码搜索new_font这个样式一共有三个,两个就是图片里里面的,将指导价和开盘时间字体转换。另一个,不是写在样式里面,但是,我们也有用,后面说:
-
我们打开在F12的控制台里面选Network查看后台给我们发的数据包,选择font直接就能看到一堆字体文件(.woff结尾的),然后打开,最下面一行都是数字,但是,有个名字含有truetype的一看就不一样,果然,里面的数字顺序是打乱的,别的文件数字顺序都是1234567890。这里面肯定存在着什么猫腻,所以我们根据这个文件名字里面有true认为这个顺序是正常顺序,那么源码中的顺序数字就按别的文件中的顺序转换,得到下图中的这个对应关系,红色数字表示源码中的数字:
-
我们运用上面的对应看看主页,按照上图的规则刚好能得到我们看到的数据,可以看到,对应的上。那么,再将这个套路运用到别的网页,我自己看了十几页都对的上,我认为的话应该就是这么实现的反爬了:
-
- 那么怎么用代码去实现这种反推呢,下面介绍:
-
用到python的第三方库:fontTools,清华镜像,我安装的源码是(你们可能是pip3):
pip install -i https://tuna.tsinghua.edu.cn/simple fontTools -
请求页面,获取字体变换的源数据,这里我就只获取了指导价格,里面用到的scrapy.Selector()对象,和lxml差不多,但是我觉得会好一点,这段就是为了获取到指导价格那串迷惑文字:
# get response detail_url = 'https://house.leju.com/dl149612/' resp = requests.get(detail_url) # get guide price from scrapy import Selector html = Selector(text=resp.text) guide_price = html.xpath("normalize-space(//span[@class='t_l'])").extract_first() print(guide_price) # result: 指导价格: 约52777-59777 元/㎡
-
利用fontTools对字体文件进行解析,这里我们不需要获取字体文件,因为,乐居将字体文件的base64编码的数据直接放在网页源码里面了,就是我刚开始说的new_font,这里还有个原因就是字体文件其实应用的是哪一个是不固定的,在下面你们会看到,字体文件变了三四次,所以,我不推荐大家直接将字体文件下载下来就不变了,另外,多请求一次浪费资源:
所以,我们就不用再请求一次字体文件了,这里直接利用python的字符串切割获得,获取的是base64字符串,直接编码为字节流,为了之后用:# get font_file_bytes by split html source b64_str = resp.text.split("src: url(data:font/truetype;charset=utf-8;base64,")[1].split(") format('woff');")[0] font_file_io = base64.b64decode(b64_str) print(type(font_file_io)) # result: <class 'bytes'>
-
利用fonttools获取字体文件,同时保存转换的字体文件关系:
# parse font file by fontTools.TTFont from fontTools.ttLib import TTFont font = TTFont(io.BytesIO(font_file_io)) # this param also can be a path txt font.saveXML('leju.xml') # save the conversion map to the path you want
正常情况下,我们需要找GlyphOrder和cmap这两种对应xml的,我也想解释下对应关系的,但是,好像,他们的coder直接给我们打了注释了,打开这个xml文件,第一个cmap有后台程序员对我们深深的爱意:
-
在字体文件中获取刚刚看到的第一个对应关系(cmap):
# get base conversion map font_map = font['cmap'].getBestCmap() print(type(font_map)) # result: <class 'dict'>
-
在cmap中获取英文数字列表,同时构造真实对应关系字典,就是将正常顺序的数字做键,上面的那个英文数字列表转成阿拉伯数字后做值,组装成一个字典:
# get conversion dict: key: html source num, value: true num base_eng_list = {'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4', 'five': '5', 'six': '6', 'seven': '7', 'eight': '8', 'nine': '9'} mapping_list = [base_eng_list[_] for _ in list(font_map.values())[:10]] base_num_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] font_dict = dict(zip(base_num_list, mapping_list))
-
利用映射字典转换网页源码中的数字:
# use the map dict converter the html source txt true_guide_price = ''.join([_ if not _.isdigit() else font_dict[_] for _ in guide_price]) font.close() print(true_guide_price) # 指导价格: 约15000-16000 元/㎡
最后,也希望大家的技术水平蒸蒸日上,如果喜欢,不妨关注啥的。
-