Разбирать даты рождения и смерти из Википедии?

Я пытаюсь написать программу на Python, которая может искать в Википедии даты рождения и смерти людей.

Например, Альберт Эйнштейн родился: 14 марта 1879 г .; умер: 18 апреля 1955 г.

Я начал с Получите статью из Википедии с помощью Python

import urllib2
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', 'Mozilla/5.0')]
infile = opener.open('http://en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvsection=0&titles=Albert_Einstein&format=xml')
page2 = infile.read()

Это работает, насколько это возможно. page2 - это xml-представление раздела со страницы википедии Альберта Эйнштейна.

И я просмотрел этот учебник, теперь, когда у меня есть страница в формате xml ... http://www.travisglines.com/web-coding/python-xml-parser-tutorial, но я не понимаю, как получить нужную мне информацию (даты рождения и смерти) из файл xml. Я чувствую, что должен быть рядом, и все же я не знаю, что делать дальше.

ИЗМЕНИТЬ

После нескольких ответов я установил BeautifulSoup. Я сейчас на стадии, когда могу напечатать:

import BeautifulSoup as BS
soup = BS.BeautifulSoup(page2)
print soup.getText()
{{Infobox scientist
| name        = Albert Einstein
| image       = Einstein 1921 portrait2.jpg
| caption     = Albert Einstein in 1921
| birth_date  = {{Birth date|df=yes|1879|3|14}}
| birth_place = [[Ulm]], [[Kingdom of Württemberg]], [[German Empire]]
| death_date  = {{Death date and age|df=yes|1955|4|18|1879|3|14}}
| death_place = [[Princeton, New Jersey|Princeton]], New Jersey, United States
| spouse      = [[Mileva Marić]] (1903–1919)<br>{{nowrap|[[Elsa Löwenthal]] (1919–1936)}}
| residence   = Germany, Italy, Switzerland, Austria, Belgium, United Kingdom, United States
| citizenship = {{Plainlist|
* [[Kingdom of Württemberg|Württemberg/Germany]] (1879–1896)
* [[Statelessness|Stateless]] (1896–1901)
* [[Switzerland]] (1901–1955)
* [[Austria–Hungary|Austria]] (1911–1912)
* [[German Empire|Germany]] (1914–1933)
* United States (1940–1955)
}}

Итак, гораздо ближе, но я все еще не знаю, как вернуть death_date в этом формате. Разве я начну разбирать вещи с re? Я могу это сделать, но мне кажется, что я буду использовать не тот инструмент для этой работы.


person JBWhitmore    schedule 03.09.2012    source источник
comment
Анализатор XML вам дальше не поможет. Прочтите, что говорит Дж. Бернардо: извлекайте данные в формате json и используйте специальный анализатор MW.   -  person georg    schedule 03.09.2012
comment
Я приложил полный код как с использованием re для его анализа, так и без него.   -  person K Z    schedule 03.09.2012
comment
Пожалуйста, не пытайтесь имитировать браузер своим User-Agent. Согласно политике пользователя-агента Викимедиа, вы должны использовать «информативный пользовательский агент. строка с контактной информацией ».   -  person svick    schedule 04.09.2012


Ответы (6)


Вы можете рассмотреть возможность использования такой библиотеки, как BeautifulSoup или lxml, чтобы проанализировать ответ html / xml.

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


Вот рабочий код с использованием Requests, BeautifulSoup и re, возможно, не лучшее решение здесь, но он довольно гибкий и может быть расширен для аналогичных проблем:

import re
import requests
from bs4 import BeautifulSoup

url = 'http://en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvsection=0&titles=Albert_Einstein&format=xml'

res = requests.get(url)
soup = BeautifulSoup(res.text, "xml")

birth_re = re.search(r'(Birth date(.*?)}})', soup.revisions.getText())
birth_data = birth_re.group(0).split('|')
birth_year = birth_data[2]
birth_month = birth_data[3]
birth_day = birth_data[4]

death_re = re.search(r'(Death date(.*?)}})', soup.revisions.getText())
death_data = death_re.group(0).split('|')
death_year = death_data[2]
death_month = death_data[3]
death_day = death_data[4]

Предложение Per @JBernardo с использованием данных JSON и mwparserfromhell, лучший ответ для этого конкретного варианта использования:

import requests
import mwparserfromhell

url = 'http://en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvsection=0&titles=Albert_Einstein&format=json'

res = requests.get(url)
text = res.json["query"]["pages"].values()[0]["revisions"][0]["*"]
wiki = mwparserfromhell.parse(text)

birth_data = wiki.filter_templates(matches="Birth date")[0]
birth_year = birth_data.get(1).value
birth_month = birth_data.get(2).value
birth_day = birth_data.get(3).value

death_data = wiki.filter_templates(matches="Death date")[0]
death_year = death_data.get(1).value
death_month = death_data.get(2).value
death_day = death_data.get(3).value
person K Z    schedule 03.09.2012
comment
Вы даже проверяли данные, чтобы узнать, поможет ли анализатор HTML / XML? подсказка: не будет - person JBernardo; 03.09.2012
comment
@JBernardo Вы правы, содержимое находится в том же теге XML. Хотя похоже, что у формата JSON такая же проблема. Я думаю, что один из предложенных вами парсеров будет анализировать данные внутри тега? - person K Z; 03.09.2012
comment
@KayZhu, значит, вы понимаете, что настоящие данные, которые он хочет проанализировать, - это формат Wiki? Использование JSON состоит в том, чтобы упростить доступ к данным Wiki (потому что JSON намного проще, чем XML) - person JBernardo; 03.09.2012
comment
@JBernardo Да, вы правы, похоже, что парсеры в ссылке будут хорошо работать и с тем, и с другим. - person K Z; 03.09.2012

Первое. API Википедии позволяет использовать JSON вместо XML, что значительно упростит задачу.

Второе: нет необходимости использовать парсеры HTML / XML (контент - это не HTML, и не должен быть контейнер). Что вам нужно проанализировать, так это этот формат Wiki внутри тега «revisions» JSON.

Проверьте некоторые парсеры Wiki здесь


Что здесь сбивает с толку, так это то, что API позволяет вам запрашивать определенный формат (XML или JSON), но это всего лишь контейнер для некоторого текста в реальном формате, который вы хотите проанализировать:

Этот: {{Birth date|df=yes|1879|3|14}}

Вы сможете это сделать с помощью одного из парсеров, указанных по ссылке выше.

person JBernardo    schedule 03.09.2012
comment
Хорошо, поэтому я могу прочитать это как JSON: infile = opener.open('http://en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvsection=0&titles=Albert_Einstein&format=json') Глядя на парсеры Wiki, с которыми вы связались, я вижу много XML / HTML, но не перечисленных JSON. У вас есть рекомендуемый? - person JBWhitmore; 03.09.2012
comment
@JBWhitmore модуль json поставляется с Python. Это просто контейнер для реальных данных, которые вы хотите проанализировать. Эти данные не в формате XML, HTML или JSON. Это в каком-то определенном формате Wiki - person JBernardo; 03.09.2012
comment
@JBWhitmore. Вы хотите проанализировать такие данные: {{Birth date|df=yes|1879|3|14}} и один из модулей в ссылке вам поможет. - person JBernardo; 03.09.2012
comment
В данном конкретном случае это лучший ответ, чем мой. Проголосуйте :) - person K Z; 03.09.2012
comment
Послушайте, я ценю, что вы, ребята, знаете, что происходит. Между тем, у меня в терминале мигающий курсор и переменная с данными JSON в формате Wiki. И да, я хотел бы разобрать именно {{Birth date|df=yes|1879|3|13}} - но вот о чем я вас спрашиваю: как мне это сделать? - person JBWhitmore; 03.09.2012
comment
@JBWhitmore один из парсеров по ссылке выше: mwparserfromhell. Я не знаю, лучше ли это, но прочитайте Использование по этой ссылке и попытайтесь понять, что он делает и почему он будет анализировать то, что вы хотите - person JBernardo; 03.09.2012

Сначала используйте pywikipedia. Он позволяет запрашивать текст статьи, параметры шаблона и т. Д. Через абстрактный интерфейс высокого уровня. Во-вторых, я бы выбрал шаблон Persondata (посмотрите в конец статьи). Кроме того, в долгосрочной перспективе вас могут заинтересовать Викиданные, внедрение которых займет несколько месяцев , но это позволит легко запрашивать большинство метаданных в статьях Википедии.

person Tgr    schedule 03.09.2012

Шаблон persondata устарел, и вместо этого вам следует обращаться к Викиданным. См. Викиданные: доступ к данным. Мой предыдущий (теперь устаревший) ответ с 2012 года был следующим:

Что вам следует сделать, так это проанализировать шаблон {{persondata}}, который можно найти в большинстве биографических статей. Существуют существующие инструменты для простого извлечения таких данных программным способом с учетом имеющихся у вас знаний и другие полезные ответы, я уверен, вы справитесь.

person Jobjörn Folkesson    schedule 04.09.2012
comment
Как бы то ни было, на случай, если он спасет кого-то еще одним щелчком мыши, Persondata, похоже, устарела. В ссылке написано, что это… было удалено. Отныне такие данные следует добавлять в Викиданные с цитатой. - person Matt V.; 09.07.2017

Одна из альтернатив в 2019 году - использовать Wikidata API, который, среди прочего, предоставляет биографические данные, такие как даты рождения и смерти, в структурированном формате, который очень легко использовать без каких-либо настраиваемых парсеров. Многие статьи Википедии полагаются на информацию из Википедии, поэтому во многих случаях это будет то же самое, как если бы вы использовали данные Википедии.

Например, посмотрите на страницу Викиданных Альберта Эйнштейна и выполните поиск по запросу «дата рождения» и «дата смерти», вы обнаружите, что они такие же, как в Википедии. У каждой сущности в Викиданных есть список «утверждений», которые представляют собой пары «свойств» и «значений». Чтобы узнать, когда родился и умер Эйнштейн, нам нужно только найти в списке утверждений соответствующие свойства, в данном случае P569 и P570. Чтобы сделать это программно, лучше всего получить доступ к объекту как json, что вы можете сделать со следующей структурой URL:

https://www.wikidata.org/wiki/Special:EntityData/Q937.json

И в качестве примера вот что P569 заявляет об Эйнштейне:

        "P569": [
          {
            "mainsnak": {
              "property": "P569",
              "datavalue": {
                "value": {
                  "time": "+1879-03-14T00:00:00Z",
                  "timezone": 0,
                  "before": 0,
                  "after": 0,
                  "precision": 11,
                  "calendarmodel": "http://www.wikidata.org/entity/Q1985727"
                },
                "type": "time"
              },
              "datatype": "time"
            },
            "type": "statement",

Вы можете узнать больше о доступе к Викиданным в этой статье, а более конкретно о датах структурирована в Help: Dates.

person Yoshiki    schedule 12.05.2019
comment
Я также хочу извлечь даты рождения / смерти - я начал с википедии / маршрута красивого супа, такого как OP, но нашел предложение Йошики использовать Викиданные намного проще. В этой статье приводятся практические примеры использования WikiData, и она мне очень помогла medium.com/freely-sharing-the-sum-of-all-knowledge/ - person sally2000; 17.09.2020

Я столкнулся с этим вопросом и оценил всю полезную информацию, которая была предоставлена ​​в @Yoshiki answer, но для получения рабочего решения потребовался некоторый синтез. Поделитесь здесь, если это будет полезно для кого-то еще. Код также находится в этой сути для тех, кто хочет его разветвить / улучшить.

В частности, здесь не так много способов обработки ошибок ...

import csv
from datetime import datetime
import json
import requests
from dateutil import parser


def id_for_page(page):
    """Uses the wikipedia api to find the wikidata id for a page"""
    api = "https://en.wikipedia.org/w/api.php"
    query = "?action=query&prop=pageprops&titles=%s&format=json"
    slug = page.split('/')[-1]

    response = json.loads(requests.get(api + query % slug).content)
    # Assume we got 1 page result and it is correct.
    page_info = list(response['query']['pages'].values())[0]
    return  page_info['pageprops']['wikibase_item']


def lifespan_for_id(wikidata_id):
    """Uses the wikidata API to retrieve wikidata for the given id."""
    data_url = "https://www.wikidata.org/wiki/Special:EntityData/%s.json"
    page = json.loads(requests.get(data_url % wikidata_id).content)

    claims = list(page['entities'].values())[0]['claims']
    # P569 (birth) and P570 (death) ... not everyone has died yet.
    return [get_claim_as_time(claims, cid) for cid in ['P569', 'P570']]


def get_claim_as_time(claims, claim_id):
    """Helper function to work with data returned from wikidata api"""
    try:
        claim = claims[claim_id][0]['mainsnak']['datavalue']
        assert claim['type'] == 'time', "Expecting time data type"

        # dateparser chokes on leading '+', thanks wikidata.
        return parser.parse(claim['value']['time'][1:])
    except KeyError as e:
        print(e)
        return None


def main():
    page = 'https://en.wikipedia.org/wiki/Albert_Einstein'

    # 1. use the wikipedia api to find the wikidata id for this page
    wikidata_id = id_for_page(page)

    # 2. use the wikidata id to get the birth and death dates
    span = lifespan_for_id(wikidata_id)

    for label, dt in zip(["birth", "death"], span):
        print(label, " = ", datetime.strftime(dt, "%b %d, %Y"))
person Jason Sundram    schedule 11.02.2021