Парсинг огромный файл XML с помощью lxml.etree.методом iterparse, показанный в Python


После решения ошибки в так (как предложил) я теперь вернуться на codereview. :-)

Задача-обработать огромный файл dblp.xml (~800 МБ), представленные DBLP. Записи в этом огромном файле выглядят, например, как этот или этот. В частности:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE dblp SYSTEM "dblp.dtd">
<dblp>
    record_1
    ...
    record_n
</dblp>

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

Я адаптировал этот подход из статьи от компании IBM developerWorks по адресу , который относится к статье инкрементальный парсинг по effbot.org. Это правильный подход для этой задачи? Или есть лучший способ?

import sys
import os
import MySQLdb
from lxml import etree


def fast_iter2(context, cursor):
    # Available elements are:   article|inproceedings|proceedings|book|incollection|phdthesis|mastersthesis|www
    elements = set(['article', 'inproceedings', 'proceedings', 'book', 'incollection', 'phdthesis', "mastersthesis", "www"])
    # Available tags are:       author|editor|title|booktitle|pages|year|address|journal|volume|number|month|url|ee|cdrom|cite|
    #                           publisher|note|crossref|isbn|series|school|chapter
    childElements = set(["title", "booktitle", "year", "journal", "ee"])

    paper = {} # represents a paper with all its tags.
    authors = []   # a list of authors who have written the paper "together".
    paperCounter = 0
    for event, element in context:
        tag = element.tag
        if tag in childElements:
            if element.text:
                paper[tag] = element.text
                # print tag, paper[tag]
        elif tag == "author":
            if element.text:
                authors.append(element.text)
                # print "AUTHOR:", authors[-1]
        elif tag in elements:
            paper["element"] = tag
            paper["mdate"] = element.get("mdate")
            paper["dblpkey"] = element.get("key")
            # print tag, element.get("mdate"), element.get("key"), event
            if paper["element"] in ['phdthesis', "mastersthesis", "www"]:
                pass # throw away "unwanted" records.
            else:
                populate_database(paper, authors, cursor)
            paperCounter += 1
            print paperCounter
            paper = {}
            authors = []
            # if paperCounter == 100:
            #     break
            element.clear()
            while element.getprevious() is not None:
                del element.getparent()[0]
    del context

def main():
        cursor = connectToDatabase()
        cursor.execute("""SET NAMES utf8""")

        context = etree.iterparse(PATH_TO_XML, dtd_validation=True, events=("start", "end"))
        fast_iter(context, cursor)

        cursor.close()


if __name__ == '__main__':
    main()


10439
6
задан 17 мая 2011 в 07:05 Источник Поделиться
Комментарии
1 ответ

В fast_iter2 название, видимо, отсутствие связи с функция на самом деле делает.

Скорее, чем иметь два комплекта быть локальные переменные, я предлагаю поставить их как глобальные константы.

Называя их элементов и childElements довольно универсальный. Я предлагаю что-то более конкретное

Ваш цикл немного неловко. А затем собирая их в том, что итерационный формате, просто захватить все, что при взгляде на элемент теги. Это делает код заполнения бумаги/авторов более простым.

А потом, если пустой блок и через блок else, инвертировать логику.

Я предлагаю перенести очистка элемента код в свою функцию. Это помогает сделать функцию актуальной задачей яснее, избегая вопроса о том, как очистить.

В дель контексте линию бесполезно. Del не разрушит объект, он просто удаляет объект из текущего контекста. Если это был единственный контекст, в котором объект был реферативных, чтобы потом его счетчик ссылок упадет до нуля. Однако, это не тот случай, здесь как вызов функции будет держать его в живых в любом случае. Даже если это и так, то экономия памяти не стоит беспокоиться, поскольку весь сценарий подходит к концу в любом случае.

Мои доработки кода, тестирование на ней:

    CATEGORIES = set(['article', 'inproceedings', 'proceedings', 'book', 'incollection', 'phdthesis', "mastersthesis", "www"])
SKIP_CATEGORIES = set(['phdthesis','mastersthesis', 'www'])
DATA_ITEMS = ["title", "booktitle", "year", "journal", "ee"]

def clear_element(element):
element.clear()
while element.getprevious() is not None:
del element.getparent()[0]

def fast_iter2(context, cursor):
# Available elements are: article|inproceedings|proceedings|book|incollection|phdthesis|mastersthesis|www
# Available tags are: author|editor|title|booktitle|pages|year|address|journal|volume|number|month|url|ee|cdrom|cite|
# publisher|note|crossref|isbn|series|school|chapter

paperCounter = 0
for event, element in context:
if element.tag in CATEGORIES:
authors = [author.text for author in element.findall("author")]
paper = {
'element' : element.tag,
'mdate' : element.get("mdate"),
'dblpkey' : element.get('key')
}
for data_item in DATA_ITEMS:
data = element.find(data_item)
if data is not None:
paper[data_item] = data

if paper['element'] not in SKIP_CATEGORIES:
populate_database(paper, authors, cursor)

paperCounter += 1
print paperCounter

clear_element(element)

Редактировать

Как избежать явного счетчика:

    CATEGORIES = set(['article', 'inproceedings', 'proceedings', 'book', 'incollection', 'phdthesis', "mastersthesis", "www"])
SKIP_CATEGORIES = set(['phdthesis','mastersthesis', 'www'])
DATA_ITEMS = ["title", "booktitle", "year", "journal", "ee"]

def clear_element(element):
element.clear()
while element.getprevious() is not None:
del element.getparent()[0]

def extract_paper_elements(context):
for event, element in context:
if element.tag in CATEGORIES:
yield element
clear_element(element)

def fast_iter2(context, cursor):
for paperCounter, element in enumerate(extract_paper_elements(context)):
authors = [author.text for author in element.findall("author")]
paper = {
'element' : element.tag,
'mdate' : element.get("mdate"),
'dblpkey' : element.get('key')
}
for data_item in DATA_ITEMS:
data = element.find(data_item)
if data is not None:
paper[data_item] = data

if paper['element'] not in SKIP_CATEGORIES:
populate_database(paper, authors, cursor)

print paperCounter

7
ответ дан 17 мая 2011 в 05:05 Источник Поделиться