python 破解字体反爬 (二)

上一篇我介绍了破解58同城的字体反爬

https://blog.csdn.net/BigBoy_Coder/article/details/103239672

中间遗漏了几个细节,在这边文章我做一下补充:

遗漏点一:

并不是所有的字体文件格式都是.ttf格式的文件,这里我们要针对原网页的,进行判断

怎么判断呢?

比如:https://su.58.com/qztech/ 右键查看源码,

我们发现,文件的字体格式是woff形式的,所以这里我们把文件写成.woff格式就好了 

那我们在进入https://sz.58.com/chuzu/这个页面。我们查看源码

我们发现,文件的字体格式是ttf形式的,所以这里我们把文件写成.ttf格式就好了  

遗漏点二:

 上一篇文章我们是在cmap,获取字符编码的,但并不是所有的都是这样,猫眼是把编码对象在glyf 

所以我们还是要灵活的去分析xml文件,

遗漏点三:

当我们看到字体文件,每个字可以看到字形和字形编码。

观察现在箭头指的地方和前面箭头指的地方的数字是不是一样啊,没错,就是通过这种方法进行映射的。

所以我们现在的思路似乎就是在源代码里找到箭头指的数字,然后再来字体里找到后替换就行了。

恭喜你,如果你也是这么想的,那你就掉坑里了。

因为每次访问,字体字形是不变的,但字符的编码确是变化的。因此,我们需要根据每次访问,动态解析字体文件

 

好了说完这两个遗漏点之后,进入到今天的主要内容,手动构建字体映射表:

由于 操作详细步骤在上篇文章,都详细做了介绍,在这篇文章只做分析过程

网页地址:https://su.58.com/qztech/

步骤(1):打开网页把字体文件保存下载,并且转换成xml格式

FontCreator打开58.woff文件:

重复步骤一:

打开58_2.woff文件:

对比两个文件,我们发现

   

 

因为每次访问,字体字形是不变的,但字符的编码确是变化的。因此,我们需要根据每次访问,动态解析字体文件

 到这里我们想通过写死字体哪种方式是不行了:

分析一下两个字体文件的对应的xml文件,搜索 “”,对应的编码:

xml的格式如下:

今天,我终于弄懂了字体反爬是个啥玩意!

文件很长,我只截取了一部分。

仔细的观察一下,你会发现~这俩下面的x,y,on值都是一毛一样的。所以我们的思路就是以一个已知的字体文件为基本,然后将获取到的新的字体文件的每个文字对应的x,y,on值进行比较,如果相同,那么说明新的文字对就 可以在基础字体那里找到对应的文字,有点绕,下面举个小例子。

假设: “我” 在基本字体中的名为uni1,对应的x=1,y=1,n=1新的字体文件中,一个名为uni2对应的x,y, n分别于上面的相等,那么这个时候就可以确定uni2 对应的文字为”我”。

查资料的时候,发现在特殊情况下,有时候两个字体中的文字对应的x,y不相等,但是差距都是在某一个阈值之内,处理方法差不多,只不过上面是相等,这种情况下就是要比较一下。

分析到这我么就可一构建字体映射表编写代码,爬取相关信息了

# coding=utf-8
import requests
import re
import time
import  lxml.html as H
import base64
from fontTools.ttLib import TTFont


def get_data(url):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36',
    }
    session = requests.session()
    con = session.get(url, headers=headers)
    doc = H.document_fromstring(con.content)

    font_data_origin = re.search(r'base64,(.*?)\)', con.text, re.S).group(1)
    font_data_after_decode = base64.b64decode(font_data_origin)

    new_font_name = "font_new.woff"
    with open(new_font_name, 'wb') as f:
        f.write(font_data_after_decode)

    map_data = tff_parse(new_font_name)
    names = doc.xpath('//span[@class="infocardName fl stonefont resumeName"]/text()')
    # 有的时候会找不到,可以多执行几次;
    if names:
        for name in names:
            print('name in page source', name)
            for j in map_data.keys():
                    name = name.replace(j, map_data[j])
            print ('name actual', name)

