爬虫案例-网站分词索引与站内搜索

文章目录

    • 1.案例简介
    • 2.设计思路
    • 3.设计结构
    • 4.关键技术
    • 5.数据结构
    • 6.数据集合
    • 7.设计过程
      • 7.1 信息采集模块
      • 7.2 索引模块
      • 7.3 网页排名和搜索
    • 8.示例效果

1.案例简介

本例使用Python建立一个指定网站专用的Web搜索引擎,它能爬取所有指定的网页信息,然后准确的进行中文分词,创建网站分词索引,从而实现对网站信息的快速检索展示。

本例以清华官网新闻网站(http://news.tsinghua.edu.cn)为练习目标,抓取所有新闻页面,然后对其进行分词索引,构建分词索引数据库,方便用户对清华新闻资讯的快速检索。
在这里插入图片描述

2.设计思路

第1步,爬取整个网站,得到所有网页链接。

爬取的方式包括:广度优先检搜索(BFS)和深度优先搜索(DFS)。本例采用广度优先搜索策略,实现完整的站内新闻检索。

第2步,获取网页源代码,解析出新闻内容、标题等信息。

第3步,对获取的内容进行分词,建立分词索引库。

索引一般包括:正排索引(正向索引)和倒排索引(反向索引)两种类型。

  • 正排索引(正向索引,forward index):正排数据表是以文档的ID为关键字,表中记录文档(即网页)中每个字或词的位置信息,查找时扫描表中每个文档中字或词的信息,直到找出所有包含查询关键字的文档。这种组织方法在建立索引的时候结构比较简单,建立比较方便,且易于维护。因为索引是根据文档建立的,若是有新的文档加入,直接为该文档建立一个新的索引块,添加在原来索引文件的后面。若是有文档删除,则直接找到该文档号对应的索引信息,将其直接删除。但是在查询的时候需要对所有的文档进行扫描,以确保没有遗漏,这样就使得搜索的时间大大延长,检索效率低下。

正排数据表的工作原理非常简单,但是由于其搜索效率太低,除非在特定情况下,否则实用价值不大。

  • 倒排索引(反向索引,inverted index):倒排数据表以字或词为关键字进行索引,表中关键字所对应的记录表项记录了出现这个字或词的所有文档,一个表项就是一个字表段,它记录该文档的ID和字符在该文档中出现的位置情况。
    由于每个字或词对应的文档数量在动态变化,所以倒排表的建立和维护都较为复杂,但是在查询的时候由于可以一次得到查询关键字所对应的所有文档,所以效率高于正排数据表。

在全站搜索中,搜索的响应速度是一个最关键的性能,而索引的建立由于在后台进行,尽管效率相对低一些,但不会影响整个搜索引擎的效率。

简单总结,正排索引是从文档到关键字的映射(已知文档求关键字),倒排索引是从关键字到文档的映射(己知关键字求文档)。

在搜索引擎中每个文件都对应一个文件ID,文件内容被表示为一系列关键词的集合。实际上,在搜索引擎索引库中关键词已经转换为关键词ID。

例如,“文档1”经过分词提取了20个关键词,每个关键词都会记录它在文档中的出现次数和出现位置,得到正向索引的结构如下:

  • “文档1”的ID --> 关键词1:出现次数,出现位置列表; 关键词2:出现次数,出现位置列表; 关键词3:出现次数,出现位置列表;…

  • “文档2”的ID --> 关键词1:出现次数,出现位置列表; 关键词2:出现次数,出现位置列表; 关键词3:出现次数,出现位置列表;…
    例如,当用户搜索关键词“研究生”时,假设只存在正向索引(forward index),那么就需要扫描索引库中的所有文档,找出所有包含关键词“研究生”的文档,再根据打分模型进行打分,排出名次后呈现给用户。因为互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。所以搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应一系列的文件,这些文件中都出现这个关键词,得到倒排索引的结构如下:

  • “关键词1”的ID --> 文档1 ID; 文档2 ID; 文档3 ID;…

  • “关键词2”的ID --> 文档1 ID; 文档2 ID; 文档3 ID;…
    第4步,搜索时,根据搜索关键词在词条索引中查询,按顺序返回相关的搜索结果,也可以按网页评价排名顺序返回相关的搜索结果。

当用户输入一串搜索字符串时,程序会先进行分词,然后依照每个词的索引找到相应网页。假如在搜索框中输入“清华大学”,搜索引擎首先会对字符串进行分词处理“清华/大学”,然后按照一定的规则对词做布尔运算.

例如,每个词之间做“与”运算,在索引中搜索“同时”包含这些词的页面。

3.设计结构

本例主要由以下4个模块组成。

  • 信息采集模块:主要是利用网络爬虫实现对网站信息的抓取。
  • 索引模块:负责对爬取的新闻网页的标题、内容进行分词并建立倒排词表。
  • 网页排名模块:TF/IDF是一种统计方法,用于评估一个关键词对于一个文件集的重要程度。
  • 用户搜索界面模块:负责用户关键字的输入,以及搜索结果信息的返回。

4.关键技术

中文分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。中文分词是网页分析索引的纂础。分词的准确性对搜索引擎来说十分重要,如果分词速度太慢,即使 再准确,对于搜索引擎来说也是不可用的,因为搜索引擎需要处理很多网页,如果分析消耗的时间过长,会严重影响搜索引擎内容更新的速度。因此,搜索引擎对于分词的准确率 和速率都提出了很高的要求。

jieba是一个支持中文分词、高准确率、高效率的Python中文分词组件,它支持繁体分词和自定义词典,并支持3种分词模式。

  • 精确模式:试图将句子最精确地切开,适合文木分析。
  • 全模式:把句子中所有可以成词的词语都扫描出来,速度非常快,但是不能解决歧义的问题。
  • 搜索引擎模式:在精确模式的基础上,对长词再次切分,提高召回率,适合用于搜索引擎分词。

使用本例之前,需要安装jieba模块:

pip install jieba

组件提供了jieba.cut()方法用于分词,cut()方法接受两个输入参数:第1个参数为需要分词的字符串;第2个参数用来控制分词模式。

jieba.cut()返回的结构是一个可迭代的生成器(generator),可以使用for循环来获得分词后得到的每一个词语,也可以用list(jieba.cut(...))转化为list列表。

jieba.cut_for_search()方法仅有一个参数,为分词的字符串,该方法适用于搜索引擎构造倒排索引的分词,粒度比较细。

deque (double-ended queue的缩写)双向队列类,似于list列表,位于Python标准库collections中。它提供了两端都可以操作的序列,这意味着在序列的前后都可以执行添加 或删除操作。

deque支持从任意一端添加元素。append()用于从右端添加一个元素,appendleft())用于从左端添加一个元素。

