【问题标题】:Modify Specific xml tags with iterparse使用 iterparse 修改特定的 xml 标签
【发布时间】:2023-03-04 08:22:02
【问题描述】:

我正在处理开放地图数据,需要能够根据它们的值更新特定标签。我已经能够读取标签,甚至将需要更新的特定标签打印到控制台,但我无法让它们更新。

我正在使用 elementree 和 lxml。我正在寻找的具体是如果 addr:street 标签的第一个词是基数方向(即北、南、东、西),而 addr:housenumber 标签的最后一个词不是基数方向,取addr:street 标签中的第一个单词并将其移动到 addr:housenumber 标签的最后一个单词。

根据以下问题进行编辑。

最初我只是用以下代码调用代码:

clean_data(OUTPUT_FILE)

我没有意识到 iterparse 不能用于直接从方法中打印(我相信这就是您所说的)。我有来自我之前使用的项目的不同部分的代码,所以我改编了你写的东西

文件前面:

import xml.etree.cElementTree as ET
from collections import defaultdict
import pprint
import re
import codecs
import json

OSM_FILE = "Utah County Map.osm"
OUTPUT_FILE = "Utah County Extract.osm"
JSON_FILE = "JSON MAP DATA.json"

项目本节代码:

def clean_data(osm_file, tags = ('node', 'way')):
    context = iter(ET.iterparse(osm_file, events=('end',)))
    for event, elem in context:
        if elem.tag == 'node':
            streetTag, street = getVal(elem, 'addr:street')
            if street is None:  # No "street"
                continue
            first_word = getWord(street, True)
            houseTag, houseNo = getVal(elem, 'addr:housenumber')
            if houseNo is None:  # No "housenumber"
                continue
            last_word = getWord(houseNo, False)
            if first_word in direct_list and last_word not in direct_list:
                streetTag.attrib['v'] = street[len(first_word) + 1:]
                houseTag.attrib['v'] = houseNo + ' ' + first_word

for i, element in enumerate(clean_data(OUTPUT_FILE)):
    print(ET.tostring(context.root, encoding='unicode', pretty_print=True, with_tail=False))

当我现在运行它时,我遇到了一个错误:

TypeError: 'NoneType' object is not iterable

我尝试添加之前用于项目另一部分的输出代码,但收到相同的错误。这也是该代码供参考。 (这段代码中的输出文件是指我删除了多个无效节点的数据清洗第一阶段的输出)。

with open(CLEAN_DATA, 'w') as output:
    output.write('<?xml version="1.0" encoding="UTF-8"?>\n')
    output.write('<osm>\n  ')

    for i, element in enumerate(clean_data(OUTPUT_FILE)):
        output.write(ET.tostring(element, encoding='unicode'))

    output.write('</osm>')

最初的编辑是为了回应 Valdi_bo 的以下问题。这是我的 xml 文件中的示例供参考。是的,我同时使用 Elementree 和 lxml,因为 lxml 似乎是 elementree 的一个子集。我之前在程序中调用的一些函数只能与其中一个一起使用,所以我同时使用了这两个函数。

<?xml version="1.0" encoding="UTF-8"?>
<osm>
  <node changeset="24687880" id="356682074" lat="40.2799548" lon="-111.6457549" timestamp="2014-08-11T20:33:35Z" uid="2253787" user="1000hikes" version="2">
    <tag k="addr:city" v="Provo" />
    <tag k="addr:housenumber" v="3570" />
    <tag k="addr:postcode" v="84604" />
    <tag k="addr:street" v="Timpview Drive" />
    <tag k="building" v="school" />
    <tag k="ele" v="1463" />
    <tag k="gnis:county_id" v="049" />
    <tag k="gnis:created" v="02/25/1989" />
    <tag k="gnis:feature_id" v="1449106" />
    <tag k="gnis:state_id" v="49" />
    <tag k="name" v="Timpview High School" />
    <tag k="operator" v="Provo School District" />
  </node>
  <node changeset="58421729" id="356685655" lat="40.2414325" lon="-111.6678877" timestamp="2018-04-25T20:23:33Z" uid="360392" user="maxerickson" version="4">
    <tag k="addr:city" v="Provo" />
    <tag k="addr:housenumber" v="585" />
    <tag k="addr:postcode" v="84601" />
    <tag k="addr:street" v="North 500 West" />
    <tag k="amenity" v="doctors" />
    <tag k="gnis:feature_id" v="2432255" />
    <tag k="healthcare" v="doctor" />
    <tag k="healthcare:speciality" v="gynecology;obstetrics" />
    <tag k="name" v="Valley Obstetrics &amp; Gynecology" />
    <tag k="old_name" v="Healthsouth Provo Surgical Center" />
    <tag k="phone" v="+1 801 374 1801" />
    <tag k="website" v="http://valleyobgynutah.com/location/provo-office-2/" />
  </node>
