Python 从维基百科解析出生和死亡日期?

Python 从维基百科解析出生和死亡日期?,python,mediawiki,wikipedia,wikipedia-api,mediawiki-api,Python,Mediawiki,Wikipedia,Wikipedia Api,Mediawiki Api,我正试图编写一个python程序,可以在维基百科上搜索人们的出生和死亡日期 例如,阿尔伯特·爱因斯坦出生于1879年3月14日;死亡日期:1955年4月18日 我从 这就行了page2是Albert Einstein维基百科页面部分的xml表示 我看了本教程,现在我有了xml格式的页面,但是我不明白如何从xml中获取我想要的信息(出生和死亡日期)。我觉得我必须离得很近,但我不知道如何从这里开始 编辑 经过几次回复,我安装了BeautifulSoup。我现在正处于打印的阶段: import Bea

我正试图编写一个python程序,可以在维基百科上搜索人们的出生和死亡日期

例如,阿尔伯特·爱因斯坦出生于1879年3月14日;死亡日期:1955年4月18日

我从

这就行了
page2
是Albert Einstein维基百科页面部分的xml表示

我看了本教程,现在我有了xml格式的页面,但是我不明白如何从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)
}}

所以,更接近了,但我仍然不知道如何以这种格式返回死亡日期。除非我开始用
re
解析东西?我可以这样做,但我觉得我会用错误的工具来做这项工作。

< P>你可以考虑使用一个库,例如或解析响应HTML/XML。 您可能还想看一看,它有一个更干净的用于发出请求的API


以下是使用
请求
美化组
重新
的工作代码,可以说不是这里的最佳解决方案,但它非常灵活,可以扩展以解决类似问题:

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]

根据@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

首先:维基百科API允许使用JSON而不是XML,这将使事情变得更加简单

Second:根本不需要使用HTML/XML解析器(内容不是HTML,容器也不需要)。您需要解析的是JSON的“revisions”标记中的Wiki格式

检查一些Wiki解析器


这里让人困惑的是,API允许您请求某种格式(XML或JSON),但这只是您想要解析的实际格式文本的容器:

这个:
{{出生日期| df=yes | 1879 | 3 | 14}


有了上面链接中提供的一个解析器,您就可以做到这一点。

首先,使用。它允许您通过高级抽象界面查询文章文本、模板参数等。其次,我将使用
Persondata
模板(请看文章末尾)。此外,从长远来看,您可能会感兴趣,这将需要几个月的时间来介绍,但这将使Wikipedia文章中的大多数元数据易于查询。

现在不推荐使用
persondata
模板,您应该改为访问Wikidata。看见我之前(现在已弃用)在2012年的回答如下:

您应该做的是解析大多数传记文章中的
模板。有,以你现有的知识和其他有用的答案,我相信你可以做到这一点


2019年的一个替代方案是使用Wikidata API,该API以结构化格式公开诸如出生和死亡日期等传记数据,无需任何自定义解析器即可轻松使用。许多维基百科文章的信息都依赖于维基百科数据,因此在许多情况下,这与您使用维基百科数据的情况相同

例如,查看并搜索“出生日期”和“死亡日期”,您会发现它们与维基百科中的相同。Wikidata中的每个实体都有一个“声明”列表,即“属性”和“值”对。要知道爱因斯坦是什么时候出生和去世的,我们只需要在语句列表中搜索适当的属性,在本例中,以及。要通过编程实现这一点,最好以json的形式访问实体,您可以使用以下url结构:

作为一个例子,以下是关于爱因斯坦的声明
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",


您可以在中了解有关访问Wikidata的更多信息,更具体地了解如何在中构建日期。

我遇到了这个问题,非常感谢在中提供的所有有用信息,但需要一些综合才能找到有效的解决方案。在这里分享,以防对其他人有用。该代码也适用于那些希望分叉/改进它的人

特别是,这里没有太多的错误处理方法

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"))

您是否检查了数据以查看HTML/XML解析器是否有帮助?提示:会的not@JBernardo没错,内容在同一个XML标记中。虽然JSON格式似乎也有同样的问题。我想你建议的解析器之一会解析标签中的数据?@KayZhu那么你意识到他想要解析的真正数据是Wiki格式吗?JSON的使用是为了使访问Wiki数据更容易(因为JSON比XML简单得多)@JBernardo是的,你是对的,虽然链接中的解析器可以很好地处理这两种情况。XML解析器不会进一步帮助你。阅读JBernardo所说的:获取json格式的数据,并使用专用的MW解析器。我已经附上了完整的代码,使用或不使用
re
对其进行解析。请不要试图通过您的用户代理模拟浏览器。根据,您应该使用“带有联系信息的信息用户代理字符串”。好的,这样我就可以将其作为JSON阅读:
infle=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。有推荐的吗?@JBWhitmore
json
模块随Python提供。它只是您想要解析的真实数据的容器。此数据不是XML、HTML或JSON格式。它在某个特定的Wiki中format@JBWhitmore您想要解析这类数据:
{{Birth date | df=yes | 1879 | 3 | 14}
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"))