deque也支持从任意一端抛出元素。pop()用于从右端抛出一个元素,popleft())用于从左端抛出一个元素。

5.数据结构

在数据库中建立两个table,其中一个是doc表,存储每个网页ID和URL链接。

在这里插入图片描述

另一个是word表,即倒排表,存储词语和其对应的网页ID的list。

在这里插入图片描述

6.数据集合

如果一个词在某个网页中出现多次,那么list中这个网页的序号也出现多次。list最后转换成一个字符串存进数据库。

例如,词“清华”出现在网页ID为12、15、18号的网页里,12号页而1次,15号页面3次,18号页面2次,它的list应为[12,15,15,15,18,18],转换成字符串“12 15 15 15 18 18”存储在word表的一条记录中,如下图所示。

在这里插入图片描述
在这里插入图片描述

7.设计过程

7.1 信息采集模块

获取初始的URL。初始的URL地址可以由用户指定的某个或某几个初始爬取网页。

根据初始的URL爬取页面,并获得新的URL。在获得初始的URL地址之后,首先需要爬取对应URL地址中的网页,在爬取了对应的URL地址中的网页后,将网页存储到原始数据库中,并目在爬取网页的同时发现新的URL地址,将已爬取的URL地址存放到一个已爬URL列表中,用于去重复判断爬取的进程。

将新的URL放到URL队列中。注意,在第2步中获取了下一个新的URL地址。之后会将新的URL地址放到URL队列中。

