超详细,手把手教你用20行Python代码制作飞花令小程序!

飞花令是古时候人们经常玩一种“行酒令”的游戏,是中国古代酒令之一,属雅令。“飞花”一词则出自唐代诗人韩翃《寒食》中 春城无处不飞花 一句。行飞花令时选用诗和词,也可用曲,但选择的句子一般不超过7个字。

在《中国诗词大会》中改良了“飞花令”,不再仅用 字,而是增加了 云、 春、月、夜 等诗词中的高频字,轮流背诵含有关键字的诗句,直至决出胜负。

今天,我们就利用 Python 定制一款“飞花令”小程序:给定一个关键字或者关键词,就能够返回许多含有这个关键字的诗句,跟朋友玩再也不怕输了!

网页分析

要利用爬虫完成这项工作需要先选择一个合适的网站,这里我们选择了 古诗文网https://www.gushiwen.cn/

在右上角的方框中输入关键词,如,就能够返回相应的结果:

我们注意到,返回的结果是一整首诗或词,关键字所在的句子仅为其中一句。后面我们爬取信息时也需要做到过滤。

往下翻页后会发现只能获取前 2 页内容,到第 3 页会出现以下提示:

也就是说要完整获取全部诗文需要下载 App,本文简化问题只爬取前 2 页的内容,后续有机会再分享 App 相关爬虫推文。在翻页的过程中我们注意一下 URL 的改变:

第 1 页:https://so.gushiwen.cn/search.aspx?value=酒

第 2 页:https://so.gushiwen.cn/search.aspx?type=title&page=2&value=酒

其中经过测试 type=title 可以去除,而page=2 显然是页码,那么 page=1 能否获取到第 1 页呢?

答案是可以的,因此不需要用 requestspost 请求,直接 get 下面的 URL 就可到达指定页面:https://so.gushiwen.cn/search.aspx?page=页码&value=关键字

大致分析完就可以写代码了

代码实现

首先导入库,设置请求头

import requests
from lxml import html

headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'}

以关键字为例,尝试获取第一页全部内容:

import requests
from lxml import html

headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'}
html_data = requests.get('https://so.gushiwen.cn/search.aspx?page=1&value=酒', headers=headers).text
print(html_data)

返回的文本中有我们需要的内容,说明组合而成的请求是没有问题的。接下来就可以解析文本获取具体内容了,本文采用 Xpath

selector = html.fromstring(html_data)
poets = selector.xpath("/html/body/div[2]/div[1]/div[@class='sons']")
for poet in poets:
    title = ''.join(poet.xpath("div[1]/p[1]/a/b//text()")).strip()
    print(title)

诗人和朝代被分隔至两行,说明之间存在换行符及空格,可以用包含.strip()的列表推导式去除:

for poet in poets:
    title = ''.join(poet.xpath("div[1]/p[1]/a/b//text()")).strip()
    source = ''.join(poet.xpath('div[1]/p[2]//text()'))
    source = ''.join([i.strip() for i in source])
    print(title, source)

最后是对诗句的解析。为了获取关键字真正在的句子,我们要通过句号或者问号将整首诗断开成多个完整句

for poet in poets:
    title = ''.join(poet.xpath("div[1]/p[1]/a/b//text()")).strip()
    source = ''.join(poet.xpath('div[1]/p[2]//text()'))
    source = ''.join([i.strip() for i in source])
    contents = ''.join(poet.xpath('div[1]/div[@class="contson"]//text()')).strip().replace('\n', '。').replace('?', '。').split('。')
    print(title, source, contents)

对每一首诗逐渐判断是否包含关键字:

for poet in poets:
    title = ''.join(poet.xpath("div[1]/p[1]/a/b//text()")).strip()
    source = ''.join(poet.xpath('div[1]/p[2]//text()'))
    source = ''.join([i.strip() for i in source])
    contents = ''.join(poet.xpath('div[1]/div[@class="contson"]//text()')).strip().replace('\n', '。').replace('?', '。').split('。')
    content_lst = []
    for i in contents:
        if '酒' in i:
            content = i.strip() + '。'
            content_lst.append(content)
            # 有的诗可能有两句都包含关键字,这两句诗就都是需求
    if not content_lst: # 有可能只有题目中含有关键词,这种诗就跳过
        continue
    for j in list(set(content_lst)): # 有可能有的诗虽然有两句都包含关键字,但这两句是一样的,需要去重
        print(j, title, source)

