您的位置:首页 > 教育 > 培训 > 建筑论坛_计算机软件开发培训机构_网站建设优化哪家公司好_手机自动排名次的软件

建筑论坛_计算机软件开发培训机构_网站建设优化哪家公司好_手机自动排名次的软件

2025/4/27 18:07:09 来源:https://blog.csdn.net/u014497060/article/details/147543872  浏览:    关键词:建筑论坛_计算机软件开发培训机构_网站建设优化哪家公司好_手机自动排名次的软件
建筑论坛_计算机软件开发培训机构_网站建设优化哪家公司好_手机自动排名次的软件

加密字体替换是网站反爬虫的常用技术之一,其核心是通过自定义字体文件对关键数据(如数字、文字)进行动态渲染,使源码中显示的字符与用户实际看到的内容不一致。下面从技术原理、实现类型和破解方法三个方向展开分析,并提供丰富的代码示例。

一、技术原理与核心逻辑

(一)字体映射机制

网站通过自定义字体文件(如.woff、.ttf)定义字符的渲染规则。

# 示例:字体文件定义的映射关系
unicode_map = {'\ue000': '0','\ue001': '1','\ue002': '2',# ...'\ue009': '9'
}
  • 源码中的 &#x518b(Unicode码)可能被映射为实际显示的字符“地”。

  • 数字“1”在字体文件中可能被编码为特殊符号(如“”),需通过字形匹配还原。

(二)动态更新策略

部分网站(如58同城、起点中文网)每次请求会生成新的字体文件,导致映射关系动态变化。

# 模拟动态字体文件获取
import requests
from io import BytesIO
from fontTools.ttLib import TTFontdef download_font(url):"""下载字体文件"""response = requests.get(url)return TTFont(BytesIO(response.content))# 使用示例:在每次请求时重新下载并解析字体文件
font_url = "https://example.com/fonts/dynamic_font.woff"
font = download_font(font_url)
cmap = font.getBestCmap()
  • 同一数字“5”在不同会话中可能对应不同的Unicode码。

(三)反OCR设计

通过扭曲字形、添加噪点或粘连字符,增加OCR识别难度。

# 示例:扭曲字形的特征
from PIL import Image, ImageDrawdef draw_distorted_digit(digit, distortion_factor=0.5):"""绘制扭曲的数字图像"""image = Image.new('L', (50, 50))draw = ImageDraw.Draw(image)# 根据数字生成基本形状if digit == '3':draw.polygon([(10,10), (40,10), (40,40), (10,40)], fill=255)# 添加扭曲效果draw.arc((15,15,35,35), start=0, end=270, fill=0)elif digit == '8':draw.ellipse((10,10,40,40), fill=255)draw.ellipse((15,15,35,35), fill=0)# 更多数字扭曲逻辑...return image# 生成扭曲的“3”和“8”用于演示
image_3 = draw_distorted_digit('3')
image_8 = draw_distorted_digit('8')
  • 字符“3”和“8”可能设计为相似轮廓,干扰传统OCR工具。

二、加密字体替换的常见类型

(一)动态字体加密

特点:每次访问生成不同的字体文件,映射关系不固定。

案例:律师执业诚信平台(credit.acla.org.cn)的字体文件会动态更新,需实时解析。

# 动态字体解析流程
from selenium import webdriver
import timedef get_dynamic_font_mapping():"""获取动态字体映射"""driver = webdriver.Chrome()driver.get("https://credit.acla.org.cn")time.sleep(2)  # 等待字体加载# 提取字体文件URL(通常在页面的CSS中)font_urls = []for style in driver.find_elements_by_tag_name('style'):if 'font-face' in style.get_attribute('innerText'):# 提取字体文件URL的正则表达式import rematches = re.findall(r'url\((.*?)\)', style.get_attribute('innerText'))for url in matches:if url.endswith(('.woff', '.ttf')):font_urls.append(url)# 下载并解析字体文件mappings = {}for font_url in font_urls:font = download_font(font_url)cmap = font.getBestCmap()# 构建映射关系for unicode_char, glyph_name in cmap.items():# 假设数字映射到特定前缀的字形名称if glyph_name.startswith('num_'):actual_digit = glyph_name.split('_')[1]mappings[chr(unicode_char)] = actual_digitdriver.quit()return mappings# 使用示例
dynamic_mapping = get_dynamic_font_mapping()
print(dynamic_mapping)

(二)静态字体加密

特点:字体文件固定,但字符与实际显示内容不一致。

案例:黄页网站(huangye88.com)使用静态加密,通过解析.woff文件可建立永久映射表。

# 静态字体解析
from fontTools.ttLib import TTFontdef build_static_mapping(font_path):"""构建静态字体映射表"""font = TTFont(font_path)cmap = font.getBestCmap()mapping = {}# 假设数字映射到特定前缀的字形名称for unicode_char, glyph_name in cmap.items():if glyph_name.startswith('num_'):actual_digit = glyph_name.split('_')[1]mapping[chr(unicode_char)] = actual_digitreturn mapping# 使用示例
static_mapping = build_static_mapping('huangye88_static.woff')
print(static_mapping)

