news 2026/7/14 10:24:41

实战解析:黄页88网站字体加密的逆向破解与Python实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
实战解析:黄页88网站字体加密的逆向破解与Python实现

1. 字体加密现象解析与实战场景

第一次爬取黄页88网站时,我盯着源码里那串"򈍃򈍇"的加密字符愣了半天——明明网页显示着清晰的手机号码,为什么源码却像天书?这就是典型的字体加密反爬技术。去年处理某电商平台数据时,我也遇到过类似情况:价格显示为"¥꯭꯱꯲꯸",源码却是""。

字体加密的原理其实很巧妙。网站通过自定义字体文件,将关键数据(如手机号、价格)的视觉形态与存储形态分离。就像密码本一样,浏览器能正确"翻译"这些字符,而直接抓取的爬虫只能得到乱码。黄页88的加密特点在于:

  • 采用16进制编码格式(如x88343)
  • 数字与编码非固定对应关系
  • 每个页面都携带独立的字体文件

实际爬虫工作中,这类加密常见于:

  • 企业联系方式(黄页类网站)
  • 商品价格(电商平台)
  • 票房数据(影视网站)
  • 招聘薪资(求职平台)

最近帮客户爬取某B2B平台时,发现他们甚至动态变更字体映射规则:同一编码在上午对应数字3,下午却变成7。这促使我研究出一套更健壮的解决方案,接下来就分享具体破解流程。

2. 逆向分析关键步骤详解

2.1 定位字体文件

首先用Chrome开发者工具检查元素,发现加密数字的HTML结构是这样的:

<span class="secret">&amp;#x88343;&amp;#x88347;...</span>

在Network面板过滤"font"类型请求,很快找到关键线索——一个.woff字体文件请求。有趣的是,黄页88的字体文件竟然直接放在页面CSS中:

@font-face { font-family: 'huangye-secret'; src: url('data:font/woff;base64,d09GRgABAAAAA...') format('woff'); }

这个base64字符串就是我们要找的字体文件。相比需要二次请求的woff文件,这种内联方式反而更方便爬虫处理。上周分析某招聘网站时,他们的字体文件需要动态计算URL参数,处理起来复杂得多。

2.2 解析字体映射关系

拿到base64数据后,我用Python的fontTools库进行解析:

from fontTools.ttLib import TTFont import base64 def parse_font(base64_str): font_data = base64.b64decode(base64_str) with open('temp.woff', 'wb') as f: f.write(font_data) font = TTFont('temp.woff') cmap = font['cmap'].getBestCmap() return {hex(k): v for k,v in cmap.items()}

执行后会得到类似这样的映射表:

{ '0x88343': 'glyph00001', '0x88347': 'glyph00002', ... }

关键在于发现glyph00001对应数字1,glyph00002对应数字3(具体规律需要分析XML)。有次我遇到更狡猾的情况——字形名称用英文单词表示数字(如'one'、'two'),这时需要额外建立映射:

WORD_MAP = {'one':1, 'two':2, ..., 'nine':9}

2.3 动态变化应对策略

黄页88的字体每周更新编码规则,但通过分析发现两个有用特征:

  1. 同一页面内的编码始终保持一致
  2. 数字字形轮廓不变(可用OCR识别)

因此我改进了解析流程:

  1. 首次遇到新字体时保存样本
  2. 自动提取数字0-9的字形特征
  3. 建立特征编码对应表
from PIL import Image, ImageDraw def extract_glyph_features(font): features = {} for i in range(10): glyph = font['glyf'][f'glyph0000{i+1}'] coords = glyph.coordinates if hasattr(glyph, 'coordinates') else [] features[i] = hash(tuple(coords)) # 简化特征处理 return features

3. Python完整实现方案

3.1 基础解密函数

结合上述分析,先实现核心解密功能:

import re from collections import defaultdict class FontDecoder: def __init__(self): self.font_map = defaultdict(dict) def decrypt(self, encrypted_text, font_base64): # 提取编码字符 codes = re.findall(r'&amp;#x(\w+);', encrypted_text) if not codes: return encrypted_text # 获取当前页面的字体映射 cmap = self._get_cmap(font_base64) # 转换为实际数字 result = [] for code in codes: glyph_name = cmap.get(f'0x{code}') if glyph_name and glyph_name in self.digit_map: result.append(str(self.digit_map[glyph_name])) else: result.append('?') # 未知字符占位 return ''.join(result)