大部分需求已经满足,最后只需要利用循环结构组装 URL 达到范围多页的目的,同时关键字可以修改为 input 交互输入,代码如下:

import requests
from lxml import html

headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'}

def poet_content(keyword,num,url):
    html_data = requests.get(url, headers=headers).text
    selector = html.fromstring(html_data)
    poets = selector.xpath("/html/body/div[2]/div[1]/div[@class='sons']")
    for poet in poets:
        title = ''.join(poet.xpath("div[1]/p[1]/a/b//text()")).strip()
        source = ''.join(poet.xpath('div[1]/p[2]//text()'))
        source = ''.join([i.strip() for i in source])
        contents = ''.join(poet.xpath('div[1]/div[@class="contson"]//text()')).strip().replace('\n', '。').replace('?','。').split('。')
        content_lst = []
        for i in contents:
            if keyword in i:
                content = i.strip() + '。'
                content_lst.append(content)
        if not content_lst:
            continue
        for j in list(set(content_lst)):
            print(num, j)
            print(f'<{title}>', source)
            print('')
            num += 1
    return num

if __name__ == '__main__':
    keyword = input('> 请输入关键词: ')
    print('')
    num = 1
    for i in range(1, 3):
        url = f'https://so.gushiwen.org/search.aspx?page={i}&value={keyword}'
        num = poet_content(keyword, num, url)

至此,我们就通过 Python 爬虫就成功制作了一款“飞花令”小工具,感兴趣的读者可以自己尝试一下!

好课推荐

推荐阅读

点分享点收藏点点赞点在看
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以为您提供一些关于使用Python代码实现Transformer的指导。 首先,您需要安装PyTorch和torchtext库。然后,您可以按照以下步骤实现Transformer模型: 1. 定义输入和输出的词汇表,并使用torchtext库加载数据集。 2. 定义Transformer模型的编码器和解码器部分。 3. 实现自注意力机制(self-attention)和前向神经网络(feed-forward network)。 4. 实现残差连接(residual connection)和层归一化(layer normalization)。 5. 定义Transformer模型的训练和评估过程。 下面是一个简单的示例代码,用于实现一个基本的Transformer模型: ```python import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F from torchtext.datasets import TranslationDataset, Multi30k from torchtext.data import Field, BucketIterator # 定义输入和输出的词汇表 SRC = Field(tokenize='spacy', tokenizer_language='de', init_token='<sos>', eos_token='<eos>', lower=True) TRG = Field(tokenize='spacy', tokenizer_language='en', init_token='<sos>', eos_token='<eos>', lower=True) # 加载数据集 train_data, valid_data, test_data = Multi30k.splits(exts=('.de', '.en'), fields=(SRC, TRG)) SRC.build_vocab(train_data, min_freq=2) TRG.build_vocab(train_data, min_freq=2) # 定义Transformer模型的编码器和解码器部分 class Encoder(nn.Module): def __init__(self, input_dim, hid_dim, n_layers, n_heads, pf_dim, dropout, device): super().__init__() self.device = device self.tok_embedding = nn.Embedding(input_dim, hid_dim) self.pos_embedding = nn.Embedding(1000, hid_dim) self.layers = nn.ModuleList([EncoderLayer(hid_dim, n_heads, pf_dim, dropout, device) for _ in range(n_layers)]) self.dropout = nn.Dropout(dropout) self.scale = torch.sqrt(torch.FloatTensor([hid_dim])).to(device) def forward(self, src, src_mask): # src: [batch_size, src_len] # src_mask: [batch_size, 1, 1, src_len] batch_size = src.shape[0] src_len = src.shape[1] pos = torch.arange(0, src_len).unsqueeze(0).repeat(batch_size, 1).to(self.device) # pos: [batch_size, src_len] src = self.dropout((self.tok_embedding(src) * self.scale) + self.pos_embedding(pos)) for layer in self.layers: src = layer(src, src_mask) return src class EncoderLayer(nn.Module): def __init__(self, hid_dim, n_heads, pf_dim, dropout, device): super().__init__() self.self_attn_layer_norm = nn.LayerNorm(hid_dim) self.ff_layer_norm = nn.LayerNorm(hid_dim) self.self_attention = MultiHeadAttentionLayer(hid_dim, n_heads, dropout, device) self.positionwise_feedforward = PositionwiseFeedforwardLayer(hid_dim, pf_dim, dropout) self.dropout = nn.Dropout(dropout) def forward(self, src, src_mask):

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值