</osm>

在此示例中,第一个节点将保持不变。在第二个区块中,addr:housenumber 标签应从 585 更改为 585 North,并且 addr:street 标签应从 North 500 West 更改为 500 West。

【问题讨论】:

  • "我正在使用 elementree 和 lxml。"您是否在同一个程序中使用这两个库?
  • “我相信你在说什么”你在和谁说话? Stack Overflow 不是一个传统的讨论论坛。请确保提供minimal reproducible example。输入 XML 在哪里?
  • “lxml 似乎是 elementree 的一个子集”。反过来说:lxml 是 ElementTree 的超集。 lxml 支持与 ElementTree 相同的基本 API 以及许多附加功能。
  • 很高兴知道,谢谢。我对解析 xml 非常陌生。

标签: python-3.x lxml elementtree large-files


【解决方案1】:

试试下面的代码:

函数/全局变量:

def getVal(nd, kVal):
    '''
    Get data from "tag" child node with required "k" attribute
    Parameters:
      nd   - "starting" node,
      kVal - value of "k" attribute.
    Results:
      - the tag found,
      - its "v" attribute
    '''
    tg = nd.find(f'tag[@k="{kVal}"]')
    if tg is None:
        return (None, None)
    return (tg, tg.attrib.get('v'))

def getWord(txt, first):
    '''
    Get first / last word from "txt"
    '''
    pat = r'^\S+' if first else r'\S+$'
    mtch = re.search(pat, txt)
    return mtch.group() if mtch else ''

direct_list = ["N", "N." "No", "North", "S", "S.",
    "So", "South", "E", "E.", "East", "W", "W.", "West"]

以及主要代码:

for nd in tree.iter('node'):
    streetTag, street = getVal(nd, 'addr:street')
    if street is None:  # No "street"
        continue
    first_word = getWord(street, True)
    houseTag, houseNo = getVal(nd, 'addr:housenumber')
    if houseNo is None:  # No "housenumber"
        continue
    last_word = getWord(houseNo, False)
    if first_word in direct_list and last_word not in direct_list:
        streetTag.attrib['v'] = street[len(first_word) + 1:]
        houseTag.attrib['v'] = houseNo + ' ' + first_word

我假设 tree 变量包含整个 XML 树。

在 22:36:33Z 之后按照评论进行编辑

我的代码也可以在基于 iterparse 的循环中工作。

准备例如input.xml 文件带有一些 root 标签和几个 node 元素里面。然后尝试以下代码(带有必要的导入, 上面介绍的函数和全局变量):

context = iter(etree.iterparse('input.xml', events=('end',)))
for event, elem in context:
    if elem.tag == 'node':
        streetTag, street = getVal(elem, 'addr:street')
        if street is None:  # No "street"
            continue
        first_word = getWord(street, True)
        houseTag, houseNo = getVal(elem, 'addr:housenumber')
        if houseNo is None:  # No "housenumber"
            continue
        last_word = getWord(houseNo, False)
        if first_word in direct_list and last_word not in direct_list:
            streetTag.attrib['v'] = street[len(first_word) + 1:]
            houseTag.attrib['v'] = houseNo + ' ' + first_word

由于 iterparse 只处理 end 事件,你甚至不需要 and event == 'end' 在第一个 if.

您既不需要代码中的初始 _, root = next(context), 因为 context.root 指向整个 XML 树。

现在,有了构建的 XML 树,您可以打印它,查看结果:

print(etree.tostring(context.root, encoding='unicode', pretty_print=True,
    with_tail=False))

注意事项:

  • 上面的代码已经写好了,没有产生任何东西, 但它会生成一个完整的 XML 树,根据您的需要更新。
  • 由于任务是构建 XML 树,因此此代码不会清除 任何事物。只有在以下情况下才需要调用 clear:
    • 从已处理的元素中检索一些数据并将其保存在其他位置,
    • 不再需要这些元素。

现在您可以将上述代码重构为“屈服”变体并使用 它在您的环境中(您没有提供代码示例的任何详细信息 被调用)。

【讨论】:

  • 里面有一些不错的部分,但我使用 iterparse 方法而不是将整个文档读入树中的原因是因为文档大小为几 GB。有没有办法让这段代码与 iterparse 方法一起工作?
  • 请提供一个可重现的例子。您的代码示例仅包含产生元素的 clean_data 函数。我不知道你的代码怎么称呼它。
猜你喜欢
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 2013-09-21
  • 2020-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多