爬虫获取::after_爬虫实战4:58字体反爬

10583bdcdb08115a70c01c07cd916681.png

背景介绍

‘字体反爬’是一种比较常见的反爬手段,它是通过页面和前端字体文件配合完成的一种反爬虫措施,常见的网站有58同城,汽车之家,猫眼电影,大众点评,美团等网站。字体反爬从一开始是依靠一个写死的字体文件来构建反爬虫措施的,到现在的动态字体文件。而字体的发爬虫也从一开始的解析字体文件做数据映射到现在依靠KNN来做动态映射。

实战操作

目标网站:https://su.58.com/qztech/

现在想从下方网站获取到真实的数据,通过网页可以发现,在原本的网页上并没有显示出真实的数据。这部分包含有姓名,性别,年龄,工作经验等数据,需要经过转换,才能得到我们想要的数据。

83e1958ead64f86601305df6f474dbc3.png

在网页端搜索font-family可以发现一个.woff的文件,我们把其中的base64的编码部分取出来

e6228ea56e3e1ffe638b515f372e8db7.png

对编码的.woff文件解码之后保存为.tff字体格式的文件

    # 正则替换出字体编码
    pattern=re.compile(r';base64,(.*?))')
    font_data_origin = pattern.findall(con)[0]
    font_data_after_decode = base64.b64decode(font_data_origin)
    new_font_name = "font_base.ttf"
    with open(new_font_name, 'wb') as f:
        f.write(font_data_after_decode)

我们先把.tff文件打开,需要使用两种工具打开:

FontCreator工具:the most popular font editor

在线FontEditor工具:FontEditor

这里我们使用FontCreator,我们把FontCreator下载下来,传来一个我们之前准备好的woff文件看看效果

a069e095db7fd5b0727e705e7de1f9fc.png

我们可以看到woff文件中每个字符都有一个编码对应,.woff实际上就是编码和字符的映射表。可以看到每一个字对应一个编码,通过我们观察可以发现,这个编码的后4位,跟在网页上的编码是一致的,

from fontTools.ttLib import TTFont

font1 = TTFont('zt01.woff')  # 打开本地字体文件01.ttf
uni_list = font1.getGlyphOrder()[2:]  # 前两个不算
print(uni_list)
# 输出信息如下
# ['uniE0D1', 'uniE0EB', 'uniE165', 'uniE39A', 'uniE3CD', 'uniE3DC', 'uniE4E6', 'uniE559', 'uniE5CE', 'uniE6FE', 'uniE74A', 'uniE811', 'uniE822', 'uniE90F', 'uniE925', 'uniE9A9', 'uniE9EB', 'uniEB2C', 'uniEC43', 'uniEC4C', 'uniEC7A', 'uniED1F', 'uniED8C', 'uniEDDB', 'uniEE02', 'uniEE6F', 'uniEF0E', 'uniEF58', 'uniEFD2', 'uniF0EB', 'uniF129', 'uniF1A3', 'uniF31A', 'uniF373', 'uniF3A5', 'uniF403', 'uniF459', 'uniF52A', 'uniF547', 'uniF56E', 'uniF58B', 'uniF5DB', 'uniF625', 'uniF832', 'uniF88E']

我们已经找到每一个字体编码对应的字,但是到这里还没完,我们重新运行一下网站,重新下载一个.woff文件,效果如下所示

485801e4a9055598ed0bddf1737ff78c.png

你会发现,这个字体编码的后4位和之前的完全不同,发生了变化。为了找到两者之间的规律,需要将.woff文件保存为xml文件,静态分析一下

# 将ttf文件转换为XML文件
from fontTools.ttLib import TTFont


font_1 = TTFont('./font_new1.ttf')
font_1.saveXML('font_new1.xml')

e29143dcdab9bf64828e59db32073dc8.png

34bff190a954c73b5ca842b578ad3179.png

可以发现两次运行得到的.woff的张编码不同,观察他们的xml文件看是否能找到相似部分

e28707a5f971cd4befc7dda031da4695.png

bafc43fa41623009db8bc75fc86f7ee7.png

通过观察可以发现pt值存在相似部分, 后一个pt标签中的x,y值 分别减去前一个pt标签的x,y值是一个固定值,

