破解拼多多网页字体加密的Python实现(静态)

一、问题背景

在爬取拼多多商品页数据时,发现价格/销量等关键数据使用字体加密技术,网页中显示的""等Unicode字符无法直接转换为真实数字。本文通过Python实现了一套完整的解密方案。​

二、解决思路

  1. ​下载字体文件​​:从网页源码中提取字体文件URL
  2. ​解析字体映射​​:使用fontTools解析字形关系
  3. ​建立字符映射​​:通过Unicode码位与真实数字的对应关系
  4. ​文本替换解密​​:根据映射表还原加密文本

我们看一下PDD的商家后台 上图

爬出来是这样的

爬取或者复制粘贴出来发现数字不是6372,而是这样的一个东西 ""从上图我们可以看出数字是一行的空白,后面被浏览器渲染后才展现的数字

我们发现每个加密的数字都有一个属性class="__spider_font"

我们搜索这个属性我们可以看到第一个这样的属性有一个URL,这个就是解密的字体包

我们请求这个链接获取他的信息

三、关键代码解析

这个字典是使用 TTFont 解析字体文件,获取 Unicode 码位到字形名称的映射:

font_url = "https://pfile.pddpic.com/webspider-sdk-api/efd675484fa54beca5fa12f92072a230-f685515c41f945f8a2c298bf72889fa9.ttf"  # 替换为实际URL
    response = requests.get(font_url)
    with open("encrypted_font.woff", "wb") as f:
        f.write(response.content)

    font = TTFont("encrypted_font.woff")
    cmap = font.getBestCmap()  # 获取 Unicode 到字形名称的映射
    print("字符编码到字形的映射:", cmap)

我们看一下这个结果其中当使用 font.getBestCmap() 获取字体映射时,得到的 cmap 字典结构如下:

#运行结果
字符编码到字形的映射: 
{46: 'period',
58203: 'uniE35B', 
58222: 'uniE36E', 
58519: 'uniE497', 
58573: 'uniE4CD', 
58925: 'uniE62D', 
58954: 'uniE64A', 
60501: 'uniEC55', 
60666: 'uniECFA', 
60934: 'uniEE06', 
61022: 'uniEE5E'}

📌 键(Key):十进制的 Unicode 码位​

  • ​含义​​:表示字符在 Unicode 标准中的唯一标识值。
  • ​获取方式​​:Python 中通过 ord(字符) 获得。
  • ​示例​​:
    字符  对应的十进制码位为 58222

📌 值(Value):字形的名称​


​🌰 举个栗子​

假设你在网页上看到一个显示为 ​2​ 的字符,背后发生的故事:

步骤数据来源示例值说明
1网页HTML源码HTML实体,对应十六进制 E36E
2cmap 字典58222: 'uniE36E'十进制码位 → 字形名称
3字体文件图形数据uniE36E 对应图形 2实际显示的符号

  • ​命名规则​​:通常以 uni 开头,后面跟着该字符的 ​​十六进制 Unicode 码位​​(大写)。
  • ​示例​​:
    uniE36E 中的 E36E 对应十六进制值,转换后为十进制:
  • 🔍 值(字形名称) ≠ 直接复制的方块符号!​


    ​📌 关键结论​
  • cmap 中的值(如 uniE35B)是字体内部的字形名称​​,不是你在网页上看到的方块符号。
  • ​复制的乱码方块符号​​(如 )本质是 Unicode 字符,其显示效果依赖字体文件。
  • ​字形名称和显示符号的关联​​:
    • 通过 cmap 的键(十进制码位)链接到字形名称
    • 通过字体文件的图形数据链接到实际显示符号

然后我们要建立映射关系​,根据观察结果手动创建映射字典:

我们需要用到一个网站

FontDrop!

我们看到有一个Unicode编码,

uni 后的 EC55 是该字符的 ​​Unicode 码位(十六进制)

