Как построить текст из смешанного xml контента, используя Python? - PullRequest
0 голосов
/ 21 января 2020

У меня есть ситуация, в которой документ XML содержит информацию различной глубины (в соответствии со схемами S1000D), и я ищу обобщенный c метод для извлечения правильных предложений.

I Мне нужно интерпретировать простой элемент, содержащий текст, как одну отдельную часть / предложение, и когда элемент, содержащий текст, содержит другие элементы, которые, в свою очередь, содержат текст, мне нужно сгладить / объединить его в одну строку / предложение. Если это будет сделано, вложенные элементы не будут посещаться снова.

Использование библиотеки Pythons lxml и применение функции tostring работает нормально, если исходный текст XML напечатан довольно красиво, так что я могу разбить объединенная строка в новые строки, чтобы получить каждое предложение. Если исходный текст не напечатан, в одной строке не будет никаких новых строк для разделения.

Я пробовал функцию iter и применял xpaths к каждому узлу, но это часто дает Python другие результаты, отличные от того, что я получаю при применении xpath в XMLSpy.

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

Я думаю, что я мог бы использовать XSLT для предварительной обработки файла XML, а затем использовать более простой сценарий Python, чтобы разделить содержимое на список предложений. для дальнейшей обработки. Использование Saxon с Python теперь выполнимо, но здесь я сталкиваюсь с проблемами, если источник XML содержит объекты, которые я не могу перенаправить для разрешения Saxon (например, & nbsp;). У меня нет проблем с парсингом файлов с lxml, поэтому я склоняюсь к более чистому Python решению.

lxml, похоже, не имеет поддержки xpath, которая может дать мне все узлы с текст, который содержит одного или нескольких дочерних элементов, содержащих текст, и все узлы, которые являются простыми элементами без родительских элементов, содержащих текстовые узлы. Есть ли способ предварительной обработки проанализированного дерева, чтобы я мог убедиться, что оно довольно распечатано в памяти, чтобы tostring работал одинаково для каждого файла XML? В противном случае, моя логика c дает мне одну строку для документа без пробелов и несколько предложений / строк, если источник был напечатан. Это не очень хорошо.

Какие у меня варианты? Используйте XSLT 1.0 в Python, другие парсеры, чтобы лучше понять, где я нахожусь в дереве, ...

Просто чтобы повторить эту проблему здесь; Я ищу общий c способ извлечения текста, и единственные правила для источника XML состоят в том, что предложение может быть построено из элемента с дочерними элементами с текстом, но дополнительных уровней не будет. Другая возможность - простой элемент, но его нельзя включить в родительский элемент с текстом, поскольку он включен в первое правило.

Помощь / мысли приветствуются.

1 Ответ

0 голосов
/ 23 января 2020

Это откровенно уродливый код, поспешный взлом без реальной мысли о форме, красоте или изяществе. Все, что мне нужно, это один из способов сделать это в Python. Я приведу все в порядок, когда найду хорошее решение, которое хочу сохранить. Это одно из возможных решений, поэтому я решил опубликовать его, чтобы посмотреть, сможет ли кто-нибудь проявить любезность и показать мне, как это сделать вместо этого.

Проблема в том, что у меня есть xpath выражения, которые могут заставить меня все элементы с текстовым содержимым, а затем действовать в зависимости от их контекста. Все мои выражения xpath дали мне правильные узлы, но также root, или предка, который вытащил более или менее полную строку в начале, поэтому я отказался от них. Мои xpath функционируют так, как должны в XSLT, но не в Python - не знаю почему ...

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

Использование lxml с xpath и tostring дает разные результаты в зависимости от того, как отформатирован источник XML, поэтому мне пришлось обойти это.

Следующие форматы были проверены:

<?xml version="1.0" encoding="UTF-8"?>
    <root>
        <subroot>
            <a>Intro, element a: <b>Nested b to be included in a, <c>and yet another nested c-element</c> and back to b.</b></a>
            <!-- Comment -->
            <a>Simple element.</a>
            <a>Text with<b> 1st nested b</b>, back in a, <b>and yet another b-element</b>, before ending in a.</a>
        </subroot>
    </root>

<?xml version="1.0" encoding="UTF-8"?>
<root>

    <subroot>
        <a>Intro, element a: <b>Nested b to be included in a, <c>and yet another nested c-element,

        </c> and back to b.</b>
        </a>
        <!-- Comment -->
        <a>Simple element.</a>
        <a>Text with<b> 1st nested b</b>, back in a, <b>and yet another b-element</b>, before ending in a.</a>
    </subroot>
</root>

<?xml version="1.0" encoding="UTF-8"?><root><subroot><a>Intro, element a: <b>Nested b to be included in a, <c>and yet another nested c-element</c> and back to b.</b></a><!-- Comment --><a>Simple element.</a><a>Text with<b> 1st nested b</b>, back in a, <b>and yet another b-element</b>, before ending in a.</a></subroot></root>

Python код:

dmParser=ET.XMLParser(resolve_entities=False, recover=True)
xml_doc = r'C:/Temp/xml-testdoc.xml'
parsed = ET.parse(xml_doc)

for elem in parsed.xpath("//*[re:match(text(), '\S')]", namespaces={"re": "http://exslt.org/regular-expressions"}):
    tmp = elem.xpath("parent::*[re:match(text(), '\S')]", namespaces={"re": "http://exslt.org/regular-expressions"})
    if(tmp and tmp[0].text and tmp[0].text.strip()): #Two first checks can yield None, and if there is something check if only white space
        continue #If so, discard this node
    elif(elem.xpath("./*[re:match(text(), '\S')]", namespaces={"re": "http://exslt.org/regular-expressions"})): #If a child node also contains text
        line =re.sub(r'\s+', ' ',ET.tostring(elem, encoding='unicode', method='text').strip()) #Replace all non wanted whitespace 
        if(line):
            print(line)
    else: #Simple element
        print(elem.text.strip())

Всегда дает:

Intro, element a: Nested b to be included in a, and yet another nested c-element, and back to b.
Simple element.
Text with 1st nested b, back in a, and yet another b-element, before ending in a.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...