当前位置: 首页 > 科技观察

教你反转JS字体,获取招聘网站的信息

时间:2023-03-21 19:13:23 科技观察

今天的网站已经加密:aHR0cHM6Ly93d3cuc2hpeGlzZW5nLmNvbS8=为了安全起见,我们对网址进行了base64编码,您可以通过base64解码网址。字体反爬字体反爬:一种常见的反爬技术,是由网页和前端字体文件共同完成的一种反爬策略。最早的字体反爬技术是58同城、汽车之家等,现在很多主流网站或APP也采用了字体反爬技术,为自己的网站或APP增加了反爬措施。字体反爬的原理:将页面中的部分数据替换为自定义字体,如果不使用正确的解码方式,将无法获取到正确的数据内容。通过@font-face在HTML中使用自定义字体,如下图:语法格式为:@font-face{font-family:"name";src:url('字体文件链接');url('字体文件链接')format('文件类型')}字体文件一般有ttf类型,eot类型,woff类型,woff类型的文件应用比较广泛,所以大家一般都会遇到woff类型的文件。以woff类型的文件为例,它的内容是什么,用什么编码方式使数据与编码一一对应?我们以某招聘网站的字体文件为例,进入百度字体编译器,打开字体文件,如下图:随机打开一个字体,如下图:可以发现字体6是放在一个平面坐标中,根据平面坐标的各个点得到字体6的编码。在这里,我将不解释如何获取字体6编码。怎么解决字体反爬?首先,映射关系可以看作是一个字典。常用的方法大致有两种:第一种:手动提取一组编码与字符的对应关系,并以字典的形式显示出来。代码如下:replace_dict={'0xf7ce':'1','0xf324':'2','0xf23e':'3',......'0xfe43':'n',}forkeyinreplace_dict:data=data.replace(key,replace_dict[key])data=data.replace(key,replace_dict[key])先将字体对应的字典及其对应的code一一定义,然后将data一一替换一个通过for循环。注:该方法主要适用于字体图较少的数据。第二种方法:先下载网站的字体文件,然后把字体文件转成XML文件,在里面找到字体映射关系的代码,通过decode函数解码,然后将解码后的代码组合成字典,然后将数据根据字典的内容一一替换。由于代码比较长,这里就不写示例代码了。该方法的代码将在后面的实战练习中展示。好了,字体反爬就到此为止。接下来我们正式爬取一个招聘网站。实战练习自定义字体文件搜索首先进入一个招聘网站,打开开发者模式,如下图:这里我们看到只有代码中的新词不能正常运行,被代码替换,初步判断是acustomfont使用的是字体文件的字体文件,这个时候就需要找到字体文件,那么在哪里找到字体文件呢,首先打开开发者模式,点击网络选项,如图下图:一般字体文件都放在字体选项卡中,我们发现这里一共有5个入口,那么自定义字体文件的入口是哪一个呢?当我们点击下一页时,自定义字体文件会被执行一次。这个时候我们只需要在网页中点击下一页就可以了,如下图所示:可以看到多了一个以file开头的入口。这时可以初步判断该文件为自定义字体文件。现在我们下载它。下载方法很简单,把文件开头入口的网址复制进去,在网页上打开就可以了。下载后在百度字体编译器中打开,如下图:这时发现打不开。是不是找错了字体文件?网站提示不支持这个。不同的文件类型,那么我们将下载文件的后缀改为.字体映射关系找到了自定义字体文件,那我们应该怎么使用呢?这时候我们先自定义方法get_fontfile()来处理自定义的字体文件,然后通过字典的方式将字体文件中的映射关系分两步传递给显示。字体文件下载和转换;字体映射关系解码。字体文件下载与转换首先,自定义字体文件的更新频率非常高。这时候我们可以实时获取网页的自定义字体文件,防止使用之前的自定义字体文件,导致数据获取不准确。首先观察自定义字体文件的url链接:https://www.xxxxxx.com/interns/iconfonts/file?rand=0.2254193167485603https://www.xxxxxx.com/interns/iconfonts/file?rand=0.4313944100724574https://www.xxxxxx.com/interns/iconfonts/file?rand=0.3615862774301839可以发现只有自定义字体文件url的rand参数发生了变化,是一个小于1的随机十六位浮点数,那么我们只需要构造rand参数就可以了,主要代码如下:defget_fontfile():rand=round(random.uniform(0,1),17)url=f'https://www.xxxxxx.com/interns/iconfonts/文件?rand={rand}'response=requests.get(url,headers=headers).contentwithopen('file.woff','wb')asf:f.write(response)font=TTFont('file.woff')字体.saveXML('file.xml')先通过random.uniform()方法控制随机数的大小,再通过round()方法控制随机数的位数,使得rand的值可以获取到,然后通过.content将URL响应内容转换成二进制写入file.woff文件,通过TTFont()方法获取文件内容,将内容保存为xml文件通过saveXML方法。xml文件内容如下图:字体解码和显示字体。xml文件一共4589行,哪一部分是字体映射关系的代码部分?首先我们回顾一下百度字体编码器的内容,如下图所示:汉字person对应的编码是f0e2,那么我们在字体中查询person的编码。可以根据它们的编码规则得到映射关系,然后通过解码得到对应的数据值,最后以字典的形式显示出来。主要代码如下:withopen('file.xml')asf:xml=f.read()keys=re.findall('',xml)values=re.findall('',xml)foriinrange(len(values)):iflen(values[i])<4:值[i]=('\\u00'+值[i])。encode('utf-8').decode('unicode_escape')else:values[i]=('\\u'+values[i]).encode('utf-8').解码('unicode_escape')word_dict=dict(zip(keys,values))首先读取file.xml文件的内容,找出代码中的code和name值分别设置为key和value,然后对value进行解码通过一个for循环得到我们想要的数据,最后通过zip()方法合并成元组,通过dict()方法转换成字典数据。转换为字典数据,然后开始发送网络请求获取数据,主要代码如下:defget_data(dict,url):response=requests.get(url,headers=headers).text.replace('&#','0')forkeyindict: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')foriindatas: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(),'优势':','.join(i.xpath('./div[2]/div[1]/span/text()').extract()),'福利':','.join(i.xpath('./div[2]/div[2]/span/text()').extract())}saving_data(list(data.values()))首先自定义方法get_data()并接收字体映射关系的字典数据,然后通过for循环将字典内容逐一替换为数据,最后通过xpath()提取出我们想要的数据,最后将数据传入我们自定义的方法saving_data()即可savethedata数据已经获取下来了,接下来就是保存数据了。主要代码如下:defsaving_data(data):db=pymysql.connect(host=host,user=user,password=passwd,port=port,db='recruit')cursor=db.cursor()sql='inserttintorecruit_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()foriinrange(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)