我们可以用这个代码查看十六位进制对应的十进制,为什么要看十进制呢?因为我们要构造一个十进制数字对应的字典,ord()函数返回的是十进制的Unicode码位​​,而字体文件中的字符映射关系(cmap)也是以十进制存储的。用十进制键能直接匹配,无需转换!

我们可以用这个代码查看十六进制对应的十进制、

print(int("002E",16))

然后我们构造一个这样的映射字典 key就是对应的十进制,后面的值就是对应的数字

custom_map = {
    60501: '1',  # 注意:Unicode码位需转换为十进制
    58222: '2',
    58519: '3',
    58203: '4',
    60666: '5',
    60934: '6',
    58925: '7',
    58573: '8',
    61022: '9',
    58954: '0',
    46:'.'

    # 继续添加其他字符...
}

然后我们需要封装一个函数,将加密文字和映射字典传入,返回解密后的数字

def decrypt_text(text, mapping):
    """将加密文本根据映射字典解密"""
    decrypted = []
    for char in text:
        # 获取字符的 Unicode 码位(十进制)
        unicode_code = ord(char)
        # 从映射字典中查找对应的真实字符,找不到则保留原字符
        decrypted_char = mapping.get(unicode_code, char)
        decrypted.append(decrypted_char)
    return ''.join(decrypted)

最后我们测试一下

encrypted_text="" #5505的加密字符

decrypted = decrypt_text(encrypted_text, custom_map)
print(decrypted)  # 预期输出: "5005"

看一下运行结果

完美

下面是完整代码我是在影刀中写的,其他编译器前四个包大家可以不用导

# 使用提醒:
# 1. xbot包提供软件自动化、数据表格、Excel、日志、AI等功能
# 2. package包提供访问当前应用数据的功能,如获取元素、访问全局变量、获取资源文件等功能
# 3. 当此模块作为流程独立运行时执行main函数
# 4. 可视化流程中可以通过"调用模块"的指令使用此模块

import xbot
from xbot import print, sleep
from .import package
from .package import variables as glv
import requests
from fontTools.ttLib import TTFont

def decrypt_text(text, mapping):
    """将加密文本根据映射字典解密"""
    decrypted = []
    for char in text:
        # 获取字符的 Unicode 码位(十进制)
        unicode_code = ord(char)
        # 从映射字典中查找对应的真实字符,找不到则保留原字符
        decrypted_char = mapping.get(unicode_code, char)
        decrypted.append(decrypted_char)
    return ''.join(decrypted)

def main(args):
    font_url = "https://pfile.pddpic.com/webspider-sdk-api/efd675484fa54beca5fa12f92072a230-f685515c41f945f8a2c298bf72889fa9.ttf"  # 替换为实际URL
    response = requests.get(font_url)
    with open("encrypted_font.woff", "wb") as f:
        f.write(response.content)

    font = TTFont("encrypted_font.woff")
    cmap = font.getBestCmap()  # 获取 Unicode 到字形名称的映射
    print("字符编码到字形的映射:", cmap)
    '''
    {46: 'period',
     58203: 'uniE35B', 4
     58222: 'uniE36E', 2
     58519: 'uniE497', 3
     58573: 'uniE4CD', 8
     58925: 'uniE62D', 7
     58954: 'uniE64A', 0
     60501: 'uniEC55', 1
     60666: 'uniECFA', 5
     60934: 'uniEE06', 6
     61022: 'uniEE5E'9
     002E:"."}
    '''

    print(int("002E",16))
    custom_map = {
    60501: '1',  # 注意:Unicode码位需转换为十进制
    58222: '2',
    58519: '3',
    58203: '4',
    60666: '5',
    60934: '6',
    58925: '7',
    58573: '8',
    61022: '9',
    58954: '0',
    46:'.'

    # 继续添加其他字符...
}
    # 假设加密文本是Unicode字符 "\ue901\ue902"
    #encrypted_text = "" #24053
    #encrypted_text="" #17278

    encrypted_text="" #5505

    decrypted = decrypt_text(encrypted_text, custom_map)
    print(decrypted)  # 预期输出: "5005"








    pass

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值