以诗之名
(2018年3月20日更新)“以诗之名”全面改版,本文内容过期。详情见“以诗之名-关于”。
这是什么?
“以诗之名”是我和 Yixiao_Li 同学共同完成的一个搜索引擎,用于搜索你的名字(或者其他的几个汉字)包含于哪首古诗词中。
如搜索马化腾的“化腾”二字,出现的第一首诗是:
造化精神无尽期,
跳腾踔厉即时追。
目前言句知多少,
罕有先生活法诗。
这首诗的第一句中含有“化”、“腾”二字,而且是对齐的。说这是马化腾名字的出处也未尝不可 🙂
搜索结果中包含有你输入的全部关键字,所以我想,在大多数时候,你应该输入“化腾”而不是“马化腾”。
搜索结果是按照一定规则排序的:关键字对齐、关键字在一句之内及总长度较短的诗会排得比较靠前,因为我们认为,这样的诗,正是你想看到的。
此搜索引擎支持查询简体字和繁体字,但程序并不能自动识别你输入的是简体字还是繁体字,需要你显式地指明——若你输入的是繁体字,就打开繁体字对应的开关,否则程序就默认你输入的是简体字。除了“繁体”外,还有“仅唐”和“仅宋”两个开关,这两个开关是互斥的,除非你的浏览器没有执行我写的脚本程序,或是你有意破坏。打开这两个开关中的一个,会只在唐人的作品或宋人的作品中搜索诗词。注意,唐人也会写词,宋人也会写诗。
目前只收录了部分唐诗宋词,数据正在进一步整理中。我希望能再添加“仅经”、“仅楚”和“仅曲’等开关。
开发过程
为何要公开开发过程呢?好吧,这只是我的备忘录而已。
第一步:寻找诗词数据库
在github上搜索“唐诗”就搜到了一个很全的唐诗数据库:chinese-poetry,感谢@jackeyGao,感谢开源!
这个“数据库”是JSON格式的,而且是繁体字版的。
先将其导入数据库。数据库的创建语句是:
CREATE DATABASE shiming DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
数据表的创建语句是:
create table SHI_f (id int AUTO_INCREMENT primary key NOT NULL,title char(100), author char(50), paragraphs text, ft_index text, type char(5), src char(25));
create table SHI_j (id int AUTO_INCREMENT primary key NOT NULL,title char(100), author char(50), paragraphs text, ft_index text, type char(5), src char(25));
其中“_
j”表示简体,“_
f”表示繁体,用Python解析JSON代码如下:
# -*-coding:utf8-*-
import os
import json
import traceback
from sql_head import *
from langconv import *
global db
db = None #全局变量,数据库连接
def insert(title, author, paragraphs, type, src):
u'''将数据插入数据库,会进行繁体转换,保存简、繁体两版'''
global db
sql_j = '''insert into SHI_j (`title`, `author`, `paragraphs`, `ft_index`, `type`, `src`) values ("{0}", "{1}", "{2}", "{3}", "{4}", "{5}")'''
sql_f = '''insert into SHI_f (`title`, `author`, `paragraphs`, `ft_index`, `type`, `src`) values ("{0}", "{1}", "{2}", "{3}", "{4}", "{5}")'''
#进行繁体到简体的转换
title_j = Converter('zh-hans').convert(title.decode('utf-8'))
title_j = title_j.encode('utf-8')
author_j = Converter('zh-hans').convert(author.decode('utf-8'))
author_j = author_j.encode('utf-8')
paragraphs_j = Converter('zh-hans').convert(paragraphs.decode('utf-8'))
index_j = ''
for i in paragraphs_j:
index_j += i+" "
index = ''
for i in paragraphs:
index += i+" "
db=linkmysql(db) #连接数据库
cursor = db.cursor() #获得游标
try:
cursor.execute(sql_j.format(title_j, author_j, paragraphs_j, index_j, type, src)) #插入记录
cursor.execute(sql_f.format(title, author, paragraphs, index, type, src)) #插入记录
db.commit()
except:
# 发生错误时回滚
traceback.print_exc()
db.rollback()
return -1
else:
return 0
def jiexi_json(src):
'''从给定源读html文件解析出标题、作者、内容等'''
if "song" in src:
type = "song"
elif "tang" in src:
type = "tang"
else:
type = "None"
f = open(src, "r")
try:
s = json.load(f, encoding='utf-8')
except:
traceback.print_exc()
f.close()
return
f.close()
for i in s:
try:
title = i['title']
author = i['author']
paragraphs=''
for item in i['paragraphs']:
if item.find(u'《')!=-1 or item.find(u'〖')!=-1:
break
paragraphs += item
insert(title, author, paragraphs, type, src)
except:
traceback.print_exc()
continue
def bianli(rootdir):
'''遍历rootdir目录中的文件'''
num = 0
for parent,dirnames,filenames in os.walk(rootdir):
for filename in filenames: #输出文件信息
if filename.endswith('.json'): #该文件是json文件
print "["+str(num)+"]",filename
src = filename
jiexi_json(src)
if __name__ == '__main__':
db=linkmysql(db) #连接数据库
bianli(".")
db.close() #关闭和数据库的连接
其中langconv是用于进行繁体字和简体字转换的Python库,作者是@Skydark Chen,sql_head是我写的一个连接数据库用到很简单的库,代码如下:
# -*-coding:utf8-*-
import sys
import MySQLdb
#指定编码为utf8
reload(sys)
sys.setdefaultencoding('utf8')
db_config = {
"hostname": "localhost",#主机名
"username": "XXXX",#数据库用户名
"password": "XXXX",#数据库密码
"databasename": "XXXX",#要存入数据的数据库名
}
def linkmysql(db):
try:#MySQLdb不支持长时间连接,在操作数据库前检查连接是否过期,过期则重连
db.ping(True)
except:
#再次连接数据库
db = MySQLdb.connect(db_config["hostname"],
db_config["username"],
db_config["password"],
db_config["databasename"],
charset='utf8')
return db
运行该脚本,约半个小时后,数据导入完成,经查询,共有57591首唐诗,254237首宋词,这个数据量还是相当可观的。但比起数千年来,中华民族创造的灿烂诗海,只是沧海一粟而已。
第二步:查询语句
创建全文索引:
ALTER TABLE SHI_f ADD FULLTEXT(`ft_index`);
ALTER TABLE SHI_j ADD FULLTEXT(`ft_index`);
repair table SHI_f;
repair table SHI_j;
在简体库中搜索同时含有“张飞”二字的诗词,按字数多少递增排序,限定显示前500条。
SELECT id, title, author, paragraphs FROM SHI_j WHERE MATCH (`ft_index`) AGAINST ("张") and MATCH (`ft_index`) AGAINST ("飞") order by LENGTH(paragraphs) limit 0,500;
第三步:编写网站
前端是用“轻量,小巧且精美的UI库”SUI Mobile 写的。
后台是用php写的。
第四步:部署上线
由于是两个人合作完成的,故有两个实例,一个是http://s.werner.wiki,还有一个是http://poetry.liyixiao.site/,这两个网站运行在不同的地方,只有很小的差别。
附:开始时的困境
一开始做这个项目时,我没有想到神奇的github,而是百度“唐诗大全”之类的字眼,妄图找到较全的唐诗数据库,当然是失败的。热情的popcorn同学得知我这一困境后为我友情提供多本唐诗宋词相关电子书,是azw3和mobi格式的。感谢popcorn同学的帮助!
当时我是这样处理电子书的:先通过一家网站将azw3格式的电子书转换成html格式,然后用Python解析html,从中提取出标题、作者、内容等我们关心的数据,并将其保存到数据库中。下面的代码是用于解析《宋词三百首全解》这本电子书的,虽然最终没有使用,但却具有纪念意义,故也将其摘录于此。
创建数据库和数据表
CREATE DATABASE SHIMING DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
create table SONGCI (id int AUTO_INCREMENT primary key NOT NULL,title char(20),author char(15),content text, src char(50));
解析网页
用Python解析网页,需安装BeautifulSoup4
sudo pip install BeautifulSoup4
代码
# -*-coding:utf8-*-
import os
import traceback
from sql_head import *
from bs4 import BeautifulSoup
def insert(title, author, content, src):
'''将数据插入数据库'''
sql = '''insert into SONGCI (`title`, `author`, `content`, `src`) values ("{0}", "{1}", "{2}", "{3}")'''
db = None
db=linkmysql(db) #连接数据库
cursor = db.cursor() #获得游标
try:
cursor.execute(sql.format(title, author, content, src)) #插入记录
db.commit()
except:
# 发生错误时回滚
db.rollback()
db.close() #关闭和数据库的连接
return -1;
else:
db.close() #关闭和数据库的连接
return 0;
def jiexi_html(src):
'''从给定源读html文件解析出标题、作者、内容等'''
f = open(src, "r")
html_doc = f.read()
f.close()
soup = BeautifulSoup(html_doc, "lxml")
[s.extract() for s in soup('sup')] #去除所有sup标签
title = soup.h1.string
author = soup.find_all("p", class_="normaltext2")[0].get_text()
content = soup.find_all("p", class_="normaltext4")[0].get_text()
return title, author, content
def bianli(rootdir):
'''遍历rootdir目录中的文件'''
for parent,dirnames,filenames in os.walk(rootdir):
for filename in filenames: #输出文件信息
if filename.endswith('.html'): #该文件是html文件
print filename
src = filename
try:
title, author, content = jiexi_html(src)
except:
traceback.print_exc()
continue
try:
ret = insert(title, author, content, src)
except:
traceback.print_exc()
continue
if ret==0:
print 'success'
else:
print 'fail'
if __name__ == '__main__':
bianli(".")
https://poem.werner.wiki/ 和 http://s.werner.wiki/ 都访问不了了。
试试 http://poetry.werner.wiki/。