抖音、猫眼网页信息加密分析与应对(2)

文集 移动端网页端爬虫

前言

本来想通过网页端来抓取猫眼电影的一些票房数据,但是发现

9549518-bb09ff07ad60de5a.png
maoyan1.png
9549518-73cb337f66aa670e.png
maoyan2.png

小看了。下面将提供两种应对策略。编码集和图像识别

分析

  • F12 -> network -> font,可以看出.woff文件其实是一个自定义的字符子,它告诉浏览器怎样显示那些奇怪的编码成人类可以看懂的字符,所以我们要做的就是要找出这些&#xe0f8 到 数字的映射关系
9549518-adf8db0e74def32d.png
maoyan3.png
这是我提前准备的, &#x 换成 uni
self.manual_dict = {
            'uniF3C5':  8,
            'uniEDEE':  6,
            'uniF38E':  2,
            'uniE824':  3,
            'uniE829':  5,
            'uniE851':  0,
            'uniEBCF':  1,
            'uniEE5A':  9,
            'uniEFFE':  4,
            'uniF35D':  7,
        }

实践

  • 如何找出映射关系 这里给出mac 中文版上的方法

    1. 下载.woff文件

    2. 修改后缀为.ttf

    3. 双击打开,即可看到

9549518-45a92c4f71a46696.png
ttf1.png
  1. 代码,生成xml文件
from fontTools.ttLib import TTFont
font = TTFont('/Users/apple/test/dolphin/opt/fonts/2c8d9a8f5031f26f4e9fe924263e31ce2076.woff')
font.saveXML('/Users/apple/test/dolphin/opt/fonts/2c8d9a8f5031f26f4e9fe924263e31ce2076.xml')
  1. xml文件的htmx节点的顺序与‘3501694728’顺序一致
9549518-812175a34ab30a4d.png
woff1.png
  • 到此,得到了我们想要的映射关系,以后所有猫眼的网页只要拿到编码&#xef6d就能得到对应的数字了。No,猫眼网页每次返回的woff文件是不同的所以映射关系也不同。怎么办!

  • 看来上面的方法得到的只是一个临时的映射关系,下面分析如何得到永久有效的映射关系

    1. 继续观察生成的xml文件(glyf节点)。正好对应是个数字
9549518-b8b294c26058e68a.png
woff2.png
  1. 每个TTGlyph节点都是xy点,看着这个xy矩阵才是真正决定编码&#xE514“画成”什么图形的关键

    9549518-a3a9d9bfc0d1edd1.png
    woff3.png
  2. 试想,如果我们把上面得到的 “编码到数字”映射关系换成“xy矩阵到数字”的映射关系会怎样!没错,每当我们请求网页的时候拿到woff文件

    woff文件 —> xml文件 —> 通过编码(&#xE514)得到xy的矩阵 —> 查找“xy矩阵到数字”映射表得到数字

    ok一切完成。

源码

# -*- coding: utf-8 -*-
import os
from utils.log import logging
from utils.requestshelper import RequestsHelper
from fontTools.ttLib import TTFont
# pip install Pillow
from PIL import Image, ImageEnhance
from pytesseract import *

logger = logging.getLogger(__name__)


class WebFont(object):
    def _init_font_map(self):
        """
        初始化猫眼的字符集模版,只在倒入模块时有构造方法调用一次
        """
        font_file = self.__save_font_file(self.url)
        font = TTFont(font_file)
        glyph_set = font.getGlyphSet()
        glyph_dict = glyph_set._glyphs.glyphs
        for k, v in self.manual_dict.items():
            self.font_dict[glyph_dict[k].data] = v

    def __save_font_file(self, url):
        filename = url.split('/')[-1]
        font_dir = "%s/%s" % (os.path.dirname(__file__), '../opt/fonts')
        # 判断文件是否存在
        if not os.path.exists("%s/%s" % (font_dir, filename)):
            if not os.path.exists(font_dir):
                os.mkdir(font_dir)
            try:
                response = RequestsHelper.get(url)
            except Exception:
                raise Exception()
            with open("%s/%s" % (font_dir, filename), 'wb') as fw:
                fw.write(response.content)
        return "%s/%s" % (font_dir, filename)

    def convert_to_num(self, series, url):
        """
        获取unicode的对应的数字, woff编码文件必须是编码所用的文件
        :param series: int 编码对应的十六进制数
        :param url: woff编码集文件的地址
        :return: int,series对应数字
        """
        font_file = self.__save_font_file(url)
        font = TTFont(font_file)
        cmap = font.getBestCmap()
        num = cmap.get(series)
        glyph_set = font.getGlyphSet()

        return self.font_dict[glyph_set._glyphs.glyphs[num].data]


class MaoYanWebFont(WebFont):
    def __init__(self):
        self.url = 'https://vfile.meituan.net/colorstone/2c8d9a8f5031f26f4e9fe924263e31ce2076.woff'
        self.plat_name = 'maoyan'
        self.font_dict = dict()

        # 手动一次整理出 "编码-数字"映射关系,程序自动获得 "xy矩阵-数字"映射关系
        self.manual_dict = {
            'uniF3C5':  8,
            'uniEDEE':  6,
            'uniF38E':  2,
            'uniE824':  3,
            'uniE829':  5,
            'uniE851':  0,
            'uniEBCF':  1,
            'uniEE5A':  9,
            'uniEFFE':  4,
            'uniF35D':  7,
        }
        self._init_font_map()


class FontManager(object):
    def __init__(self):
        self.fonts = dict()

    def add_font(self, webfont):
        """
        倒入该模块时调用此方法初始化字符集模版
        :param webfont: WebFont
        """
        if webfont.plat_name == 'maoyan':
            self.fonts['maoyan'] = webfont
        elif webfont.plat_name == 'douyin':
            pass
        else:
            raise Exception('平台:%s 暂不支持' % webfont.plat_name)

    def get_font(self, plat_name):
        try:
            return self.fonts[plat_name]
        except KeyError:
            raise Exception('请先调用get_font()来添加平台%s的字符集' % plat_name)

    def convert_to_num_ocr(self, fp):
        """
        获取unicode的对应的数字
        :param fp: 图片文件的路径或文件对象(必须byte方式打开)
        :return: 图片对应的数字, 如果不符合数字格式,则返回图片上的文本
        """
        im = Image.open(fp)
        enhancer = ImageEnhance.Contrast(im)
        image_enhancer = enhancer.enhance(4)
        im_orig = image_enhancer.resize((image_enhancer.size[0]*2, image_enhancer.size[1]*2), Image.BILINEAR)

        text = image_to_string(im_orig)
        try:
            return int(text)
        except ValueError:
            return text





fm = FontManager()
fm.add_font(MaoYanWebFont())
maoyan_font = fm.get_font('maoyan')

#########################
# 通过woff文件
#########################
# 任意一次打开猫眼网页,给定编码对应的十六进制数和编码所用的woff文件,自动分析出数字
logger.info(maoyan_font.convert_to_num(0xf8c3, 'https://vfile.meituan.net/colorstone/7986a5279399aeee3ef19fe37989a00d2088.woff'))

#########################
# 通过 ocr
#########################
fp = open('/Users/apple/test/dolphin/opt/WX20180607-172930@2x.png', 'rb')
print(fm.convert_to_num_ocr(fp))

备注:无论如何都需要手动整理一次woff的映射关系,上面给出了mac上的方面,至于windows,思路:1.根据xml文件得到所有的编码;2. 仿照原网页写个简单的页面,包含所有的编码,就能看到编码对应的数字

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值