字体反爬简介
什么是字体反爬?
就是我们在网页上看到的内容和我们直接解析出来的内容不一样,以大众点评网站为例:
我们在网页上看到的是这样:
这些数字是正常显示的,但是我们点击F12,查看HTML时,却是这样的:
用requests发送请求,获取到网页信息,发现是这样的:
也就是说,这个网页中,有些数字和字是经过了加密处理,像原来那样直接解析的话,得到的是每个字的‘密码’,而不是我们想要的汉字或者数字。
字体反爬大致可以分为两种,一种是不同字体文件中,每个字的Unicode编码不一样,但字形完全一样(即每个字的contour的坐标完全一样),比如大众点评;一种是Unicode编码不一样,并且在不同字体文件中,字形也不完全一样,只是非常相似,比如美团。
对于第一种,我们的处理方式如下:
设置基准字典,以contour为key,以该contour描绘出来的字作为value;当遇到需要解析的字时,我们通过这个字的‘密码’先找到这个字的Unicode,然后用Unicode来找到这个字的contour,最后用这个contour去基准字典中找到对应的汉字或数字。
对于第二种,我们的处理方式如下:
设置基准字典,以contour为key,以该contour描绘出来的字作为value;当遇到需要解析的字时,我们通过这个字的Unicode来找到这个字的contour;然后对比基准字典中的所有contour,看哪个与待解析字的contour最接近(比如可以使用K近邻的方法),然后把最接近的那个contour的value赋给待解析的字。
本文主要介绍第一种。
import requests
import re
from lxml import etree
import fontTools
from fontTools.ttLib import TTFont
import hashlib
import pandas as pd
发送请求,获取网页源码
url ='http://www.dianping.com/dayi/ch10'
headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Mobile Safari/537.36'}
html = requests.get(url,headers=headers)
提取字体信息,并将字体文件下载到本地
一个网页中,可能使用了多种字体文件来对页面中的某些文字进行加密,这些字体文件信息保存在一个css文件中,因此我们首先要拿到该css文件的链接,然后在该链接的网页中查找字体信息
#拿到含有字体的css url
css_url = re.findall('href="(.*s3plus.*)"',html.text)
#拿到字体的url
woff_html = requests.get('http:'+css_url[0],headers=headers)
woff_url = ['http:'+url for url in re.findall(",url\(\"(.*?)\"\);}",woff_html.text)]
#拿到字体的名字
woff_name = re.findall('font-family: "PingFangSC-Regular-(.*?)";',woff_html.text)
#将字体名字和url一一对应起来
woff_name_url = {
}
for i in range(len(woff_name)):
if woff_name[i] != 'reviewTag': #这样处理的原因是本例中用不到reviewTag这个字体文件,并且它是重复的,所以去掉它。
woff_name_url[woff_name[i]] = woff_url[i]
woff_name_url
{'shopNum': 'http://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/3afae22b.woff',
'tagName': 'http://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/276defdb.woff',
'address': 'http://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/33f1a1f2.woff'}
可以看到,一共有3个字体文件,名字分别是shopNum、tagName、address,目前我们都拿到了它们对应的链接。
#下载字体文件到本地
for key in woff_name_url:
name = key
url = woff_name_url[key]
response = requests.get(url,headers=headers)
with open('%s.woff'%name,'wb') as f:
f.write(response.content)
f.close()
fonts = {
}
for key in woff_name_url:
file_name = '{}.woff'.format(key)
fonts[key] = TTFont(file_name)
fonts
{'shopNum': <fontTools.ttLib.ttFont.TTFont at 0x2609b2bdf48>,
'tagName': <fontTools.ttLib.ttFont.TTFont at 0x2609b2bd4c8>,
'address': <fontTools.ttLib.ttFont.TTFont at 0x2609b2c1bc8>}
我们使用在线的字体解析工具看看这些字体文件:
可以看到,每个字都有对应的Unicode编码(红色圈起来的那个)
建立基准字典
# basefont_char是基准字典中所含有的全部字符,是有一定顺序的(按照font.getGlyphOrder()的顺序排的)
# 一共有601个字符,不知道是哪位大神按顺序整理的,十分感谢!
# 这个顺序和上图的顺序是一样的
basefont_char = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '店', '中', '美', '家', '馆', '小', '车', '大', '市', '公', '酒',
'行', '国', '品', '发', '电', '金', '心', '业', '商', '司', '超', '生', '装', '园', '场', '食', '有', '新', '限', '天', '面',
'工', '服', '海', '华', '水', '房', '饰', '城', '乐', '汽', '香', '部', '利', '子', '老', '艺', '花', '专', '东', '肉', '菜',
'学', '福', '饭', '人', '百', '餐', '茶', '务', '通', '味', '所', '山', '区', '门', '药', '银', '农', '龙', '停', '尚', '安',
'广', '鑫', '一', '容', '动', '南', '具', '源', '兴', '鲜', '记', '时', '机', '烤', '文', '康', '信',