前言
记得好早之前,我做过关于外卖平台字体加密反爬的总结笔记,今天给大家记录另外一种常见的反爬 —— SVG 映射
什么是SVG呢?
SVG 全称为 Scalable Vector Graphics,是一种基于 XML 并可以缩放的矢量图片文件格式
而 SVG 反爬是利用 CSS 样式及 SVG 图片,将 SVG 图片中提取字符内容,映射到网页元素中,由于不能直接通过网页元素直接提取数据,所以起到了反爬的目的。
解决思路和流程
1、分析
打开我们碰到的目标页面,查看页面中电话号码的网页元素
我们发现,电话号码中的每一个数字都对应一个 d 标签,d 标签中的 class 属性值都是以「 vhk 」开头的
查看右侧 Styles 样式标签后,发现上面匹配的 d 标签的背景图片「 background-image 」被设置为一张 SVG 图片,d 标签的宽度固定为 14px,高度为 30px
在样式「 vhkbvu 」中,通过定义 background_position 指定背景图片的移动方向,比如:图中相当于背景图片向左移动 386px,向上移动 97px
然后,我们查看 svg 图片的源文件
- 字体大小为 14px,颜色值为 #666
- 4 个 text 标签代表 4 行数字
- 4 行数字对应的 y 轴坐标值固定为 38、83、120、164
因此,我们能得出一个结论,即:d 标签通过 SVG 背景图片,结合一定的偏移量 + 宽高度,从 SVG 图片中固定一个数字展示在页面上
2、实现
首先,我们获取 SVG 图片、CSS 样式文件的源文件
PS:为了方便后面样式的解析,需要将 CSS 样式源码的换行符和空格全部删除掉
import requests
...
def start(self):
# 1、svg源文件
self.svg_source = requests.get("http://www.porters.vip/confusion/font/food.svg").text
# 2、css文件源文件
# 注意:将换行符、空格清除,方便下一步匹配
self.css_source = requests.get("http://www.porters.vip/confusion/css/food.css").text.replace('\n', '').replace(
' ', '')
...
然后,使用正则表达式解析 SVG 图片源文件,提取所有的 y 轴坐标及行数据列表
import re
...
def parse_svg(self):
"""
解析svg文件
:return:
"""
# 获取y轴值及数据
text_content = re.findall('y="(.*?)">(.*?)</text>', self.svg_source, re.S)
# print(text_content)
# y轴值及行数据
return [item[0] for item in text_content], [item[1] for item in text_content]
...
数据格式如下:
# y轴值列表及行数据值如下
# y轴值列表
['38', '83', '120', '164']
# 对应的行数据值列表
['154669136497975167479825383996313925720573', '560862462805204755437571121437458524985017', '671260781104096663000892328440489239185923', '684431081139502796807382']
最后,遍历要解析的样式名,从 SVG 图片中提取对应的数值
需要注意的是,SVG 图片的文字大小固定为 14px,根据行内容提取值时,只需要利用偏移量获取索引值,最后拿到实际指向的数值即可
...
def get_number_from_svg(self, style_name, y_nums, datas):
"""
获取svg中实际的数据
:param y_nums: y轴坐标列表
:param datas: 数据值列表
:return:
"""
# 使用正则表达式从样式内容中匹配x轴、y轴的偏移量(去除了换行符、空格),匹配正数字,\d+
deviation_x, deviation_y = \
re.compile('.%s{background:-(\d+)px-(\d+)px;}' % style_name).findall(self.css_source)[0]
# 获取svg图片中的y轴坐标
# 注意:这里取首次大于偏移量的y轴坐标,作为截取数据真实的y轴坐标
position_y = [y for y in y_nums if int(deviation_y) <= int(y)][0]
# 获取y轴坐标的索引值,然后获取该行的内容
content = datas[y_nums.index(position_y)]
# svg字体大小为14px,因此可以通过【偏移量/字体大小】拿到x轴的索引
x_index = int(int(deviation_x) / 14)
# 通过内容及索引,获取实际截取的数字
return str(content[x_index])
...
以上就完成了对页面中「 电话号码 」的解析,我们只需要提取页面元素中的样式名,获取映射的数值进行替换即可
扩展:
CSS 偏移反爬:https://blog.csdn.net/JavaShark/article/details/125135513
字体加密:https://blog.csdn.net/weixin_42277380/article/details/99288286?spm=1001.2014.3001.5501