Как вернуть данные из анализатора Python SAX? - PullRequest
5 голосов
/ 16 января 2012

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

class SpamExtractor(sax.ContentHandler):
    def startElement(self, name, attrs):
        if name == "spam":
            print("We found a spam!")
            # now what?

.проблема в том, что я не понимаю, как на самом деле return или, лучше, yield, то, что этот обработчик находит для вызывающей стороны, не дожидаясь анализа всего файла.До сих пор я возился с threading.Thread и Queue.Queue, но это приводит ко всем видам проблем с потоками, которые действительно отвлекают меня от реальной проблемы, которую я пытаюсь решить.

Я знаю, что могу запустить парсер SAX в отдельном процессе, но я чувствую, что должен быть более простой способ получить данные.Есть ли?

Ответы [ 4 ]

6 голосов
/ 16 января 2012

Я подумал, что дам это как еще один ответ, потому что это совершенно другой подход.

Возможно, вы захотите проверить xml.etree.ElementTree.iterparse, так как он, кажется, делает больше, чемВы хотите:

Поэтапно анализирует секцию XML в дереве элементов и сообщает пользователю, что происходит.Источник - это имя файла или объект файла, содержащий данные XML.events - это список событий, о которых нужно сообщить.Если опущено, сообщается только о «конечных» событиях.parser - необязательный экземпляр parser.Если не указан, используется стандартный синтаксический анализатор XMLParser.Возвращает итератор, предоставляющий пары (event, elem).

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

например:

def find_spam(xml):
    for event, element in xml.etree.ElementTree.iterparse(xml):
        if element.tag == "spam":
            print("We found a spam!")
            # Potentially do something
            yield element

Разница в основном в том, что вы хотите.Итераторский подход ElementTree больше относится к сбору данных, а подход SAX - к действию.

5 голосов
/ 16 января 2012

Дэвид Бизли демонстрирует , как «получить» результаты из саксофона ContentHandler с использованием сопрограммы:

cosax.py

import xml.sax

class EventHandler(xml.sax.ContentHandler):
    def __init__(self,target):
        self.target = target
    def startElement(self,name,attrs):
        self.target.send(('start',(name,attrs._attrs)))
    def characters(self,text):
        self.target.send(('text',text))
    def endElement(self,name):
        self.target.send(('end',name))

def coroutine(func):
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)
        cr.next()
        return cr
    return start

# example use
if __name__ == '__main__':
    @coroutine
    def printer():
        while True:
            event = (yield)
            print event

    xml.sax.parse("allroutes.xml",
                  EventHandler(printer()))

Выше, каждый раз, когда вызывается self.target.send, код внутри printer запускается, начиная с event = (yield). event присваивается аргументам self.target.send, а код в printer выполняется до достижения следующего (yield), что-то вроде того, как работает генератор.

В то время как генератор обычно управляется for-loop, сопрограмма (например, printer) управляется send вызовами.

0 голосов
/ 16 января 2012

По сути, есть три способа синтаксического анализа XML:

  1. SAX -approach: это реализация шаблона посетителя, идея состоит в том, что события передаются в вашcode.
  2. StAX -приступ: когда вы извлекаете следующий элемент, пока вы готовы (полезно для частичного разбора, то есть чтения только заголовка SOAP)
  3. DOM - подход, при котором вы загружаете все в дерево в памяти

Кажется, вам нужен второй, но я не уверен, что он находится где-то в стандартной библиотеке.

0 голосов
/ 16 января 2012

Насколько я понимаю, синтаксический анализатор SAX предназначен для выполнения работы, а не просто для передачи данных обратно по пищевой цепочке.

например:

class SpamExtractor(sax.ContentHandler):
    def __init__(self, canning_machine):
        self.canning_machine = canning_machine

    def startElement(self, name, attrs):
        if name == "spam":
            print("We found a spam!")
            self.canning_machine.can(name, attrs)
...