(1001-763,890-890) => (238,0). woff2的计算结果(951-713,840-840)=>(238,0)。

通过这个规律,我们就可以制作映射关系的字典啦。 用计算结果当成key,对应的汉字当作value。

data_map={(238, 0): '张', (-833, 0): 'E', (0, 110): '博', (784, 0): '无', (-46, -550): '验', (-121, 62): '6', (582, 0): '届', (1298, 0): '高', (1588, 0): '男', (299, 0): '陈', (128, -74): '9', (228, 306): '科', (0, -508): '周', (924, 0): '李', (1460, 0): '吴', (0, 1549): 'B', (868, 0): '王', (265, -118): '专', (0, 1026): 'M', (-74, -366): '刘', (164, 0): '以', (0, 125): '2', (-764, 0): '杨', (-110, -150): '女', (210, 358): '校', (159, -123): '3', (0, 132): '技', (0, 134): '8', (156, 262): '赵', (660, 0): '黄', (0, 144): '经', (825, 367): '大', (0, 1325): '1', (146, 78): '应', (770, 0): '中', (-221, 0): 'A', (746, 0): '士', (0, -1023): '4', (0, 410): '0', (-52, -52): '本', (0, -227): '5', (928, 0): '生', (-244, -426): '7', (1944, 0): '下', (230, 390): '硕'}

完成代码展示为

import requests
import re
import time
from io import BytesIO
import base64
from fontTools.ttLib import TTFont
from lxml import etree


headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
}
data_map={(238, 0): '张', (-833, 0): 'E', (0, 110): '博', (784, 0): '无', (-46, -550): '验', (-121, 62): '6', (582, 0): '届', (1298, 0): '高', (1588, 0): '男', (299, 0): '陈', (128, -74): '9', (228, 306): '科', (0, -508): '周', (924, 0): '李', (1460, 0): '吴', (0, 1549): 'B', (868, 0): '王', (265, -118): '专', (0, 1026): 'M', (-74, -366): '刘', (164, 0): '以', (0, 125): '2', (-764, 0): '杨', (-110, -150): '女', (210, 358): '校', (159, -123): '3', (0, 132): '技', (0, 134): '8', (156, 262): '赵', (660, 0): '黄', (0, 144): '经', (825, 367): '大', (0, 1325): '1', (146, 78): '应', (770, 0): '中', (-221, 0): 'A', (746, 0): '士', (0, -1023): '4', (0, 410): '0', (-52, -52): '本', (0, -227): '5', (928, 0): '生', (-244, -426): '7', (1944, 0): '下', (230, 390): '硕'}


def hex2word(font_map, t):
    temp_list = []
    for i in t:
        if i in font_map:
            temp_list.append(font_map[i])
        else:
            temp_list.append(i)
    return "".join(temp_list)


def get_font_map(content):
    font_map = {}
    result = re.search(r"base64,(.*?))", content, flags=re.S).group(1)
    b = base64.b64decode(result)
    tf = TTFont(BytesIO(b))
    for index, i in enumerate(tf.getGlyphNames()[1:-1]):
        temp = tf["glyf"][i].coordinates
        # x
        x1, y1 = temp[0]
        x2, y2 = temp[1]
        new = (x2-x1, y2-y1)
        key = i.replace("uni", r"u").lower()
        key = key.encode('utf-8').decode('unicode_escape')
        font_map[key] = data_map[new]
    return font_map


if __name__ == '__main__':
    url = "https://su.58.com/qztech/"
    response = requests.get(url, headers=headers)
    font_map = get_font_map(response.text)
    print(font_map)
    html = etree.HTML(response.text)
    names = html.xpath('//span[@class="infocardName fl stonefont resumeName"]/text()')
    print(names)
    for name in names:
        print ('name in page source', name)
        for j in font_map.keys():
                name = name.replace(j, font_map[j])
        print ('name actual', name)

521dbb8828225316b369ab5309e1e8ef.png

随着大数据的时代的到来,数据变得越来越重要,数据可以帮助我们来看清行业的本质,也可以帮助我们更加快速的了解一个行业,关注公众号——DT学说,走进数据的时代

daf58d56c0b1b5ae9fcf93da0a0447f4.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值