我正在尝试编写一个python程序,可以在维基百科上搜索人的出生和死亡日期。
例如,阿尔伯特爱因斯坦出生于1879年3月14日;死亡:1955年4月18日。
我从Fetch a Wikipedia article with 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
是来自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ć]]&nbsp;(1903–1919)<br>{{nowrap|[[Elsa Löwenthal]]&nbsp;(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
解析内容?我可以这样做,但我觉得我会使用错误的工具来完成这项工作。
答案 0 :(得分:8)
您可以考虑使用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
答案 1 :(得分:5)
首先:维基百科API允许使用 JSON 而不是XML,这将使事情变得更加容易。
第二:根本不需要使用HTML / XML解析器(内容不是HTML,也不是容器)。您需要解析的是JSON的“revisions”标记内的 Wiki 格式。
检查一些Wiki解析器here
这里似乎令人困惑的是,API允许您请求某种格式(XML或JSON),但这只是您要解析的实际格式的某些文本的容器:
这一个:{{Birth date|df=yes|1879|3|14}}
使用上面链接中提供的解析器之一,您将能够做到这一点。
答案 2 :(得分:4)
首先,使用pywikipedia。它允许您通过高级抽象接口查询文章文本,模板参数等。其次,我会使用Persondata
模板(看看文章末尾)。此外,从长远来看,您可能会对Wikidata感兴趣,这将花费几个月的时间来介绍,但它会使维基百科文章中的大多数元数据都易于查询。
答案 3 :(得分:1)
现在不推荐使用persondata
模板,您应该访问维基数据。见Wikidata:Data access。我之前的(现已弃用)2012年答案如下:
您应该做的是解析大多数传记文章中的
{{persondata}}
模板。有existing tools for easily extracting such data programmatically,您现有的知识和其他有用的答案,我相信您可以做到这一点。
答案 4 :(得分:0)
2019年的一种替代方法是使用Wikidata API,该API除其他外,以结构化格式公开诸如出生日期和死亡日期之类的传记数据,无需任何自定义解析器就可以很容易地使用它。许多Wikipedia文章都依赖Wikidata来获取信息,因此在许多情况下,这与使用Wikipedia数据时是相同的。
例如,查看Wikidata page for Albert Einstein并搜索“出生日期”和“死亡日期”,您会发现它们与Wikipedia中的相同。 Wikidata中的每个实体都有一个“声明”列表,该列表是“属性”和“值”对。要知道爱因斯坦何时出生和去世,我们只需要在语句列表中搜索适当的属性,在本例中为P569和P570。要以编程方式执行此操作,最好将实体作为json访问,您可以使用以下网址结构进行操作:
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",
您可以在this article中了解有关访问Wikidata的更多信息,更具体地说,可以在Help:Dates中了解日期的结构。
答案 5 :(得分:0)
我遇到了这个问题,并感谢 @Yoshiki 的 answer 中提供的所有有用信息,但需要进行一些综合才能找到可行的解决方案。在这里分享,以防对其他人有用。代码也在 this gist 中,供那些希望分叉/改进它的人使用。
特别是,这里的错误处理方式并不多......
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"))