字体文件的后缀名大多是woff、tff两种
有些网站在前端显示的是正确的文字,我们在获取网页源代码的时候,发现源代码中有些字是乱码
以猫眼验证中心为例:
可以看到显示这样,这种就是字体进行了加密
当前文字运用的是这种字体,我们在全局搜索,可以找到定义该字体的CSS文件
在这里进行定义的
我们可以在抓包中看到这个字体文件发送的请求,直接双击进行下载就可以了
每次请求的时候会有一个后缀为woff的文件,这个就是乱码字体的文件,把文件下载下来
这种woff文件,下载完成之后是打不开的,我们需要用专门的软件打开,软件地址:Download font software进行下载就可以了
woff文件打开之后是这样的:
红色方框对应的是字体的Unicode,绿色方框中的内容是字体,Unicode和字体的搭配是不固定的,每次请求woff文件都会改变,所以不能用固定的方法得到它们的对应关系
我们可以将woff文件转化成xml文件,便于找到Unicode和字体之间的关联,我们需要使用字体处理的库fontTools,直接在控制台输入pip install fontTools下载模块
会生成一个这样的文件
在文件里找到
name里面的是字体的Unicode,x、y表示字体通过哪些坐标绘画成的,on的内容表示画的黑线还是白线
理论上来说我们可以通过字体的绘画轨迹确定规律,从而制定一套模板,把woff文件里字体的Unicode在模板里寻找,找到对应的字体,但是绘画的时候点的轨迹是会有差别的,这种方法并不可行,作为爬虫,我们要学会投机,如果硬刚的话,只能使用机器学习进行训练
投机的方法就是确定一个字体对应的点的黑线、白线,也就是on的值,用一个woff文件制定一个模板,当使用其它的woff文件时,用模板进行比对,找到Unicode对应的正确字体,这样不管怎么打乱顺序,都能找到正确的搭配,这种方法只能适用于加密字体种类少的(都是数字0~9)
代码的主要思路是,先下载一个woff文件,转化成xml,然后找到
代码实现:
import requests
import re # 正则表达式
from fontTools.ttLib import TTFont # 字体处理相关库
from urllib.parse import urljoin # 进行url拼接
from lxml import etree # xpath
# 获取一个woff文件,制定一个字体模板
def get_same():
font1 = TTFont("2a70c44b.woff")
# 处理成xml,在xml中找规律
font1.saveXML("font1.xml")
dic = {
'0':'uniEBA2',
'1':'uniED8F',
'2':'uniE886',
'3':'uniE583',
'4':'uniF16B',
'5':'uniE83D',
'6':'uniF23F',
'7':'uniE132',
'8':'uniF05A',
'9':'uniEC4B',
}
for k,v in dic.items():
on = font1['glyf'][v].flags.replace(b'\x01', b'')
dic[k] = on
return dic
# 获取网页源代码
def get_page(url):
headers = {
"User-Agent":'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
'Referer':'https://www.maoyan.com/board?',
"Cookie":'__mta=20289031.1691468350838.1691484912827.1691484918225.13; uuid_n_v=v1; uuid=BA861EB035A211EE83DDF5FAAAD1D78DA74E30FA828944D6A5ED149C4737DB76; _csrf=5477ed82cf6b809219afe73fd94994eb65baee610e144ea243816b1496754f0e; _lxsdk_cuid=189d35ec51dc8-03da99cc5a2a7-26031e51-1fa400-189d35ec51dc8; _lxsdk=BA861EB035A211EE83DDF5FAAAD1D78DA74E30FA828944D6A5ED149C4737DB76; Hm_lvt_703e94591e87be68cc8da0da7cbd0be2=1691468351; __mta=20289031.1691468350838.1691468416974.1691475725237.4; Hm_lpvt_703e94591e87be68cc8da0da7cbd0be2=1691484918; _lxsdk_s=189d45b7aec-868-d2c-c90%7C%7C5',
}
resp = requests.get(url=url, headers=headers)
resp.encoding = 'utf-8'
return resp.text
# 下载当前网页使用的woff字体文件
def get_font(page, url):
obj = re.compile(r'@font-face.*?,url\("(?P.*?)"\);', re.S)
font_url = obj.search(page, re.S).group('font_url')
font_url = urljoin(url, font_url)
name = font_url.split('/')[-1]
font_resp = requests.get(url=font_url)
with open(name, mode='wb') as f:
f.write(font_resp.content)
return name
# 将当前woff文件的的Unicode通过模板找到正确的字体,返回一个Unicode和正确的字体相对应的字典
def get_data(name, dic):
font = TTFont(name)
orders = font.getGlyphOrder()[2:] # 获取woff字体文件的Unicode列表
data_dic = {}
for unicode in orders:
for k,v in dic.items():
on = font['glyf'][unicode].flags.replace(b'\x01', b'')
if on == v:
data_dic[unicode] = k
break
if len(data_dic) == 9: # 有概率会缺少几个字体,缺少一个还可以补上,超过一个的话进行循环重新进行请求,得到另一个woff文件
for unicode in orders:
if unicode not in data_dic:
result = list(set(dic.keys()) - set(data_dic.values()))[0]
data_dic[unicode] = result
break
return data_dic
if __name__ == '__main__':
for i in range(3):
url = 'https://www.maoyan.com/board/6?timeStamp=1691475822817&channelId=40011&index=8&signKey=d32ebd787d8d3cdd9ba1e6651bbdee42&sVersion=1&webdriver=false'
dic = get_same()
page_text = get_page(url)
name = get_font(page_text,url)
result = get_data(name, dic)
if len(result) != 10:
print("重新请求!!!")
continue
page_text = page_text.replace('', 'uni')
for k,v in result.items():
k = k.lower()
page_text = page_text.replace(k+';', v)
tree = etree.HTML(page_text)
dd_list = tree.xpath("//dl[@class='board-wrapper']/dd")
for dd in dd_list:
name = dd.xpath("./div/div/div[1]/p[1]/a/text()")[0]
num1 = dd.xpath("./div/div/div[2]/p[1]/span/span/text()")[0]
num2 = dd.xpath("./div/div/div[2]/p[2]/span/span/text()")[0]
print(f"=======================\n{name}\n本月新增想看:{num1}人\n总想看:{num2}人")
break