0%

历史上的今天

如果要在网页上显示一些随机的语句,获得一些人生的经验,你会选择什么?
也许,一言是一个很好的选择,它可以提供一些动漫中的台词,或是网络上的各种小段子。
在前面的文章有趣的Linux命令行工具中提到的fortune命令或许也可以满足需求,pure-ftpd就可以设置在连接成功时显示随机的来自fortune的语句。
那除此之外呢?历史上的今天是一个不错的方案。当你访问一些门户网站的首页,有时会看到这样的栏目。它也是一些百科全书网站,比如维基百科的传统。事实上,在Mac上就自带了一个小型的『历史上的今天』数据库,执行:

1
cat /usr/share/calendar/calendar.history

就可以看到。
这篇文章将介绍如何搭建一个提供『历史上的今天』信息的API。

建立数据库

首先,建立MySQL数据库,用于存储爬取的信息。

1
2
3
4
5
6
7
8
9
10
11
CREATE DATABASE dbname;
#建立数据库,dbname保持一致即可
USE dbname;
CREATE TABLE event (
id int(10) UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
type int(1) DEFAULT NULL,
year varchar(6) DEFAULT NULL,
date varchar(6) DEFAULT NULL,
info varchar(300) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
QUIT;

获取数据

数据来源是维基百科,使用Python爬取数据,并存入数据库中。代码中的usernamepassworddbname分别是你的登录用户名、密码和数据库名。数据表名默认是event,也可以自行修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#!/usr/bin/env python3

username = "username"
password = "password"
dbname = "dbname"

import requests
from bs4 import BeautifulSoup
import re
import datetime
import pymysql

conn = pymysql.connect(host = "127.0.0.1", user = username, password = password, db = dbname, charset = "utf8")
print(conn)
cur = conn.cursor()
cur.execute("SET NAMES utf8mb4")

def savedb(data):
print(data)
try:
cur.execute("insert into event values(null,%s,%s,%s,%s)", data)
except pymysql.err.InternalError:
print("\033[31mERROR: Incorrect string value.\033[0m", data)
with open("failed.txt", "a") as myfile:
myfile.write(str(data) + "\n")
except pymysql.err.DataError:
print("\033[31mERROR: Data too long.\033[0m", data)
with open("failed.txt", "a") as myfile:
myfile.write(str(data) + "\n")

def getDateList():
list = []
date = datetime.date(2016, 1, 1)
for i in range(366):
date_str = str(date.month) + "月" + str(date.day) + "日"
list.append(date_str)
date += datetime.timedelta(days = 1)
return list

def getInfo(html, type, date):
typeList = ["大事记", "出生", "逝世"]
flag = re.compile("(<h2><span id=.*<span class=\"mw-headline\" id=.*?" + typeList[type] + "[\s\S]*?</ul>\s*?)<h2>").search(html)
if flag:
bsObj = BeautifulSoup(flag.group(1), "html.parser").findAll("li")
for li in bsObj:
match = re.compile("((^前|^)\d{1,4}年):([\s\S]*$)").match(li.get_text())
if match:
year = match.group(1)
info = re.sub("\[\d{1,}\]", "", match.group(3).strip())
data = (type, year, date, info)
savedb(data)

list = getDateList()
for date in list:
print(date)
url = "https://zh.wikipedia.org/zh-cn/%s" % date
r = requests.get(url)
getInfo(r.text, 0, date) # 大事记
getInfo(r.text, 1, date) # 出生
getInfo(r.text, 2, date) # 逝世

cur.connection.commit()
cur.close()
conn.close()

这里的两个except分别处理的是特殊字符(非UTF-8范围)和内容长度超出的问题。

查询数据

比较简单的方式是按照json格式进行输出,这里给出一种使用Python的方案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/env python3

username = "username"
password = "password"
dbname = "dbname"

import datetime
import pymysql
from flask import Flask
from flask import request

app = Flask(__name__)

conn = pymysql.connect(host = "127.0.0.1", user = username, password = password, db = dbname, charset = "utf8")
print(conn)
cur = conn.cursor()

@app.route("/", methods = ["GET", "POST"])
def query():
date = datetime.date.today()
today = str(date.month) + "月" + str(date.day) + "日"
_date = request.args.get("date") or today
_type = int(request.args.get("type") or 0)
_count = int(request.args.get("count") or 1)
cur.execute("select * from `event` where `date` = %s and `type` = %s order by RAND() limit %s", (_date, _type, _count))
rows = cur.fetchall()
result = []
for row in rows:
result.append({"year": row[2], "info": row[4]})
return str(result)

app.run(debug = True, port = 8080, host = "0.0.0.0")

同样将usernamepassworddbname根据情况替换,就可以访问了。
当然,用PHP也是可以实现的,毕竟只需要进行MySQL查询,没有静态文件,很方便。可以使用如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
$mysqli = new mysqli("localhost", "username", "password", "dbname");

/* 检查连接 */
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}

$date = $_GET['date'] ? $mysqli->real_escape_string($_GET['date']) : date("m月d日");
$type = $_GET['type'] ? (int)$mysqli->real_escape_string($_GET['type']) : 0;
$count = $_GET['count'] ? (int)$mysqli->real_escape_string($_GET['count']) : 1;
$result = array();

/* 创建一个预编译 SQL 语句 */
if ($stmt = $mysqli->prepare("select * from `event` where `date` = ? and `type` = ? order by RAND() limit ?")) {
/* 对于参数占位符进行参数值绑定 */
$stmt->bind_param("dii", $date, $type, $count);
/* 执行查询 */
$stmt->execute();
/* 将查询结果绑定到变量 */
$stmt->bind_result($id, $type, $year, $date, $info);
/* 获取查询结果值 */
while ($stmt->fetch()) {
$arr = array('year' => $year, 'info' => $info);
$result[] = $arr;
};
echo json_encode($result, JSON_UNESCAPED_UNICODE);
/* 关闭语句句柄 */
$stmt->close();
}
/* 关闭连接 */
$mysqli->close();
?>

项目地址:TodayInHistory-Crawler,可以打包下载所有文件。

博主搭建的API:历史上的今天

🍭支持一根棒棒糖!