乐趣区

关于python:保姆级反爬教学JS逆向实现字体反爬

大家好,我是查理~ 网站的反爬措施有很多,例如:js 反爬、ip 反爬、css 反爬、字体反爬、验证码反爬、滑动点击类验证反爬等等,明天咱们通过爬取某招聘来实战学习字体反爬。

字体反爬

字体反爬 :一种常见的反爬技术,是网页与前端字体文件配合实现的反爬策略,最早应用字体反爬技术的有 58 同城、汽车之家等等,当初很多支流的网站或 APP 也应用字体反爬技术为本身的网站或 APP 减少一种反爬措施。

字体反爬原理 :通过自定义的字体来替换页面中某些数据,当咱们不应用正确的解码形式就无奈获取正确的数据内容。

在 HTML 中通过 @font-face 来应用自定义字体,如下图所示:

其语法格局为:

@font-face{
font-family:"名字";
src:url('字体文件链接');
url('字体文件链接')format('文件类型')
}

字体文件个别是 ttf 类型、eot 类型、woff 类型,woff 类型的文件使用比拟宽泛,所以大家个别碰到的都是 woff 类型的文件。

以 woff 类型文件为例,其内容是怎么的呢,又是以什么编码方式使得数据与代码一一对应的呢?

咱们以某招聘网站的字体文件为例,进入百度字体编译器并关上字体文件,如下图所示:

随机关上一个字体,如下图所示:

能够发现字体 6 放在一个平面坐标外面,依据平面坐标的每个点来得出字体 6 的编码,这里就不解释如何得出字体 6 的编码了。

如何解决字体反爬呢?

首先映射关系能够看作为字典,大抵有两种罕用的办法:

第一种:手动把一组编码和字符的对应关系提取进去并用字典的模式展现,代码如下所示:

replace_dict={
    '0xf7ce':'1',
    '0xf324':'2',
    '0xf23e':'3',
    .......
    '0xfe43':'n',}
for key in replace_dict:
    数据 = 数据.replace(key,replace_dict[key])

首先定义字体与其对应的代码一一对应的字典,再通过 for 循环把数据一一替换。

留神:这种办法次要实用于字体映射少的数据。

第二种:首先下载网站的字体文件,再把字体文件转换为 XML 文件,找到外面的字体映射关系的代码,通过 decode 函数解码,而后将解码的代码组合成一个字典,再依据字典内容将数据一一替换,因为代码比拟长,这里就不写示例代码了,待会在实战演练中会展现这种办法的代码。

好了,字体反爬就简略讲到这里,接下来咱们正式爬取某招聘网站。

实战演练

自定义字体文件查找

首先进入某招聘网并关上开发者模式,如下图所示:

这里咱们看到代码中只有生字不能失常函数,而是用来代码来代替,初步断定为应用了自定义的字体文件,这时就要找到字体文件了,那么字体文件在哪里找呢,首先关上开发者模式,并点击 Network 选项,如下图所示:

个别状况下,字体文件放在 Font 选卡中,咱们发现这里一共有 5 个条目,那么哪个是自定义字体文件的条目呢,当咱们每次点击下一页的时候,自定义字体文件就会执行一次,这时咱们只须要点击网页中的下一页即可,如下图所示:

能够看到多了一个以 file 结尾的条目,这时能够初步断定该文件为自定义字体文件,当初咱们把它下载下来,下载方式很简略,只须要把 file 结尾的条目标 URL 复制并在网页上关上即可,下载下来后在百度字体编译器关上,如下图所示:

这时发现关上不了,是不是找错了字体文件,网站提醒说不反对这种文件类型,那么咱们把下载的文件后缀改为.woff 在关上试试,如下图所示:

这时就胜利关上了。

字体映射关系

找到自定义字体文件了,那么咱们该怎么利用呢?这时咱们先自定义办法 get_fontfile() 来解决自定义字体文件,而后在通过两步来把字体文件中的映射关系通过字典的形式展现进去。

  1. 字体文件下载与转换;
  2. 字体映射关系解码。

字体文件下载与转换

首先自定义字体文件更新频率是很高的,这时咱们能够实时获取网页的自定义字体文件来避免利用了之前的自定义字体文件从而导致获取数据不精确。首先察看自定义字体文件的 url 链接:

https://www.xxxxxx.com/intern…
https://www.xxxxxx.com/intern…
https://www.xxxxxx.com/intern…

能够发现自定义字体文件的 URL 只有 rand 这个参数发生变化,而且是随机的十六位小于 1 的浮点数,那么咱们只须要结构 rand 参数即可,次要代码如下所示:

