严重声明:本文仅用于学习交流,不得用于商业用途,同时希望大家遵循robots协议,维护网络和谐。
本猿最近在逛一些网站的时间。在打开浏览器的f12查看人家前端代码咋写的时候,经常会发现就是页面上显示的内容和源码里面的不一样,然后自己请求一遍也还是不一样,奇怪,猿,妙不可言?本着猿精神,上网查了下,这种属于字体反爬策略。应用的还是不少的,所以,在这里将在下对字体反爬的见解写一下。
- 先观察人人车网站: 人人车
- 打开一个详情页,看页面标签,直接年款啥的直接都不一样了,查看n多网页后发现,变化的都是数字:
- 查看css样式的时候发现,应用了个字体样式,同时名称直接关联:
- 查看服务器给我们发的包,直接发现一个跟上面标签属性名,以及css样式选择器一样名称的包,利用谷歌浏览器的规范输出,直接查看,发现最下面的一行数字顺序是不太对的,找规律:
- 发现这样对应就是本应该在6的位置上他换上了8,7的位置换成了6,8换成了7:
5.我们再看首页的源码和展示给我们的标题,利用上面的规则一看,猿来如此:
- 打开一个详情页,看页面标签,直接年款啥的直接都不一样了,查看n多网页后发现,变化的都是数字:
- 那么,作为一名pythonic,怎么会被这些迷惑操作遮住我们睿智的双眼,下面是本猿利用python代码实现的字体复原(悲惨的利用三天时间,学了爬虫,再加上查资料,终于有了点眉目),代码贴在下面,bug勿喷,安心食用:
# !/usr/bin/python3
# -*- coding: utf-8 -*-
"""
# @Time : 2020/4/25 13:32
# @Author : hupoc
# @File : second_hand_car_renrenche.py
# @Desc :
"""
import io
import os
import requests
from fontTools.ttLib import TTFont
from scrapy import Selector
def font_transfor(translate_text, html, url):
"""
网页上使用字体文件的文本转换
:param translate_text: 需要转换的文本
:param html: 经过scrapy.Selector()转换后的响应对象
:param url: 请求的url
:return: 转换后的文本
"""
# 获取字体名所在标签文本
font_interface_str = html.xpath("//div[@class='title']/h1/@class").extract_first()
if font_interface_str and 'title-name' in font_interface_str:
# 提取字体文件名称
font_interface_name = font_interface_str.replace('title-name', '').strip()
else:
# 如果没有title-name表示没有应用字体文件
print('没有提取到字体名所在标签文本,url:{0}'.format(html.url))
return
file_name = url.rsplit("/", 1)[-1] + '_' + font_interface_str
# 拼接字体文件 在本地保存路径
file_ttf = '/tmp/renrenche_font/{0}.woff'.format(file_name)
if not os.path.exists(os.path.dirname(file_ttf)):
os.makedirs(os.path.dirname(file_ttf))
# 下载字体文件,保存到本地路径
url = 'https://misc.rrcimg.com/ttf/{0}.woff'.format(font_interface_name)
resp = requests.get(url)
if not resp:
print('请求字体文件失败')
try:
font = TTFont(io.BytesIO(resp.content))
except Exception as e:
return
font_obj = font['cmap']
font_tables = font['cmap'].tables
uni_list = font['cmap'].tables[0].ttFont.getGlyphOrder()
# 生成转换规则
base_num_list = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
base_eng_list = {'zero': '0', 'one': '1', 'two': '2', 'three': '3', 'four': '4', 'five': '5',
'six': '6', 'seven': '7', 'eight': '8', 'nine': '9'}
mapping_list = [base_eng_list[_] for _ in uni_list[1:]]
font_dict = dict(zip(mapping_list, base_num_list))
# 对需要转换的文本进行转换
transfor_str = [_ if not _.isdigit() else font_dict[_] for _ in translate_text]
# 关闭字体文件
font.close()
# 返回正确的文本
return ''.join(transfor_str)
def get_renrenche_detail():
# 请求页面获取响应
detail_url = 'https://www.renrenche.com/bj/car/c2d4fdd2902df36b'
response = requests.get(detail_url)
if not response or response.status_code != 200:
print('人人车详情页请求失败')
return
# 将响应文本转为lxml对象,为了方便,使用的是scrapy.Selector()
html = Selector(text=response.text)
# 获取标题,并规格化标题文本
titles = html.xpath("//div[@class='title']/h1/text()").extract()
title = ' '.join(titles).replace('\n', '').strip()
# 转换标题文字,获取正确文本
true_title = font_transfor(title, html, detail_url)
# 打印文本
print(true_title)
if __name__ == '__main__':
# 请求人人车的二手车详情页
get_renrenche_detail()