2. 报告文件结构解析:HTML vs 纯文本输出

拿到 MolProbidity 报告,你会发现有两种输出格式:HTML 和纯文本。这个选择不是随便来的。

我刚开始做批量解析的时候,第一反应是「当然用 HTML,结构完整、标签清晰」。结果跑了几个案例就发现,HTML 版本虽然看着漂亮,真要批量提取数据,反而比纯文本更「折腾」。这章我就带你摸透这两种格式的门道。

2.1 两种格式,各有脾气

我把自己用过的感受总结成了一张对比表,你看看就明白了:

对比维度 HTML 报告 纯文本报告
文件大小 较大(含 CSS/JS) 小,干净利落
结构化程度 标签清晰,DOM 可遍历 靠缩进/分隔符定义
数据提取难度 需要解析 HTML/去除样式 正则一把梭,简单直接
可读性 加分页、色彩、图表 纯黑字,但信息完整
批量处理适配 中(需用 BeautifulSoup) 高(行级解析)
常见产出场景 单结构展示 批量流水线、脚本集成

我曾经踩过的坑:HTML 文件内包含动态图表(JavaScript 绘制),我用 requests 抓回来发现显示不全——渲染节点在浏览器里才生成。如果直接基于 HTML 文件提取,那些图表后面的数据可能直接就丢了。

2.2 主要数据块,都有哪些?

不管格式怎么变,MolProbity 报告的核心数据块就那几块。你就记住这个框架:

MolProbity 报告核心数据块结构 MolProbity 报告 HTML 版本 纯文本版本 Ramachandran Rotamer Clashscore MolProbity Score favored + allowed outliers 占比 所有原子对 综合打分 注意: 所有数据块在纯文本中通过行首注释符 (#) 或分隔线 (-----) 标识。

说白了,你真正需要关心的是这四个数据块。不管是哪种格式,你的解析脚本最终目标就是把这四个值从报告里「抠」出来。

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 结构变了,你的解析器就废了。但纯文本格式变化极小,基本是隔几年才调整一次分隔符。这就是工程上常说的「稳健性优先于便利性」。