从URL队列中读取新的URL,并依据新的URL爬取网页,同时从新网页中获取新URL,并重复上述的爬取过程。

当满足爬虫系统设置的停止条件时停止爬取。在编写爬虫的时候一般会设置相应的停止条件,如果没有设置停止条件,爬虫会一直爬取下去,直到无法获取新的URL地址为止,若设置了停止条件,爬虫则会在停止条件满足时停止爬取。

使用unvisited队列存储待爬取URL链接的集合,并使用广度优先搜索,使用visited集合存储已访问过的URL链接。

7.2 索引模块

建立倒排词表。解析新闻网页内容,这个过程需要根据网站的网页结构的具体情况来处理。

提取出的网页内容存在于title, article中,对它们进行中文分词,并对每个分出的词语建立倒排词表。

# test1.py  信息采集和索引模块
import sys
from collections import deque
import urllib
from urllib import request
import re
from bs4 import BeautifulSoup
import lxml
import sqlite3
import jieba



safelock=input('整个检索可能需要很长时间,确定要重构清华网站的新闻词库?(y/n)')
if safelock!='y':
    sys.exit('退出')

url='http://news.tsinghua.edu.cn/publish/thunews/index.html'  #'http://news.tsinghua.edu.cn/'#入口

unvisited=deque()#待爬取链接的列表,使用广度优先搜索
visited=set()    #已访问的链接集合
unvisited.append(url)
conn=sqlite3.connect('tsinghua_news.db')    # 创建或打开数据库
c=conn.cursor() # 获取游标指针
# 创建数据表
# c.execute('drop table doc')
c.execute('create table  IF NOT EXISTS   doc (id int primary key,link text)')
# c.execute('drop table word')
c.execute('create table  IF NOT EXISTS   word (term varchar(25) primary key,list text)')
conn.commit()   # 提交操作
conn.close()    # 关闭连接

print('***************开始,请耐心等待***************')
cnt=0
while unvisited:
    url=unvisited.popleft() # 从左侧读取第一个url列表项
    visited.add(url)    # 同时存入到已读取列表中
    cnt+=1
    print('开始抓取第',cnt,'个链接:',url)
    
    # 爬取网页内容,时长30秒,避免假死问题
    try:
        response=request.urlopen(url,timeout=3) # 设置请求失效为3秒,可根据情况调整
        content=response.read().decode('utf-8') # 读取网页内容
    except: # 如果读取失败,则继续下一条
        continue

    # 检索下一个可爬取的链接,因为搜索范围是网站内,所以对链接有格式要求,这个格式要求根据具体情况而定
    # 解析网页内容,可能有几种情况,这个也是根据这个网站网页的具体情况而定
    soup=BeautifulSoup(content,'lxml')
    # all_a=soup.find_all('a',{'class':"content"})   # 本页面所有内容链接
    # all_a=soup.select(".content li a,figure a")   # 本页面所有列表项链接
    all_a=soup.select("a[href]")    # 本页面所有包含href属性的链接
    for a in all_a:
        x=a.attrs['href']           # 获取网址
        if re.match(r'http.+',x):   # 排除是http开头,而不是http://news.tsinghua.edu.cn的网址,限定检索链接的范围,仅检索清华新闻网页
            if not re.match(r'http\:\/\/news\.tsinghua\.edu\.cn\/.+',x):
                continue
        if re.match(r'\/publish\/.+',x):       #"/publish/thunews/9654/20191234.html"
            x='http://news.tsinghua.edu.cn'+x
        elif re.match(r'publish/.+',x) :       #"publish/thunews/9654/20191234.html"
            x='http://news.tsinghua.edu.cn/'+x 
        elif re.match(r'\.\.\/publish/.+',x):  #"../publish/thunews/9654/20191234.html" 
            x='http://news.tsinghua.edu.cn'+x[2:]
        elif re.match(r'\.\.\/\.\.\/publish/.+',x):  #"../../publish/thunews/9654/2019123420314.html" 
            x='http://news.tsinghua.edu.cn'+x[5:]
        if not re.match(r'.+\.html?$',x):     # 过滤掉非.html、.htm网页文件,要根据具体网站而定,对于动态生成页面,可能为其他扩展名,主要目的是过滤掉.pdf、.jpg、.png等非网页文件
            continue
        if (x not in visited) and (x not in unvisited):     # 存入未访问列表         
            unvisited.append(x)

    # 分词
    title=soup.title    # 网页标题
    article=soup.find('article',class_='article')   # 新闻文章内容
    if title==None and article==None:
        print('无内容的页面。')
        continue
    elif article==None:
        print('只有标题。')
        title=title.text
        title=''.join(title.split())
        article=''
    else:
        title=title.text
        title=''.join(title.split())
        article=article.get_text("",strip=True)
        article=''.join(article.split())

    print('网页标题:',title)

    # 提取出的网页内容存在title,article两个变量里,对它们进行中文分词
    seggen=jieba.cut_for_search(title)
    seglist=list(seggen)
    seggen=jieba.cut_for_search(article)
    seglist+=list(seggen)

    # 数据存储
    conn=sqlite3.connect("tsinghua_news.db")
    c=conn.cursor()
    print(cnt)
    print(url)
    c.execute('insert into doc values(?,?)',(cnt,url))
    conn.commit()

    #对每个分出的词语建立词表
    for word in seglist:
        # 检验看看这个词语是否已存在于数据库
        c.execute('select list from word where term=?',(word,))
        result=c.fetchall()
        # 如果不存在
        if len(result)==0:
            docliststr=str(cnt)
            c.execute('insert into word values(?,?)',(word,docliststr))
        # 如果已存在
        else:
            docliststr=result[0][0] # 得到字符串
            docliststr+=' '+str(cnt)
            c.execute('update word set list=? where term=?',(docliststr,word))

    conn.commit()
    conn.close()
