精华内容
下载资源
问答
  • 起点小说字体加密python TTFont解析流

    千次阅读 2020-09-29 17:28:37
    字体文件对应的编号都不一样,这就说明每一次都会变化,需要每次读取这个文件 查询对应的(字体映射) 这里我们用 python from fontTools.ttLib import TTFont world = TTFont('wwOMhmLd.ttf') # print(world....

    起点小说详情加密 阅读数 点击数

    起点目标网址:

    第一步

    静态请求 发现关键地方 点击数是乱码 类似
    <span class="wlRaebhI">&#100100;&#100104;&#100098;&#100094;&#100102;</span></em><cite>万字</cite><i>
    在这里插入图片描述

    第二步

    找到对应的字体加密的文件 用百度字体打开
    在这里插入图片描述
    这不就是和页面上的乱码一模一样吗, 但是多次请求发现每一次的请求 字体文件对应的编号都不一样,这就说明每一次都会变化,需要每次读取这个文件 查询对应的(字体映射)
    这里我们用 python
    from fontTools.ttLib import TTFont

        world = TTFont('wwOMhmLd.ttf')
        # print(world.getGlyphOrder()) # 字典的value 字体文件有哪些值 对应的值
        ['.notdef', 'period', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine']
    
        # print(world.getBestCmap()) # 获得对应的字符对应的值
        {100181: 'two', 100183: 'zero', 100184: 'three', 100185: 'eight', 100186: 'seven', 100187: 'six', 100188: 'period', 100189: 'four', 100190: 'five', 100191: 'nine', 100192: 'one'}
    
    

    第三步

    走到这一步就简单了,读取静态页面,替换乱码 和上面的键一一对应,这样就知道

            number_dict = {
                '.notdef': "我在找我的派大星",
                'period': '.',
                'zero': '0',
                'one': '1',
                'two': '2',
                'three': '3',
                'four': '4',
                'five': '5',
                'six': '6',
                'seven': '7',
                'eight': '8',
                'nine': '9',
            }
    
    

    PS:

    python 字体处理的包 from fontTools.ttLib import TTFont
    但是 这个是读取文件,修改TTFont 源代码
    自己稍微修改了一下源代码 命名为 XYYTTFont 可以直接读取content流数据,这样就不用读取文件 直接请求链接的流数据,(避免今后多进程 文件名的问题,)

    起点详情字体解密git地址:

    欢迎star

    我知道你很懒,下面是源码,有些包还是得去git 看,目录里还有其他的

    # -*- coding:utf-8 -*-
    # Chance favors the prepared mind.
    # author : pyl owo,
    # time : 2020/9/21
    import json
    import random
    import re
    
    from fake_useragent import UserAgent
    from fontTools.ttLib import TTFont
    from lxml import etree
    import requests
    from my_font_content import XYYTTFont
    
    # 获取代理
    def get_proxy():
        pass
    
    # 统一请求函数
    def unify_requests(method="GET",url="",headers={},proxies={},data={},verify=False,cookies={}):
        if method=="GET":
            response = requests.get(url, headers=headers,proxies=proxies,data=data,cookies=cookies,timeout=5)
            return response
        else:
            response = requests.post(url, headers=headers,proxies=proxies,data=data,verify=verify,cookies=cookies,timeout=5)
            return response
    class SFQingNovel:
        def __init__(self, use_proxy=True):
            self.proxy = get_proxy() if use_proxy else None
            self.headers = {
                'User-Agent': UserAgent().random,
                "Proxy-Tunnel": str(random.randint(1, 10000)),
                'authority': 'book.qidian.com',
                'cache-control': 'max-age=0',
                'upgrade-insecure-requests': '1',
                'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.102 Safari/537.36',
                'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',
                'sec-fetch-site': 'same-site',
                'sec-fetch-mode': 'navigate',
                'sec-fetch-user': '?1',
                'sec-fetch-dest': 'document',
                'referer': 'https://www.qidian.com/',
                'accept-language': 'zh-CN,zh;q=0.9',
                # 'Cookie': 'newstatisticUUID=1600686041_884209914; _csrfToken=nXOEpjuFF7PUkwPJoOkBd7dTo2BV5jSkPu3suGGs'
            }
            # self.novel_url_pre = "https://t.shuqi.com/cover/"
    
        # 获取小说所有详细信息
        def get_novel_info(self, novel_url, **kwargs):
            respose = unify_requests(url=novel_url, headers=self.headers, proxies=self.proxy)
            search_result = self.parse_novel_info(respose, novel_url, **kwargs)
            return search_result
    
        def get_id(self, novel_url, **kwargs):
            return novel_url.split('/')[-1]
    
        def get_info(self, info_response, **kwargs):
            number_dict = {
                '.notdef': "薛忆阳",
                'period': '.',
                'zero': '0',
                'one': '1',
                'two': '2',
                'three': '3',
                'four': '4',
                'five': '5',
                'six': '6',
                'seven': '7',
                'eight': '8',
                'nine': '9',
            }
            response = info_response
    
            # 拿到下载字体的网址
            # @font-face.*?src: url.*?src: url(.*?) format('woff'),
            content = re.search(re.compile(r"@font-face.*?src: url.*?src: url(.*?)format.*?,", re.S), response.text)
            # ('https://qidian.gtimg.com/qd_anti_spider/yMxThZoL.woff')
            font_url = content.groups()[0].strip("( | )").strip("'")
            # print(font_url)
    
            font_content = unify_requests(url=font_url, headers={
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:60.0) Gecko/20100101 Firefox/60.0'}, proxies=self.proxy).content
    
            # with open('qidian_lx.woff', 'wb') as f:
            #     f.write(font_content)
            #
            # font1 = TTFont('qidian_lx.woff')
            # font1.saveXML('qidian_lx.xml')
    
            # 源码中提取十进制数据:
    
            data = re.findall(re.compile(
                r'<div class="book-info ">.*?<p>.*?<span class=".*?">(.*?)</span>.*?<span class=".*?">(.*?)</span>.*?<span class=".*?">(.*?)</span>.*?<span class=".*?">(.*?)</span>',
                re.S), response.text)[0]
    
            four_list = []
            # 遍历这四组数据
            for d in data:
                # print(d)  # 拿到元组中的一个  &#100152;&#100153;&#100153;&#100150;&#100157;&#100157;
                one_list = []
                d = d.split(';')  # 去除分号
                # 遍历每组数据
                for x in d:
                    res = x.replace('&#', '')
                    if res:  # 判断是否有空格有的话不转化
                        # 将res十进制转化成16进制
                        a = int(res)  # 先转化成int类型
                        one_list.append(a)
    
                four_list.append(one_list)
            map_dict = XYYTTFont(font_content).getBestCmap()
            # print(map_dict)
            result_list = []
            # 遍历含有四组数据的列表
            for one in four_list:
                two_string = ""
                # 遍历每一组数据
                for a in one:
                    # print("a",a)
                    if a in map_dict:
                        number = map_dict[a]  # 找到对应的键
                        number = number_dict[number]  # 通过键找到对应的值
                        # print(number)
                        two_string += number
    
                result_list.append(two_string)
            return result_list
        def get_int_num(self, numstr):
            if '.' in numstr:
                return int(numstr.replace('.','')) * 100
            else:
                return int(numstr)
        # 搜索视频响应解析
        def parse_novel_info(self, respose_info, novel_url='', **kwargs) -> dict:
            try:
                # print(novel_url)
                response_data = etree.HTML(respose_info.text)
                info_list = self.get_info(respose_info, **kwargs)
            except Exception as e:
                print(e)
                return {}
            else:
                # info_book_dict = info_dict.get('book', {})
                novel_dict = dict()
                novel_dict['all_recommend_str'] = self.get_int_num(info_list[2])  # 总推荐数 str book_interact
                novel_dict['month_recommend_str'] = None  # 月推荐数 str
                novel_dict['week_recommend_str'] = self.get_int_num(info_list[3])  # 周推荐数 str
                novel_dict['all_read_int'] = None  # 总阅读数 int
                novel_dict['month_read_int'] = None  # 月阅读数 int
                novel_dict['week_read_int'] = None  # 周阅读数 int
                novel_dict['all_words_number_int'] = self.get_int_num(info_list[0]) # 总字数
                novel_dict['book_status_str'] = response_data.xpath('//p[@class="tag"]/span/text()')[0]  # 书籍状态 (连载,完结,暂无)bookCP
                novel_dict['book_property_str'] = response_data.xpath('//p[@class="tag"]/span/text()')[1] # 书籍属性 (免费,会员,限免)
                novel_dict['author_type_str'] = "".join(response_data.xpath('//div[@class="author-photo"]/span/text()')) # 作者类型 (金牌,签约,独立 默认无)
                novel_dict['book_type_str'] = '|'.join(response_data.xpath('//p[@class="tag"]/a/text()'))  # 书籍分类 (玄幻 ,科幻,言情...)按搜索结果来多个按|分割
                novel_dict['book_update_time'] = ''.join(response_data.xpath('//li[@class="update"]/div/p[@class="cf"]/em/text()'))  # 书籍更新日期 年-月-日
                novel_dict['book_zong_zhang_jie_int'] = ''  # 书籍总的章节 完结的,未完结就填目前的总章节
                novel_dict['book_zui_xin_zhang_jie_name_str'] = ''.join(response_data.xpath('//li[@class="update"]/div/p[@class="cf"]/a/text()')) # 最新章节名称
                novel_dict['book_introduce_text'] =  ''.join(response_data.xpath('//div[@class="book-intro"]/p//text()')).replace(' ', '').replace('\u3000', '').replace('\r', '').replace('\n', '').replace('\t', '') # 书籍简介 text
                novel_dict['book_lable_str'] = '|'.join(response_data.xpath('//p[@class="tag"]/a/text()')) # 书籍标签 (用|分割的字符串 ''科幻|现实|励志'')
                novel_dict['book_cover_image_str'] = "https:" + "".join(response_data.xpath('//div[@class="book-information cf"]/div[@class="book-img"]/a/img/@src')).replace('\n', '') # 书籍封面 URL
                novel_dict['book_detail_url_str'] = novel_url  # 书籍详情URL
                novel_dict['book_detail_id_int'] = None  # 书籍详情ID 数字形式
                novel_dict['book_detail_id_str'] = None  # 书籍详情ID 字符形式
                novel_dict['book_zhan_dian_str'] = None  # 书籍站点 (男生,女生,暂无)
                novel_dict['book_publish_str'] = '起点中文网'  # 出版社 默认侵权平台'
                novel_dict['book_commeds_int'] = None  # 书籍评论数 Pinglunfont
                novel_dict['author_grade_float'] = None  # 作者评分
                novel_dict['author_id_str'] =  None # 作者ID 字符形式 ## 新增
                novel_dict['author_page_url_str'] = "https:" + ''.join(response_data.xpath('//a[@class="writer"]/@href')) # 作者主页链接 userId
                author_info_data = response_data.xpath('//ul[@class="work-state cf"]/li/em/text()')
                novel_dict['author_book_number_int'] = author_info_data[0]  # 作者书籍总数
                novel_dict['author_likes_int'] = None  # 作者获赞总数
                novel_dict['author_all_words_number_str'] = author_info_data[1]  # 作者累计创作字数
                novel_dict['author_produce_days_str'] = author_info_data[2]  # 作者累计创作天数
                novel_dict['author_fens_number_int'] = None  # 作者粉丝数
                novel_dict['author_head_image_url_str'] = "https:" + "".join(response_data.xpath('//div[@class="author-photo"]/a/img/@src'))  # 作者头像URL
                # novel_dict[''] = ''  #
                return novel_dict
    
    
    # 统一的调用 search_novels
    search_novel_info = SFQingNovel(use_proxy=True).get_novel_info
    if __name__ == "__main__":
        result = search_novel_info('https://book.qidian.com/info/1010734492')
        print(result)
    
    
    展开全文
  • 我正在使用reportlab生成pdf文件。当我在pdf上画一个字符串时...在代码:# Register fonts.pdfmetrics.registerFont(ttfonts.TTFont('fz1', 'fz1.ttf'))pdfmetrics.registerFont(ttfonts.TTFont('fz3', 'fz3.ttf'))...

    我正在使用reportlab生成pdf文件。当我在pdf上画一个字符串时,我遇到了一些问题。如何使用TTFont获取字符串的高度?在

    代码:# Register fonts.

    pdfmetrics.registerFont(ttfonts.TTFont('fz1', 'fz1.ttf'))

    pdfmetrics.registerFont(ttfonts.TTFont('fz3', 'fz3.ttf'))

    pdfmetrics.registerFont(ttfonts.TTFont('fz4', 'fz4.ttf'))

    pdfmetrics.registerFont(ttfonts.TTFont('fz5', 'fz5.ttf'))

    pdfmetrics.registerFont(ttfonts.TTFont('w5', 'w5.ttc'))

    def draw_text(canvas, fontName, fontSize, x, y, text, cmyk_color=None):

    t = canvas.beginText(x * mm, y * mm)

    t.setFont(fontName, fontSize)

    if cmyk_color is None:

    cmyk_color = (0, 0, 0, COLOR_DIV_RATIO)

    canvas.setFillColorCMYK(cmyk_color[0] / COLOR_DIV_RATIO,

    cmyk_color[1] / COLOR_DIV_RATIO,

    cmyk_color[2] / COLOR_DIV_RATIO,

    cmyk_color[3] / COLOR_DIV_RATIO)

    t.textLine(text)

    canvas.drawText(t)

    c.drawImage('f1.jpg', 0, 0, CANVAS_WIDTH * mm, CANVAS_HEIGHT * mm)

    draw_text(c, 'fz1', 15, mm2pixel(5), mm2pixel(45), u'This is a string')

    我可以通过以下方法得到弦的宽度:

    ^{pr2}$

    但是,我怎么知道绳子的高度呢?在

    展开全文
  • 看图 从网页上解析下载到本地的fontfile.woff文件经fontcreator打开没问题,可以正常查看里面的信息, 然后在python中通过实例化TTFont对象操作,只要调用该对象的一些方法,如getGlyphOrder(),saveXML(),...
  • 1.解决的办法就是字体沾的有问题

    1.解决的办法就是字体沾的有问题

    展开全文
  • argument to the TTFont constructor (default is False). <p>With recalcUnicodeRanges=True, the OS/2 'ulUnicodeRange' values will be recalculated upon save/compile according to the Unicode ...
  • This PR moves the xmlWriter import from within the public <code>saveXML</code> method to the top of the ttFont module. <p>Replicated and confirmed test failure with this test: <p>...
  • 'wb') as f: # 将文件保存到本地 f.write(woff.content) # 使用 TTFont 库打开刚才下载的 woff 文件 font = TTFont(filename) 因为 TTFont 可以直接读取 woff 文件的结构,所以这里不需要将 woff 保存为 XML 文件。...

    在 CSS3 之前,Web 开发者必须使用用户计算机上已有的字体。但是在 CSS3 时代,开发者可以使用 @font-face 为网页指定字体,对用户计算机字体的依赖。

    开发者可将心仪的字体文件放在 Web 服务器上,并在 CSS 样式中使用它。用户使用浏览器访问 Web 应用时,对应的字体会被浏览器下载到用户的计算机上。

    在学习浏览器和页面渲染的相关知识时,我们了解到 CSS 的作用是修饰 HTML ,所以在页面渲染的时候不会改变 HTML 文档内容。

    由于字体的加载和映射工作是由 CSS 完成的,所以即使我们借助 Splash、Selenium 和 Puppeteer 工具也无法获得对应的文字内容。字体反爬虫正是利用了这个特点,将自定义字体应用到网页中重要的数据上,使得爬虫程序无法获得正确的数据。

    一、字体反爬虫示例

    示例 7:字体反爬虫示例。

    任务:爬取影片信息展示页中的影片评分、评价人数和票房数据,页面内容如图 6-32 所示。示例7

    在编写代码之前,我们需要确定目标数据的元素定位。定位时,我们在 HTML 中发现了一些奇怪的符号,HTML 代码如下:

    用户评分

    ☒.☒

    ☒☒. ☒☒ 万人评分

    页面中重要的数据都是一些奇怪的字符,本应该显示“9.7”的地方在 HTML 中显示的是“☒.☒”,而本应该显示“56.83”的地方在 HTML 中显示的是“☒☒.☒☒”。与 6.3 节中的映射反爬虫不同,案例中的文字都被“☒”符号代替了,根本无法分辨。这就很奇怪了,“☒”能代表这么多种数字吗?

    要注意的是,Chrome 开发者工具的元素面板中显示的内容不一定是相应正文的原文,要想知道“☒”符号是什么,还需要到网页源代码中确认。对应的网页源代码如下:

    用户评分

    .

    .万人评分

    从网页源代码中看到的并不是符号,而是由 开头的一些字符,这与示例 6 中的 SVG 映射反爬虫非常相似。我们将页面显示的数字与网页源代码中的字符进行比较,映射关系如图 6-33 所示。字符与数字的映射关系

    字符与数字是一一对应的,我们只需要多找一些页面,将 0 ~ 9 数字对应的字符凑齐即可。但如果目标网站的字体是动态变化的呢?映射关系也是变化的呢?

    根据 6.3 节的学习和分析,我们知道人为映射并不能解决这些问题,必须找到映射关系的规律,并使用 Python 代码实现映射算法才行。

    继续往下分析,难道字符映射是先异步加载数据再使用 JavaScript 渲染的?请求记录

    网络请求记录如图 6-34 所示,请求记录中并没有发现异步请求,这个猜测并没有得到证实。CSS样式方面有没有线索呢?页面中包裹符号的标签的 class 属性值都是 stonefont:

    .

    . 万

    .

    但对应的 CSS 样式中仅设置了字体:

    .stonefont {

    font-family: stonefont;

    }

    既然是自定义字体,就意味着会加载字体文件,我们可以在网络请求中找到加载的字体文件 movie.woff,并将其下载到本地,接着使用百度字体编辑器看一看里面的内容。

    百度字体编辑器 FontEditor (详见 http://fontstore.baidu.com/static/editor/index.html)是一款在线字体编辑软件,能够打开本地或者远程的 ttf、woff、eot、otf 格式的字体文件,具备这些格式字体文件的导入和导出功能,并且提供字形编辑、轮廓编辑和字体实时预览功能,界面如图 6-35 所示。百度文字编辑器界面

    打开页面后,将 movie.woff 文件拖曳到百度字体编辑器的灰色区域即可,字体文件内容如图 6-36所示。字体文件 movie.woff 预览

    该字体文件中共有 12 个字体块,其中包括 2 个空白字体块和 0 ~ 9 的数字字体块。我们可以大胆地猜测,评分数据和票房数据中使用的数字正是从此而来。

    由此看来,我们还需要了解一些字体文件格式相关的知识,在了解文件格式和规律后,才能够找到更合理的解决办法。

    二、字体文件 WOFF

    WOFF(Web Open Font Format,Web 开放字体格式)是一种网页所采用的字体格式标准。本质上基于 SFNT 字体(如 TrueType),所以它具备 TrueType 的字体结构,我们只需要了解 TrueType 字体的相关知识即可。

    TrueType 字体是苹果公司与微软公司联合开发的一种计算机轮廓字体,TrueType 字体中的每个字形由网格上的一系列点描述,点是字体中的最小单位,字形与点的关系如图 6-37 所示。字形与点的关系

    字体文件中不仅包含字形数据和点信息,还包括字符到字形映射、字体标题、命名和水平指标等,这些信息存在对应的表中,所以我们也可以认为 TrueType 字体文件由一系列的表组成,其中常用的表及其作用如图 6-38 所示。构成字体文件的常用表及其作用

    如何查看这些表的结构和所包含的信息呢?我们可以借助第三方 Python 库 fonttools 将 WOFF 等字体文件转换成 XML 文件,这样就能查看字体文件的结构和表信息了。首先我们要安装 fonttools 库,安装命令为:

    $ pip install fonttools

    安装完毕后就可以利用该库转换文件类型,对应的 Python 代码为:

    from fontTools.ttLib import TTFont

    font = TTFont('movie.woff') # 打开当前目录的 movie.woff 文件

    font.saveXML('movie.xml') # 另存为 movie.xml

    代码运行后就会在当前目录生成名为 movie 的 XML 文件。文件中字符到字形映射表 cmap的内容如下:

    map 标签中的 code 代表字符,name 代表字形名称,关系如图 6-39 所示。字符到字形映射关系示例

    XML 中的字符 0xe339 与网页源代码中的字符  对应,这样我们就确定了 HTML 中的字符码与 movie.woff 字体文件中对应的字形关系。字形数据存储在表 glyf 中,每个字形的数据都是独立的,例如字形 uniE339 的字形数据如下:

    ...

    ...

    TTGlyph 标签中记录着字形的名称、x轴坐标和y轴坐标(坐标也可以理解为字形的宽高)。contour 标签记录的是字形的轮廓信息,也就是多个点的坐标位置,正是这些点构成了如图 6-40 所示的字形。字形 uniE339 的轮廓

    XML 文件中记录的是字形坐标信息,实际上,我们没有办法直接通过字形数据获得文字,只能从其他方面想办法。虽然目标网站使用多套字体,但相同文字的字形也是相同的。比如现在有movie.woff 和 food.woff 这两套字体,它们包含的字形如下:

    # movie.woff

    # 包含 10 个字形数据:[0123456789]

    # 数字 6

    # 数字 9

    # 数字 2

    # 数字 7

    # 数字 5

    # 数字 0

    # 数字 8

    # 数字 3

    # 数字 1

    # 数字 4

    # food.woff

    # 包含 3 个字形数据:[012]

    # 数字 0

    # 数字 1

    # 数字 2

    要实现自动识别文字,需要先准备参照字形,也就是人为地准备数字 0 ~ 9 的字形映射关系和字形数据,如:

    # 0 和 7 与字形名称的映射伪代码,data 键对应的值是字形数据

    font_mapping = [

    {'name': 'uniE9C7', 'words': '7', 'data': 'uniE9C7_contour_pt'},

    {'name': 'uniEE76', 'words': '0', 'data': 'uniEE76_countr_pt'},

    ]

    当我们遇到目标网站上其他字体文件时,就可以使用参照字形中的字形数据与目标字形进行匹配,如果字形数据非常接近,就认为这两个字形描述的是相同的文字。字形数据包含记录字形名称和字形起止坐标的 TTGlyph 标签以及记录点坐标的 pt 标签,起止坐标代表的是字形在画布上的位置,点坐标代表字形中每个点在画布上的位置。在起止坐标中,x 轴差值代表字形宽度,y 轴差值代表字形高度。

    如图 6-42 所示,两个字形的起止坐标和宽高都有很大的差别,但是却能够描述相同的文字,所以字形在画布中的位置并不会影响描述的文字,字形宽度和字形高度也不会影响描述的文字。描述相同文字的两个字形

    点坐标的数量和坐标值可以作为比较条件吗?

    如图 6-43 所示,两个不同文字的字形数据是不一样的。虽然这两种字形的 name 都是 uniE9C7,但是字形数据中大部分 pt 标签 x 和 y 的差距都很大,所以我们可以判定这两个字形描述的并不是同一个文字。你可能会想到点的数量也可以作为排除条件,也就是说如果点的数量不相同,那么这个两个字形描述的就不是同一个文字。真的是这样吗?描述不同文字的字形数据对比

    在图 6-44 中,左侧描述文字 7 的字形有 17 个点,而右侧描述文字 7 的字形却有 20 个点。对应的字形信息如图 6-45 所示。描述相同文字的字形描述相同文字的字形信息

    虽然点的数量不一样,但是它们的字形并没有太大的变化,也不会造成用户误读,所以点的数量并不能作为排除不同字形的条件。因此,只有起止坐标和点坐标数据完全相同的字形,描述的才是相同字符。

    三、字体反爬虫绕过实战

    要确定两组字形数据描述的是否为相同字符,我们必须取出 HTML 中对应的字形数据,然后将待确认的字形与我们准备好的基准字形数据进行对比。现在我们来整理一下这一系列工作的步骤。

    (1) 准备基准字形描述信息。

    (2) 访问目标网页。

    (3) 从目标网页中读取字体编码字符。

    (4) 下载 WOFF 文件并用 Python 代码打开。

    (5) 根据字体编码字符找到 WOFF 文件中的字形轮廓信息。

    (6) 将该字形轮廓信息与基准字形轮廓信息进行对比。

    (7) 得出对比结果。

    我们先完成前 4 个步骤的代码。下载 WOFF 文件并将其中字形描述的文字与人类认知的文字进行映射。由于字形数据比较庞大,所以我们可以将字形数据进行散列计算,这样得到的结果既简短又唯一,不会影响对比结果。这里以数字 0 ~ 9 为例:

    base_font = {

    "font": [{"name": "uniEE76", "value": "0", "hex": "fc170db1563e66547e9100cf7784951f"},

    {"name": "uniF57B", "value": "1", "hex": "251357942c5160a003eec31c68a06f64"},

    {"name": "uniE7DF", "value": "2", "hex": "8a3ab2e9ca7db2b13ce198521010bde4"},

    {"name": "uniF19A", "value": "3", "hex": "712e4b5abd0ba2b09aff19be89e75146"},

    {"name": "uniF593", "value": "4", "hex": "e5764c45cf9de7f0a4ada6b0370b81a1"},

    {"name": "uniEA16", "value": "5", "hex": "c631abb5e408146eb1a17db4113f878f"},

    {"name": "uniE339", "value": "6", "hex": "0833d3b4f61f02258217421b4e4bde24"},

    {"name": "uniE9C7", "value": "7", "hex": "4aa5ac9a6741107dca4c5dd05176ec4c"},

    {"name": "uniEFD4", "value": "8", "hex": "c37e95c05e0dd147b47f3cb1e5ac60d7"},

    {"name": "uniE624", "value": "9", "hex": "704362b6e0feb6cd0b1303f10c000f95"}]

    }

    字典中的 name 代表该字形的名称,value 代表该字形描述的文字,hex 代表字形信息的 MD5 值。

    考虑到网络请求记录中的字体文件路径有可能会变化,我们必须找到 CSS 中设定的字体文件路径,引入 CSS 的 HTML 代码为:

    由引入代码得知该 CSS 文件的路径为 http://www.porters.vip/confusion/css/movie.css,文件中 @font-face 处就是设置字体的代码:

    @font-face {

    font-family: stonefont;

    src:url('../font/movie.woff') format('woff');

    }

    import re

    from parsel import Selector

    from urllib import parse

    from fontTools.ttLib import TTFont

    url = 'http://www.porters.vip/confusion/movie.html'

    resp = requests.get(url)

    sel = Selector(resp.text)

    # 提取页面加载的所有 css 文件路径

    css_path = sel.css('link[rel=stylesheet]::attr(href)').extract()

    woffs = []

    for c in css_path:

    # 拼接正确的 css 文件路径

    css_url = parse.urljoin(url, c)

    # 向 css 文件发起请求

    css_resp = requests.get(css_url)

    # 匹配 css 文件中的 woff 文件路径

    woff_path = re.findall("src:url\('..(.*.woff)'\) format\('woff'\);",

    css_resp.text)

    if woff_path:

    # 如故路径存在则添加到 woffs 列表中

    woffs += woff_path

    woff_url = 'http://www.porters.vip/confusion' + woffs.pop()

    woff = requests.get(woff_url)

    filename = 'target.woff'

    with open(filename, 'wb') as f:

    # 将文件保存到本地

    f.write(woff.content)

    # 使用 TTFont 库打开刚才下载的 woff 文件

    font = TTFont(filename)

    因为 TTFont 可以直接读取 woff 文件的结构,所以这里不需要将 woff 保存为 XML 文件。接着以评分数据 9.7 对应的编码 #xe624.#xe9c7 进行测试,在原来的代码中引入基准字体数据 base_font,然后新增以下代码:

    web_code = '#xe624.#xe9c7'

    # 编码文字替换

    woff_code = [i.upper().replace('', 'uni') for i in web_code.split('.')]

    import hashlib

    result = []

    for w in woff_code:

    # 从字体文件中取出对应编码的字形信息

    content = font['glyf'].glyphs.get(w).data

    # 字形信息 MD5

    glyph = hashlib.md5(content).hexdigest()

    for b in base_font.get('font'):

    # 与基准字形中的 MD5 值进行对比,如果相同则取出该字形描述的文字

    if b.get('hex') == glyph:

    result.append(b.get('value'))

    break

    # 打印映射结果

    print(result)

    以上代码运行结果为:

    ['9', '7']

    运行结果说明能够正确映射字体文件中字形描述的文字。

    四、小结

    字体反爬能给爬虫工程师带来很大的麻烦。虽然爬虫工程师找到了应对方法,但这种方法依赖的条件比较严苛,如果开发者频繁改动字体文件或准备多套字体文件并随机切换,那真是一件令爬虫工程师头疼的事。不过,这些工作对于开发者来说也不是轻松的事。

    本文转载于,《NightTeam》公众号,

    为何转载?只因为它解决了我为何总是老抓取到空字符串!!!

    展开全文
  • 3、研究字体文件 刚刚的.ttf文件我们是看不到内部的东西的,所以这个时候我们要对字体文件进行转换格式,将其转换为xml格式,然后来查看: 具体操作如下: 1 2 3 from fontTools.ttLib import TTFont font_1=TTFont...
  • onlineFonts = TTFont('fonts.woff') onlineFonts.saveXML('test.xml') 注: 此步骤在实际生产过程中并不需要, 为了便于理解, 我加上了 5.通过fontTools库, 获取字体信息中不变的部分 baseFonts = TTFont('...
  • 字体文件.zip

    2019-09-17 15:54:54
    python reportlab生成pdf所需要的字体文件simsun pdfmetrics.registerFont(TTFont('simsun', './resource/simsun.ttc')) pdfmetrics.registerFont(TTFont('simhei', './resource/simhei.ttf'))
  • Python 字体爬虫应对

    2020-12-10 11:08:50
    from fontTools.ttLib import TTFont font = TTFont('./2b72be2a972359c0de8bac4681d75ecc2080.woff') font.saveXML('2b72be2a972359c0de8bac4681d75ecc2080.xml') 保存的文件内容如下: ....下面还有很多代码 可以...
  • SimSun.ttf

    2019-11-23 18:30:55
    from reportlab.pdfbase import pdfmetrics pdfmetrics.registerFont(TTFont('SimSun', 'SimSun.ttf')) # 注册字体 python画图包所需要的注册字体
  • c = font['cmap'].tables[0].ttFont.tables['cmap'].tables[0].cmap 结果为{38006: 'glyph00005', 38287: 'glyph00007', 39228: 'glyph00004', 39499: 'glyph00003', 40506: 'glyph00010', 40611: 'glyph...
  • 用代码来获取编码和name的对应关系: from fontTools.ttLib import TTFontfont = TTFont('58.ttf') # 打开本地的ttf文件font.saveXML('58.xml') # 转换为xml文件bestcmap = font['cmap'].getBestCmap() # 获取cmap...
  • PDF宋体(simsun.ttf)

    2021-07-18 19:00:25
    PDF宋体(simsun.ttf)
  • Python_FontTools使用

    千次阅读 2019-12-11 00:23:45
    font = TTFont('local_fonts.woff') # 保存为xml文件: font.saveXML('local_fonts.xml') # 获取各节点名称,返回为列表 print(font.keys()) # ['GlyphOrder', 'head', 'hhea', 'maxp', 'OS/2', 'hmtx', 'cmap', '...
  • python 破解字体反爬 (一)

    千次阅读 2019-11-25 17:08:37
    font = TTFont('58.ttf') # 打开本地的ttf文件 font.saveXML('58.xml') # 转换成xml 打开xml文件,可以看到类似html标签的文件结构: 点开GlyphOrder标签,可以看到Id和name 点开glyf标签,看到的是...
  • Python 爬虫:字体反爬处理

    千次阅读 2018-10-13 19:50:00
    font=TTFont( '01.ttf' )  #打开本地字体文件01.ttf 4 font.saveXML( '01.xml' )  #将ttf文件转化成xml格式并保存到本地,主要是方便我们查看内部数据结构 先把字体文件转化成xml格式,以便打开查看里面的数据结构...
  • 猫眼电影,自定义字体解决方法

    千次阅读 2018-04-26 11:36:17
    TTFont ( 'base.otf' ) maoyanFont = TTFont ( 'maoyan.otf' ) uniList = maoyanFont [ 'cmap' ] . tables [ 0 ] . ttFont . getGlyphOrder () numList = [] baseNumList = [ '.' , '3' ,...
  • 各种字体打包合集

    2019-01-28 10:50:35
    十多种字体打包合集,为了让我们在编程或者字体安装时,引用或安装ttf格式字体
  • pdfmetrics.registerFont(TTFont( ' pingbold ' , ' PingBold.ttf ' )) pdfmetrics.registerFont(TTFont( ' ping ' , ' ping.ttf ' )) pdfmetrics.registerFont(TTFont( ' hv ' , ' Helvetica.ttf ' )) # 生成...
  • 使用fontTools库

    2021-05-30 00:14:17
    from fontTools.ttLib import TTFont import numpy as np from PIL import Image font = TTFont("D:/zitigen/字体/字体管家方萌简.ttf") # 输出的uniMap是一个字典,key代表的unicode的int值,value代表unicode的...
  • python解析字体反爬

    2020-11-20 22:43:12
    )'\)",response.text).group(1) b=base64.b64decode(base64_str) font=TTFont(io.BytesIO(b)) bestcmap= font['cmap'].getBestCmap() newmap=dict()for key inbestcmap.keys(): value= int(re.search(r'(\d+)', ...
  • 1、包含了simsun字体文件 2、说明了simsun字体的使用方法
  •  font = TTFont(BytesIO(response.content))  cmap = font.getBestCmap()  font.close()  return cmap 结果:{100166: 'nine', 100168: 'five', 100169: 'eight', 100170: 'two', 100171: 'four', 100172: '...
  • 反反爬技术,破解猫眼网加密数字

    千次阅读 2018-01-24 18:09:02
    uniList = maoyanFont['cmap'].tables[0].ttFont.getGlyphOrder() numList = [] baseNumList = ['.', '3', '5', '1', '2', '7', '0', '6', '9', '8', '4'] baseUniCode = ['x', 'uniE64B', 'uniE183', 'uniED06'...
  • Python生成pdf问题解决总结

    千次阅读 2019-01-02 15:57:48
    pdfmetrics.registerFont(TTFont('song', settings.MEDIA_ROOT+'/file/SURSONG.TTF')) from reportlab.lib import fonts from django.template import Context, loader  fonts.ps2tt = lambda...
  • # TTFont打开字体文件 font = TTFont("字体文件.woff") # 将字体文件保存为可读的xml文件 font.saveXML('font.xml') # 找字体的映射关系,字体的映射关系在cmap中体现 font_map = font['cmap'].getBestCmap() print...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,410
精华内容 564
关键字:

TTFont