Извлечение заголовков страниц и участников из MediaWiki XML

У меня есть очень большой (7 ГБ) XML-дамп MediaWiki, который состоит из записей о каждом изменении, сделанном на каждой странице Wiki. Я пытаюсь записать, какие пользователи внесли свой вклад в каждую страницу, и поэтому я хочу извлечь это из XML.

XML выглядит примерно так:

<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.3/">
 <page>
  <title>Unique Page title</title>
  <id>11</id>
  <restrictions>sysop</restrictions>
  <revision>
    <id>11</id>
    <timestamp>2005-10-26T02:23:03Z</timestamp>
    <contributor>
      <ip>MediaWiki default</ip>
    </contributor>
    <text xml:space="preserve">i</text>
  </revision>
 </page>
 <page> ... </page>
 <page> ... </page>
 ...
</mediawiki>

Я считаю, что для файла такого размера мне нужно использовать iterparse. Пока я просто пытаюсь распечатать заголовок, но когда я запускаю следующий код, он печатает «Нет».

NS = '{http://www.mediawiki.org/xml/export-0.3/}'
from xml.etree.ElementTree import iterparse
with open('XMLFile.xml') as f:
    for event, elem in iterparse(f):
        if elem.tag == NS + 'page':
            for node in elem:
                if node.tag == NS + 'title':
                    print node.text()
        elem.clear()

person Jeremy    schedule 31.12.2012    source источник
comment
mediawiki-utilities должен помочь.   -  person Nemo    schedule 05.11.2015


Ответы (3)


Вы получаете None при печати текстового содержимого элемента title, поскольку вы используете elem.clear() "слишком рано". По умолчанию только iterparse() генерирует "конечные" события. Когда генерируется событие "конец" для page, все его подэлементы, включая title, уже очищены (опустошены).

Если elem.clear() в коде вопроса переместить всего на один уровень отступа (четыре пробела) вправо, он будет работать как положено. Еще один способ заставить ваш код работать — изменить iterparse(f) на iterparse(f, events=["start"]).

И node.text() должно быть node.text.

Дополнительные сведения см. на странице http://effbot.org/zone/element-iterparse.htm. iterparse().


Предположим, дамп XML (mw.xml) выглядит следующим образом:

<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.3/">
  <page>
    <title>Unique Page title 1</title>
    <id>11</id>
    <restrictions>sysop</restrictions>
    <revision>
      <id>11</id>
      <timestamp>2005-10-26T02:23:03Z</timestamp>
      <contributor>
       <username>Alice</username>
      </contributor>
      <text xml:space="preserve">i</text>
    </revision>
  </page>

  <page>
    <title>Unique Page title 2</title>
    <id>11</id>
    <restrictions>sysop</restrictions>
    <revision>
      <id>11</id>
      <timestamp>2005-10-26T02:23:03Z</timestamp>
      <contributor>
       <username>Bob</username>
      </contributor>
      <text xml:space="preserve">j</text>
    </revision>
  </page>
</mediawiki>

Вот предложение о том, как вы можете получить титул и участника:

from xml.etree.ElementTree import iterparse

NS = '{http://www.mediawiki.org/xml/export-0.3/}'

with open('mw.xml') as f:
    for event, elem in iterparse(f):
        if elem.tag == '{0}page'.format(NS):
            title = elem.find("{0}title".format(NS))
            contr = elem.find(".//{0}username".format(NS))

            if title is not None:
                print title.text
            if contr is not None:
                print contr.text

            elem.clear()

Выход:

Unique Page title 1 
Alice
Unique Page title 2 
Bob

Я предполагаю, что вам нужно имя пользователя автора. Согласно последней схеме XML, contributor может содержать username, ip и/или id дочерних элементов (это справедливо и для версии схемы 0.3).

person mzjn    schedule 02.01.2013

Попробуйте вытащить элементы «заголовок» непосредственно во время итеративного синтаксического анализа вместо выполнения вторичного цикла:

NS = '{http://www.mediawiki.org/xml/export-0.3/}'
from xml.etree.ElementTree import iterparse
with open('XMLFile.xml') as f:
    for event, elem in iterparse(f):
            if elem.tag == NS + 'title':
                print elem.text
            elem.clear()

кажется, работает для меня.

person Brion    schedule 31.12.2012
comment
Мне нужно удостовериться, что элемент заголовка и вкладчик ссылаются на одну и ту же ревизию, то есть оба имеют один и тот же родительский элемент. Похоже, это решение не делает этого, верно? - person Jeremy; 01.01.2013

У меня нет опыта использования Python и iterparse, но, как правило, способ, которым вы делаете это с помощью итеративного синтаксического анализатора XML, будет выглядеть следующим образом:

  • Вне цикла синтаксического анализа настройте переменные для хранения текущего заголовка страницы и списка участников.
  • Внутри цикла всякий раз, когда открывается тег page, сбрасывайте переменные.
  • Когда вы сталкиваетесь с тегом title, установите переменную заголовка страницы в его содержимое.
  • Когда вы встретите тег contributor, добавьте его содержимое в список участников.
  • Когда тег page будет закрыт, выведите собранный заголовок и список участников.
person Ilmari Karonen    schedule 01.01.2013