起因:
前天本来接的一个小单子,一开始客户上来问抖音粉丝能获取吗?我寻思这玩意看起来是字体加密,但是我并不知道这个字体文件是多久更新一次,要是像猫眼字体反爬那样,刷新一下就变,那就太孤儿了,所以就磨蹭了一会儿看看是不是会更新(好吧我承认我之前没搞过抖音字体加密所以还没分析过),结果,人客户走了,走了,了。。好吧,索性后面就来分析一波,发现他这个抖音字体加密字体文件是不会变的,那就好整了阿,分分钟的事。
分析:
字体文件:
第一步当然是确定加密字体在哪里,我们可以打开开发者工具,按F12,进入泰克模式(进入开发者工具)
上图中的框框里就是我们想要的粉丝数量,But,他是一个一个小正方形,在源代码中显示的是JavaScript中的unicode编码,我们获取不到我们想要的粉丝数量。其实这是因为他用了自己的一套字体加密规则,所以我们只要找到这套规则,就可以获取到我们的数据了。
获取字体文件:
从这个我们就可以进入到他的加载字体的地方,我们进去看到如下:
而我们要的字体文件就在这个地方,一般的字体文件后缀都是ttf
、woff
我们找woff
这个文件的,并且在浏览器中可以直接下载他,然后接下来就数撸代码的时候了。
这里我就不再介绍如何去获取页面的信息了,本文主要是分析一下字体加密。
获取字体映射规则:
这里我们介绍fonttool
这个工具,有了这个库我们就可以在python中操作字体文件了。
安装:
pip install fontTools
使用:
https://blog.csdn.net/weixin_43411585/article/details/103484643
分析映射规则:
我们在保存的字体文件中找到这个后缀会xml的文件,打开并且找到cmap(cmap 是字符与字体字形对应的表)
我们看到了一一对应的关系,而这个 num_ 这些有时什么玩意呢?其实这些就算抖音的字体映射了,我们可以利用一些工具获取到他的对应规则
是不是很简单!当然这种是傻瓜式的操作,后期我会更新一个方法就算他的字体每天变一次都是没有关系的。工具在上面的使用中有介绍,并且有安装链接,我就不再赘述了
代码部分:
# -*- coding: utf-8 -*-
# @Time : 2020/3/27 23:43
# @Author : Key-lei
# @File : 抖音粉丝2020.3.27.py
import requests
from fontTools.ttLib import TTFont
from parsel import Selector
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.130 Safari/537.36'
}
# 分析得到得字体文件
url = 'https://s3.pstatp.com/ies/resource/falcon/douyin_falcon/static/font/iconfont_9eb9a50.woff'
# 这些映射关系是再 当前文件夹下xml中找到得
real_2_map = {
'x': '',
'num_': '1', 'num_1': '0', 'num_2': '3', 'num_3': '2', 'num_4': '4', 'num_5': '5', 'num_6': '6', 'num_7': '9',
'num_8': '7',
'num_9': '8'
}
two_2_sixteenn = {
"": "num_",
"": "num_1",
"": "num_2",
"": "num_3",
"": "num_4",
"": "num_5",
"": "num_6",
"": "num_7",
"": "num_8",
"": "num_9",
"": "num_4",
"": "num_1",
"": "num_",
"": "num_5",
"": "num_3",
"": "num_2",
"": "num_6",
"": "num_8",
"": "num_9",
"": "num_7",
"": "num_1",
"": "num_3",
"": "num_",
"": "num_4",
"": "num_2",
"": "num_5",
"": "num_8",
"": "num_9",
"": "num_7",
"": "num_6",
}
# 拿到字体文件
def get_font(url):
response_woff = requests.get(url)
filename = url.split('/')[-1]
with open(filename, "wb") as f:
f.write(response_woff.content)
return filename
def get_map_url(font_name):
# 把字体文件读取为python能理解的对象
base_font = TTFont(font_name)
base_font.saveXML('font.xml')
# 获取映射规则
for key in two_2_sixteenn.keys():
two_2_sixteenn[key] = real_2_map[two_2_sixteenn[key]]
return two_2_sixteenn
# 格式化输出
def format_str(character):
character = [x.replace(" ", '') for x in character]
character = "".join(character)
character = character.replace(";", '')
return character
if __name__ == '__main__':
font_name = get_font(url)
two_2_sixteenn = get_map_url(font_name)
# 得到请求页面
response = requests.get(
'https://v.douyin.com/77hwgS/',
headers=headers)
with open('替换之前的.html', mode='w', encoding='utf-8') as f:
f.write(response.text)
new_html = response.text
# 替换html中得字体
for key, value in two_2_sixteenn.items():
new_html = new_html.replace(str(key).lower(), value)
with open('替换之后的.html', mode='w', encoding='utf-8') as f:
f.write(new_html)
# 提取粉丝数量
selector = Selector(new_html)
fans_num = format_str(selector.xpath('//span[@class="follower block"]//span[@class="num"]//text()').getall())
star_num = format_str(selector.xpath('//span[@class="focus block"]//span[@class="num"]//text()').getall())
like_num = format_str(selector.xpath('//span[@class="liked-num block"]//span[@class="num"]//text()').getall()
print('粉丝的数量:', fans_num)
print('关注的数量:', star_num)
print('赞的数量:', like_num)