成语接龙是中华民族传统的文字游戏,它有着悠久的历史,也有广泛的社会基础,是老少皆宜的民间文化娱乐活动!一般聚会时会玩这个游戏做互动,还有就是QQ有一个成语接龙红包,有时会因为自己的成语储备量不够,而接不下去。
那么大家有没有想过自己去实现一个成语接龙的程序呢?接下来,我就用Python来实现一个成语接龙小程序,废话不多说,开始~~~~
说到成语接龙,首先就得保证拥有足够多的成语,这个条件就不满足,我没有成语哎,散会~
开玩笑,身为一个Python码农,爬个数据还是没啥子问题的,没有成语不要紧,有办法,
我发现了一个网站:chengyu.t086.com/list/A_1.ht…, 这个网站上有很多的成语及解释啥的,废话不多说,我都给它爬下来。
通过网页抓包,分析出以下特点:每次请求都会发出:http://chengyu.t086.com/list/{A-Z}_{页码}.html
这个请求,如下图,是首字母为A的第一页。
解析网页有“下一页”时,循环翻页,例如从 chengyu.t086.com/list/A_1.ht… 翻页至 chengyu.t086.com/list/A_2.ht…, 当解析网页解析不到“下一页”时,就要请求拼音首字母的下一个chengyu.t086.com/list/B_1.ht…, 依次循环下去,直至爬完。
这样就是有两层循环,第一层循环A-Z,第二层循环页码,然后拼成http://chengyu.t086.com/list/{A-Z}_{页码}.html
去请求,没有下一页时,就跳出第二层循环,循环下一个拼音首字母。
如下标记的内容即为成语具体信息的跳转链接,去发请求该链接的话,返回来的是该成语的释意等具体信息。也是我们需要的,需要爬下来。
代码如下:
import requests from bs4 import BeautifulSoup class Idiom: def __init__(self): self.num = 0 self.url = 'http://chengyu.t086.com/list/{}_{}.html' self.url_info = 'http://chengyu.t086.com/{}' self.headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/81.0.4044.43 Safari/537.36', 'Referer': 'http://chengyu.t086.com/' } self.all_idiom = {} self.pinyin_initials = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S' , 'T', 'W', 'X', 'Y', 'Z'] def idiom_spider(self): """ 爬取所有成语 """ idiom_list = [] for initial in self.pinyin_initials: page = 1 while True: url = self.url.format(initial, page) print(url) start_html = requests.get(url, headers=self.headers) start_html.encoding = 'gb18030' soup = BeautifulSoup(start_html.text, "html.parser") # 查找所有class=listw的div listw = soup.find('div', class_='listw') a2 = soup.find("div", class_="a2") # 查找所有的a标签 lista = listw.find_all('a') lastpage = a2.find_all('a') for p in lista: print("词语", p.text) print((p["href"])) info_url = self.url_info.format(p["href"]) print("infourl", info_url) info_html = requests.get(info_url, headers=self.headers) info_html.encoding = 'gb18030' soup = BeautifulSoup(info_html.text, "html.parser") # 查找所有td标签 td_list = soup.findAll('td') # 成语释意 print("含义:", td_list[5].text) new_idiom = {"idiom": p.text, "paraphrase": td_list[5].text, "first_pinxin":initial} idiom_list.append(new_idiom) if not lastpage or str(lastpage[-1]).find("下一页") == -1: # 如果没有下一页的超链接标签,break break page += 1 idiom = Idiom() idiom.idiom_spider()
运行过程图:
可以按照自己的需求把成语存为指定格式,我是把所有成语存到了sqlite3
数据库,因为Python就内置了sqlite3
数据库,所以,在Python中使用sqlite3
,不需要安装任何东西,可以方便直接使用,总共爬取成语30880条,可能不全,但是也够用了。
实现原理其实不说,大家也可能已经想到了,就是根据刚才爬取的成语,与指定成语进行字符串首尾条件匹配,如果拼音可以匹配上,就是接龙成功了。
判断拼音是否相同,在Python中有一个第三方库pypinyin
,可以使用pip install pypinyin
进行安装。 使用下面代码可以获取指定汉字的拼音:
from pypinyin import lazy_pinyin print(lazy_pinyin("全菜工程师"))
结果为:['quan', 'cai', 'gong', 'cheng', 'shi']
,是一个列表。 下面我将成语接龙程序分解:
逻辑很简单,就是查询指定字符串是否在爬取的成语库里,在则为成语,不在则不是:
说到查询成语库,刚才爬取的成语我是放在了sqlite3
数据库之中,那下面为Python连接sqlite3
代码,为了方便使用,提取出来:
import sqlite3 def sqlite_conn(): try: conn = sqlite3.connect('meta.db') return conn except Exception as e: print(e)
那么,判断是否为成语的代码如下:
def idiom_exist(user_idiom): """ 查询指定成语是否在成语库中 :param user_idiom: string :return: bool """ cursor = sqlite_conn().cursor() db_res = cursor.execute("SELECT id, idiom, paraphrase, first_pinxin from idiom where idiom='{}'".format(user_idiom)) for idiom in db_res: if idiom[1]: return True return False
接龙也很简单,根据用户输入成语,获取该成语尾字拼音首字母,根据拼音首字母查询成语库中符合条件的成语,在判定这些成语的首字拼音是否和用户输入的尾字拼音相同,在符合条件的成语中,随机返回一个即可:
def solitaire(user_idiom): """ 返回成语及含义 :param user_idiom: 用户输入的成语 :return: 返回成语及含义 """ cursor = sqlite_conn().cursor() # 如果没有指定需要接龙的成语,则随机挑选一个返回即可 if not user_idiom: random_num = random.randint(1, 30880) random_idiom = cursor.execute("SELECT id, idiom, paraphrase, first_pinxin from idiom where id={}".format(random_num)) for idiom in random_idiom: return idiom[1], idiom[2] player = lazy_pinyin(user_idiom)[-1][0].upper() # 获取玩家输入的最后一个拼音首字母 db_idiom = cursor.execute( "SELECT id, idiom, paraphrase, first_pinxin from idiom where first_pinxin='{}'".format(player)) chioce_idiom = [] # 可供选择的成语 for idiom in db_idiom: if lazy_pinyin(user_idiom)[-1] == lazy_pinyin(idiom[1])[0]: chioce_idiom.append([idiom[1], idiom[2]]) if not chioce_idiom: return None, None return random.choice(chioce_idiom)[0], random.choice(chioce_idiom)[1]
逻辑是先查询用户输入是否为成语,如果是,再判断其是否符合接龙规则,比较简单:
def judge(bot_idiom, user_idiom): if lazy_pinyin(user_idiom)[0] == lazy_pinyin(bot_idiom)[-1]: return True return False
至此,成语接龙核心代码,已经完成,为了交互友好,写了一个交互函数,由于代码篇幅过长,这里只贴截图:
在交互函数中,加入了先后手选择,可以选择先手或者后手,以及记忆集合,用于判断成语是否被重复使用,还加了能接龙10次就成功的机制。
下面展示运行截图:
可以看出,程序可能没有特别完美,单已经很好的实现了预期,对战了几把,由于我的成语储备量不高,统统以失败告终,感兴趣的同学可以试一下,看看你能不能行,哈哈哈。
之前的文章总是以知识点
的形式输出,我发现,写多了往往就成了知识点总结,自己看来都没有什么趣味性,提不起阅读兴趣,以后尽量在自己的文章中加入一些趣味性的东西,也算是提升自己的写作能力了,加油!!