(三)Base64内嵌字体

特点:字体文件以Base64编码形式嵌入CSS或JavaScript中。

破解步骤:提取Base64字符串 → 解码为二进制文件 → 解析字体映射关系。

# 提取并解析Base64内嵌字体
import base64
from io import BytesIOdef extract_base64_font(css_content):"""从CSS中提取Base64字体"""import rematches = re.findall(r'data:font/woff;base64,(.*?)\'\)', css_content)for match in matches:font_data = base64.b64decode(match)with open('extracted_font.woff', 'wb') as f:f.write(font_data)return 'extracted_font.woff'return None# 使用示例:从CSS内容中提取字体文件
css_content = """
@font-face {font-family: 'embedded_font';src: url('data:font/woff;base64,...');
}
"""
font_path = extract_base64_font(css_content)
if font_path:static_mapping = build_static_mapping(font_path)print(static_mapping)

三、破解方法与实战代码示例

(一)字体文件解析与映射

工具:使用Python的fontTools库解析字体文件。

from fontTools.ttLib import TTFont# 解析字体文件并提取字符映射
font = TTFont("encrypted.woff")
cmap = font.getBestCmap()  # 获取Unicode与字形名称的映射# 假设数字映射到特定前缀的字形名称
relation_table = {'zero': '0','one': '1','two': '2','three': '3','four': '4','five': '5','six': '6','seven': '7','eight': '8','nine': '9'
}# 构建最终映射表
final_mapping = {}
for unicode_char, glyph_name in cmap.items():if glyph_name in relation_table:final_mapping[chr(unicode_char)] = relation_table[glyph_name]print(final_mapping)

(二)动态更新处理

若字体文件频繁变化,需在每次请求时重新下载并解析。

# 动态字体更新处理
import requests
from fontTools.ttLib import TTFont
from io import BytesIOdef get_current_font_mapping(font_url):"""获取当前字体映射"""response = requests.get(font_url)font = TTFont(BytesIO(response.content))cmap = font.getBestCmap()final_mapping = {}for unicode_char, glyph_name in cmap.items():if glyph_name in relation_table:final_mapping[chr(unicode_char)] = relation_table[glyph_name]return final_mapping# 使用示例:在每次请求前更新映射
font_url = "https://example.com/fonts/dynamic_font.woff"
current_mapping = get_current_font_mapping(font_url)

(三)字形特征匹配

步骤:提取加密字符的坐标点(contour节点中的x、y、on属性),与标准字体库对比相似度。

# 字形特征提取与匹配
from fontTools.pens.recordingPen import RecordingPendef get_glyph_contours(font, unicode_char):"""获取字形的轮廓数据"""glyph_name = font.getBestCmap().get(ord(unicode_char))if not glyph_name:return Nonepen = RecordingPen()font[glyph_name].draw(pen)return pen.valuedef calculate_similarity(encrypted_contours, standard_contours):"""计算轮廓相似度(简化版)"""if len(encrypted_contours) != len(standard_contours):return float('inf')total_diff = 0for enc_op, std_op in zip(encrypted_contours, standard_contours):if enc_op[0] != std_op[0]:  # 操作类型不同total_diff += 1else:# 比较坐标点enc_args = enc_op[1]std_args = std_op[1]for enc_coord, std_coord in zip(enc_args, std_args):total_diff += abs(enc_coord[0] - std_coord[0]) + abs(enc_coord[1] - std_coord[1])return total_diff# 使用示例:匹配加密字符与标准字符
standard_font = TTFont("standard_font.woff")
encrypted_font = TTFont("encrypted_font.woff")standard_contours = {}
for char in '0123456789':standard_contours[char] = get_glyph_contours(standard_font, char)encrypted_char = '\ue005'
encrypted_contours = get_glyph_contours(encrypted_font, encrypted_char)matched_char = match_glyph(encrypted_contours, standard_contours)
print(f"匹配结果: {encrypted_char} → {matched_char}")

(四)自动化替换与数据还原

流程:解析网页源码 → 提取加密字符 → 替换为真实值。

# 自动化数据还原
from bs4 import BeautifulSoupdef decrypt_text(encrypted_text, mapping):"""解密文本"""decrypted = []for char in encrypted_text:decrypted.append(mapping.get(char, char))  # 保留未映射的字符return ''.join(decrypted)# 使用示例:解析网页并还原加密数据
encrypted_html = """
<div class="price"></div>
<div class="address">北京市朝阳区</div>
"""soup = BeautifulSoup(encrypted_html, 'html.parser')
price_element = soup.find('div', class_='price')
address_element = soup.find('div', class_='address')# 假设我们已经构建了映射表
current_mapping = {'\ue004': '5','\ue005': '3','\ue006': '8'
}decrypted_price = decrypt_text(price_element.text, current_mapping)
decrypted_address = decrypt_text(address_element.text, current_mapping)print(f"加密价格: {price_element.text} → 解密后: {decrypted_price}")
print(f"加密地址: {address_element.text} → 解密后: {decrypted_address}")

