本次以天眼查和猫眼电影为例,解析怎样爬取替换的真实数据,感谢 "两个眼" 。。,本文使用python3。
分析
先来个简单点的:天眼查
-
打开天眼查,找到阿里巴巴的页面 https://www.tianyancha.com/company/59837300,可以看到勇哥帅气的照片。。。
(注意:直接打开需要登陆的话通过百度搜索 “天眼查 阿里” 再点击访问,如果是用python请求,headers 带上 "Referer": "https://www.baidu.com/",伪装成百度跳转过来的)
-
查看源代码后发现,注册资本和注册时间是无法直接在源代码中获取正确的值。
这时需要思考原因,应该是 js 修改过或者替换过 字体文件,发现两个值的类都是 ”tyc-num“ ,可以尝试查看一下类的样式。经过搜索,找到样式如下:
@font-face {
font-family: "tyc-num";
src: url("https://static.tianyancha.com/web-require-js/public/fonts/tyc-num-ad584829a0.eot");
/* IE9*/
src: url("https://static.tianyancha.com/web-require-js/public/fonts/tyc-num-ad584829a0.eot#iefix") format("embedded-opentype"), url("https://static.tianyancha.com/web-require-js/public/fonts/tyc-num-832854095c.woff") format("woff"), url("https://static.tianyancha.com/web-require-js/public/fonts/tyc-num-7f971a8be7.ttf") format("truetype"), url("https://static.tianyancha.com/web-require-js/public/fonts/tyc-num-67f91eabd9.svg#tic") format("svg");
/* iOS 4.1- */
}
.tyc-num {
font-family: "tyc-num" !important;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
可以看出,这是用css更改了加载的字体文件,自定义了字体。
-
在开发者工具中找到这个这个文件,看一下做了什么改变。
显然是数字这个地方做了手脚,对比一下正常的字体。
好像比正常的还少了一个 "4",多了一个 "." ,猜想 "4" 对应的就是 "." ,所以这样可以得出数字的对应关系。
- 把网页中的数据进行对应一下,果然没错。
423176999999万美元 ---> 15298.000000万美元
3995-90-38 ---> 2007-03-26
- 之后在提取数据时做一个逻辑判断替换即可得到真实数据了。
猫眼电影
-
打开猫眼电影详情页 http://maoyan.com/films/1198214,查看用户评分和累计票房,发现源码中是乱码。
他们的class都是 "stonefont" ,找到 "stonefont" 的定义。
@font-face {
font-family: stonefont;
src: url('//vfile.meituan.net/colorstone/1881db7c788dfdf9d2d00a926734d0973168.eot');
src: url('//vfile.meituan.net/colorstone/1881db7c788dfdf9d2d00a926734d0973168.eot?#iefix') format('embedded-opentype'),
url('//vfile.meituan.net/colorstone/632958fd02509dc28d915375c3a835e02088.woff') format('woff');
}
.stonefont {
font-family: stonefont;
}
刷新几次,看到每次 url 是变化的,确定是动态生成字体,不要紧,生成哪个下载哪个。
加载一个页面,把这个字体文件下载下来拿去分析,可能是在文件中数字的地方进行了替换。在网上正好有一片是防止爬虫采集的文章: 利用自定义web-font实现数据防采集,看过后恍然大悟,这不正是我们要的吗,嘿嘿,爬虫是防不住的!
用 python 的 fonttools 库提取字体,fonttools 的用法可以网上查找一下
pip3 install fonttools # 安装
- 把 woff 的文件转换成我们熟悉的 xml 格式
from fontTools.ttLib import TTFont # 导包
font = TTFont('./632958fd02509dc28d915375c3a835e02088.woff') # 打开文件
font.saveXML('./6329.xml') # 转换成 xml 文件并保存
-
打开 xml 文件,红框内即为我们要的,略微不同,把 uni 改成 &#x ,后面再加一个分号 。
-
跟源代码中对应验证一下可以得出他们的对应关系如下。
用fonttools 可以直接从文件得到这些值
from fontTools.ttLib import TTFont # 导包
font = TTFont('./632958fd02509dc28d915375c3a835e02088.woff') # 打开文件
gly_list = font.getGlyphOrder() # 获取 GlyphOrder 字段的值
for gly in gly_list[2:]: # 前两个值不是我们要的,切片去掉
print(gly) # 打印
- 最后补充完整代码
思路:前面知道字体库是随机的,可以一些提前把能刷新到的下载到本地,抓取页面时,如果已经在本地,直接使用,不在的话再下载下来。
本地目录:字体存在 fonts 目录下
代码
完整代码
import requests
import re
import os
from fontTools.ttLib import TTFont
class MaoYan(object):
def __init__(self):
self.url = 'http://maoyan.com/films/1198214'
self.headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36"
}
# 发送请求获得响应
def get_html(self, url):
response = requests.get(url, headers=self.headers)
return response.content
# 创建 self.font 属性
def create_font(self, font_file):
# 列出已下载文件
file_list = os.listdir('./fonts')
# 判断是否已下载
if font_file not in file_list:
# 未下载则下载新库
print('不在字体库中, 下载:', font_file)
url = 'http://vfile.meituan.net/colorstone/' + font_file
new_file = self.get_html(url)
with open('./fonts/' + font_file, 'wb') as f:
f.write(new_file)
# 打开字体文件,创建 self.font属性
self.font = TTFont('./fonts/' + font_file)
# 把获取到的数据用字体对应起来,得到真实数据
def modify_data(self, data):
# 获取 GlyphOrder 节点
gly_list = self.font.getGlyphOrder()
# 前两个不是需要的值,截掉
gly_list = gly_list[2:]
# 枚举, number是下标,正好对应真实的数字,gly是乱码
for number, gly in enumerate(gly_list):
# 把 gly 改成网页中的格式
gly = gly.replace('uni', '&#x').lower() + ';'
# 如果 gly 在字符串中,用对应数字替换
if gly in data:
data = data.replace(gly, str(number))
# 返回替换后的字符串
return data
def start_crawl(self):
html = self.get_html(self.url).decode('utf-8')
# 正则匹配字体文件
font_file = re.findall(r'vfile\.meituan\.net\/colorstone\/(\w+\.woff)', html)[0]
self.create_font(font_file)
# 正则匹配星级
star = re.findall(r'<span class="index-left info-num ">\s+<span class="stonefont">(.*?)</span>\s+</span>', html)[0]
star = self.modify_data(star)
# 正则匹配评论的人数
people = ''.join(re.findall(r'''<span class='score-num'><span class="stonefont">(.*?万)</span>(人评分)</span>''', html)[0])
people = self.modify_data(people)
# 正则匹配累计票房
ticket_number = ''.join(re.findall(r'''<span class="stonefont">(.*?)</span><span class="unit">(亿)</span>''', html)[0])
ticket_number = self.modify_data(ticket_number)
print('用户评分: %s 星' % star)
print('评分人数: %s' % people)
print('累计票房: %s' % ticket_number)
if __name__ == '__main__':
maoyan = MaoYan()
maoyan.start_crawl()
效果图
Github地址:猫眼电影字体加载还原
个人博客:反爬虫解析-字体替换(天眼查/猫眼电影)
---- END ----