
本文讲解如何使用Python和BeautifulSoup解析HTML文件中被HTML实体编码(如)包裹的嵌套DOM结构,解决因内容被双重编码导致find_all()无法定位目标class的问题。
本文讲解如何使用python和beautifulsoup解析html文件中被html实体编码(如`<div>`)包裹的嵌套dom结构,解决因内容被双重编码导致`find_all()`无法定位目标class的问题。
在实际网页抓取或静态HTML解析任务中,有时会遇到一种“伪装型”HTML结构:外层是合法HTML,但其文本内容(例如 <p> 标签内的 text)并非纯文本,而是经过HTML实体编码的另一层HTML源码(如
此时,若直接用 BeautifulSoup(html_content, "html.parser") 解析整个文件,BeautifulSoup只会将 <p class="p1"> 视为一个普通段落标签,而其内部所有 , " 等均被当作纯文本字符处理——因此 soup.find_all('div', class_='PlayerNameCell_player-name-cell') 返回空列表,因为真实 <div> 标签根本未被解析为DOM节点。
✅ 正确解法分三步:
- 定位外层容器(如 <p class="p1">);
- 对容器内文本进行HTML实体解码(html.unescape()),还原为原始HTML字符串;
- 用BeautifulSoup重新解析该字符串,此时深层结构才真正可被索引。
以下是完整、健壮的实现示例:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
立即学习“前端免费学习笔记(深入)”;
import html
from bs4 import BeautifulSoup
# 读取原始HTML文件
with open('my_file.html', 'r', encoding='utf-8') as f:
soup = BeautifulSoup(f, 'html.parser')
# 步骤1:定位包含编码HTML的外层p标签
p_tag = soup.find('p', class_='p1')
if not p_tag:
raise ValueError("未找到 class='p1' 的 <p> 标签")
# 步骤2:解码HTML实体(< → <, > → >, " → ", etc.)
encoded_html = p_tag.get_text() # 或 p_tag.string,确保获取纯文本内容
decoded_html = html.unescape(encoded_html)
# 步骤3:重新解析解码后的HTML
inner_soup = BeautifulSoup(decoded_html, 'html.parser')
# ✅ 现在可以正常查找深层class
player_cells = inner_soup.find_all('div', class_='PlayerNameCell_player-name-cell')
exposure_x_cells = inner_soup.find_all('div', class_='PlayerExposureTable_detail-cell')
# 注意:exposure_x 和 exposure_y 共享同一class,需按位置配对(见下方逻辑)为准确提取 player, team, position, exposure_x, exposure_y 五元组,建议结合结构层级提取(而非依赖全局class匹配):
rows = inner_soup.find_all('div', role='row', class_=lambda x: x and 'PlayerExposureTable_dk-grid-row' in x)
results = []
for row in rows:
# 提取球员信息单元格(第一个gridcell)
name_cell = row.find('div', role='gridcell', class_=lambda x: x and 'PlayerNameCell_player-name-cell' in str(x))
if not name_cell:
continue
# 解析姓名、球队、位置
player_name = name_cell.find('div', class_='PlayerNameCell_player-name').get_text(strip=True) if name_cell.find('div', class_='PlayerNameCell_player-name') else ''
team_abbr = name_cell.find('div', class_='PlayerNameCell_position-and-team').find_all('div')[2].get_text(strip=True) if name_cell.find('div', class_='PlayerNameCell_position-and-team') else ''
position = name_cell.find('div', class_='PlayerNameCell_position-and-team').find_all('div')[0].get_text(strip=True) if name_cell.find('div', class_='PlayerNameCell_position-and-team') else ''
# 提取两个exposure值(第二个和第三个gridcell)
detail_cells = row.find_all('div', role='gridcell', class_=lambda x: x and 'PlayerExposureTable_detail-cell' in str(x))
exposure_x = detail_cells[0].get_text(strip=True) if len(detail_cells) > 0 else ''
exposure_y = detail_cells[1].get_text(strip=True) if len(detail_cells) > 1 else ''
results.append(f"{player_name}, {team_abbr}, {position}, {exposure_x}, {exposure_y}")
# 输出结果
for line in results:
print(line)⚠️ 关键注意事项:
- class_ 是BeautifulSoup中用于参数传递的关键字(因 class 是Python保留字),但在CSS选择器或字典键中应直接写 'class'(如 {'class': 'xxx'});
- 始终指定 encoding='utf-8' 打开文件,避免中文乱码;
- 使用 class_=lambda x: ... 进行模糊匹配,比严格字符串匹配更鲁棒(因某些class可能含动态后缀);
- p_tag.get_text() 比 p_tag.text 更安全,能自动合并子节点文本并处理换行/空白;
- 若HTML结构高度动态,建议优先使用CSS选择器(如 inner_soup.select('div[role="row"]'))提升可维护性。
通过这一流程,即可可靠地穿透HTML实体编码层,精准提取深层嵌套结构中的业务数据。


