四、进阶策略与实战优化

(一)多线程实时更新

在面对频繁更新的字体文件时,可以使用多线程技术实时监控和更新映射表。

import threading
from queue import Queue
import timeclass FontUpdaterThread(threading.Thread):def __init__(self, font_url, update_interval=60):threading.Thread.__init__(self)self.font_url = font_urlself.update_interval = update_intervalself.mapping = {}self._stop_event = threading.Event()def run(self):while not self._stop_event.is_set():try:self.mapping = get_current_font_mapping(self.font_url)print("字体映射已更新")except Exception as e:print(f"更新字体映射失败: {e}")finally:time.sleep(self.update_interval)def stop(self):self._stop_event.set()# 使用示例:启动字体更新线程
font_updater = FontUpdaterThread("https://example.com/fonts/dynamic_font.woff", update_interval=30)
font_updater.start()# 在主程序中使用当前映射
current_mapping = font_updater.mapping# 停止更新线程
font_updater.stop()
font_updater.join()

(二)结合机器学习的字形匹配

对于复杂变形的字形,可以使用机器学习模型进行特征匹配。

import numpy as np
from sklearn.neighbors import NearestNeighbors# 示例:使用NearestNeighbors进行字形匹配
def vectorize_contours(contours):"""将轮廓数据转换为特征向量"""features = []for op in contours:op_type = op[0]args = op[1]features.append(len(args))  # 操作参数数量for coord in args:features.extend([coord[0], coord[1]])  # 坐标点return np.array(features)# 准备训练数据
X = []
y = []
for char in '0123456789':contours = get_glyph_contours(standard_font, char)X.append(vectorize_contours(contours))y.append(char)# 训练模型
model = NearestNeighbors(n_neighbors=1)
model.fit(X)# 使用模型进行匹配
encrypted_char = '\ue005'
encrypted_contours = get_glyph_contours(encrypted_font, encrypted_char)
X_encrypted = [vectorize_contours(encrypted_contours)]distances, indices = model.kneighbors(X_encrypted)
matched_char = y[indices[0][0]]
print(f"机器学习匹配结果: {encrypted_char} → {matched_char}")

(三)处理字体加密的完整流程

以下是一个处理字体加密的完整示例,模拟从网页请求到数据解密的全过程。

# 完整流程示例:处理字体加密
from selenium import webdriver
from selenium.webdriver.common.by import By
from fontTools.ttLib import TTFont
import requests
from io import BytesIO
import time
from bs4 import BeautifulSoupdef get_font_mapping_from_page(driver):"""从页面获取字体映射"""# 提取字体文件URLfont_urls = []style_elements = driver.find_elements(By.TAG_NAME, 'style')for style in style_elements:style_text = style.get_attribute('innerText')if 'font-face' in style_text and '.woff' in style_text:import rematches = re.findall(r'src: url\((.*?)\)', style_text)for url in matches:if url.endswith('.woff'):font_urls.append(url)# 下载并解析字体文件mappings = {}for font_url in font_urls:try:response = requests.get(font_url)font = TTFont(BytesIO(response.content))cmap = font.getBestCmap()for unicode_char, glyph_name in cmap.items():# 假设数字映射到特定前缀的字形名称if glyph_name.startswith('num_'):actual_digit = glyph_name.split('_')[1]mappings[chr(unicode_char)] = actual_digitexcept Exception as e:print(f"处理字体文件失败: {e}")return mappingsdef decrypt_page_data(driver, url):"""解密页面数据"""driver.get(url)time.sleep(2)  # 等待页面加载# 获取字体映射font_mapping = get_font_mapping_from_page(driver)if not font_mapping:print("未找到字体映射")return# 提取页面内容page_source = driver.page_sourcesoup = BeautifulSoup(page_source, 'html.parser')# 解密价格信息price_elements = soup.find_all('div', class_='price')for element in price_elements:encrypted_text = element.textdecrypted_text = ''.join([font_mapping.get(c, c) for c in encrypted_text])print(f"加密价格: {encrypted_text} → 解密后: {decrypted_text}")# 更多数据解密逻辑...# 使用示例:解密58同城房产信息
driver = webdriver.Chrome()
try:decrypt_page_data(driver, "https://house.58.com/")
finally:driver.quit()

五、总结

加密字体替换是网站常用的反爬虫技术,其核心是通过自定义字体文件改变字符的显示方式。针对不同类型的加密字体,我们可以采用以下策略:

  • 动态字体加密:实时下载并解析字体文件,构建映射表。

  • 静态字体加密:一次性解析字体文件,建立永久映射表。

  • Base64内嵌字体:提取Base64字符串,解码后解析字体文件。

在实际操作中,结合字体文件解析、字形特征匹配和自动化替换技术,可以有效应对加密字体替换带来的挑战。同时,建议使用多线程和机器学习等技术优化处理流程,提高识别准确率和效率。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com