-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathauto_publish.py
More file actions
216 lines (179 loc) · 7.37 KB
/
auto_publish.py
File metadata and controls
216 lines (179 loc) · 7.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#!/usr/bin/env python3
"""
自动发布到微信公众号草稿箱
"""
import os
import sys
import argparse
from pathlib import Path
from bs4 import BeautifulSoup
try:
from wechat_api import WeChatMPAPI
except ImportError:
print("❌ 无法导入 wechat_api 模块")
print(" 请确保 wechat_api.py 文件存在")
sys.exit(1)
def find_latest_article():
"""查找最新生成的文章"""
output_dir = "./output"
html_files = list(Path(output_dir).glob("article_*.html"))
if not html_files:
return None
# 按修改时间排序,取最新的
return str(sorted(html_files, key=lambda x: x.stat().st_mtime, reverse=True)[0])
def extract_article_info(html_file: str) -> dict:
"""从 HTML 文件提取文章信息"""
with open(html_file, 'r', encoding='utf-8') as f:
html_content = f.read()
soup = BeautifulSoup(html_content, 'html.parser')
# 提取标题
title_tag = soup.find('h1')
title = title_tag.get_text() if title_tag else "无标题"
# 微信标题限制:简短版本 (保守策略 < 40 bytes)
if len(title.encode('utf-8')) > 40:
# 截断为合适长度
short_title = title[:12] + "..."
else:
short_title = title
# 提取作者
author_div = soup.find('div', class_='author')
author = author_div.get_text().replace('作者:', '').strip() if author_div else "ruby鑫燕"
# 提取正文内容区域
content_div = soup.find('div', class_='content')
if not content_div:
raise ValueError("未找到内容区域")
# 移除内容中的 h1(标题已单独提取)
h1_in_content = content_div.find('h1')
if h1_in_content:
h1_in_content.decompose()
# 获取完整的 HTML 内容
content_html = str(content_div)
# 提取摘要(用于微信公众号)
first_p = content_div.find('p')
digest = first_p.get_text()[:54] if first_p else title[:54]
# 查找封面图
cover_image = None
cover_div = soup.find('div', class_='cover')
if cover_div:
cover_img = cover_div.find('img')
if cover_img and cover_img.get('src'):
cover_image = cover_img.get('src')
# 查找正文图片
inline_images = []
for img in content_div.find_all('img'):
src = img.get('src')
if src and src not in inline_images:
inline_images.append(src)
return {
'title': short_title, # 使用简短版本的标题
'full_title': title, # 保留完整标题用于显示
'author': author,
'content': content_html,
'cover_image': cover_image,
'inline_images': inline_images,
'digest': digest
}
def main():
parser = argparse.ArgumentParser(description='自动发布文章到微信公众号草稿箱')
parser.add_argument('html_file', nargs='?', help='HTML 文件路径(留空则发布最新文章)')
parser.add_argument('--app-id', help='微信公众号 AppID')
parser.add_argument('--app-secret', help='微信公众号 AppSecret')
parser.add_argument('--cover', help='封面图路径(可选)')
parser.add_argument('--images', nargs='*', help='配图路径列表(可选)')
args = parser.parse_args()
print("\n" + "=" * 70)
print("📤 微信公众号自动发布工具")
print("=" * 70 + "\n")
# 确定要发布的文件
if args.html_file:
html_file = args.html_file
else:
html_file = find_latest_article()
if not html_file:
print("❌ 未找到 HTML 文件")
print(" 请先生成文章,或指定 HTML 文件路径")
sys.exit(1)
print(f"📄 使用最新文章: {html_file}\n")
# 检查文件是否存在
if not os.path.exists(html_file):
print(f"❌ 文件不存在: {html_file}")
sys.exit(1)
try:
# 提取文章信息
print("📋 提取文章信息...")
article_info = extract_article_info(html_file)
print(f" 标题: {article_info['full_title']}")
print(f" 微信标题: {article_info['title']}")
print(f" 作者: {article_info['author']}")
print(f" 封面图: {article_info['cover_image'] or '无'}")
print(f" 配图数: {len(article_info['inline_images'])} 张\n")
# 检查封面图
if not article_info['cover_image']:
if args.cover:
article_info['cover_image'] = args.cover
print(f" 使用指定封面图: {args.cover}")
else:
print("⚠️ 警告: 未找到封面图,将使用默认图片")
# 可以在这里设置默认封面图路径
# article_info['cover_image'] = "./images/default_cover.png"
# 使用指定的配图(如果提供)
if args.images:
article_info['inline_images'] = args.images
print(f" 使用指定配图: {len(args.images)} 张")
if not article_info['cover_image'] or not os.path.exists(article_info['cover_image']):
print("❌ 封面图不存在或未设置")
print(" 微信公众号草稿必须有封面图")
print("\n💡 解决方案:")
print(" 1. 确保生成文章时包含封面图")
print(" 2. 或手动指定封面图路径")
sys.exit(1)
# 初始化微信 API
print("🔌 连接微信公众号 API...\n")
wechat = WeChatMPAPI(
app_id=args.app_id,
app_secret=args.app_secret
)
# 发布到草稿箱
result = wechat.publish_draft(
title=article_info['title'], # 使用简短标题
content=article_info['content'],
cover_image_path=article_info['cover_image'],
inline_images=article_info['inline_images'],
author=article_info['author'],
digest=article_info.get('digest', '')
)
print("\n" + "=" * 70)
if result['success']:
print("✅ 发布成功!")
print("=" * 70)
print(f"\n📌 Media ID: {result['media_id']}")
print(f"📌 标题: {result['title']}")
print(f"\n💡 下一步:")
print(f" 1. 登录微信公众号后台: https://mp.weixin.qq.com/")
print(f" 2. 进入「素材管理」→「草稿箱」")
print(f" 3. 找到刚才发布的文章")
print(f" 4. 预览、编辑或直接群发")
else:
print("❌ 发布失败")
print("=" * 70)
print(f"\n错误信息: {result.get('error', '未知错误')}")
print(f"\n💡 故障排查:")
print(f" 1. 检查环境变量配置: config/WECHAT_SETUP.md")
print(f" 2. 确认公众号已认证")
print(f" 3. 检查图片是否存在")
print(f" 4. 运行测试: python3 wechat_api.py")
print("=" * 70 + "\n")
except ValueError as e:
print(f"❌ 文章解析失败: {e}")
sys.exit(1)
except Exception as e:
print(f"❌ 发布失败: {e}")
print("\n💡 请检查:")
print(" 1. 微信公众号 API 配置是否正确")
print(" 2. 公众号是否已认证")
print(" 3. 是否有草稿箱权限")
print(" 4. 图片文件是否存在")
print(f"\n详细配置请查看: config/WECHAT_SETUP.md\n")
sys.exit(1)
if __name__ == "__main__":
main()