2. 报告文件结构解析:HTML vs 纯文本输出
拿到 MolProbidity 报告,你会发现有两种输出格式:HTML 和纯文本。这个选择不是随便来的。
我刚开始做批量解析的时候,第一反应是「当然用 HTML,结构完整、标签清晰」。结果跑了几个案例就发现,HTML 版本虽然看着漂亮,真要批量提取数据,反而比纯文本更「折腾」。这章我就带你摸透这两种格式的门道。
2.1 两种格式,各有脾气
我把自己用过的感受总结成了一张对比表,你看看就明白了:
| 对比维度 | HTML 报告 | 纯文本报告 |
|---|---|---|
| 文件大小 | 较大(含 CSS/JS) | 小,干净利落 |
| 结构化程度 | 标签清晰,DOM 可遍历 | 靠缩进/分隔符定义 |
| 数据提取难度 | 需要解析 HTML/去除样式 | 正则一把梭,简单直接 |
| 可读性 | 加分页、色彩、图表 | 纯黑字,但信息完整 |
| 批量处理适配 | 中(需用 BeautifulSoup) | 高(行级解析) |
| 常见产出场景 | 单结构展示 | 批量流水线、脚本集成 |
我曾经踩过的坑:HTML 文件内包含动态图表(JavaScript 绘制),我用 requests 抓回来发现显示不全——渲染节点在浏览器里才生成。如果直接基于 HTML 文件提取,那些图表后面的数据可能直接就丢了。
2.2 主要数据块,都有哪些?
不管格式怎么变,MolProbity 报告的核心数据块就那几块。你就记住这个框架:
说白了,你真正需要关心的是这四个数据块。不管是哪种格式,你的解析脚本最终目标就是把这四个值从报告里「抠」出来。
2.3 HTML 报告的幕后结构
如果你打开一份 HTML 报告,别被满屏的 CSS 吓到。核心数据都在 <div> 和 <table> 里。我一般会先看页面源代码,找到类似这样的模式:
<div class="section" id="rama">
<h2>Ramachandran Plot</h2>
<table class="stats">
<tr><td>Favored</td><td>96.50%</td></tr>
<tr><td>Allowed</td><td>3.20%</td></tr>
<tr><td>Outliers</td><td>0.30%</td></tr>
</table>
</div>
这就是 HTML 版本的优势——数据结构体明确,每个区块都有 ID 或 class。但问题也来了:不同版本的 MolProbity,CSS class 命名可能不一样。我遇到过好几次升级后 class 名从 section 改成 molprobity-block,脚本直接崩了。
我的建议:如果是做长期批量解析,尽量避开对 class 名的硬依赖。优先用 id 属性,或者用 h2 的文本内容做定位。
2.4 纯文本:简洁但隐晦
纯文本版本,说白了就是服务器端把格式化后的数据「打印」成了字符串。你下载下来一看,格式是这样的:
# Ramachandran Plot Statistics
-------------------------------
Favored residues : 96.50%
Allowed residues : 3.20%
Outlier residues : 0.30%
# Rotamer Outliers
-------------------------------
Number of outliers : 3
Outlier percentage : 1.85%
# Clashscore
-------------------------------
Number of bad overlaps : 12
Clashscore : 5.30
# MolProbity Score
-------------------------------
Score : 1.82
看到没有?每个数据块以 # 标题开头,然后是分隔线,接着是键值对。
2.5 数据块识别实战——正则就对了
我一般推荐刚入门的同学先从纯文本入手。原因很简单:正则表达式一把就能搞定。
比如抓 Ramachandran 的 Favored 值:
# Python 示例
import re
text = """# Ramachandran Plot Statistics
-------------------------------
Favored residues : 96.50%
Allowed residues : 3.20%
Outlier residues : 0.30%"""
match = re.search(r"Favored residues\s*:\s*([\d.]+)%", text)
if match:
print(f"Favored: {match.group(1)}%")
# 输出: Favored: 96.50%
小技巧:推荐使用 re.DOTALL 模式匹配跨行文本。我习惯先按 # 把一个报告分割成多个数据块,再对每个块单独正则取值,这样不容易串值。
2.6 HTML 解析——工具选不对,白费力气
如果你非要解析 HTML 版本,就别用正则硬干了——用它解析 DOM 非常痛苦。我个人强烈推荐用 BeautifulSoup 加 html5lib 解析器。
from bs4 import BeautifulSoup
html = """<div class="section" id="rama">
<h2>Ramachandran Plot</h2>
<table class="stats">
<tr><td>Favored</td><td>96.50%</td></tr>
<tr><td>Allowed</td><td>3.20%</td></tr>
</table>
</div>"""
soup = BeautifulSoup(html, 'html.parser')
rama_div = soup.find('div', id='rama') # 推荐使用 id
table = rama_div.find('table')
rows = table.find_all('tr')
for row in rows:
cols = row.find_all('td')
if cols[0].text.strip() == 'Favored':
print(f"Favored: {cols[1].text.strip()}")
记得我开头说的那个坑:有些 HTML 报告的表格里混入 <script> 动态插入的行,你用 BeautifulSoup 直接解析可能拿不到。最好在解析前先检查报告是否属于「静态 HTML 输出」模式。
2.7 选择哪种格式?看场景
这个问题其实没有标准答案。我自己的经验是:
- 开发阶段/调试:先用纯文本,速度快、看得清。
- 生产批量解析:如果全自动、不人工干预,纯文本和 HTML 都行,但纯文本更抗版本变化。
- 需要做可视化/看板:HTML 报告的 CSS 和图表可以直接复用,省去自己画图的功夫。
- 需要长期维护:我建议你最终都落地到 纯文本 解析为主,HTML 只用来做二次校验。
你想想看,一个脚本跑几个月,突然某天 MolProbity 升级了,HTML 结构变了,你的解析器就废了。但纯文本格式变化极小,基本是隔几年才调整一次分隔符。这就是工程上常说的「稳健性优先于便利性」。