3.2 自动映射生成

对于动态字体,增加自动学习功能:

def _analyze_font(self, font): # 获取0-9的数字字形 glyph_order = font.getGlyphOrder()[1:11] # 通常前10个是数字 # 人工验证或OCR识别 for i, name in enumerate(glyph_order): self.digit_map[name] = i # 保存特征用于后续匹配 self.font_features.append({ 'hash': self._font_hash(font), 'map': dict(self.digit_map) })

3.3 实战案例测试

用实际页面数据测试:

if __name__ == '__main__': # 示例数据 encrypted_phone = '&amp;#x88343;&amp;#x88347;&amp;#x8834a;&amp;#x88342;...' font_data = 'd09GRgABAAAAA...' # 实际base64数据 decoder = FontDecoder() print(decoder.decrypt(encrypted_phone, font_data)) # 输出:13800138000

处理带区号的号码时需要额外注意:

def format_phone(decrypted): if len(decrypted) == 13: # 带区号格式 return f"{decrypted[:4]}-{decrypted[4:]}" return decrypted

4. 高级技巧与异常处理

4.1 性能优化方案

频繁解析字体文件会影响效率,我通过三种方式优化:

  1. 缓存机制:对字体特征值建立MD5索引
import hashlib def _font_hash(self, font): key = ''.join(font.getGlyphOrder()) return hashlib.md5(key.encode()).hexdigest()
  1. 多线程处理:使用concurrent.futures加速批量解析
from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(4) as executor: results = list(executor.map(decrypt, phones_list))
  1. 预处理字体库:提前收集常见字体特征

4.2 常见异常处理

异常类型触发场景解决方案
字体格式变化网站更新woff为ttf统一转换为TTFont对象
编码偏移数字从glyph00002开始动态检测起始位置
混合加密部分数字未加密正则表达式混合处理

典型错误处理代码:

try: font = TTFont(BytesIO(font_data)) except TTLibError: # 尝试其他字体格式 font = TTFont(BytesIO(font_data), fontNumber=0)

4.3 日志监控建议

建立监控机制记录异常情况:

import logging logging.basicConfig( filename='font_decrypt.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) def decrypt(self, text): try: # ...解密逻辑 except Exception as e: logging.error(f"解密失败: {text[:20]}... 错误: {str(e)}") return text # 返回原文

最近三个月日志显示,这套方案的首次解密成功率达92%,经过自动学习后提升到99.7%。对于特别复杂的案例,我会保存原始HTML和字体文件进行后续分析。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/14 10:22:29

在Linux上部署Switch模拟器:从零开始的性能调优与避坑指南

1. 为什么选择Linux运行Switch模拟器&#xff1f; 作为一个在Linux上折腾Switch模拟器多年的老玩家&#xff0c;我可以很负责任地说&#xff1a;Linux平台运行yuzu或Ryujinx模拟器的体验&#xff0c;远比大多数人想象的要好。特别是在Manjaro、Arch这类滚动更新的发行版上&…

作者头像 李华
网站建设 2026/7/14 10:22:16

GalaxyBudsClient固件管理:解锁三星耳机完整潜能的进阶指南

GalaxyBudsClient固件管理&#xff1a;解锁三星耳机完整潜能的进阶指南 【免费下载链接】GalaxyBudsClient Unofficial Galaxy Buds Manager for Windows, macOS, Linux, and Android 项目地址: https://gitcode.com/gh_mirrors/ga/GalaxyBudsClient 你是否曾因三星官方…

作者头像 李华
网站建设 2026/7/14 10:21:52

如何快速掌握DLSS Swapper:面向新手的游戏性能优化终极指南

如何快速掌握DLSS Swapper&#xff1a;面向新手的游戏性能优化终极指南 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper DLSS Swapper是一款功能强大的Windows工具&#xff0c;专门用于管理和切换游戏中的DLSS、FSR和Xe…

作者头像 李华
网站建设 2026/7/14 10:21:08

AI推理成本优化:模型压缩与运行时技术解析

1. AI推理成本暴跌背后的技术驱动力18个月内实现240倍的成本下降&#xff0c;这种指数级进步在技术史上极为罕见。作为深度参与AI基础设施建设的从业者&#xff0c;我亲眼见证了这场变革如何从实验室走向产业化。核心突破来自三个技术栈的协同进化&#xff1a;1.1 模型压缩技术…

作者头像 李华