Aristo.py
Фев. 13, 2012 12:55:00
Здравствуйте, Ув. Питоновцы. Прошу Вашего внимания! Кто сталкивался с такой проблемой: нужно распарсить ООООООГРОМНЫЙ xml (естественно через sax) с 4-5 уровневой структурой, и не просто распарсить, а передать текст определенных тегов в определенные переменные. Но наделе все вышло не так просто. Даже с 2-3 уровневой структурой появляются (те же что и у меня в работе) errors.
пример xml:
<?xml version=“1.0” encoding=“windows-1251”?>
<main>
<Данные>
<ДатаНачала>22.03.2011</ДатаНачала>
<ДатаОкончания>22.03.2011</ДатаОкончания>
</Данные>
</main>
код:
from xml.sax.handler import ContentHandler
from xml import sax
from xml.sax.handler import ContentHandler
from xml import sax
class RASP (ContentHandler):
def __init__(self, rasp):
ContentHandler.__init__(self)
self.rasp = rasp
self.in_rasp = False
def startElement (self,name,attrs):
self.in_rasp = True
if name == ‘ДатаНачала’:
parser.setContentHandler(for_DataNachala(Dbeg))
parser.parse(“new2.xml”)
if name =='ДатаОкончания':
parser.setContentHandler(for_DataKonca(Dend))
parser.parse(“new2.xml”)
def endElement (self, name):
self.in_rasp = False
class for_DataNachala(ContentHandler):
def __init__(self, Dbeg):
ContentHandler.__init__(self)
self.Dbeg = Dbeg
self.txt_databegin =
self.in_Dbeg = False
def startElement (self,name,attrs):
self.in_Dbeg = True
self.txt_databegin = “”
def characters(self, txt_databegin):
if self.in_Dbeg:
self.txt_databegin = txt_databegin
def endElement (self, name):
if name == ‘ДатаНачала’:
self.in_Dbeg = False
self.Dbeg.append(self.txt_databegin)
class for_DataKonca(ContentHandler):
def __init__(self, Dend):
ContentHandler.__init__(self)
self.Dend = Dend
self.txt_dataend = None
self.in_Dend = False
def startElement (self,name,attrs):
self.in_Dend = True
self.txt_dataend = “”
def characters(self, txt_dataend):
if self.in_Dend:
self.txt_dataend = txt_dataend
def endElement (self, name):
if name == ‘ДатаОкончания’:
self.in_Dend = False
self.Dend.append(self.txt_dataend)
if __name__ == ‘__main__’:
Dbeg =
Dend =
rasp = None
parser = sax.make_parser()
parser.setContentHandler(RASP(rasp))
parser.parse(“new2.xml”)
в итоге я получаю кучу ошибок:
Traceback (most recent call last):
File “C:\workspace\for_xml\src\main.py”, line 83, in <module> # не пойму что со строкой не так!!!!! ковычки менял, файл на месте….
parser.parse(“new2.xml”)
File “C:\Python25\lib\xml\sax\expatreader.py”, line 107, in parse
xmlreader.IncrementalParser.parse(self, source)
File “C:\Python25\lib\xml\sax\xmlreader.py”, line 125, in parse
self.close()
File “C:\Python25\lib\xml\sax\expatreader.py”, line 217, in close
self.feed(“”, isFinal = 1)
File “C:\Python25\lib\xml\sax\expatreader.py”, line 211, in feed
self._err_handler.fatalError(exc)
File “C:\Python25\lib\xml\sax\handler.py”, line 38, in fatalError
raise exception
xml.sax._exceptions.SAXParseException: for_web.xml:1:0: no element found
Возможно ли, что проблема кроется в потоках, в том, что для каждого разбора, требуется инициировать свой поток.
Господа Знатоки! Помогите разрешить вопрос, очень прошу.
Ed
Фев. 13, 2012 19:56:11
Вот так пойдет?
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from xml.sax.handler import ContentHandler
from xml import sax
class Handler(ContentHandler):
def __init__(self, fields):
ContentHandler.__init__(self)
self.results = dict([(name, []) for name in fields])
self.target = None
def startElement(self, name, attrs):
if name in self.results:
self.target = name
def endElement(self, name):
self.target = None
def characters(self, content):
if self.target:
self.results[self.target].append(content)
if __name__ == '__main__':
parser = sax.make_parser()
handler = Handler((u"ДатаНачала", u"ДатаОкончания",
u"Должность", u"Параметр4", u"СтепеньОтветственности"))
parser.setContentHandler(handler)
parser.parse("new2.xml")
for key, values in handler.results.iteritems():
print key
for item in values:
print "\t\t", item
У меня оно отпарсило такой xml (я поменял кодировку поскольку запускал под Линухом):
<?xml version="1.0" encoding="utf-8"?>
<main>
<Данные>
<ДатаНачала>22.03.2011</ДатаНачала>
<ДатаОкончания>22.03.2011</ДатаОкончания>
</Данные>
</main>
И вывело вот это:
ДатаНачала [u'22.03.2011'
ДатаОкончания
Aristo.py
Фев. 14, 2012 06:46:01
вариант не плох, но причем тут zip? ….
и тем не менее, стоит учесть, что в задаче далеко не такой простой xml, там он 4-5ти уровневый, к примеру, xml:
<?xml version=“1.0” encoding=“utf-8”?>
<main>
<Данные>
<ДатаНачала>22.03.2011</ДатаНачала>
<Назначение>Торговля</Назначение>
<Комментарии/>
<ДатаОкончания>22.03.2011</ДатаОкончания>
</Данные>
<Отчет>
<ДатаНачала>08.09.2011</ДатаНачала>
<ТипОтчета>Годовой</ТипОтчета>
<Состояние>Готов</Состояние>
<Участники>
<Лицо>
<Должность>Главный бухгалтер</Должность>
<ФИО>Жожо Клара Максимовна</ФИО>
<СтепеньОтветственности>Основная составительная</СтепеньОтветственности>
</Лицо>
<Лицо>
<Должность>Бухгалтер</Должность>
<ФИО>Плеханова Елена Дмитриевна</ФИО>
<СтепеньОтветственности>Частичная</СтепеньОтветственности>
</Лицо>
</Участники>
<ОтветственноеЛицо>Директор</ОтветственноеЛицо>
<ФИО>Кузьмин Герасим Аркадъевич</ФИО>
<Параметр1>Ложь</Параметр1>
<Параметр2>Ложь</Параметр2>
<Параметр3>Истина</Параметр3>
<Параметр4>Ложь</Параметр4>
</Отчет>
<Документ>
<ДатаСоздания>04.11.2011</ДатаСоздания>
<Наименование>Договор №07</Наименование>
<Состояние>В доработке</Состояние>
<Автор>
<Должность>Директор по внешней торговле</Должность>
<ФИО>Лобачев Олег Алексеевич</ФИО>
</Документ>
</main>
И таких нодов как “Отчет” и “Документ” там сотни (а может и тысячи) и наполнение каждого поднода этого нода должно идти в переменную, что бы потом использоваться в обработке.
вывод результата на экран не главное
Ed
Фев. 14, 2012 14:16:58
Хех :(. Вы видимо не поняли. Это не решение вашей задачи, а подсказка. То, что я сделал возьмет как параметр список полей, содержимое которых нужно достать и вернет вам словарь вида {“имя поля”: <список с содержимым всех полей с этим именем>, …} независимо от вложенности. Посмотрите внимательно на код. Постарайтесь понять что он делает. Скормите ему поля разного уровня вложенности и посмотрите на результаты. Задайте вопросы, если что-то непонятно.
PS: zip здесь для того, чтобы проинициализировать словарь с результатами.
Ed
Фев. 14, 2012 15:26:41
Я подправил чуть-чуть скрипт в моем посте и отпарсил ваш новый xml. Чисто для иллюстрации вышесказанного. Вот результат:
СтепеньОтветственности
Основная составительная
Частичная
Должность
Главный бухгалтер
Бухгалтер
Директор по внешней торговле
ДатаОкончания
22.03.2011
Параметр4
Ложь
ДатаНачала
22.03.2011
08.09.2011
PS: Избавился от zip, чтобы вас не пугать :)
Aristo.py
Фев. 14, 2012 15:35:33
Спасибо) Буду разбираться. Пока, как временное решение, было создано еще 2 потока парсера, все они работают на разных уровнях вложенности нодов. На сколько это правильно - пока не знаю. Но определенно понимаю, что в Вашем примере много полезного. Как разберусь - отпишусь.
Ed
Фев. 14, 2012 16:15:09
Из вашего изначального кода я понял, что на каждом уровне вложенности вы пытаетесь парсить тот же самый xml с самого начала. Это крайне неэффективный подход. Тоже самое можно сказать о создании дополнительных потоков. Поэтому я и предложил вам однопроходных вариант.
Aristo.py
Фев. 15, 2012 06:36:57
Да, вы правы, но увы другого способа парсинга с учетом структуры на глаза не попадалось. все это задумывается для переправки данных в бд, где уже созданы таблицы. И схема реализации в голове была такой:
каждый раз когда парсер встречает тэг Данные он начинает раскладывать все его состовляющие по переменным,
DataNachala = ‘22.03.2011’,
Naznach = ‘Торговля’
Comment = None
DataOkonch = ‘22.03.2011’
а когда встречает тэг Отчет, то раскладывал бы содержимое этого нода и его поднодов в переменные, определенные для такого типа нодов 2го уровня вложенности, причем учесть, что любой, из определённых в данном типе нодов, тег может быть одиночным, т.е. не нести никакого значения. После обработки каждого такого нода (2го уровня, к примеру <Отчет>), он должен будет отправить в бд.
В предложенном варианте я вижу, что он просто вытаскивает все значения нодов и поднодов принадлежащих ноду с тегом, входящим в число определенных в handler и складывает их в копилку.
Как тут организовать условный переход для каждого уровня (до конца не понятна схема действия всего парсера)?
Что такое fields (в своих книгах о нем не упоминается)?
Ed
Фев. 15, 2012 14:45:36
насчет переменных я не понял. Класть данные в базу - понятно, про раскладывание по переменным - нет.
Все, что вы хотите, можно сделать слегка модифицировав мой код.
Навскидку это нужно делать так:
1. В startElement и endElement нужно отслеживать уровень. Например, заведя атрибут self.level = и добавляя в него имя текущего элемента в startElement и убирая в endElement. Тогда у нас в self.level будет текущий уровень элемента.
2. В __init__ передавать пути к тэгам, которые нас интересуют
3. В startElement если путь к текущему элементу начинается с того, что нам интересно, устанавливая self.target, почти как у меня сделано.
4. В endElement проверяем текущий путь - если он из интересующих нас, то у нас в self.result результаты нашего сбора, зовем некий коллбэк, в который передаем эти результаты и путь. В коллбэке делаем что хотим - складываем в базу, посылаем куда-нибудь или игнорируем. И никаких переменных.
Я могу вам нарисовать прототипчик, если вы мне конкретно напишете какие поля на каких уровнях вас интересуют. А то похоже на словах это объяснять будет дольше, чем сделать.
PS: fields не относится к xml парсеру, это мой параметр. Он нужен для того, чтобы сделать класс generic, чтобы не хардкодить туда имена полей, а передавать их туда как параметр.
Aristo.py
Фев. 16, 2012 13:14:24
Возникла идея отправлять внутренности нодов второго ур. в переменную, а ее потом скармливать новому парсер-потоку. Насколько жизненна данная затея? Как в таком случае записать часть xml файла (вместе с тегами) в переменную типа строки?