Есть ли способ получить номер строки из элемента ElementTree?

Итак, я анализирую некоторые файлы XML с помощью cElementTree Python 3.2.1, и во время анализа я заметил, что в некоторых тегах отсутствует информация об атрибутах. Мне было интересно, есть ли простой способ получить номера строк этих элементов в файле xml.


person John Smith    schedule 04.08.2011    source источник


Ответы (4)


Глядя на документы, я не вижу способа сделать это с помощью cElementTree.

Однако мне повезло с lxml версией реализации XML. Предполагается, что это почти замена с использованием libxml2. И элементы имеют атрибут sourceline. (А также получить множество других функций XML).

Единственное предостережение в том, что я использовал его только в python 2.x - не знаю, как/работает ли он под 3.x - но, возможно, стоит посмотреть.

Приложение: на их первой странице они говорят:

Инструментарий lxml XML — это привязка Pythonic для библиотек C libxml2 и libxslt. Он уникален тем, что сочетает в себе скорость и полноту функций XML этих библиотек с простотой собственного API Python, в основном совместимого, но превосходящего хорошо известный API ElementTree. Последний выпуск работает со всеми версиями CPython от 2.3 до 3.2. См. введение для получения дополнительной информации об истории и целях проекта lxml. Ответы на некоторые распространенные вопросы приведены в FAQ.

Так что похоже, что python 3.x в порядке.

person Michael Anderson    schedule 05.08.2011
comment
Работает отлично, падение почти 1: 1. Единственная разница, которую я нашел до сих пор, - это исключения. - person John Smith; 05.08.2011

Мне потребовалось некоторое время, чтобы понять, как это сделать с помощью Python 3.x (здесь используется 3.3.2), поэтому я решил резюмировать:

# Force python XML parser not faster C accelerators
# because we can't hook the C implementation
sys.modules['_elementtree'] = None
import xml.etree.ElementTree as ET

class LineNumberingParser(ET.XMLParser):
    def _start_list(self, *args, **kwargs):
        # Here we assume the default XML parser which is expat
        # and copy its element position attributes into output Elements
        element = super(self.__class__, self)._start_list(*args, **kwargs)
        element._start_line_number = self.parser.CurrentLineNumber
        element._start_column_number = self.parser.CurrentColumnNumber
        element._start_byte_index = self.parser.CurrentByteIndex
        return element

    def _end(self, *args, **kwargs):
        element = super(self.__class__, self)._end(*args, **kwargs)
        element._end_line_number = self.parser.CurrentLineNumber
        element._end_column_number = self.parser.CurrentColumnNumber
        element._end_byte_index = self.parser.CurrentByteIndex
        return element

tree = ET.parse(filename, parser=LineNumberingParser())
person Duncan Harris    schedule 05.04.2016
comment
Спасибо. Это работает на Python 2.7.11. После filename нет ненужного ) . - person flied onion; 07.08.2016
comment
Спасибо, исправил ложную скобку - person Duncan Harris; 09.08.2016
comment
Может ли кто-нибудь добавить строку, показывающую использование атрибута _start_line_number? Я пытаюсь tree.getroot()._start_line_number и получаю AttributeError. - person 7yl4r; 21.03.2017
comment
В Python 3 функция _start_list должна быть _start как в определении (def _start(self, *args, **kwargs):), так и в вызове (element = super(self.__class__, self)._start(*args, **kwargs) ). - person noe; 07.11.2017
comment
@ 7yl4r Мне удалось заставить его работать на Python 3.6. Ключевым моментом является добавление этой строки: sys.modules['_elementtree'] = None перед первым импортом xml.etree.ElementTree в любом месте вашей программы. Например, вы можете добавить sys.modules['_elementtree'] = None в начало вашего скрипта. Тогда после вызова tree = ET.parse(filename, parser=LineNumberingParser()) сработает tree.getroot()._start_line_number. - person JustAC0der; 05.08.2018

Я сделал это в elementtree, создав подкласс ElementTree.XMLTreeBuilder. Затем, когда у меня есть доступ к self._parser (Expat), у него есть свойства _parser.CurrentLineNumber и _parser.CurrentColumnNumber.

http://docs.python.org/py3k/library/pyexpat.html?highlight=xml.parser#xmlparser-objects содержит подробные сведения об этих атрибутах.

Во время синтаксического анализа вы можете распечатать информацию или поместить эти значения в атрибуты выходного элемента XML.

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

person Jelly Stone    schedule 05.08.2011

Один (хакерский) способ сделать это - вставить фиктивный атрибут, содержащий номер строки, в каждый элемент перед синтаксическим анализом. Вот как я сделал это с минидомом:

строка/столбец отчета python о происхождении узла XML

Это можно тривиально настроить на cElementTree (или на самом деле любой другой XML-парсер python).

person Tfry    schedule 22.12.2014