def tff_parse(font_parse_name):
    # 我这里的字体的顺序,如果你的不同,一定要修改
    font_dict = [u'博', u'经', u'硕', u'届', u'大', u'刘', u'8', u'1', u'士', u'E', u'2', u'6', u'张',
                 u'M', u'验', u'5', u'本', u'赵', u'陈', u'吴', u'李', u'生', u'4', u'校', u'以', u'应', u'黄',
                 u'技', u'无', u'女', u'A', u'周', u'中', u'3', u'王', u'7', u'0', u'9', u'科', u'高', u'男',
                 u'杨', u'专', u'下', u'B']
    font_base = TTFont('font_base.ttf')
    font_base_order = font_base.getGlyphOrder()[1:]
    # font_base.saveXML('font_base.xml')  调试用

    font_parse = TTFont(font_parse_name)
    # font_parse.saveXML('font_parse_2.xml')调试用
    font_parse_order = font_parse.getGlyphOrder()[1:]

    f_base_flag = []
    for i in font_base_order:
        flags = font_base['glyf'][i].flags
        f_base_flag.append(list(flags))

    f_flag = []
    for i in font_parse_order:
        flags = font_parse['glyf'][i].flags
        f_flag.append(list(flags))

    result_dict = {}
    for a, i in enumerate(f_base_flag):
        for b, j in enumerate(f_flag):
            if comp(i, j):
                key = font_parse_order[b].replace('uni', '')
                key = eval(r'u"\u' + str(key) + '"').lower()
                result_dict[key] = font_dict[a]
    return result_dict

def comp(L1, L2):

    if len(L1) != len(L2):
        return 0
    for i in range(len(L2)):
        if L1[i] == L2[i]:
            pass
        else:
            return 0
    return 1




if __name__ == '__main__':
    url = "https://su.58.com/qztech/"
    get_data(url)

反扒日系更新,以下两篇文章,推荐看一看

https://www.freebuf.com/articles/web/206966.html

https://www.jianshu.com/p/4d28dd440cdd

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【为什么学爬虫?】        1、爬虫入手容易,但是深入较难,如何写出高效率的爬虫,如何写出灵活性高可扩展的爬虫都是一项技术活。另外在爬虫过程中,经常容易遇到被反爬虫,比如字体反爬、IP识别、验证码等,如何层层攻克难点拿到想要的数据,这门课程,你都能学到!        2、如果是作为一个其他行业的开发者,比如app开发,web开发,学习爬虫能让你加强对技术的认知,能够开发出更加安全的软件和网站 【课程设计】 一个完整的爬虫程序,无论大小,总体来说可以分成三个步骤,分别是:网络请求:模拟浏览器的行为从网上抓取数据。数据解析:将请求下来的数据进行过滤,提取我们想要的数据。数据存储:将提取到的数据存储到硬盘或者内存中。比如用mysql数据库或者redis等。那么本课程也是按照这几个步骤循序渐进的进行讲解,带领学生完整的掌握每个步骤的技术。另外,因为爬虫的多样性,在爬取的过程中可能会发生被反爬、效率低下等。因此我们又增加了两个章节用来提高爬虫程序的灵活性,分别是:爬虫进阶:包括IP代理,多线程爬虫,图形验证码识别、JS加密解密、动态网页爬虫、字体反爬识别等。Scrapy和分布式爬虫:Scrapy框架、Scrapy-redis组件、分布式爬虫等。通过爬虫进阶的知识点我们能应付大量的反爬网站,而Scrapy框架作为一个专业的爬虫框架,使用他可以快速提高我们编写爬虫程序的效率和速度。另外如果一台机器不能满足你的需求,我们可以用分布式爬虫让多台机器帮助你快速爬取数据。 从基础爬虫到商业化应用爬虫,本套课程满足您的所有需求!【课程服务】 专属付费社群+定期答疑
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值