重构:基础代码

This commit is contained in:
周中平 2023-11-03 16:58:54 +08:00
parent 75ee065752
commit 02bbaaa788
Signed by: zhouzhongping
GPG Key ID: 6666822800008000
9 changed files with 536 additions and 275 deletions

44
main.py
View File

@ -1,8 +1,10 @@
from flask import Flask, render_template, redirect, url_for from flask import Flask, render_template, redirect, url_for
from loguru import logger from loguru import logger
from src.config import Config import const
from src.generator import build_data import models
import tools
from generator import Generator
app = Flask(__name__) app = Flask(__name__)
logger.add("endofyear.log") logger.add("endofyear.log")
@ -10,28 +12,36 @@ logger.add("endofyear.log")
@app.route('/') @app.route('/')
def home(): def home():
# 默认主题 painting # 重定向 painting
return redirect(url_for('painting')) return redirect(url_for('painting'))
@app.route('/painting') @app.route('/painting')
def painting(): def painting():
if Config("config.ini").web_status: # 站点数据
# web 服务 site = models.Site(
# 如果数据存在,直接返回 service=const.SITE_SERVICE,
if blog_data := Config("config.ini").blog_data: title=const.SITE_NAME
return render_template('painting.html', data=blog_data, web_status=1) ).to_dict()
# 如果数据不存在,需要生成,并写入配置 # 自定义数据
return render_template('painting.html', data=build_data(), web_status=1) custom = models.Custom(
else: yiyan=tools.get_yiyan()
# Github 静态 ).to_dict()
# 数据需要生成,并写入静态文件
html_data = render_template('painting.html', data=build_data(), web_status=0)
with open("static/index.html", "w") as f:
f.write(html_data)
return 'OK' # 初始化数据生成器
generator = Generator("https://blog.7wate.com/rss.xml")
# 渲染模板
return render_template('painting.html',
site=site,
blog=generator.blog(),
special_post=generator.special_post(),
sentiment_post=generator.sentiment_post(),
long_post=generator.long_post(),
short_post=generator.short_post(),
custom=custom
)
if __name__ == '__main__': if __name__ == '__main__':

View File

View File

@ -1,5 +1,3 @@
from typing import Any
import jieba.analyse import jieba.analyse
import pytz import pytz
from dateutil.parser import parse from dateutil.parser import parse
@ -7,31 +5,52 @@ from loguru import logger
from lunardate import LunarDate from lunardate import LunarDate
from snownlp import SnowNLP from snownlp import SnowNLP
import const
# 计算文本内容情感分数 # 计算文本内容情感分数
def analyze_sentiment(text): def analyze_sentiment(keys):
""" """
博客文章情感分计算有点问题酌情使用 博客文章情感分计算
:param text:文章文本
:param keys:文章关键字
:return:分数 :return:分数
""" """
s = SnowNLP(text) score_lists = [SnowNLP(key).sentiments for key in keys]
return round(s.sentiments * 100) all_score = sum(score_lists)
if len(score_lists) > 10:
max_score = max(score_lists)
min_score = min(score_lists)
average_score = (all_score - max_score - min_score) / (len(keys) - 2)
return int(average_score * 1000)
elif 10 > len(score_lists) > 6:
average_score = all_score / len(keys)
return int(average_score * 900)
elif 6 > len(score_lists) > 3:
average_score = all_score / len(keys)
return int(average_score * 800)
elif 3 > len(score_lists) > 0:
average_score = all_score / len(keys)
return int(average_score * 500)
else:
return 0
def classify_and_extract_keywords(text: str, topK: int, stopwords: str, def extract_keywords(text,
tech_terms_file: str) -> tuple[None, list[Any]] | tuple[int, Any]: topK,
stopwords):
""" """
博客文章关键字提取 文章关键字提取
:param text:文章文本 :param text:文章文本
:param topK:关键字数量建议20个 :param topK:关键字数量
:param stopwords:停词文本去掉无意义词组 :param stopwords:停词文本去掉无意义词组
:param tech_terms_file:专业词语区分文章类目
:return: :return:
""" """
try: try:
jieba.analyse.set_stop_words(stopwords) jieba.analyse.set_stop_words(stopwords)
keywords = jieba.analyse.extract_tags(text, topK=topK) keywords = jieba.analyse.extract_tags(text, topK=topK)
return keywords
except ValueError as e: except ValueError as e:
logger.error(f"关键词提取出错:{e}") logger.error(f"关键词提取出错:{e}")
return None, [] return None, []
@ -39,72 +58,52 @@ def classify_and_extract_keywords(text: str, topK: int, stopwords: str,
logger.error(f"关键词提取出错:{e}") logger.error(f"关键词提取出错:{e}")
return None, [] return None, []
def check_category(tech_terms_file, keywords):
"""
文章分类判断
:param keywords: 文章关键词
:param tech_terms_file: 分类词典文件
:return: 分类常量
"""
with open(tech_terms_file, 'r', encoding='utf-8') as f: with open(tech_terms_file, 'r', encoding='utf-8') as f:
tech_terms_set = {line.strip().lower() for line in f} tech_terms_set = {line.strip().lower() for line in f} # 读取分类词典文件,将其转化为小写并创建集合
for keyword in keywords: for keyword in keywords:
if keyword.lower() in tech_terms_set: if keyword.lower() in tech_terms_set: # 判断关键词是否在分类词典集合中
return 1, keywords return const.BLOG_POST_CATEGORY_TECH # 若关键词存在,则返回技术类分类常量
return 2, keywords return const.BLOG_POST_CATEGORY_LIFE # 若关键词不存在,则返回生活类分类常量
def calculate_weight(time_str: str): def calculate_weight(time_str: str) -> int:
""" """
博客文章特殊日期权重分数计算 计算文章特殊日期的权重分数
- 传统节假日 +10 - 传统节假日 +10
- 节假日 +7 - 节假日 +7
- 凌晨 +5 - 凌晨 +5
- 早上 +4 - 早上 +4
- 下午 +3 - 下午 +3
- 晚上 +2 - 晚上 +2
:param time_str: 时间字符串 :param time_str: 时间字符串
:return:总分数特殊日期 :return: 总分数整数
""" """
dt = parse(time_str) dt = parse(time_str)
dt = dt.astimezone(pytz.timezone('Asia/Shanghai')) dt = dt.astimezone(pytz.timezone(const.TIME_ZONE))
weight = 0 weight = 0
date_str = ""
# 农历节日权重计算
LUNAR_HOLIDAYS = {
(1, 1): '春节',
(1, 15): '元宵节',
(2, 2): '龙抬头',
(5, 5): '端午节',
(7, 7): '七夕节',
(7, 15): '中元节',
(8, 15): '中秋节',
(9, 9): '重阳节',
(12, 8): '腊八节',
(12, 23): '小年',
(12, 30): '除夕'
}
# 计算农历节假日的权重
lunar_date = LunarDate.fromSolarDate(dt.year, dt.month, dt.day) lunar_date = LunarDate.fromSolarDate(dt.year, dt.month, dt.day)
if (lunar_date.month, lunar_date.day) in LUNAR_HOLIDAYS: if (lunar_date.month, lunar_date.day) in const.LUNAR_HOLIDAYS:
weight += 10 weight += 10
date_str = LUNAR_HOLIDAYS[(lunar_date.month, lunar_date.day)]
# 公历节日权重计算 # 计算公历节假日的权重
SOLAR_HOLIDAYS = { if (dt.month, dt.day) in const.SOLAR_HOLIDAYS:
(1, 1): '元旦',
(2, 14): '情人节',
(3, 8): '国际妇女节',
(4, 4): '清明节',
(5, 1): '国际劳动节',
(10, 1): '国庆节',
(12, 13): '南京大屠杀纪念日',
(9, 18): '九一八事变纪念日',
(12, 7): '南京保卫战胜利纪念日',
(8, 15): '抗日战争胜利纪念日'
}
if (dt.month, dt.day) in SOLAR_HOLIDAYS:
weight += 7 weight += 7
date_str = SOLAR_HOLIDAYS[(dt.month, dt.day)]
# 计算时间节点的权重
if 22 <= dt.hour or dt.hour < 7: if 22 <= dt.hour or dt.hour < 7:
weight += 5 weight += 5
elif 7 <= dt.hour < 12: elif 7 <= dt.hour < 12:
@ -116,7 +115,25 @@ def calculate_weight(time_str: str):
else: else:
weight += 0 weight += 0
if not date_str: return weight
date_str = f"{dt.month}{dt.day}"
return weight, date_str
def special_date_calculation(time_str):
"""
特殊日期计算
:param time_str: 时间字符串
:return:总分数
"""
dt = parse(time_str)
dt = dt.astimezone(pytz.timezone(const.TIME_ZONE))
# 农历节假日计算
lunar_date = LunarDate.fromSolarDate(dt.year, dt.month, dt.day)
if (lunar_date.month, lunar_date.day) in const.LUNAR_HOLIDAYS:
return const.LUNAR_HOLIDAYS[(lunar_date.month, lunar_date.day)]
# 公历节假日计算
if (dt.month, dt.day) in const.SOLAR_HOLIDAYS:
return const.SOLAR_HOLIDAYS[(dt.month, dt.day)]
return f"{dt.month}{dt.day}"

49
src/const.py Normal file
View File

@ -0,0 +1,49 @@
# 时区
TIME_ZONE = "Asia/Shanghai"
# 时间格式
FORMAT_TIME = "%Y-%m-%d %H:%M:%S"
# 站点服务模式
SITE_SERVICE = 1
# 站点标题
SITE_NAME = "EndOfYear"
# 博客文章分类-生活
BLOG_POST_CATEGORY_LIFE = 1
# 博客文章分类-技术
BLOG_POST_CATEGORY_TECH = 2
# 博客文章关键字数量
BLOG_MAX_KEYS = 7
# 农历节假日
LUNAR_HOLIDAYS = {
(1, 1): '春节',
(1, 15): '元宵节',
(2, 2): '龙抬头',
(5, 5): '端午节',
(7, 7): '七夕节',
(7, 15): '中元节',
(8, 15): '中秋节',
(9, 9): '重阳节',
(12, 8): '腊八节',
(12, 23): '小年',
(12, 30): '除夕'
}
# 公历节假日
SOLAR_HOLIDAYS = {
(1, 1): '元旦',
(2, 14): '情人节',
(3, 8): '妇女节',
(4, 4): '清明节',
(5, 1): '劳动节',
(10, 1): '国庆节',
(12, 13): '南京大屠杀纪念日',
(9, 18): '九一八事变纪念日',
(12, 7): '南京保卫战胜利纪念日',
(8, 15): '抗日战争胜利纪念日'
}

View File

@ -1,97 +1,116 @@
from collections import Counter
from loguru import logger from loguru import logger
from .analyzer import analyze_sentiment, calculate_weight, classify_and_extract_keywords import models
from .config import Config import scraper
from .scraper import Blog
from .tools import get_yiyan
def build_data(): class Generator:
def __init__(self, rss):
""" """
目前只有一个主题构建数据部分后期会再进行重构拆分 初始化Generator类
:return: 网页渲染数据 :param rss: RSS链接
""" """
# 读取配置
config = Config("config.ini")
# 创建博客对象
try: try:
my_blog = Blog(config.rss_url) self._my_blog = scraper.Blog(rss)
except Exception as e: logger.debug(self._my_blog)
logger.error(f"Feed 无法创建博客对象: {str(e)}") for i, post in enumerate(self._my_blog.post_lists, 1):
return None
logger.debug(my_blog)
# 构建博客基本数据
data = {
"blog_name": my_blog.title,
"blog_link": my_blog.link,
"blog_article_count": my_blog.article_count,
"blog_article_word_count": my_blog.article_word_count,
"blog_end_yiyan": get_yiyan()
}
if my_blog.life is None:
data.update({
"blog_life": 0
})
else:
data.update({
"blog_life_year": my_blog.life // 365,
"blog_life_day": my_blog.life % 365,
})
# 博客文章处理
for i, post in enumerate(my_blog.post_lists(), 1):
# 情感分
post.score = analyze_sentiment(post.content)
# 分类, 关键字
post.category, post.keys = classify_and_extract_keywords(text=post.content, topK=21,
stopwords='data/stop_words.txt',
tech_terms_file='data/tech_terms.txt')
# 权重, 日子计算
post.weight, post.date = calculate_weight(post.time)
logger.info(f"Post #{i}:") logger.info(f"Post #{i}:")
logger.info(post) logger.info(post)
except Exception as e:
logger.error(f"Generator 无法创建 Blog 对象: {str(e)}")
# 博客文章权重计算 def blog(self):
weights = [post.weight for post in my_blog.post_lists()] """
max_weight = max(weights) 获取博客信息
max_item = [post for post in my_blog.post_lists() if post.weight == max_weight][0] :return: Blog字典
"""
return models.Blog(
name=self._my_blog.title,
link=self._my_blog.link,
life=self._my_blog.life,
article_count=self._my_blog.article_count,
article_word_count=self._my_blog.article_word_count,
top_post_keys=self._my_blog.keys,
category=self._my_blog.category
).to_dict()
data.update({ def special_post(self):
"blog_title": max_item.title, """
"blog_content": max_item.content[0:50], 获取特殊日期的文章
"blog_content_date": max_item.date, :return: Post字典
}) """
max_item_special_date = self._get_post_with_max("special_date_score")
return models.Post(
title=max_item_special_date.title,
content=max_item_special_date.content,
keys=max_item_special_date.keys,
time=max_item_special_date.time,
date=max_item_special_date.date
).to_dict()
# 暂时只有一个主题 def sentiment_post(self):
# 博客关键词计算 5 个 """
all_keys = [] 获取情感最优文章
for post in my_blog.post_lists(): :return: Post字典
all_keys.extend(post.keys) """
max_item_sentiment = self._get_post_with_max("sentiment_score")
return models.Post(
title=max_item_sentiment.title,
content=max_item_sentiment.content,
keys=max_item_sentiment.keys,
time=max_item_sentiment.time,
date=max_item_sentiment.date
).to_dict()
keyword_counts = Counter(all_keys) def long_post(self):
top_keywords = keyword_counts.most_common(5) """
data.update({ 获取最长文章数据
"blog_top_keywords": top_keywords :return: Post字典
}) """
max_item_long = self._get_post_with_max("word_count")
return models.Post(
title=max_item_long.title,
content=max_item_long.content,
keys=max_item_long.keys,
time=max_item_long.time,
date=max_item_long.date,
).to_dict()
# 博客分类计算 def short_post(self):
categories = [post.category for post in my_blog.post_lists()] """
cat_counts = Counter(categories) 获取最短文章数据
most_common_cat = cat_counts.most_common(1)[0][0] :return: Post字典
"""
max_item_short = self._get_post_with_min("word_count")
return models.Post(
title=max_item_short.title,
content=max_item_short.content,
keys=max_item_short.keys,
time=max_item_short.time,
date=max_item_short.date,
).to_dict()
data.update({ def _get_post_with_max(self, score_attr):
"blog_category": "技术" if most_common_cat == 1 else "生活" """
}) 获取具有最大属性值的文章
:param score_attr: 属性
:return:
"""
max_score = max(getattr(post, score_attr) for post in self._my_blog.post_lists)
max_posts = [post for post in self._my_blog.post_lists if getattr(post, score_attr) == max_score]
if max_posts:
return max_posts[0]
return None
# 输出 def _get_post_with_min(self, score_attr):
logger.debug(data) """
# 写入 config.ini 避免重复计算 获取具有最小属性值的文章
config.blog_data = data :param score_attr:
return data :return:
"""
min_score = min(getattr(post, score_attr) for post in self._my_blog.post_lists)
min_posts = [post for post in self._my_blog.post_lists if getattr(post, score_attr) == min_score]
if min_posts:
return min_posts[0]
return None

80
src/models.py Normal file
View File

@ -0,0 +1,80 @@
from dataclasses import dataclass
from enum import Enum
from typing import List
@dataclass
class Site:
"""
站点数据模型
- service 服务模式
- title 站点标题
"""
service: int
title: str
def to_dict(self) -> dict:
"""
将Site对象转换为字典形式
"""
return {k: v if not isinstance(v, Enum) else v.value for k, v in vars(self).items()}
@dataclass
class Blog:
"""
博客数据模型
- name名称
- link链接
- life域名注册天数
- article_count博客文章总和
- article_word_count博客文章字数总和
- top_post_keys博客关键字
- category博客分类
"""
name: str
link: str
life: int
article_count: int
article_word_count: int
top_post_keys: List[str]
category: int
def to_dict(self) -> dict:
"""
将Blog对象转换为字典形式
"""
return {k: v if not isinstance(v, Enum) else v.value for k, v in vars(self).items()}
@dataclass
class Post:
"""
文章数据模型
- title标题
- content内容
- keys关键字列表
- date日期字符串
"""
title: str
content: str
keys: List[str]
time: str
date: str
def to_dict(self) -> dict:
"""
将Post对象转换为字典形式
"""
return {k: v if not isinstance(v, Enum) else v.value for k, v in vars(self).items()}
@dataclass
class Custom:
"""
自定义数据模型
- yiyan一言
"""
yiyan: str
def to_dict(self) -> dict:
"""
将Custom对象转换为字典形式
"""
return vars(self)

View File

@ -1,85 +1,139 @@
import re
from collections import Counter
import feedparser import feedparser
from loguru import logger from loguru import logger
from . import tools import analyzer
import const
import tools
class Blog: class Blog:
def __init__(self, url): def __init__(self, rss):
try: try:
self.feed = feedparser.parse(url) # 解析RSS feed
self._feed = feedparser.parse(rss)
# 解析feed中的所有文章
self._posts = [Post(entry) for entry in self._feed.entries]
except Exception as e: except Exception as e:
logger.error(f'解析 RSS feed 时发生错误: {str(e)}') logger.error(f'Feedparser 解析 RSS feed 时发生错误: {str(e)}')
raise raise
self.posts = [Post(entry) for entry in self.feed.entries]
def _get_feed_field(self, field): def _get_feed_field(self, field):
""" if field_value := self._feed.feed.get(field):
RSS feed 中获取特定字段
"""
field_value = self.feed.feed.get(field)
if field_value is None:
logger.warning(f'{field} 字段不存在!')
return field_value return field_value
logger.warning(f'Feedparser {field} 字段不存在!')
return ""
@property @property
def title(self): def title(self):
return self._get_feed_field('title') # 获取RSS feed的标题
return self._feed.feed.get('title')
@property @property
def link(self): def link(self):
return self._get_feed_field('link') # 获取RSS feed的链接
return self._feed.feed.get('link')
@property @property
def life(self): def life(self):
domain = tools.get_domain(self.link) # 获取RSS feed链接的域名存活时间
return tools.get_domain_life(domain) return tools.get_domain_life(self.link)
@property @property
def article_count(self): def article_count(self):
return len(self.posts) # 获取文章数量
return len(self._posts) if self._posts else 0
@property @property
def article_word_count(self): def article_word_count(self):
return sum(post.word_count for post in self.posts) # 获取文章总字数
return sum(post.word_count for post in self._posts) if self._posts else 0
@property
def keys(self):
if self._posts:
# 提取所有关键字
all_keys = [key for post in self._posts for key in post.keys]
# 过滤出中文关键字
chinese_keys = [key for key in all_keys if re.search(r'[\u4e00-\u9fff]+', key)]
# 计算关键字出现的次数
keyword_counts = Counter(chinese_keys)
# 提取出现次数最多的关键字
top_keywords = keyword_counts.most_common(const.BLOG_MAX_KEYS)
return top_keywords
return []
@property
def category(self):
# 获取博客的分类
if self._posts:
# 如果博客有帖子
categories = [post.category for post in self._posts]
# 获取所有帖子的分类
cat_counts = Counter(categories)
# 统计每个分类的个数
most_common_cat = cat_counts.most_common(1)[0][0]
# 获取出现次数最多的分类
return most_common_cat
# 如果博客没有帖子
return const.BLOG_POST_CATEGORY_LIFE
@property
def post_lists(self): def post_lists(self):
return self.posts # 获取文章列表
return self._posts if self._posts else []
def __str__(self): def __str__(self):
return f"Blog: {self.title}, Life:{self.life}, Count{self.article_count}. Word count:{self.article_word_count}" return f"""
博客 {self.title}
链接 {self.link}
时间 {self.life}
文章 {self.article_count}
字数 {self.article_word_count}
分类 {self.category}
关键字 {self.keys}
"""
class Post: class Post:
def __init__(self, entry): def __init__(self, entry):
# 日期权重
self._weight = None
# 日子
self._date = None
# 情感分
self._score = None
# 关键字
self._keys = None
# 分类
self._category = None
self.entry = entry self.entry = entry
# 文章内容
self._content = self._get_content()
# 文章时间
self._time = tools.format_datetime(self._get_entry_field('published'))
# 文章日期
self._date = analyzer.special_date_calculation(self._time)
# 特殊日期分
self._special_date_score = analyzer.calculate_weight(self._get_entry_field('published'))
# 关键字
self._keys = analyzer.extract_keywords(text=self._content,
topK=tools.get_multiple_of_100(self._content),
stopwords='data/stop_words.txt')
# 文章情感分
self._sentiment_score = analyzer.analyze_sentiment(self._keys)
# 分类
self._category = analyzer.check_category(tech_terms_file='data/tech_terms.txt', keywords=self._keys)
def _get_entry_field(self, field): def _get_entry_field(self, field):
""" return self.entry.get(field)
RSS entry 中获取特定字段
"""
field_value = self.entry.get(field)
if field_value is None:
pass
# logger.warning(f'{field} 字段不存在!')
return field_value
@property def _get_content(self):
def title(self): """
return self._get_entry_field('title') 获取文章内容
:return: 文章的描述或内容根据以下规则
@property - 如果'content'字段存在那么返回'content'字段的值
def content(self): - 如果'description'字段的长度小于128并且'content'字段存在那么返回'content'字段的值
- 否则返回'description'字段的值
- 如果'description''content'字段都不存在返回空字符串
"""
description = self._get_entry_field('description') description = self._get_entry_field('description')
content = self._get_entry_field('content') content = self._get_entry_field('content')
if content: if content:
@ -94,60 +148,61 @@ class Post:
return description return description
@property @property
def time(self): def title(self):
return self._get_entry_field('published') # 获取文章标题
return self._get_entry_field('title')
@property @property
def link(self): def content(self):
return self._get_entry_field('link') # 获取文章内容
return self._content
@property @property
def word_count(self): def word_count(self):
# 获取文章字数
return len(self.content) if self.content else 0 return len(self.content) if self.content else 0
@property @property
def keys(self): def time(self):
return self._keys # 获取文章时间
return self._time
@keys.setter
def keys(self, value):
self._keys = value
@property
def score(self):
return self._score
@score.setter
def score(self, value):
self._score = value
@property
def category(self):
return self._category
@category.setter
def category(self, value):
self._category = value
@property @property
def date(self): def date(self):
# 获取日期分
return self._date return self._date
@date.setter @property
def date(self, value): def link(self):
self._date = value # 获取文章链接
return self._get_entry_field('link')
@property @property
def weight(self): def keys(self):
return self._weight # 获取文章关键字
return self._keys
@weight.setter @property
def weight(self, value): def category(self):
self._weight = value # 获取文章分类
return self._category
@property
def special_date_score(self):
# 获取特殊日期分
return self._special_date_score
@property
def sentiment_score(self):
# 获取文章情感分
return self._sentiment_score
def __str__(self): def __str__(self):
return (f"Post title={self.title[:20]}..., " return (f" 标题:{self.title}, "
f" content={self.content[:20]}..., " f" 内容:{self.content[:20]}..., "
f" time={self.time}, " f" 时间:{self.time}, "
f" link={self.link}, " f" 链接:{self.link}, "
f" word_count={self.word_count}") f" 日期分:{self.special_date_score}"
f" 情感分:{self.sentiment_score}"
f" 类目:{self.category}"
f" 关键字:{self.keys}")

View File

@ -1,10 +1,14 @@
from datetime import datetime from datetime import datetime
from urllib.parse import urlparse from urllib.parse import urlparse
import pytz
import requests import requests
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from dateutil.parser import parse
from loguru import logger from loguru import logger
import const
def check_website_status(url): def check_website_status(url):
""" """
@ -54,10 +58,10 @@ def get_domain_life(url):
headers = { headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3" "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
} }
domain_url = f"https://rdap.verisign.com/com/v1/domain/{url}" domain = get_domain(url)
try: try:
response = requests.get(domain_url, headers=headers, timeout=30) response = requests.get(f"https://rdap.verisign.com/com/v1/domain/{domain}", headers=headers, timeout=30)
response.raise_for_status() # Raises stored HTTPError, if one occurred. response.raise_for_status() # Raises stored HTTPError, if one occurred.
registration_date = response.json().get('events')[0].get('eventDate') registration_date = response.json().get('events')[0].get('eventDate')
@ -87,7 +91,7 @@ def get_domain_life(url):
except Exception as err: except Exception as err:
logger.error(f"未预期的错误: {err}") logger.error(f"未预期的错误: {err}")
return None return 0
def remove_html_tags(text): def remove_html_tags(text):
@ -105,7 +109,8 @@ def get_yiyan():
:return:一言 :return:一言
""" """
try: try:
response = requests.get("https://v1.hitokoto.cn/?c=d&min_length=12&encode=text", timeout=30) # Set timeout to 5 seconds response = requests.get("https://v1.hitokoto.cn/?c=d&min_length=18&max_length=24&encode=text",
timeout=30) # Set timeout to 5 seconds
if response.status_code == 200: if response.status_code == 200:
return response.text return response.text
else: else:
@ -123,3 +128,27 @@ def get_yiyan():
except Exception as e: except Exception as e:
logger.error(f"一言未知错误,错误:{e}") logger.error(f"一言未知错误,错误:{e}")
return False return False
def get_multiple_of_100(string):
"""
获取文章长度 100 的整除
:return:建议关键字数量
"""
length = len(string)
multiple = length // 100
if multiple < 1:
multiple = 1
return multiple
def format_datetime(dt_str):
"""
格式化时间字符串为指定格式
:param dt_str:时间字符串
:return:指定格式
"""
dt = parse(dt_str)
tz = pytz.timezone(const.TIME_ZONE)
formatted_dt = dt.astimezone(tz).strftime(const.FORMAT_TIME)
return formatted_dt

View File

@ -3,8 +3,8 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EndOfYear</title> <title>{{ site.title }}</title>
{% if web_status == 1 %} {% if site.service == 1 %}
<link rel="stylesheet" href="{{ url_for('static', filename='painting/css/normalize.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='painting/css/normalize.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='painting/css/animate.min.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='painting/css/animate.min.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='painting/css/painting.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='painting/css/painting.css') }}">
@ -19,7 +19,7 @@
<body> <body>
<div class="container active animate__animated animate__fadeIn animate__slow" id="tab1"> <div class="container active animate__animated animate__fadeIn animate__slow" id="tab1">
<audio id="bgm" loop> <audio id="bgm" loop>
{% if web_status == 1 %} {% if site.service == 1 %}
<source src="{{ url_for('static', filename='painting/music/bgm.mp3') }}" type="audio/mpeg">Your browser does <source src="{{ url_for('static', filename='painting/music/bgm.mp3') }}" type="audio/mpeg">Your browser does
not support the audio element. not support the audio element.
{% else %} {% else %}
@ -31,7 +31,7 @@
<div class="notice"> <div class="notice">
<h4>温馨提示</h4> <h4>温馨提示</h4>
<hr> <hr>
<p>EndofYear 使用互联网公开的 RSS 数据源,并使用自建的 Umami 服务统计访问量,绝对不会主动获取个人隐私信息。🫣🫣🫣 <p>EndofYear 使用互联网公开的 RSS 数据源,并使用自建的 Umami 服务统计访问量,绝对不会主动获取个人隐私信息。🫣🫣🫣
<br> <br>
<br> <br>
开启方式:小手轻轻点 ~ 开启方式:小手轻轻点 ~
@ -47,14 +47,14 @@
</div> </div>
<div class="container animate__animated animate__fadeIn animate__slow" id="tab2"> <div class="container animate__animated animate__fadeIn animate__slow" id="tab2">
<div class="tab2-box"> <div class="tab2-box">
<p class="animate__animated animate__fadeIn animate__delay-1s">亲爱的{{ data.blog_name }}</p> <p class="animate__animated animate__fadeIn animate__delay-1s">亲爱的{{ blog.name }}</p>
{% if data.blog_life == 0 %} {% if blog.life == 0 %}
<p class="animate__animated animate__fadeIn animate__delay-2s">旧事如梦,一年已过</p> <p class="animate__animated animate__fadeIn animate__delay-2s">旧事如梦,一年已过</p>
<p class="animate__animated animate__fadeIn animate__delay-2s">贰三年、感谢有你!</p> <p class="animate__animated animate__fadeIn animate__delay-2s">贰三年、感谢有你!</p>
{% else %} {% else %}
<p class="animate__animated animate__fadeIn animate__delay-2s">今天是我们相识的</p> <p class="animate__animated animate__fadeIn animate__delay-2s">今天是我们相识的</p>
<p class="animate__animated animate__fadeIn animate__delay-3s"><small>{{ data.blog_life_year }}</small> <p class="animate__animated animate__fadeIn animate__delay-3s"><small>{{ blog.life // 365 }}</small>
<small>{{ data.blog_life_day }}</small></p> <small>{{ blog.life % 365 }}</small></p>
{% endif %} {% endif %}
</div> </div>
@ -62,23 +62,23 @@
<div class="container animate__animated animate__fadeIn animate__slow" id="tab3"> <div class="container animate__animated animate__fadeIn animate__slow" id="tab3">
<div class="tab3-box"> <div class="tab3-box">
<p class="animate__animated animate__fadeInUp animate__delay-1s">这一年你写下了</p> <p class="animate__animated animate__fadeInUp animate__delay-1s">这一年你写下了</p>
<p class="animate__animated animate__fadeInUp animate__delay-2s"><small>{{ data.blog_article_count }}</small> <p class="animate__animated animate__fadeInUp animate__delay-2s"><small>{{ blog.article_count }}</small>
篇博文</p> 篇博文</p>
<p class="animate__animated animate__fadeInUp animate__delay-3s"> <p class="animate__animated animate__fadeInUp animate__delay-3s">
<small>{{ data.blog_article_word_count }}</small> 个文字</p> <small>{{ blog.article_word_count }}</small> 个文字</p>
</div> </div>
</div> </div>
<div class="container animate__animated animate__fadeIn animate__slow" id="tab4"> <div class="container animate__animated animate__fadeIn animate__slow" id="tab4">
<div class="tab4-box"> <div class="tab4-box">
<p class="animate__animated animate__fadeInDown animate__delay-1s">{{ data.blog_content_date }}那天,你写下了</p> <p class="animate__animated animate__fadeInDown animate__delay-1s">{{ special_post.date }}那天,你写下了</p>
<p class="animate__animated animate__fadeInDown animate__delay-2s">{{ data.blog_title }}</p> <p class="animate__animated animate__fadeInDown animate__delay-2s">{{ special_post.title }}</p>
<p class="animate__animated animate__fadeInDown animate__delay-3s">{{ data.blog_content }}<small>……</small> <p class="animate__animated animate__fadeInDown animate__delay-3s">{{ special_post.content[:50] }}<small>……</small>
</p> </p>
</div> </div>
</div> </div>
<div class="container animate__animated animate__fadeIn animate__slow" id="tab5"> <div class="container animate__animated animate__fadeIn animate__slow" id="tab5">
<div class="tab5-box"> <div class="tab5-box">
{% for keyword in data.blog_top_keywords %} {% for keyword in blog.top_post_keys[0:5] %}
<p>{{ keyword[0] }}</p> <p>{{ keyword[0] }}</p>
{% endfor %} {% endfor %}
<p class="animate__animated animate__fadeInDown animate__delay-1s">这些都是<small></small></p> <p class="animate__animated animate__fadeInDown animate__delay-1s">这些都是<small></small></p>
@ -87,13 +87,15 @@
</div> </div>
<div class="container animate__animated animate__fadeIn animate__slow" id="tab6"> <div class="container animate__animated animate__fadeIn animate__slow" id="tab6">
<div class="tab6-box"> <div class="tab6-box">
<p class="animate__animated animate__fadeInLeft animate__delay-1s">热爱{{ data.blog_category }}的你</p> <p class="animate__animated animate__fadeInLeft animate__delay-1s">
热爱{% if blog.category == 1 %}生活{% else %}技术{% endif %}的你
</p>
<p class="animate__animated animate__fadeInLeft animate__delay-2s">一定要继续砥砺前行!</p> <p class="animate__animated animate__fadeInLeft animate__delay-2s">一定要继续砥砺前行!</p>
</div> </div>
</div> </div>
<div class="container animate__animated animate__fadeIn animate__slow" id="tab7"> <div class="container animate__animated animate__fadeIn animate__slow" id="tab7">
<div class="tab7-box"> <div class="tab7-box">
<p id="yiyan"> {{ data.blog_end_yiyan }}</p> <p id="yiyan"> {{ custom.yiyan }}</p>
</div> </div>
</div> </div>