Изменить теги Specifi c xml с помощью iterparse - PullRequest
0 голосов
/ 01 февраля 2020

Я работаю с открытыми данными карты и должен иметь возможность обновлять определенные c теги на основе их значений. Мне удалось прочитать теги и даже напечатать указанные теги c, которые необходимо обновить на консоли, но я не смог их обновить.

Я использую elementree и l xml. Что мне особенно нужно, так это если первое слово тега addr: street является направлением мощности (ie Север, Юг, Восток, Запад), а последнее слово тега 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, и l xml, поскольку l xml кажется подмножеством 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 северной широты, а тег addr: street - с северной части 500 западных на 500 западных.

1 Ответ

0 голосов
/ 02 февраля 2020

Попробуйте следующий код:

Функции / глобальные переменные:

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

Я предполагаю, что дерево переменная содержит все дерево XML.

Редактировать после комментария от 22: 36: 33Z

Мой код работает также на основе oop iterparse .

Подготовьте, например, input. xml файл с некоторым тегом root и парой узлов элементов внутри. Затем попробуйте следующий код (с необходимыми импортами, функциями и глобальными переменными, представленными выше):

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' в первом , если .

Вам также не нужны начальные _, root = next(context) из вашего кода, так как context. root указывает на весь XML дерево.

И теперь, имея построенное дерево XML, вы можете распечатать его, чтобы увидеть результат:

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

Примечания:

  • Приведенный выше код написан без каких-либо результатов, но он генерирует полное дерево XML, обновляется в соответствии с вашими потребностями.
  • Поскольку задача состоит в construct дерево XML, этот код не ничего не очищает . Вызовы clear необходимы только тогда, когда вы:
    • извлекают некоторые данные из обработанных элементов и сохраняют их в другом месте,
    • эти элементы больше не нужны.

Теперь вы можете реконструировать приведенный выше код в «уступающий» вариант и использовать его в своей среде (вы не предоставили никаких подробностей о том, как называется ваш пример кода).

...