首页 > 解决方案 > 提取 xml 中的第一个元素 - 使用 xxx.find() 会导致非类型错误?

问题描述

xml 文件的结构基本上是这样的,它是 MARC21-xml 格式的书目数据(各地的图书馆都在使用):

<?xml version="1.0" encoding="UTF-8"?><collection xmlns="http://www.loc.gov/MARC21/slim">
<record type="Bibliographic">
    <leader> ... </leader>
    <controlfield> ... </controlfield>
    ...
    <controlfield> ... </controlfield>
    <datafield tag="123" ... >
        <subfield code="x"> ... </subfield>
        ...
        <subfield code="x"> ... </subfield>
    </datafield>
    <datafield tag="456" ...> 

这里有一个适当的示例文件:https ://www.loc.gov/standards/marcxml/Sandburg/sandburg.xml - 然而,这仅代表一个项目(例如一本特定的书),通常这些文件包含数百到数千记录 - 所以记录标签及其所有内容都是可重复的。

我使用的文件中有超过 10,000 个记录标签(都代表不同的项目),所有这些标签都有一个带有标签“082”的数据字段,然后是几个子字段。我现在正在尝试使用 - 提取子字段中的文本,code="a"但是,由于该字段也是可重复的,并且某些记录有其中两个,所以我总是只想要第一个。我当前的代码提取了code="a"这些数据字段中所有子字段的文本,如下所示:

 for child in record.findall("{http://www.loc.gov/MARC21/slim}datafield[@tag='082']"):
        for subelement in child:
            if subelement.attrib['code'] == "a":
                ddc = subelement.text
                ddccoll.append(ddc)

这可行,但是,正如我所说,返回太多元素,如果我运行它然后打印我的列表的长度,它会返回 10277,但是,这个文件中只有 10123 条记录,所以有一些太多,可能是由于它的可重复性。

我尝试使用find而不是findall,但随后收到错误消息`TypeError:

TypeError                                 Traceback (most recent call last)
<ipython-input-23-fae786776bcf> in <module>
     18         idcoll.append("nicht vorhanden")
     19 
---> 20     for child in record.find("{http://www.loc.gov/MARC21/slim}datafield[@tag='082']"):
     21         for subelement in child:
     22             if subelement.attrib['code'] == "a":

TypeError: 'NoneType' object is not iterable

我不完全确定为什么,因为字段 082 应该出现在每条记录中 - 但由于我实际上是在子字段之后,无论如何这可能不是正确的方法。现在我尝试更深入一层,只需使用以下代码查找第一个子元素,代码为 a:

for child in record.findall("{http://www.loc.gov/MARC21/slim}datafield[@tag='082']"):
    for subelement in child.find("{http://www.loc.gov/MARC21/slim}subfield[@code='a']"):
        if subelement: 
            ddc = subelement.text
            ddccoll.append(ddc)

但是,这不会返回相应的。附加任何内容,如果我之后打印列表的长度,它会显示“0”。我也为作者和 ID 做了同样的事情,它对那些人有效。我正在努力做到这一点,以便之后我可以创建一个包含作者、ID、标题等的数据框。

我目前完全被困在这个:路径错了吗?还有另一种更简单、更好的方法吗?

标签: pythonxmlelementtree

解决方案


我假设您已经使用以下代码阅读了您的 XML:

import xml.etree.ElementTree as et

tree = et.parse('Input.xml')
root = tree.getroot()

要达到您想要的元素,您可以使用以下代码:

# Namespace dictionary
ns = {'slim': 'http://www.loc.gov/MARC21/slim'}
# Process "datafield" elements with the required "tag" attribute
for it in root.findall('.//slim:datafield[@tag="082"]', ns):
    print(f'{it.tag:10}, {it.attrib}')
    # Find the first child with "code" == "a"
    child = it.find('slim:*[@code="a"]', ns)
    if isinstance(child, et.Element):  # Something found
        print(f'  {child.tag:10}, {child.attrib}, {child.text}')
    else:
        print('  Nothing found')

在上面的示例中,我只包含了找到的元素的打印语句,但是您可以对它们做任何您想做的事情。

使用以下源 XML:

<?xml version="1.0" encoding="UTF-8"?>
<collection xmlns="http://www.loc.gov/MARC21/slim">
  <record type="Bibliographic">
    <leader>...</leader>
    <controlfield>...</controlfield>
    <datafield tag="082" id="1">
        <subfield code="a">a1</subfield>
        <subfield code="x">x1</subfield>
        <subfield code="a">a2</subfield>
    </datafield>
    <datafield tag="456" id="2">
        <subfield code="a">a3</subfield>
    </datafield>
    <datafield tag="082" id="3">
        <subfield code="a">a4</subfield>
        <subfield code="x">x2</subfield>
        <subfield code="a">a5</subfield>
    </datafield>
  </record>
  <record type="Bibliographic">
    <leader>...</leader>
    <controlfield>...</controlfield>
    <datafield tag="082" id="4">
        <subfield code="a">a6</subfield>
        <subfield code="x">x3</subfield>
        <subfield code="a">a7</subfield>
    </datafield>
    <datafield tag="456" id="5">
        <subfield code="a">a8</subfield>
    </datafield>
  </record>
</collection>

我得到以下结果:

{http://www.loc.gov/MARC21/slim}datafield, {'tag': '082', 'id': '1'}
  {http://www.loc.gov/MARC21/slim}subfield, {'code': 'a'}, a1
{http://www.loc.gov/MARC21/slim}datafield, {'tag': '082', 'id': '3'}
  {http://www.loc.gov/MARC21/slim}subfield, {'code': 'a'}, a4
{http://www.loc.gov/MARC21/slim}datafield, {'tag': '082', 'id': '4'}
  {http://www.loc.gov/MARC21/slim}subfield, {'code': 'a'}, a6

推荐阅读