parent
d62923f2b4
commit
f060a18f08
@ -0,0 +1,380 @@ |
||||
import os, sys |
||||
|
||||
sys.path.append(os.path.join(os.path.abspath(__file__).split('AutoInfo')[0] + 'AutoInfo')) |
||||
|
||||
import asyncio |
||||
from datetime import datetime |
||||
import httpx |
||||
from utils.utils import SendEmail |
||||
|
||||
url = "https://60s.erhe.link" |
||||
headers = {"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36"} |
||||
text = '' |
||||
n = 0 |
||||
|
||||
api_list = [ |
||||
"/v2/60s", |
||||
"/v2/douyin", |
||||
"/v2/rednote", |
||||
"/v2/bili", |
||||
"/v2/weibo", |
||||
"/v2/baidu/hot", |
||||
"/v2/baidu/tieba", |
||||
"/v2/toutiao", |
||||
"/v2/zhihu", |
||||
"/v2/hacker-news/top", |
||||
"/v2/hacker-news/new", |
||||
"/v2/hacker-news/best" |
||||
] |
||||
|
||||
api_name_map = {api.replace('/v2/', '').replace('/', '-'): api for api in api_list} |
||||
|
||||
pending_to_send_data = {} |
||||
|
||||
|
||||
async def fetch_api(client, api_name, api): |
||||
source = api.replace('/v2/', '') |
||||
source_name = source.replace('/', '-') |
||||
|
||||
print(f"Fetching {source_name}") |
||||
|
||||
try: |
||||
response = await client.get(url + api, headers=headers, timeout=10.0) |
||||
except Exception as e: |
||||
print(f"Error fetching {source_name}: {str(e)}") |
||||
return |
||||
|
||||
if response.status_code != 200: |
||||
print(f"Error {response.status_code} for {source_name}") |
||||
return |
||||
|
||||
data = response.json() |
||||
data = data.get('data') |
||||
|
||||
if not data: |
||||
return |
||||
|
||||
if type(data) == dict: |
||||
news_list = data.get('news') |
||||
created = data.get('created') |
||||
|
||||
if news_list: |
||||
if api_name not in pending_to_send_data: |
||||
pending_to_send_data[api_name] = [] |
||||
|
||||
for news in news_list: |
||||
pending_to_send_data[api_name].append({ |
||||
'title': news.get('title'), |
||||
'active_time': created |
||||
}) |
||||
|
||||
elif type(data) == list: |
||||
for record in data: |
||||
if api_name not in pending_to_send_data: |
||||
pending_to_send_data[api_name] = [] |
||||
|
||||
pending_to_send_data[api_name].append({ |
||||
'title': record.get('title'), |
||||
'link': record.get('link'), |
||||
'active_time': record.get('active_time') |
||||
}) |
||||
|
||||
|
||||
async def get_api_data(api_name_map, concurrent_limit=6): |
||||
# 将API列表分成批次,每批最多concurrent_limit个 |
||||
api_items = list(api_name_map.items()) |
||||
|
||||
async with httpx.AsyncClient() as client: |
||||
# 分批处理 |
||||
for i in range(0, len(api_items), concurrent_limit): |
||||
batch = api_items[i:i + concurrent_limit] |
||||
|
||||
# 创建并发的协程任务 |
||||
tasks = [fetch_api(client, api_name, api) for api_name, api in batch] |
||||
|
||||
# 等待这一批任务完成 |
||||
await asyncio.gather(*tasks, return_exceptions=True) |
||||
|
||||
# 批次之间等待3秒 |
||||
if i + concurrent_limit < len(api_items): |
||||
print(f"Waiting 3 seconds before next batch...") |
||||
await asyncio.sleep(3) |
||||
|
||||
|
||||
def generate_html_content(pending_to_send_data): |
||||
"""将新闻数据组装成HTML格式""" |
||||
|
||||
html_template = """<!DOCTYPE html> |
||||
<html lang="zh-CN"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>每日热点新闻汇总</title> |
||||
<style> |
||||
body {{ |
||||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif; |
||||
line-height: 1.6; |
||||
color: #333; |
||||
max-width: 1000px; |
||||
margin: 0 auto; |
||||
padding: 20px; |
||||
background-color: #f5f5f5; |
||||
}} |
||||
.container {{ |
||||
background: white; |
||||
border-radius: 8px; |
||||
box-shadow: 0 2px 10px rgba(0,0,0,0.1); |
||||
padding: 30px; |
||||
}} |
||||
.header {{ |
||||
text-align: center; |
||||
margin-bottom: 30px; |
||||
padding-bottom: 20px; |
||||
border-bottom: 2px solid #e8e8e8; |
||||
}} |
||||
.header h1 {{ |
||||
color: #2c3e50; |
||||
margin: 0; |
||||
font-size: 28px; |
||||
}} |
||||
.update-time {{ |
||||
color: #7f8c8d; |
||||
font-size: 14px; |
||||
margin-top: 5px; |
||||
}} |
||||
.source-section {{ |
||||
margin-bottom: 25px; |
||||
border: 1px solid #e8e8e8; |
||||
border-radius: 6px; |
||||
overflow: hidden; |
||||
}} |
||||
.source-header {{ |
||||
background: #3498db; |
||||
color: white; |
||||
padding: 12px 20px; |
||||
font-size: 18px; |
||||
font-weight: bold; |
||||
}} |
||||
.news-list {{ |
||||
padding: 0; |
||||
margin: 0; |
||||
list-style: none; |
||||
}} |
||||
.news-item {{ |
||||
padding: 12px 20px; |
||||
border-bottom: 1px solid #f0f0f0; |
||||
transition: background-color 0.2s; |
||||
}} |
||||
.news-item:hover {{ |
||||
background-color: #f8f9fa; |
||||
}} |
||||
.news-item:last-child {{ |
||||
border-bottom: none; |
||||
}} |
||||
.news-title {{ |
||||
font-size: 15px; |
||||
margin-bottom: 5px; |
||||
}} |
||||
.news-link {{ |
||||
color: #2980b9; |
||||
text-decoration: none; |
||||
}} |
||||
.news-link:hover {{ |
||||
text-decoration: underline; |
||||
}} |
||||
.news-time {{ |
||||
font-size: 12px; |
||||
color: #95a5a6; |
||||
}} |
||||
.no-data {{ |
||||
padding: 20px; |
||||
text-align: center; |
||||
color: #7f8c8d; |
||||
font-style: italic; |
||||
}} |
||||
.footer {{ |
||||
text-align: center; |
||||
margin-top: 30px; |
||||
padding-top: 20px; |
||||
border-top: 1px solid #e8e8e8; |
||||
color: #7f8c8d; |
||||
font-size: 12px; |
||||
}} |
||||
</style> |
||||
</head> |
||||
<body> |
||||
<div class="container"> |
||||
<div class="header"> |
||||
<h1>📰 每日热点新闻汇总</h1> |
||||
<div class="update-time">更新时间: {update_time}</div> |
||||
</div> |
||||
|
||||
{content} |
||||
|
||||
<div class="footer"> |
||||
数据来源于各大平台 • 自动生成 • 共 {total_news} 条热点 |
||||
</div> |
||||
</div> |
||||
</body> |
||||
</html>""" |
||||
|
||||
# 生成内容部分 |
||||
content_parts = [] |
||||
total_news = 0 |
||||
|
||||
# 按平台名称排序,让显示更有序 |
||||
sorted_sources = sorted(pending_to_send_data.keys()) |
||||
|
||||
for source in sorted_sources: |
||||
news_list = pending_to_send_data[source] |
||||
total_news += len(news_list) |
||||
|
||||
# 平台标题(美化显示名称) |
||||
display_name = { |
||||
'60s': '🐯 每日60秒', |
||||
'douyin': '🎵 抖音热榜', |
||||
'rednote': '📕 小红书', |
||||
'bili': '📺 B站热门', |
||||
'weibo': '🐧 微博热搜', |
||||
'baidu-hot': '🔍 百度热点', |
||||
'baidu-tieba': '💬 百度贴吧', |
||||
'toutiao': '📄 今日头条', |
||||
'zhihu': '📚 知乎热榜', |
||||
'hacker-news-top': '💻 Hacker News Top', |
||||
'hacker-news-new': '💻 Hacker News New', |
||||
'hacker-news-best': '💻 Hacker News Best' |
||||
}.get(source, f'📊 {source}') |
||||
|
||||
source_html = f""" |
||||
<div class="source-section"> |
||||
<div class="source-header">{display_name} ({len(news_list)}条)</div> |
||||
""" |
||||
|
||||
if news_list: |
||||
news_items = [] |
||||
for i, news in enumerate(news_list, 1): |
||||
title = news.get('title', '无标题') |
||||
link = news.get('link', '') |
||||
active_time = news.get('active_time', '') |
||||
|
||||
# 格式化时间 |
||||
if active_time: |
||||
try: |
||||
if isinstance(active_time, int): |
||||
# 如果是时间戳 |
||||
active_time = datetime.fromtimestamp(active_time).strftime('%Y-%m-%d %H:%M') |
||||
else: |
||||
# 如果是字符串时间 |
||||
active_time = str(active_time) |
||||
except: |
||||
active_time = str(active_time) |
||||
|
||||
title_html = f'<a href="{link}" class="news-link" target="_blank">{title}</a>' if link else title |
||||
time_html = f'<div class="news-time">{active_time}</div>' if active_time else '' |
||||
|
||||
news_item = f""" |
||||
<li class="news-item"> |
||||
<div class="news-title">{i}. {title_html}</div> |
||||
{time_html} |
||||
</li> |
||||
""" |
||||
news_items.append(news_item) |
||||
|
||||
source_html += f'<ul class="news-list">{"".join(news_items)}</ul>' |
||||
else: |
||||
source_html += '<div class="no-data">暂无数据</div>' |
||||
|
||||
source_html += '</div>' |
||||
content_parts.append(source_html) |
||||
|
||||
# 组装完整HTML |
||||
html_content = html_template.format( |
||||
update_time=datetime.now().strftime('%Y-%m-%d %H:%M:%S'), |
||||
content='\n'.join(content_parts), |
||||
total_news=total_news |
||||
) |
||||
|
||||
return html_content |
||||
|
||||
|
||||
def generate_text_content(pending_to_send_data): |
||||
"""生成纯文本版本的新闻内容""" |
||||
|
||||
text_parts = [] |
||||
text_parts.append("📰 每日热点新闻汇总") |
||||
text_parts.append(f"更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") |
||||
text_parts.append("=" * 50) |
||||
|
||||
total_news = 0 |
||||
|
||||
# 按平台名称排序 |
||||
sorted_sources = sorted(pending_to_send_data.keys()) |
||||
|
||||
for source in sorted_sources: |
||||
news_list = pending_to_send_data[source] |
||||
total_news += len(news_list) |
||||
|
||||
display_name = { |
||||
'60s': '每日60秒', |
||||
'douyin': '抖音热榜', |
||||
'rednote': '小红书', |
||||
'bili': 'B站热门', |
||||
'weibo': '微博热搜', |
||||
'baidu-hot': '百度热点', |
||||
'baidu-tieba': '百度贴吧', |
||||
'toutiao': '今日头条', |
||||
'zhihu': '知乎热榜', |
||||
'hacker-news-top': 'Hacker News Top', |
||||
'hacker-news-new': 'Hacker News New', |
||||
'hacker-news-best': 'Hacker News Best' |
||||
}.get(source, source) |
||||
|
||||
text_parts.append(f"\n【{display_name}】({len(news_list)}条)") |
||||
|
||||
for i, news in enumerate(news_list, 1): |
||||
title = news.get('title', '无标题') |
||||
link = news.get('link', '') |
||||
active_time = news.get('active_time', '') |
||||
|
||||
time_str = f" ({active_time})" if active_time else "" |
||||
link_str = f" 链接: {link}" if link else "" |
||||
|
||||
text_parts.append(f"{i}. {title}{time_str}\n{link_str}") |
||||
|
||||
text_parts.append(f"\n共 {total_news} 条热点") |
||||
text_parts.append("数据来源于各大平台 • 自动生成") |
||||
|
||||
return '\n'.join(text_parts) |
||||
|
||||
|
||||
# 在main函数中使用 |
||||
async def main(): |
||||
await get_api_data(api_name_map, concurrent_limit=6) |
||||
|
||||
# 生成HTML内容 |
||||
html_content = generate_html_content(pending_to_send_data) |
||||
|
||||
# 如果你需要同时保留文本版本 |
||||
text_content = generate_text_content(pending_to_send_data) |
||||
|
||||
print("HTML内容已生成,长度:", len(html_content)) |
||||
print("文本内容已生成,长度:", len(text_content)) |
||||
|
||||
# 返回两种格式的内容,根据需要选择使用 |
||||
return { |
||||
'html': html_content, |
||||
'text': text_content, |
||||
'data': pending_to_send_data |
||||
} |
||||
|
||||
|
||||
# 执行 |
||||
if __name__ == "__main__": |
||||
result = asyncio.run(main()) |
||||
# print(result['html']) # HTML内容 |
||||
# print(result['text']) # 是纯文本内容 |
||||
# result['data'] 是原始数据 |
||||
title = '60S - ' + str(datetime.now().strftime('%Y-%m-%d %H:%M:%S')) |
||||
sub = '60S' |
||||
SendEmail(subject=sub, title=title, text=result['text']).send() |
||||
print("执行完成!") |
||||
Loading…
Reference in new issue