python 破解字体反爬 (二)

本文详细解析了58同城网站使用的字体反爬技术,包括不同字体格式的判断、字体文件动态解析方法及构建字体映射表的过程,旨在帮助读者理解并应对字体反爬挑战。

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

https://blog.youkuaiyun.com/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

 

### 使用 Python 实现字体解析与处理 当遇到字体加密的虫机制时,通常是因为网站使用了自定义字体文件来替代标准字符编码。为了应对这种情况,可以通过以下方法进行破解: #### 1. 获取原始字库映射关系 通过浏览器开发者工具中的网络请求面板找到加载的 `.woff` 或者其他类型的字体文件链接。下载该字体文件并利用 `fontTools` 库读取其表结构。 ```python from fontTools.ttLib import TTFont # 加载本地保存下来的 .woff 字体文件 font = TTFont('path_to_font_file.woff') # 打印出所有的 cmap 表项 (即 Unicode 编码到 glyph ID 的映射) for table in font['cmap'].tables: for code, name in table.cmap.items(): print(f"{code}: {name}") ``` 此部分操作能够帮助建立从网页源代码中获取的乱码字符串到实际可读文本之间的对照表[^3]。 #### 2. 解析 CSS 文件定位 @font-face 定义 许多情况下,网站会动态生成或修改用于指定特殊字体样式的CSS样式规则。因此还需要进一步分析目标站点上的 CSS 资源,特别是查找包含有 `@font-face` 声明的部分,从中提取出字体名称以及对应的 URL 地址。 ```css /* 示例 */ @font-face { font-family: 'CustomFont'; src: url('/fonts/custom-font.woff') format('woff'); } ``` 了解这些信息有助于确认之前所提到的字体文件确实是用来替换正文内容的关键所在。 #### 3. 构建解密函数完成最终转换 一旦获得了完整的字形映射数据之后就可以编写一个简单的辅助程序来进行批量替换了。这里给出一段基于前面两步工作的简单例子: ```python def decrypt_text(encrypted_str, mapping_dict): decrypted_chars = [] # 遍历输入串里的每一个字符,并依据事先准备好的映射字典做相应变换 for char in encrypted_str: if ord(char) in mapping_dict: decrypted_chars.append(chr(mapping_dict[ord(char)])) else: decrypted_chars.append(char) return ''.join(decrypted_chars) if __name__ == '__main__': sample_encrypted_string = "..." unicode_map = {...} # 这里应该填入由第一步得到的结果 result = decrypt_text(sample_encrypted_string, unicode_map) print(result) ``` 上述过程展示了如何运用 Python 来解决因字体加密而导致的数据采集难题。值得注意的是,在具体实施过程中可能还会涉及到更多细节调整和技术手段的应用,例如处理多套不同版本的字体方案或是适应更加复杂的编码逻辑变化等问题。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值