print('***************词库重构完毕***************')

7.3 网页排名和搜索

网页排名采用TF/IDF统计。TF/IDF是一种用于信息检索与数据挖掘的常用加权技术。TF/IDF是一种用于评估一词对于一个文件集或一个语料库中的一份文件的重要程度。TF的意思是词频(Term Frequency), IDF的意思是逆文本频率指数(Inverse Document Frequency)。TF表示词条t在文档d中出现的频率。IDF的主要思想是:如果包含词条t的文档越少,则词条t的IDF越大,说明词条t具有很好的类别区分能力。

词条t的IDF计算公式如下:ids = log(N/df),其中,N是文档总数,df是包含词条t的文档数量。

在本例中tf={文档号:出现次数},存储的是某个词在文档中出现的次数。例如,"清华"的tf={12:1,15:3,18:2},即词“清华”出现在网页ID为12、15、18号的网页里,12号页面1次,15号页面3次,18号页面2次。

score= {文档号:文档得分}用于存储搜索到文档的排名得分。

# test2.py  打分和搜索模块

import re
import urllib
from urllib import request
from collections import deque
from bs4 import BeautifulSoup
import lxml
import sqlite3
import jieba
import math

conn=sqlite3.connect("tsinghua_news.db")
c=conn.cursor()
c.execute('select count(*) from doc')
N=1+c.fetchall()[0][0]  # 文档总数
target=input('请输入搜索词:')
seggen=jieba.cut_for_search(target)
score={} # 文档号:文档得分
for word in seggen:
    print('得到查询词:',word)
    # 计算score
    tf={}   # 文档号:文档数
    c.execute('select list from word where term=?',(word,))
    result=c.fetchall()
    if len(result)>0:
        doclist=result[0][0]        # 字符串“12 35 35 35 88 88”
        doclist=doclist.split(' ')  # ['12','35','35','35','88','88']
        doclist=[int(x) for x in doclist] # 把字符串转换为元素为int的list [12,35,35,35,88,88]
        df=len(set(doclist))        # 当前word对应的df数(即几篇文档中出现)
        idf=math.log(N/df)
        print('idf:',idf)
        for num in doclist:
            if num in tf:
                tf[num]=tf[num]+1
            else:
                tf[num]=1
        # tf统计结束,现在开始计算score
        for num in tf:
            if num in score:
                # 如果该num文档已经有分数了,则累加
                score[num]=score[num]+tf[num]*idf
            else:
                score[num]=tf[num]*idf
