10

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

例如,阿尔伯特爱因斯坦出生于:1879 年 3 月 14 日;逝世:1955 年 4 月 18 日。

我从使用 Python 获取 Wikipedia 文章开始

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是来自 Albert Einstein 的维基百科页面的部分的 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? 我可以做到,但我觉得我在这项工作中使用了错误的工具。

4

6 回答 6

8

您可以考虑使用BeautifulSouplxml之类的库来解析响应 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]

根据@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
于 2012-09-03T15:31:47.587 回答
5

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

于 2012-09-03T19:43:23.960 回答
5

第一:维基百科 API 允许使用JSON而不是 XML,这将使事情变得更容易。

第二:根本不需要使用 HTML/XML 解析器(内容不是 HTML,容器也不需要)。您需要解析的是JSON 的“修订”标签内的这种Wiki格式。

在此处检查一些 Wiki 解析器


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

这个: {{Birth date|df=yes|1879|3|14}}

使用上面链接中提供的解析器之一,您将能够做到这一点。

于 2012-09-03T15:43:28.430 回答
1

persondata模板现已弃用,您应改为访问 Wikidata。参见维基数据:数据访问。我在 2012 年的早期(现已弃用)答案如下:

您应该做的是解析{{persondata}}大多数传记文章中的模板。现有的工具可以轻松地以编程方式提取此类数据,使用您现有的知识和其他有用的答案,我相信您可以完成这项工作。

于 2012-09-04T09:25:15.300 回答
1

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

特别是,这里的错误处理方式并不多......

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"))
于 2021-02-11T00:55:30.070 回答
1

2019 年的一种替代方法是使用 Wikidata API,除其他外,它以结构化格式公开出生和死亡日期等传记数据,无需任何自定义解析器即可轻松使用。许多 Wikipedia 文章的信息依赖于 Wikidata,因此在许多情况下,这与您使用 Wikipedia 数据相同。

例如,查看Albert Einstein 的 Wikidata 页面并搜索“出生日期”和“死亡日期”,您会发现它们与 Wikipedia 中的相同。Wikidata 中的每个实体都有一个“声明”列表,它们是“属性”和“值”对。要知道爱因斯坦何时出生和死亡,我们只需要在语句列表中搜索适当的属性,在本例中为P569P570。要以编程方式执行此操作,最好以 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",

您可以在本文中了解有关访问 Wikidata 的更多信息,以及更具体地了解Help:Dates中的日期结构。

于 2019-05-12T20:23:49.657 回答