def get_fontfile():
    rand=round(random.uniform(0,1),17)
    url=f'https://www.xxxxxx.com/interns/iconfonts/file?rand={rand}'
    response=requests.get(url,headers=headers).content
    with open('file.woff','wb')as f:
        f.write(response)
    font = TTFont('file.woff')
    font.saveXML('file.xml')

首先通过 random.uniform() 办法来管制随机数的大小,再通过 round() 办法管制随机数的位数,这样就能够失去 rand 的值,再通过.content 把 URL 响应内容转换为二进制并写入 file.woff 文件中,在通过 TTFont() 办法获取文件内容,通过 saveXML 办法把内容保留为 xml 文件。xml 文件内容如下图所示:

字体解码及展示

该字体.xml 文件一共有 4589 行那么多,哪个局部才是字体映射关系的代码局部呢?

首先咱们看回在百度字体编码器的内容,如下图所示:

汉字人对应的代码为 f0e2,那么咱们就在字体.xml 文件中查问人的代码,如下图所示:

能够发现一共有 4 个后果,但仔细观察每个后果都雷同,这时咱们能够依据它们代码法则来获取映射关系,再通过解码来获取对应的数据值,最初以字典的模式展现,次要代码如下所示:

def get_fontfile():
    rand=round(random.uniform(0,1),17)
    url=f'https://www.xxxxxx.com/interns/iconfonts/file?rand={rand}'
    response=requests.get(url,headers=headers).content
    with open('file.woff','wb')as f:
        f.write(response)
    font = TTFont('file.woff')
    font.saveXML('file.xml')

首先读取 file.xml 文件内容,找出把代码中的 code、name 的值并别离设置为 keys 键,values 值,再通过 for 循环把 values 的值解码为咱们想要的数据,最初通过 zip() 办法合并为一个元组并通过 dict() 办法转换为字典数据,运行后果如图所示:

获取招聘数据

在上一步中,咱们胜利把字体映射关系转换为字典数据了,接下来开始收回网络申请来获取数据,次要代码如下所示:

def get_data(dict,url):
    response=requests.get(url,headers=headers).text.replace('&#','0')
    for key in dict:
        response=response.replace(key,dict[key])
    XPATH=parsel.Selector(response)
    datas=XPATH.xpath('//*[@id="__layout"]/div/div[2]/div[2]/div[1]/div[1]/div[1]/div')
    for i in datas:
        data={'workname':i.xpath('./div[1]/div[1]/p[1]/a/text()').extract_first(),
            'link':i.xpath('./div[1]/div[1]/p[1]/a/@href').extract_first(),
            'salary':i.xpath('./div[1]/div[1]/p[1]/span/text()').extract_first(),
            'place':i.xpath('./div[1]/div[1]/p[2]/span[1]/text()').extract_first(),
            'work_time':i.xpath('./div[1]/div[1]/p[2]/span[3]/text()').extract_first()+i.xpath('./div[1]/div[1]/p[2]/span[5]/text()').extract_first(),
            'company_name':i.xpath('./div[1]/div[2]/p[1]/a/text()').extract_first(),
            'Field_scale':i.xpath('./div[1]/div[2]/p[2]/span[1]/text()').extract_first()+i.xpath('./div[1]/div[2]/p[2]/span[3]/text()').extract_first(),
            'advantage': ','.join(i.xpath('./div[2]/div[1]/span/text()').extract()),
            'welfare':','.join(i.xpath('./div[2]/div[2]/span/text()').extract())
        }
        saving_data(list(data.values()))

首先自定义办法 get_data() 并接管字体映射关系的字典数据,再通过 for 循环将字典内容与数据一一替换,最初通过 xpath() 来提取咱们想要的数据,最初把数据传入咱们自定义办法 saving_data() 中。

保留数据

数据曾经获取下来了,接下来将保留数据,次要代码如下所示:

def saving_data(data):
    db = pymysql.connect(host=host, user=user, password=passwd, port=port, db='recruit')
    cursor = db.cursor()
    sql = 'insert into recruit_data(work_name, link, salary, place, work_time,company_name,Field_scale,advantage,welfare) values(%s,%s,%s,%s,%s,%s,%s,%s,%s)'
    try:
        cursor.execute(sql,data)
        db.commit()
    except:
        db.rollback()
    db.close()

启动程序

好了,程序曾经写得差不多了,接下来将编写代码运行程序,次要代码如下所示:

if __name__ == '__main__':
    create_db()
    get_fontfile()
    for i in range(1,3):
        url=f'https://www.xxxxxx.com/interns?page={i}&type=intern&salary=-0&city=%E5%85%A8%E5%9B%BD'
        get_data(get_dict(),url)

后果展现

好了,学习字体反爬并爬取某招聘就讲到这里了!!!
如果文章对您有帮忙,点赞 + 关注,您的反对是我最大的能源

退出移动版