声明:本帖子仅是用于学习用途,请勿与用于恶意破坏别人网站,本人不承担法律责任。
前言
简单描述一下这种手段,html源码的数字跟页面展示的数字是不一致的!当时就一脸黑人问号,嗯???
经过分析,当前这种字体反爬机制是:通过获取指定链接的woff字体文件,然后根据html源码的数字
去woff字体文件里面查找真正的数字,讲到底就是一个映射关系/查找字典。如html源码是123,去woff文件里面
查找出来的是:623。好了,看到这里,你一定想说:废话讲那么多干嘛?赶紧上教程啊!!
那先来看一下大致流程呗:

tips: 一开始不知道是怎么下手,只能谷歌搜索字体反爬,一搜果然很多说法,有说woff文件的、有说CSS的、还有说svg曲线啥的, 然后我就去查看Network里面的All,就发现关键字眼woff,就开始猜测可能是属于这种类型的反爬手段,接着开始干活。
混淆前字体:

混淆后的字体:

找了一会,发现.woff2文件和woff文件前后不一样,然后开始着手解决
如需下载woff文件,请点击这里, 提取码: ghnx
但是本地打不开woff字体文件,需要借助的软件是fontcreator,这个你自己去找一下,很多激活成功教程的

但是这好像看不出什么,然后我们接着需要从另外一方面下手,重点来了》将woff文件转换为xml文件
如下:
import os import requests from fontTools.ttLib import TTFont base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) url = "http://xxxxxx.xxx.woff" woff_dir = os.path.join(base_dir, "statics/woffs/") file_name = url.split("/")[-1] xml_name = file_name.replace(file_name.split(".")[-1], "xml") save_woff = os.path.join(woff_dir, file_name) save_xml = os.path.join(woff_dir, xml_name) resp = requests.get(url="xxx") with open(save_woff, "wb") as f: f.write(resp.content) f.close() font = TTFont(save_woff) font.saveXML(save_xml) # 转换为xml文件
①的映射关系,在这里我定义为before_code_id
②的映射关系,在这里我定义为affter_code_id,结果如下:
before_code_id = {
"0": "cid00019",
"1": "cid00020",
"2": "cid00017",
"3": "cid00021",
"4": "cid00022",
"5": "cid00024",
"6": "cid00026",
"7": "cid00025",
"8": "cid00023",
"9": "cid00018"
}
affter_code_id = {
"cid00017": 2,
"cid00018": 3,
"cid00019": 4,
"cid00020": 5,
"cid00021": 6,
"cid00022": 7,
"cid00023": 8,
"cid00024": 9,
"cid00025": 10,
"cid00026": 11
}
然后从html源码到before_code_id, affter_code_id应用起来就是如下:
前端数字—中间人code—最终的数字,即:
"0"——"cid00019"——4
"1"——"cid00020"——5
"2"——"cid00017"——2
"3"——"cid00021"——6
"4"——"cid00022"——7
"5"——"cid00024"——9
"6"——"cid00026"——11
"7"——"cid00025"——10
"8"——"cid00023"——8
"9"——"cid00018——3
我们再简化一步,直接从html源码数字到最终的数字映射为如下(即直接省去中间的cidxxxxx这串):
"0"——4
"1"——5
"2"——2
"3"——6
"4"——7
"5"——9
"6"——11
"7"——10
"8"——8
"9"——3
但是你们发现这映射后的数字很奇怪吗,比如"6"、"7"映射之后分别为11和10,
但是在我们的正常逻辑之中不对呀,要不我们再列一下html源码跟前端的肉眼看到的数字的映射关系呗:
"0"——2
"1"——3
"2"——0
"3"——4
"4"——5
"5"——7
"6"——9
"7"——8
"8"——6
"9"——1
哇,这列出来之后不是很相似吗,跟前面的结果,要不我再放在一起给你们好对比一下呗:
xml提取的映射 html源码跟网页展示的,提取的映射
"0"——4 "0"——2
"1"——5 "1"——3
"2"——2 "2"——0
"3"——6 "3"——4
"4"——7 "4"——5
"5"——9 "5"——7
"6"——11 "6"——9
"7"——10 "7"——8
"8"——8 "8"——6
"9"——3 "9"——1
到此,我们发现从xml提取的映射跟html源码跟网页展示的提取的映射数值都是相差2,所以我们大胆猜测:网页上看到的数值是可以从xml提取的映射关系里面每个数字减去2所得的,即:
"0"——4-2=2 "1"——5--2=3 "2"——2-2=0 "3"——6-2=4 "4"——7-2=5 "5"——9-2=7 "6"——11-2=9 "7"——10-2=8 "8"——8-2=6 "9"——3-2=1
所以这就是激活成功教程了嘛,到此,这个教程总可以理解吧,写得辣么辛苦、改的辣么辛苦,赶快评论点赞收藏一套走起来
好了,别嗨了,实操才是王道,下面来看一下核心代码,如下:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time : 2019/8/19 13:08
# @Author : qizai
# @File : crawl_woff.py
# @Software: PyCharm
# 先安装:pip3 install fontTools
import os
import requests
from fake_useragent import UserAgent
from fontTools.ttLib import TTFont # 对字体文件进行格式转换
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ua = UserAgent()
header = {
"user-agent": ua.chrome,
}
def parse_woff(url=""):
"""这里是下载字体并且解析对应的值"""
global cookie
global header
woff_dir = os.path.join(base_dir, "statics/woffs/")
file_name = url.split("/")[-1]
xml_name = file_name.replace(file_name.split(".")[-1], "xml")
save_woff = os.path.join(woff_dir, file_name)
save_xml = os.path.join(woff_dir, xml_name)
if os.path.exists(save_woff): # 存在本地的话直接提取本地的文件去解析即可省去下载,避免浪费资源
font = TTFont(save_woff)
else:
resp = requests.get(url=url, cookies=cookie, headers=header)
with open(save_woff, "wb") as f:
f.write(resp.content)
f.close()
font = TTFont(save_woff)
font.saveXML(save_xml) # 转换为xml文件
cmap = font.getBestCmap() # 这个是xml源码里面的【数值-中间人code】映射,数值还不一定是html源码里面的数值,而是每位数经过加上一定的数值之后的
tmp = { # 这个是对应的才是我们需要的值,或者你也可以在每次获取的时候,将这个值对应减去48即可,就可以省去这这个映射
48: 0, # html源码里面的0对应xml源码里面的48
49: 1, # html源码里面的1对应xml源码里面的49
50: 2, # html源码里面的2对应xml源码里面的50
51: 3, # html源码里面的3对应xml源码里面的51
52: 4, # html源码里面的4对应xml源码里面的52
53: 5, # html源码里面的5对应xml源码里面的53
54: 6, # html源码里面的6对应xml源码里面的54
55: 7, # html源码里面的7对应xml源码里面的55
56: 8, # html源码里面的8对应xml源码里面的56
57: 9, # html源码里面的9对应xml源码里面的57
} # 注意:个人猜测以上这个tmp字典,xml源码的数字跟html源码数字的映射关系可能会定期改变的
before_code_id = {} # 转换之后before_code_id为:1:cid00019 key就是html源码数字,value就是用来查询的中间人code
for k, v in cmap.items():
if k not in set(range(48, 58)):
continue
before_code_id[tmp.get(k)] = v # 这一步其实是将49:cid00019的映射格式转换为好理解的1:cid00019映射关系
code_id_list = font.getGlyphOrder()[2:] # 这个返回的值有11个,但是我这里只是取了第三个到最后一个,是用来取计算前端看到的真正的数值
affter_code_id = {k:v for k,v in zip(code_id_list, range(2, 12))} # 将每一个按照顺序映射为cid00562:2这种
return before_code_id, affter_code_id
if __name__ == '__main__':
"""使用如下"""
before_code_id, affter_code_id = parse_woff(url="xxxx")
# html源码数字:假设为0
html_number = 0
tmp_code = before_code_id.get(html_number) # 先匹配中间人code
real_number = affter_code_id.get(tmp_code) - 2 # 再提取中间人code对应的真正的数字,记得要减去2,因为本来是每位数字已经多了2
print("当前html源码数字html_number:{} 真正的数字为real_number:{}".format(html_number, real_number))
当前的woff字体反爬已经激活成功教程了,如果有不妥的地方请指出,大家一起学习。
至此本文教程写完了,希望能够帮助到各位在爬虫路上的小伙伴们,觉得不错点个赞呗
感谢认真读完这篇教程的您
先别走呗,这里有可能有你需要的文章:
打赏喝咖啡
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/201769.html原文链接:https://javaforall.net
