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

文集 移动端网页端爬虫

前言

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

maoyan1.png
maoyan2.png

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

分析

  • F12 -> network -> font,可以看出.woff文件其实是一个自定义的字符子,它告诉浏览器怎样显示那些奇怪的编码成人类可以看懂的字符,所以我们要做的就是要找出这些&#xe0f8 到 数字的映射关系
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. 双击打开,即可看到

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’顺序一致
woff1.png
  • 到此,得到了我们想要的映射关系,以后所有猫眼的网页只要拿到编码&#xef6d就能得到对应的数字了。No,猫眼网页每次返回的woff文件是不同的所以映射关系也不同。怎么办!

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

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

    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. 仿照原网页写个简单的页面,包含所有的编码,就能看到编码对应的数字

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容

  • 猫眼电影反爬攻防 文集 移动端网页端爬虫 票房信息爬取示例 按照上文操作一直很顺利,直到文章最后 图1. 编码与实...
    独占潇洒_b556阅读 1,337评论 0 0
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong阅读 22,295评论 1 92
  • 大年初五,俗称“破五”。 如何写出一篇文章 无论如何你必须先打开电脑,找到一个地方可以开始独自打字,至于要开始打什...
    MarioZ阅读 181评论 0 1
  • 今天,坐上广州到北京的高铁,原以为十个小时时间很长,不料此刻经过石家庄站,已经过了八个半小时了。我称此程为临在之旅...
    bef243e45c7f阅读 382评论 1 1
  • 基本步骤就是:1、获取当前绘图上下文2、设置笔触颜色3、设置笔触宽度4、设置填充颜色5、然后就开始绘制了(当然也是...
    brzhang阅读 3,713评论 0 2