sortedlist=sorted(score.items(),key=lambda d:d[1],reverse=True)
# print('得分列表',sortedlist)

cnt=0
for num,docscore in sortedlist:
    cnt=cnt+1
    c.execute('select link from doc where id=?',(num,))
    url=c.fetchall()[0][0]
    print(url,'得分:',docscore)

    try:
        response=request.urlopen(url)
        content=response.read().decode('utf-8')
    except:
        print('oops...读取网页出错')
        continue

    soup=BeautifulSoup(content,'lxml')
    title=soup.title
    if title==None:
        print('No title.')
    else:
        title=title.text
        print(title)
    if cnt>20:
        break
if cnt==0:
    print('无搜索结果')

8.示例效果

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/473508.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智慧安全:守护智慧城市的安全屏障

随着信息技术的迅猛发展,智慧城市已成为现代城市发展的重要方向。智慧城市通过集成应用先进的信息通信技术,实现城市管理、服务、运行的智能化,为城市的可持续发展注入了新的活力。然而,在智慧城市的建设过程中,安全问…

综合案例-淘宝轮播图

代码&#x1f447; <!DOCTYPE html><html lang"en" xmlns"http://www.w3.org/1999/xhtml"> <head><meta charset"utf-8" /><title>淘宝轮播图</title><style>*{margin:0px;padding:0px;}.tb-promo {…

流畅的 Python 第二版(GPT 重译)(四)

第二部分&#xff1a;函数作为对象 第七章&#xff1a;函数作为一等对象 我从未认为 Python 受到函数式语言的重大影响&#xff0c;无论人们说什么或想什么。我更熟悉命令式语言&#xff0c;如 C 和 Algol 68&#xff0c;尽管我将函数作为一等对象&#xff0c;但我并不认为 Py…

Java 设计模式系列:行为型-中介者模式

简介 中介者模式是一种行为型设计模式&#xff0c;它定义了一个中介对象&#xff0c;用于简化对象之间的交互。中介者模式通过引入一个中介对象来解耦多个对象之间的交互&#xff0c;使得这些对象可以独立地改变和复用。 中介者模式的适用场景包括多个对象之间存在复杂的引用…

asp.net在线租车平台

说明文档 运行前附加数据库.mdf&#xff08;或sql生成数据库&#xff09; 主要技术&#xff1a; 基于asp.net架构和sql server数据库 功能模块&#xff1a; asp.net在线租车平台 用户功能有首页 行业新闻用户注册车辆查询租车介绍访问后台 后台管理员可以进行用户管理 管…

xinput1_3.dll丢失如何修复,xinput1_3.dll的安装修复教程分享

在Windows操作系统环境下&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“找不到xinput13.dll”。由于xinput1_3.dll是微软DirectX SDK的一部分&#xff0c;主要用于支持游戏手柄和其他外部设备的输入功能&#xff0c;缺失这一动态链接库文件可能导致某些依赖…

【WEB3安全基建项目Secwarex】空投指南

GoPlusSecurity是WEB3安全基建项目&#xff0c;3月8日完成400万美元的私募融资&#xff0c;目前总融资已经高达1500万美元&#xff0c;其中包括Binance Labs、Huobi Incubator、Kucoin Ventures、Avalanche等知名机构参投。 1、打开网址&#xff1a;secwarex.io&#xff0c;点…

node.js常用的命令

Node.js 是一个用于执行 JavaScript 代码的运行时环境。以下命令是 Node.js 开发中常用的命令&#xff0c;可以帮助你进行包管理、项目配置和代码执行等操作。 node -v&#xff1a;检查 Node.js 的版本。npm -v&#xff1a;检查 npm&#xff08;Node.js 包管理器&#xff09;的…

通配符ssl证书有哪几种

通配符SSL证书是数字证书中比较特别的一种。它可以同时保护主域名以及主域名下所有的子域名&#xff0c;对所保护的网站传输数据进行加密。在证书有效期内&#xff0c;通配符SSL证书还可以免费增加子域名站点。随着互联网的发展&#xff0c;越来越多的个人和企事业单位的开发者…

