Читать XML-файл во время его записи (на Python)
Я должен следить за тем, чтобы файл XML записывался инструментом, работающим весь день. Но файл XML правильно заполнен и закрыт только в конце дня.
Те же ограничения, что и при обработке потока XML:
- Анализируйте неполный XML-файл на лету и запускайте действия
- Отслеживайте последнюю позицию в файле, чтобы избежать повторной обработки с самого начала
В ответ на вопрос о необходимости чтения XML-файлов в виде потока с использованием BeautifulSoup в Python, slezica предлагает xml.sax
, xml.etree.ElementTree
а также cElementTree
, Но безуспешно с моими попытками использовать xml.etree.ElementTree
а также cElementTree
, Это также xml.dom
, xml.parsers.expat
а также lxml
но я не вижу поддержки для анализа "на лету".
Мне нужны более очевидные примеры...
В настоящее время я использую Python 2.7 в Linux, но я перейду на Python 3.x =>, пожалуйста, также предоставьте советы по новым возможностям Python 3.x. Я также использую watchdog
для обнаружения изменений в файле XML => При необходимости повторно watchdog
механизм. Опционально поддерживается также Windows.
Пожалуйста, предоставьте простые для понимания / поддержки решения. Если это слишком сложно, я могу просто использовать tell()
/ seek()
чтобы переместиться в файл, используйте глупый текстовый поиск в необработанном XML и, наконец, извлеките значения, используя основное регулярное выражение.
Пример XML:
<dfxml xmloutputversion='1.0'>
<creator version='1.0'>
<program>TCPFLOW</program>
<version>1.4.6</version>
</creator>
<configuration>
<fileobject>
<filename>file1</filename>
<filesize>288</filesize>
<tcpflow packets='12' srcport='1111' dstport='2222' family='2' />
</fileobject>
<fileobject>
<filename>file2</filename>
<filesize>352</filesize>
<tcpflow packets='12' srcport='3333' dstport='4444' family='2' />
</fileobject>
<fileobject>
<filename>file3</filename>
<filesize>456</filesize>
...
...
Первый тест с использованием SAX не удался:
import xml.sax
class StreamHandler(xml.sax.handler.ContentHandler):
def startElement(self, name, attrs):
print 'start: name=', name
def endElement(self, name):
print 'end: name=', name
if name == 'root':
raise StopIteration
if __name__ == '__main__':
parser = xml.sax.make_parser()
parser.setContentHandler(StreamHandler())
with open('f.xml') as f:
parser.parse(f)
Ракушка:
$ while read line; do echo $line; sleep 1; done <i.xml >f.xml &
...
$ ./test-using-sax.py
start: name= dfxml
start: name= creator
start: name= program
end: name= program
start: name= version
end: name= version
Traceback (most recent call last):
File "./test-using-sax.py", line 17, in <module>
parser.parse(f)
File "/usr/lib64/python2.7/xml/sax/expatreader.py", line 107, in parse
xmlreader.IncrementalParser.parse(self, source)
File "/usr/lib64/python2.7/xml/sax/xmlreader.py", line 125, in parse
self.close()
File "/usr/lib64/python2.7/xml/sax/expatreader.py", line 220, in close
self.feed("", isFinal = 1)
File "/usr/lib64/python2.7/xml/sax/expatreader.py", line 214, in feed
self._err_handler.fatalError(exc)
File "/usr/lib64/python2.7/xml/sax/handler.py", line 38, in fatalError
raise exception
xml.sax._exceptions.SAXParseException: report.xml:15:0: no element found
2 ответа
Через три часа после публикации моего вопроса ответа не последовало. Но я наконец-то реализовал простой пример, который искал.
Мое вдохновение исходит из ответа Саадж и основывается на xml.sax
а также watchdog
,
from __future__ import print_function, division
import time
import watchdog.events
import watchdog.observers
import xml.sax
class XmlStreamHandler(xml.sax.handler.ContentHandler):
def startElement(self, tag, attributes):
print(tag, 'attributes=', attributes.items())
self.tag = tag
def characters(self, content):
print(self.tag, 'content=', content)
class XmlFileEventHandler(watchdog.events.PatternMatchingEventHandler):
def __init__(self):
watchdog.events.PatternMatchingEventHandler.__init__(self, patterns=['*.xml'])
self.file = None
self.parser = xml.sax.make_parser()
self.parser.setContentHandler(XmlStreamHandler())
def on_modified(self, event):
if not self.file:
self.file = open(event.src_path)
self.parser.feed(self.file.read())
if __name__ == '__main__':
observer = watchdog.observers.Observer()
event_handler = XmlFileEventHandler()
observer.schedule(event_handler, path='.')
try:
observer.start()
while True:
time.sleep(10)
finally:
observer.stop()
observer.join()
Пока работает скрипт, не забудьте touch
один файл XML или смоделируйте запись на лету с помощью следующей команды:
while read line; do echo $line; sleep 1; done <in.xml >out.xml &
Со вчерашнего дня я нашел ответ Питера Гибсона о недокументированных xml.etree.ElementTree.XMLTreeBuilder._parser.EndElementHandler
,
Этот пример похож на другой, но использует xml.etree.ElementTree
(а также watchdog
).
Не работает когда ElementTree
заменяется cElementTree
:-/
import time
import watchdog.events
import watchdog.observers
import xml.etree.ElementTree
class XmlFileEventHandler(watchdog.events.PatternMatchingEventHandler):
def __init__(self):
watchdog.events.PatternMatchingEventHandler.__init__(self, patterns=['*.xml'])
self.xml_file = None
self.parser = xml.etree.ElementTree.XMLTreeBuilder()
def end_tag_event(tag):
node = self.parser._end(tag)
print 'tag=', tag, 'node=', node
self.parser._parser.EndElementHandler = end_tag_event
def on_modified(self, event):
if not self.xml_file:
self.xml_file = open(event.src_path)
buffer = self.xml_file.read()
if buffer:
self.parser.feed(buffer)
if __name__ == '__main__':
observer = watchdog.observers.Observer()
event_handler = XmlFileEventHandler()
observer.schedule(event_handler, path='.')
try:
observer.start()
while True:
time.sleep(10)
finally:
observer.stop()
observer.join()
Пока работает скрипт, не забудьте touch
один XML-файл или смоделируйте запись на лету с помощью этого однострочного сценария:
while read line; do echo $line; sleep 1; done <in.xml >out.xml &
Для информации, xml.etree.ElementTree.iterparse
не поддерживает запись файла. Мой тестовый код:
from __future__ import print_function, division
import xml.etree.ElementTree
if __name__ == '__main__':
context = xml.etree.ElementTree.iterparse('f.xml', events=('end',))
for action, elem in context:
print(action, elem.tag)
Мой вывод:
end program
end version
end creator
end filename
end filesize
end tcpflow
end fileobject
end filename
end filesize
end tcpflow
end fileobject
end filename
end filesize
Traceback (most recent call last):
File "./iter.py", line 9, in <module>
for action, elem in context:
File "/usr/lib64/python2.7/xml/etree/ElementTree.py", line 1281, in next
self._root = self._parser.close()
File "/usr/lib64/python2.7/xml/etree/ElementTree.py", line 1654, in close
self._raiseerror(v)
File "/usr/lib64/python2.7/xml/etree/ElementTree.py", line 1506, in _raiseerror
raise err
xml.etree.ElementTree.ParseError: no element found: line 20, column 0