请开发一个基于 Flask 的抖音作品采集工具,满足以下全部需求:
## 1. 核心功能
- 用户输入抖音**作者主页链接**或**单个作品链接**,系统自动采集该链接下的所有作品(视频/图集)并下载保存到本地文件夹。
- 对每个作品调用 **DeepSeek API** 进行内容分析,生成 120 字以内的内容总结,并将总结结果展示在网页上。
- 提供简单的 Web 操作界面,支持任务提交、状态实时查看、结果展示。
## 2. 技术栈
- Python 3.8+
- Flask(Web 框架)
- requests(HTTP 请求)
- python-dotenv(环境变量管理)
- 不使用数据库,任务状态存储于内存(简单字典即可)
- 抖音解析:使用**第三方解析 API**(需要在代码中预留接口,用户可自行替换为实际可用的服务,例如聚合数据、快解析等,本提示词中默认使用一个虚构的 API `https://api.example.com/douyin/parse`,开发者需自行替换)
- AI 总结:调用 **DeepSeek Chat API**(`https://api.deepseek.com/v1/chat/completions`),模型使用 `deepseek-chat`,API Key 从环境变量 `DEEPSEEK_API_KEY` 读取。
## 3. 项目结构
douyin_collector/
├── app.py # Flask 主程序(路由、任务管理)
├── config.py # 配置文件(下载目录、API Key 默认设置等)
├── requirements.txt # 依赖列表
├── utils/
│ ├── init.py
│ ├── douyin_parser.py # 抖音解析模块
│ ├── downloader.py # 下载器模块
│ └── ai_summarizer.py # DeepSeek 总结模块
├── templates/
│ ├── index.html # 首页:输入链接的表单
│ └── result.html # 结果页:展示任务状态和结果(含 AJAX 轮询)
├── static/
│ └── style.css # 可选,简单样式(可省略)
└── downloads/ # 下载文件保存目录(运行时自动创建)
## 4. 模块详细要求
### 4.1 抖音解析模块 (`utils/douyin_parser.py`)
- 函数 `parse_douyin_url(url: str) -> List[Dict]`,接收用户输入的链接,返回**作品信息列表**(即使只有一个作品也返回列表)。
- 该函数内部调用第三方解析 API(POST 或 GET 均可,示例使用 GET 请求,参数 `url`)。
- 解析 API 返回 JSON 格式,假设包含字段:
- `is_author`: 布尔值,表示是否为作者主页(若为作者主页,则 `works` 字段是一个包含多个作品信息的列表)
- 若 `is_author` 为 `True`,则从 `data["works"]` 中提取作品列表;
- 若为 `False`,则直接返回一个作品对象(包装成列表)。
- 每个作品对象包含以下字段(根据实际 API 可能有所不同,但需在代码中做兼容处理):
```python
{
"type": "video" 或 "image",
"author": "作者昵称",
"title": "作品标题或描述",
"video_url": "无水印视频直链(type=video时必填)",
"image_urls": ["图片链接1", "图片链接2", ...] (type=image时必填),
"cover": "封面图链接(可选)",
"duration": 15, # 视频时长(秒,可选)
"music": "背景音乐名称(可选)",
"likes": 100, # 点赞数(可选)
"comments": 20, # 评论数(可选)
"shares": 5, # 分享数(可选)
"create_time": "2023-01-01 12:00:00" # 发布时间(可选)
}
```
- 注意:第三方 API 可能没有所有这些字段,需要给出默认值(例如 `duration` 默认 0,`music` 默认 "无",等等)。
- 如果解析失败(网络错误、API 返回错误等),抛出异常,由上层捕获。
### 4.2 下载器模块 (`utils/downloader.py`)
- 函数 `download_work(work: Dict, save_dir: str) -> str`,接收单个作品信息和保存目录,返回下载后的本地文件路径(视频文件路径或图集文件夹路径)。
- 根据 `work["type"]` 分两种情况:
- **视频**:从 `work["video_url"]` 下载视频文件,文件名格式建议为 `{作者}_{标题前20字符}.mp4`(需处理非法字符),使用流式下载(requests.get(stream=True))。
- **图集**:创建子文件夹,文件夹名同样为 `{作者}_{标题前20字符}`,依次下载所有图片,保存为 `image_1.jpg`, `image_2.jpg`, ...。
- 确保 `save_dir` 存在,若不存在则创建。
- 如果下载失败,抛出异常。
### 4.3 AI 总结模块 (`utils/ai_summarizer.py`)
- 函数 `summarize_work(work: Dict) -> str`,接收作品信息,返回内容总结字符串(不超过 120 字)。
- 使用 `requests.post` 调用 DeepSeek API:
- URL: `https://api.deepseek.com/v1/chat/completions`
- 请求头:`Content-Type: application/json`,`Authorization: Bearer {DEEPSEEK_API_KEY}`
- 请求体 JSON:
```json
{
"model": "deepseek-chat",
"messages": [
{"role": "system", "content": "你是一个严谨的内容总结助手。"},
{"role": "user", "content": "这里是完整的提示词模板(见下)"}
],
"temperature": 0.7,
"max_tokens": 300,
"stream": false
}
```
- API Key 从环境变量 `DEEPSEEK_API_KEY` 读取,若未设置则返回错误信息。
- **完整提示词模板**(用户消息内容,使用 f-string 填充作品信息):
```
你是一个专业的内容分析师,擅长从抖音作品的元数据中提炼核心信息。请根据以下作品信息,生成一段简洁、准确、有吸引力的内容总结(不超过120字),帮助用户快速了解作品主题和亮点。
【作品信息】
作者:{author}
标题/描述:{title}
类型:{'视频' if type == 'video' else '图集'}
时长:{duration}秒
背景音乐:{music}
点赞数:{likes}
评论数:{comments}
分享数:{shares}
发布时间:{create_time}
【总结要求】
1. 概括作品的主要内容或主题;
2. 突出作品的特色或可能的爆点(如情感、搞笑、知识、颜值等);
3. 语言流畅,避免冗余,直接输出总结文本,不要任何前缀或解释。
```
- 如果 API 调用失败(如网络问题、密钥错误等),捕获异常并返回错误信息字符串,格式如 `"DeepSeek API 调用失败:{错误详情}"`。
### 4.4 Flask 主程序 (`app.py`)
- 定义以下路由:
- `GET /`:渲染 `index.html`,显示一个表单,包含一个输入框(name="url")和提交按钮。
- `POST /submit`:接收表单中的 `url`,创建任务 ID(使用 `os.urandom(8).hex()`),将任务信息存入全局字典 `tasks`(结构见下),启动后台线程执行 `process_task(task_id, url)`,然后重定向到 `/task/<task_id>`。
- `GET /task/<task_id>`:根据任务 ID 从 `tasks` 字典获取任务,渲染 `result.html`,传入任务对象和任务 ID。
- `GET /api/task/<task_id>`:返回该任务的 JSON 数据(用于前端 AJAX 轮询),若任务不存在返回 404。
- **任务字典结构**:
```python
tasks[task_id] = {
'status': 'pending', # pending / parsing / downloading / completed / error
'message': '任务已提交',
'results': [] # 最终结果为列表,每个元素为字典,包含 work, local_path, summary
}
```
- `process_task(task_id, url)` 函数逻辑:
1. 更新状态为 `parsing`,调用 `parse_douyin_url(url)` 获取作品列表。
2. 如果列表为空或解析失败,设置状态为 `error`,消息为对应错误,返回。
3. 更新状态为 `downloading`,遍历作品列表,对每个作品:
- 调用 `download_work(work, config.DOWNLOAD_DIR)` 下载,得到本地路径。
- 调用 `summarize_work(work)` 生成 AI 总结。
- 将 `{'work': work, 'local_path': local_path, 'summary': summary}` 追加到 `tasks[task_id]['results']`。
- 更新消息为 `下载中 {idx+1}/{total}`。
4. 全部完成后,设置状态为 `completed`,消息为 `处理完成,共 {total} 个作品`。
5. 若过程中出现异常,设置状态为 `error`,消息包含错误详情。
### 4.5 配置文件 (`config.py`)
- 定义下载目录 `DOWNLOAD_DIR = os.path.join(os.path.dirname(__file__), 'downloads')`
- 尝试从 `.env` 文件加载环境变量(使用 `python-dotenv` 的 `load_dotenv()`)
- 不直接写死 API Key,全部从环境变量读取。
### 4.6 前端模板
- `index.html`:简单的 HTML 表单,包含一个输入框和提交按钮,表单 action 指向 `/submit`,method 为 POST。
- `result.html`:显示任务状态(status 和 message),并使用 JavaScript 每 2 秒通过 `fetch('/api/task/{{ task_id }}')` 轮询任务状态,当状态变为 `completed` 或 `error` 时停止轮询,并渲染结果列表(如果有 results)。结果列表中每一项显示:作品标题、作者、本地路径、AI 总结。页面底部提供返回首页的链接。
## 5. 依赖文件 `requirements.txt`
flask==2.3.2
requests==2.31.0
python-dotenv==1.0.0
## 6. 运行说明
- 用户需要在项目根目录创建 `.env` 文件,内容为 `DEEPSEEK_API_KEY=sk-xxxx`(实际密钥)。
- 用户需要将 `utils/douyin_parser.py` 中的第三方解析 API URL 替换为实际可用的服务,并确保返回格式符合上述约定(或自行调整解析逻辑)。
- 运行 `python app.py` 启动服务,默认端口 5000。
## 7. 注意事项
- 代码中要包含适当的注释,特别是关键步骤(如解析、下载、API 调用)。
- 请确保所有模块都能正确导入,路径处理正确(使用 `os.path`)。
- 需要处理文件名中的非法字符(如 `/ \ : * ? " < > |`),替换为下划线。
- 异常处理要完善,避免进程崩溃,所有错误都应反馈到任务状态中。
- 生成的代码应该是完整可运行的,不需要用户再手动补充核心逻辑。
请严格按照以上要求生成完整的项目代码,包括所有目录下的文件内容。