354 lines
12 KiB
Python
354 lines
12 KiB
Python
|
|
"""
|
||
|
|
小红书发布示例
|
||
|
|
演示小红书平台的各种发布场景。
|
||
|
|
"""
|
||
|
|
import asyncio
|
||
|
|
import sys
|
||
|
|
from pathlib import Path
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
# 添加项目根目录到Python路径
|
||
|
|
project_root = Path(__file__).parent.parent
|
||
|
|
sys.path.insert(0, str(project_root))
|
||
|
|
|
||
|
|
from core.publisher import Publisher
|
||
|
|
from core.models import ImageNote, VideoContent, PlatformType, AccountInfo
|
||
|
|
from utils.logger import setup_logger, get_logger
|
||
|
|
|
||
|
|
# 设置日志
|
||
|
|
setup_logger(level="INFO")
|
||
|
|
logger = get_logger(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
async def xiaohongshu_image_note_example():
|
||
|
|
"""小红书图文笔记发布示例"""
|
||
|
|
print("=" * 60)
|
||
|
|
print("小红书图文笔记发布示例")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
async with Publisher(headless=False) as publisher:
|
||
|
|
# 设置账号
|
||
|
|
account_name = "your_xiaohongshu_account" # 替换为实际账号名
|
||
|
|
print(f"1. 设置小红书账号: {account_name}")
|
||
|
|
|
||
|
|
success = await publisher.setup_platform("xiaohongshu", account_name)
|
||
|
|
if not success:
|
||
|
|
print("❌ 账号设置失败,请检查网络和账号信息")
|
||
|
|
return
|
||
|
|
|
||
|
|
print("✅ 账号设置成功")
|
||
|
|
|
||
|
|
# 创建多种类型的图文笔记
|
||
|
|
notes = [
|
||
|
|
# 美食分享笔记
|
||
|
|
ImageNote(
|
||
|
|
title="今日美食分享 🍜",
|
||
|
|
description="今天尝试了一家超级好吃的面馆,汤底浓郁,面条劲道,配上特制的辣椒酱,简直是绝配!强烈推荐给大家。#美食分享 #探店",
|
||
|
|
images=[
|
||
|
|
"media/images/food1.jpg",
|
||
|
|
"media/images/food2.jpg",
|
||
|
|
"media/images/food3.jpg"
|
||
|
|
],
|
||
|
|
tags=["美食", "探店", "面食", "推荐"]
|
||
|
|
),
|
||
|
|
|
||
|
|
# 旅行风景笔记
|
||
|
|
ImageNote(
|
||
|
|
title="周末游山玩水 🏔️",
|
||
|
|
description="这个周末去了附近的山里徒步,空气特别清新,风景也很美。爬山虽然有点累,但是看到山顶的风景一切都值得了。#旅行 #周末 #户外",
|
||
|
|
images=[
|
||
|
|
"media/images/travel1.jpg",
|
||
|
|
"media/images/travel2.jpg",
|
||
|
|
"media/images/travel3.jpg",
|
||
|
|
"media/images/travel4.jpg",
|
||
|
|
"media/images/travel5.jpg"
|
||
|
|
],
|
||
|
|
tags=["旅行", "户外", "徒步", "风景"]
|
||
|
|
),
|
||
|
|
|
||
|
|
# 生活记录笔记
|
||
|
|
ImageNote(
|
||
|
|
title="生活小确幸 ☀️",
|
||
|
|
description="今天收到了朋友送的花,心情特别好。生活中的小确幸就是这样简单,一朵花,一杯茶,一本好书,就是最美好的时光。#生活 #小确幸 #感恩",
|
||
|
|
images=[
|
||
|
|
"media/images/life1.jpg"
|
||
|
|
],
|
||
|
|
tags=["生活", "日常", "心情", "感恩"]
|
||
|
|
)
|
||
|
|
]
|
||
|
|
|
||
|
|
# 发布笔记
|
||
|
|
for i, note in enumerate(notes, 1):
|
||
|
|
print(f"\n{i}. 发布笔记: {note.title}")
|
||
|
|
try:
|
||
|
|
result = await publisher.publish("xiaohongshu", note, account_name)
|
||
|
|
|
||
|
|
if result.success:
|
||
|
|
print(f"✅ 发布成功!")
|
||
|
|
print(f" 任务ID: {result.task_id}")
|
||
|
|
print(f" 耗时: {result.duration:.2f}秒")
|
||
|
|
else:
|
||
|
|
print(f"❌ 发布失败: {result.message}")
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"❌ 发布异常: {e}")
|
||
|
|
|
||
|
|
# 发布间隔
|
||
|
|
if i < len(notes):
|
||
|
|
print("⏳ 等待5秒后发布下一篇...")
|
||
|
|
await asyncio.sleep(5)
|
||
|
|
|
||
|
|
|
||
|
|
async def xiaohongshu_video_note_example():
|
||
|
|
"""小红书视频笔记发布示例"""
|
||
|
|
print("=" * 60)
|
||
|
|
print("小红书视频笔记发布示例")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
async with Publisher(headless=False) as publisher:
|
||
|
|
# 设置账号
|
||
|
|
account_name = "your_xiaohongshu_account" # 替换为实际账号名
|
||
|
|
print(f"1. 设置小红书账号: {account_name}")
|
||
|
|
|
||
|
|
success = await publisher.setup_platform("xiaohongshu", account_name)
|
||
|
|
if not success:
|
||
|
|
print("❌ 账号设置失败")
|
||
|
|
return
|
||
|
|
|
||
|
|
print("✅ 账号设置成功")
|
||
|
|
|
||
|
|
# 创建视频笔记
|
||
|
|
video_notes = [
|
||
|
|
# 生活vlog
|
||
|
|
VideoContent(
|
||
|
|
title="我的日常vlog 📸",
|
||
|
|
description="记录一下今天的生活,从早上起床到晚上睡觉的点点滴滴。希望大家喜欢这种分享形式。#vlog #日常 #生活记录",
|
||
|
|
video_path="media/videos/daily_vlog.mp4", # 替换为实际视频路径
|
||
|
|
tags=["vlog", "日常", "生活"]
|
||
|
|
),
|
||
|
|
|
||
|
|
# 美食制作
|
||
|
|
VideoContent(
|
||
|
|
title="美食制作教程 🍳",
|
||
|
|
description="今天教大家做一道超级简单又好吃的家常菜,新手也能轻松学会。详细的步骤都在视频里哦。#美食 #教程 #烹饪",
|
||
|
|
video_path="media/videos/cooking_tutorial.mp4", # 替换为实际视频路径
|
||
|
|
tags=["美食", "教程", "烹饪", "家常菜"]
|
||
|
|
)
|
||
|
|
]
|
||
|
|
|
||
|
|
# 发布视频笔记
|
||
|
|
for i, video_note in enumerate(video_notes, 1):
|
||
|
|
print(f"\n{i}. 发布视频笔记: {video_note.title}")
|
||
|
|
try:
|
||
|
|
result = await publisher.publish("xiaohongshu", video_note, account_name)
|
||
|
|
|
||
|
|
if result.success:
|
||
|
|
print(f"✅ 发布成功!")
|
||
|
|
print(f" 任务ID: {result.task_id}")
|
||
|
|
print(f" 耗时: {result.duration:.2f}秒")
|
||
|
|
else:
|
||
|
|
print(f"❌ 发布失败: {result.message}")
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"❌ 发布异常: {e}")
|
||
|
|
|
||
|
|
# 发布间隔
|
||
|
|
if i < len(video_notes):
|
||
|
|
print("⏳ 等待10秒后发布下一个视频...")
|
||
|
|
await asyncio.sleep(10)
|
||
|
|
|
||
|
|
|
||
|
|
async def xiaohongshu_batch_publish_example():
|
||
|
|
"""小红书批量发布示例"""
|
||
|
|
print("=" * 60)
|
||
|
|
print("小红书批量发布示例")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
# 批量准备内容
|
||
|
|
contents = [
|
||
|
|
ImageNote(
|
||
|
|
title=f"批量测试笔记 {i+1}",
|
||
|
|
description=f"这是第{i+1}篇批量发布的测试笔记,内容为{i+1}。",
|
||
|
|
images=[f"media/images/batch_test_{i+1}.jpg"], # 替换为实际图片路径
|
||
|
|
tags=[f"批量测试{i+1}", "自动化"]
|
||
|
|
)
|
||
|
|
for i in range(3)
|
||
|
|
]
|
||
|
|
|
||
|
|
async with Publisher(headless=False) as publisher:
|
||
|
|
# 设置账号
|
||
|
|
account_name = "your_xiaohongshu_account" # 替换为实际账号名
|
||
|
|
success = await publisher.setup_platform("xiaohongshu", account_name)
|
||
|
|
|
||
|
|
if not success:
|
||
|
|
print("❌ 账号设置失败")
|
||
|
|
return
|
||
|
|
|
||
|
|
print("✅ 账号设置成功")
|
||
|
|
print(f"开始批量发布 {len(contents)} 篇笔记...")
|
||
|
|
|
||
|
|
# 创建发布任务
|
||
|
|
from core.models import PublishTask
|
||
|
|
tasks = []
|
||
|
|
for i, content in enumerate(contents):
|
||
|
|
task = PublishTask(
|
||
|
|
id=f"xhs_batch_{i+1}",
|
||
|
|
platform=PlatformType.XIAOHONGSHU,
|
||
|
|
account=AccountInfo(
|
||
|
|
platform=PlatformType.XIAOHONGSHU,
|
||
|
|
username=account_name,
|
||
|
|
cookie_file=f"{account_name}.json"
|
||
|
|
),
|
||
|
|
content=content
|
||
|
|
)
|
||
|
|
tasks.append(task)
|
||
|
|
|
||
|
|
# 执行批量发布
|
||
|
|
try:
|
||
|
|
results = await publisher.batch_publish(tasks)
|
||
|
|
|
||
|
|
# 统计结果
|
||
|
|
success_count = sum(1 for r in results if r.success)
|
||
|
|
print(f"\n📊 批量发布完成:")
|
||
|
|
print(f" 成功: {success_count}/{len(results)}")
|
||
|
|
print(f" 失败: {len(results) - success_count}/{len(results)}")
|
||
|
|
|
||
|
|
# 详细结果
|
||
|
|
for result in results:
|
||
|
|
status = "✅" if result.success else "❌"
|
||
|
|
print(f" {status} {result.task_id}: {result.message}")
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"❌ 批量发布异常: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
async def xiaohongshu_content_validation_example():
|
||
|
|
"""小红书内容验证示例"""
|
||
|
|
print("=" * 60)
|
||
|
|
print("小红书内容验证示例")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
async with Publisher() as publisher:
|
||
|
|
# 测试各种内容验证场景
|
||
|
|
test_cases = [
|
||
|
|
# 有效内容
|
||
|
|
{
|
||
|
|
"name": "有效的图文笔记",
|
||
|
|
"content": ImageNote(
|
||
|
|
title="有效标题",
|
||
|
|
description="有效描述",
|
||
|
|
images=["test.jpg"]
|
||
|
|
),
|
||
|
|
"expected": True
|
||
|
|
},
|
||
|
|
|
||
|
|
# 无效内容 - 空标题
|
||
|
|
{
|
||
|
|
"name": "空标题",
|
||
|
|
"content": ImageNote(
|
||
|
|
title="",
|
||
|
|
description="有效描述",
|
||
|
|
images=["test.jpg"]
|
||
|
|
),
|
||
|
|
"expected": False
|
||
|
|
},
|
||
|
|
|
||
|
|
# 无效内容 - 空描述
|
||
|
|
{
|
||
|
|
"name": "空描述",
|
||
|
|
"content": ImageNote(
|
||
|
|
title="有效标题",
|
||
|
|
description="",
|
||
|
|
images=["test.jpg"]
|
||
|
|
),
|
||
|
|
"expected": False
|
||
|
|
},
|
||
|
|
|
||
|
|
# 无效内容 - 无图片
|
||
|
|
{
|
||
|
|
"name": "无图片",
|
||
|
|
"content": ImageNote(
|
||
|
|
title="有效标题",
|
||
|
|
description="有效描述",
|
||
|
|
images=[]
|
||
|
|
),
|
||
|
|
"expected": False
|
||
|
|
},
|
||
|
|
|
||
|
|
# 无效内容 - 图片过多
|
||
|
|
{
|
||
|
|
"name": "图片过多",
|
||
|
|
"content": ImageNote(
|
||
|
|
title="有效标题",
|
||
|
|
description="有效描述",
|
||
|
|
images=[f"test{i}.jpg" for i in range(15)] # 15张图片
|
||
|
|
),
|
||
|
|
"expected": False
|
||
|
|
}
|
||
|
|
]
|
||
|
|
|
||
|
|
print("开始内容验证测试...")
|
||
|
|
for i, test_case in enumerate(test_cases, 1):
|
||
|
|
print(f"\n{i}. 测试: {test_case['name']}")
|
||
|
|
|
||
|
|
try:
|
||
|
|
is_valid, error_msg = await publisher.validate_content(
|
||
|
|
"xiaohongshu",
|
||
|
|
test_case['content']
|
||
|
|
)
|
||
|
|
|
||
|
|
if is_valid == test_case['expected']:
|
||
|
|
print(f" ✅ 验证结果符合预期")
|
||
|
|
else:
|
||
|
|
print(f" ❌ 验证结果不符合预期")
|
||
|
|
print(f" 期望: {test_case['expected']}")
|
||
|
|
print(f" 实际: {is_valid}")
|
||
|
|
|
||
|
|
if not is_valid:
|
||
|
|
print(f" 错误信息: {error_msg}")
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f" ❌ 验证异常: {e}")
|
||
|
|
|
||
|
|
|
||
|
|
async def main():
|
||
|
|
"""主函数"""
|
||
|
|
print("🚀 小红书发布示例")
|
||
|
|
print("=" * 60)
|
||
|
|
print("请确保:")
|
||
|
|
print("1. 替换示例中的账号名称")
|
||
|
|
print("2. 准备好图片和视频文件")
|
||
|
|
print("3. 确保网络连接正常")
|
||
|
|
print("=" * 60)
|
||
|
|
|
||
|
|
try:
|
||
|
|
# 运行示例
|
||
|
|
print("选择要运行的示例:")
|
||
|
|
print("1. 图文笔记发布")
|
||
|
|
print("2. 视频笔记发布")
|
||
|
|
print("3. 批量发布")
|
||
|
|
print("4. 内容验证")
|
||
|
|
|
||
|
|
choice = input("\n请输入选择 (1-4): ").strip()
|
||
|
|
|
||
|
|
if choice == "1":
|
||
|
|
await xiaohongshu_image_note_example()
|
||
|
|
elif choice == "2":
|
||
|
|
await xiaohongshu_video_note_example()
|
||
|
|
elif choice == "3":
|
||
|
|
await xiaohongshu_batch_publish_example()
|
||
|
|
elif choice == "4":
|
||
|
|
await xiaohongshu_content_validation_example()
|
||
|
|
else:
|
||
|
|
print("❌ 无效选择")
|
||
|
|
|
||
|
|
except KeyboardInterrupt:
|
||
|
|
print("\n⏹️ 用户中断")
|
||
|
|
except Exception as e:
|
||
|
|
print(f"\n❌ 运行异常: {e}")
|
||
|
|
import traceback
|
||
|
|
traceback.print_exc()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
asyncio.run(main())
|