01.Queue-Basic

1. 队列简介 队列&#xff08;Queue&#xff09;&#xff1a;一种线性表数据结构&#xff0c;是一种只允许在表的一端进行插入操作&#xff0c;而在表的另一端进行删除操作的线性表。 我们把队列中允许插入的一端称为 「队尾&#xff08;rear&#xff09;」&#xff1b;把允许删…

nginx使用与配置文件

nginx服务配置与配置优化 nginx服务脚本配置 mkdir wwwroot cd wwwroot/ mkdir nginx1 touch index.php vim index.php<?php echo $_SERVER["REMOTE_ADDR"]; ​ ​ vim conf/nginx.confserver {listen 80;server_name localhost;root /www/wwwroot/nginx…

分布式之SleuthZipkin

Sleuth&Zipkin 学习当前课程&#xff0c;比必须要先掌握SpringCloud的基本应用&#xff08;Nacos&#xff0c;Feign调用&#xff09; 对Docker有一定的了解&#xff0c;知道docker-compose.yml如何启动一个容器 RabbitMQ&#xff0c;Elasticsearch有一定了解。 而且学习…

SQLiteC/C++接口详细介绍sqlite3_stmt类(五)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;四&#xff09;- 下一篇&#xff1a; 无 12. sqlite3_bind_text16函数 sqlite3_bind_text16函数用于将UTF-16编码的文本数据&#xff08;字符串&#xff09;绑定…

HTML,子元素使用float后,导致父元素高度塌陷

HTML学习中遇到的一个任务&#xff1a;header 标签有两个元素 div&#xff08;标题&#xff09; 和 nav&#xff08;导航&#xff09;&#xff0c;希望实现的效果是标题在左侧&#xff0c;导航在右侧。 基础代码如下&#xff1a; <!DOCTYPE html> <html><head&…

辐射展—2024深圳辐射监测与防护展览会

2024深圳辐射监测与防护展览会 展会时间&#xff1a;2024年5月15-17日 展会地点&#xff1a;深圳国际会展中心&#xff08;宝安&#xff09; 主办单位&#xff1a;广东省辐射防护协会 广东省环境监测协会 深圳中国环境监测总站技术创新研究院&#xff08;福田&#xff09;…

mysql未完成事务查看

因为MySQL的事务管理主要是基于InnoDB存储引擎的&#xff0c;并且事务的状态&#xff08;例如&#xff0c;是否已提交或回滚&#xff09;通常是内部的、不直接暴露给用户的,但是可以通过一些方法间接地检查或诊断与事务相关的问题 查看正在运行的事务 使用SHOW ENGINE INNODB…

Flink RocksDB状态后端优化总结

截至当前&#xff0c;Flink 作业的状态后端仍然只有 Memory、FileSystem 和 RocksDB 三种可选&#xff0c;且 RocksDB 是状态数据量较大&#xff08;GB 到 TB 级别&#xff09;时的唯一选择。RocksDB 的性能发挥非常仰赖调优&#xff0c;如果全部采用默认配置&#xff0c;读写性…

小程序应用市场的前世今生

最开始的开始&#xff0c;为软件开发者提供软件发布、运营和推广服务的平台称之为软件应用市场&#xff0c;其发展经历了以下3个阶段&#xff1a; 起源阶段&#xff1a;应用市场的起源可以追溯到桌面互联网时代&#xff0c;当时软件的开发和分发主要依靠软件网站。软件开发者可…

【每日一题】 2024年2月汇编(下)

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 【2.16】103.二叉树的锯齿形层序遍历 103. 二叉树的锯齿形层序遍历https://leetcode.cn/problems/binary-tree-zigzag-level-or…

python课后习题一

题目&#xff1a; 1. 2. 解题过程&#xff1a; 1. """计算年数和天数""" minute int(input("请输入分钟数&#xff1a;")) hours minute // 60 days hours // 24 years days // 365 last_days days % 365 print(f"{minut…
最新文章