使用 BeautifulSoup 中的条件从不同标签获取值
Get values from different tags using a condition in BeautifulSoup
我有一个这样的 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>"""
我想做的是从与 dbSource="PDB" 和 dbSource="UniProt" 相同的行中检索 dbResNum 值。但是,只有在 dbSource="PDB" 行中没有 dbResNum="null" 并且它与 dbSource="UniProt" 行中的 dbAccessionId="P0DTC2" 没有任何不同的值时,我才需要这些值。所以,我的输出将是这样的:
PDB_Res = '13', '16'
Uniprot_Res = '14, '16'
我尝试这样做的代码:
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']
问题是函数 not_null 没有正常工作。欢迎任何想法。
首先,需要将解析器从“lxml”更改为“xml”。
lxml是lxml.html.soupparser的别名,是HTML.
的别名
另请注意,您问题中的 XML 片段是无效的 XML 文件,没有结束段或 listResidue 元素。没有根 XML 元素,只有 2 个无效的实体元素。 BeautifulSoup 处理无效文档,但始终建议尽可能从有效的 XML 文档开始。
如果要跳过整个 residue 组和其中的所有 crossRefDb children 则需要遍历所有residue 并检查是否有任何 child crossRefDb 在 dbSource="PDB".[= 同一行的 dbresnum 中有一个空值12=]
尝试这样的事情:
from bs4 import BeautifulSoup
data = '''<entity ...'''
soup = BeautifulSoup(data, 'xml')
uni_res = set()
pdb_res = set()
for residue in soup.find_all('residue'):
if residue.find('crossRefDb', {'dbSource': 'PDB', 'dbResNum': 'null'}):
continue
for cross in residue.find_all('crossRefDb'):
res_num = cross.get('dbResNum')
if cross.get("dbAccessionId") == "P0DTC2":
uni_res.add(res_num)
if cross.get("dbSource") == "PDB":
pdb_res.add(res_num)
print("PDB_Res =", pdb_res, "Uniprot =", uni_res)
输出:
PDB_Res = {'1', '16', '14'} Uniprot_Res = {'16', '13'}
我有一个这样的 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>"""
我想做的是从与 dbSource="PDB" 和 dbSource="UniProt" 相同的行中检索 dbResNum 值。但是,只有在 dbSource="PDB" 行中没有 dbResNum="null" 并且它与 dbSource="UniProt" 行中的 dbAccessionId="P0DTC2" 没有任何不同的值时,我才需要这些值。所以,我的输出将是这样的:
PDB_Res = '13', '16' Uniprot_Res = '14, '16'
我尝试这样做的代码:
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']
问题是函数 not_null 没有正常工作。欢迎任何想法。
首先,需要将解析器从“lxml”更改为“xml”。
lxml是lxml.html.soupparser的别名,是HTML.
另请注意,您问题中的 XML 片段是无效的 XML 文件,没有结束段或 listResidue 元素。没有根 XML 元素,只有 2 个无效的实体元素。 BeautifulSoup 处理无效文档,但始终建议尽可能从有效的 XML 文档开始。
如果要跳过整个 residue 组和其中的所有 crossRefDb children 则需要遍历所有residue 并检查是否有任何 child crossRefDb 在 dbSource="PDB".[= 同一行的 dbresnum 中有一个空值12=]
尝试这样的事情:
from bs4 import BeautifulSoup
data = '''<entity ...'''
soup = BeautifulSoup(data, 'xml')
uni_res = set()
pdb_res = set()
for residue in soup.find_all('residue'):
if residue.find('crossRefDb', {'dbSource': 'PDB', 'dbResNum': 'null'}):
continue
for cross in residue.find_all('crossRefDb'):
res_num = cross.get('dbResNum')
if cross.get("dbAccessionId") == "P0DTC2":
uni_res.add(res_num)
if cross.get("dbSource") == "PDB":
pdb_res.add(res_num)
print("PDB_Res =", pdb_res, "Uniprot =", uni_res)
输出:
PDB_Res = {'1', '16', '14'} Uniprot_Res = {'16', '13'}