You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在BeautifulSoup中按条件提取不同标签的dbResNum属性值

问题背景

现有如下XML格式数据:

data =   """<entity type="protein" entityId="A">
    <segment segId="7ddd_A_1_1208" start="1" end="1208">
      <listResidue>
        <residue dbSource="PDBe" dbCoordSys="PDBe" dbResNum="1" dbResName="MET">
          <crossRefDb dbSource="PDB" dbCoordSys="PDBresnum" dbAccessionId="7ddd" dbResNum="null" dbResName="MET" dbChainId="A"/>
          <crossRefDb dbSource="UniProt" dbCoordSys="UniProt" dbAccessionId="P0DTC2" dbResNum="1" dbResName="M"/>
          <crossRefDb dbSource="NCBI" dbCoordSys="UniProt" dbAccessionId="2697049" dbResNum="1" dbResName="M"/>
          <residueDetail dbSource="PDBe" property="Annotation">Not_Observed</residueDetail>
        </residue>
        <residue dbSource="PDBe" dbCoordSys="PDBe" dbResNum="14" dbResName="GLN">
          <crossRefDb dbSource="PDB" dbCoordSys="PDBresnum" dbAccessionId="7ddd" dbResNum="14" dbResName="GLN" dbChainId="A"/>
          <crossRefDb dbSource="UniProt" dbCoordSys="UniProt" dbAccessionId="P0DTC2" dbResNum="13" dbResName="Q"/>
          <crossRefDb dbSource="NCBI" dbCoordSys="UniProt" dbAccessionId="2697049" dbResNum="14" dbResName="Q"/>
          <residueDetail dbSource="PDBe" property="codeSecondaryStructure">T</residueDetail>
          <residueDetail dbSource="PDBe" property="nameSecondaryStructure">loop</residueDetail>
        </residue>
  </entity>
  <entity type="protein" entityId="B">
    <segment segId="7ddd_B_1_1208" start="1" end="1208">
      <listResidue>
        <residue dbSource="PDBe" dbCoordSys="PDBe" dbResNum="1" dbResName="MET">
          <crossRefDb dbSource="PDB" dbCoordSys="PDBresnum" dbAccessionId="7ddd" dbResNum="1" dbResName="MET" dbChainId="B"/>
          <crossRefDb dbSource="UniProt" dbCoordSys="UniProt" dbAccessionId="PXCDT" dbResNum="1" dbResName="M"/>
          <crossRefDb dbSource="NCBI" dbCoordSys="UniProt" dbAccessionId="2697049" dbResNum="1" dbResName="M"/>
          <residueDetail dbSource="PDBe" property="Annotation">Not_Observed</residueDetail>
        </residue>
        <residue dbSource="PDBe" dbCoordSys="PDBe" dbResNum="16" dbResName="VAL">
          <crossRefDb dbSource="PDB" dbCoordSys="PDBresnum" dbAccessionId="7ddd" dbResNum="16" dbResName="VAL" dbChainId="B"/>
          <crossRefDb dbSource="UniProt" dbCoordSys="UniProt" dbAccessionId="P0DTC2" dbResNum="16" dbResName="V"/>
          <crossRefDb dbSource="NCBI" dbCoordSys="UniProt" dbAccessionId="2697049" dbResNum="16" dbResName="V"/>
          <residueDetail dbSource="PDBe" property="codeSecondaryStructure">T</residueDetail>
          <residueDetail dbSource="PDBe" property="nameSecondaryStructure">loop</residueDetail>
        </residue>"""
提取要求

需要提取crossRefDb节点中dbSource为"PDB"和"UniProt"的dbResNum属性值,满足以下两个条件:

  • PDB对应节点的dbResNum值不为"null"
  • UniProt对应节点的dbAccessionId必须为"P0DTC2"
原有实现及问题

原有代码如下:

from bs4 import BeautifulSoup
import re
def not_null(dbresnum):
    return dbresnum and not re.compile("null").search(dbresnum)

xml_file = BeautifulSoup(data, 'lxml')
XML_without_null = xml_file.find_all(dbresnum=not_null)
for cross in XML_without_null:
    if cross('crossrefdb', {"dbaccessionid":"P0DTC2"}):
        Uniprot_id = cross['dbresnum']
    if cross('crossrefdb', {"dbsource":"PDB"}):
        PDB_id = cross['dbresnum']

原有代码存在三个核心问题:

  1. 解析XML时使用lxml解析器会自动将标签和属性名转为小写,且没有将同属一个residue节点下的PDB和UniProt信息关联,导致取值混乱
  2. not_null函数使用正则判断冗余,且过滤逻辑应用范围错误
  3. 没有初始化结果列表,每次循环只会覆盖变量,无法收集所有符合条件的值
修正后代码
from bs4 import BeautifulSoup

# 初始化结果列表
pdb_res = []
uniprot_res = []

# 使用lxml-xml解析器,保留原标签和属性的大小写
soup = BeautifulSoup(data, 'lxml-xml')

# 遍历所有残基节点
for residue in soup.find_all('residue'):
    # 查找当前残基下的PDB节点
    pdb_node = residue.find('crossRefDb', {'dbSource': 'PDB'})
    # 查找当前残基下符合条件的UniProt节点
    uniprot_node = residue.find('crossRefDb', {'dbSource': 'UniProt', 'dbAccessionId': 'P0DTC2'})
    
    # 两个节点都存在且PDB的dbResNum不为null时,收集对应值
    if pdb_node and uniprot_node and pdb_node.get('dbResNum') != 'null':
        pdb_res.append(pdb_node['dbResNum'])
        uniprot_res.append(uniprot_node['dbResNum'])

# 格式化输出结果
print(f"PDB_Res = {', '.join(repr(i) for i in pdb_res)}")
print(f"Uniprot_Res = {', '.join(repr(i) for i in uniprot_res)}")
输出结果
PDB_Res = '14', '16'
Uniprot_Res = '13', '16'

注:原需求给出的期望输出两个列表值写反,上述输出为匹配XML数据的正确结果。

内容的提问来源于stack exchange,提问作者Fabiano Almeida

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 13:48:03