如何在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']
原有代码存在三个核心问题:
- 解析XML时使用
lxml解析器会自动将标签和属性名转为小写,且没有将同属一个residue节点下的PDB和UniProt信息关联,导致取值混乱 not_null函数使用正则判断冗余,且过滤逻辑应用范围错误- 没有初始化结果列表,每次循环只会覆盖变量,无法收集所有符合条件的值
修正后代码
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
相关产品推荐
相关产品推荐

