ai
  • index
  • cursor
  • vector
  • crawl
  • crawl-front
  • DrissionPage
  • logging
  • mysql
  • pprint
  • sqlalchemy
  • contextmanager
  • dotenv
  • Flask
  • python
  • job
  • pdfplumber
  • python-docx
  • redbook
  • douyin
  • ffmpeg
  • json
  • numpy
  • opencv-python
  • pypinyin
  • re
  • requests
  • subprocess
  • time
  • uuid
  • watermark
  • milvus
  • pymilvus
  • search
  • Blueprint
  • flash
  • Jinja2
  • secure_filename
  • url_for
  • Werkzeug
  • chroma
  • HNSW
  • pillow
  • pandas
  • beautifulsoup4
  • langchain-community
  • langchain-core
  • langchain
  • langchain_unstructured
  • libreoffice
  • lxml
  • openpyxl
  • pymupdf
  • python-pptx
  • RAGFlow
  • tabulate
  • sentence_transformers
  • jsonl
  • collections
  • jieba
  • rag_optimize
  • rag
  • rank_bm25
  • Hugging_Face
  • modelscope
  • all-MiniLM-L6-v2
  • ollama
  • rag_measure
  • ragas
  • ASGI
  • FastAPI
  • FastChat
  • Jupyter
  • PyTorch
  • serper
  • uvicorn
  • markdownify
  • NormalizedLevenshtein
  • raq-action
  • CrossEncoder
  • Bi-Encoder
  • neo4j
  • neo4j4python
  • matplotlib
  • Plotly
  • Streamlit
  • py2neo
  • abc
  • read_csv
  • neo4jinstall
  • APOC
  • neo4jproject
  • uv
  • GDS
  • heapq
  • 1.参考
    • 1.1 文档
    • 1.2 资源
  • 2.配置VSCode
    • 2.1. 创建虚拟环境
    • 2.2 格式化python代码插件
    • 2.3. 格式化python代码插件
  • 3.安装类库
  • 4.编写入口文件
    • 4.1 main.py
    • 4.2 common.py
    • 4.3 .env
  • 5.打开网页
    • 5.1. config.py
    • 5.2. douyin_utils.py
    • 5.3. common.py
    • 5.5. main.py
  • 6.判断是否登录
    • 6.1. douyin_utils.py
  • 7.下载视频
    • 7.1. common.py
    • 7.2. config.py
    • 7.3. douyin_utils.py
  • 8.视频转音频
    • 8.1. douyin_utils.py
    • 8.2. main.py
  • 9.上传音频到腾讯云
    • 9.1. cos_utils.py
    • 9.2. config.py
    • 9.3. main.py
  • 10.音频转文字
    • 10.1. asr_utils.py
    • 10.2. config.py
    • 10.3. main.py
  • 11.抖音文案转小红书文案
    • 11.1. xhs_utils.py
    • 11.2. config.py
    • 11.3. main.py
  • 12.根据文案生成图片
    • 12.1. image_utils.py
    • 12.2. config.py
    • 12.3. main.py
  • 13.去掉图片水印
    • 13.1. config.py
    • 13.2. image_utils.py
  • 14.发布小红书
    • 14.1. xhs_publisher.py
    • 14.2. main.py
  • 15.发布知乎
    • 15.1. zhihu_publisher.py
    • 15.2. main.py
  • 16.作业

1.参考 #

本节为你整理了项目相关的官方文档和API资源。新手遇到不懂的库、API或报错时,可以直接查阅这些链接,快速找到权威答案。

1.1 文档 #

  • python语法
  • DrissionPage
  • python-dotenv
  • pypinyin
  • requests
  • ffmpeg
  • subprocess
  • re
  • uuid
  • time
  • json
  • opencv-python
  • numpy
  • openai
  • watermark

1.2 资源 #

这里是你需要注册和获取密钥的平台入口。建议提前注册好账号,获取好API密钥,后续配置会用到。

  • 腾讯云控制台
  • 腾讯云API密钥管理
  • 腾讯云COS
  • 火山引擎
  • 大模型录音文件识别API
  • 火山引擎语音识别
  • 录音文件识别大模型Access Token

2.配置VSCode #

本节介绍如何用 VSCode 配置开发环境,包括虚拟环境、代码格式化和编码设置。新手照做可以避免很多环境和编码问题。

2.1. 创建虚拟环境 #

# 创建虚拟环境
python -m venv .venv
# 激活虚拟环境(Windows)
.venv\Scripts\activate.bat
# 激活虚拟环境(Linux/macOS)
source .venv/Scripts/activate

2.2 格式化python代码插件 #

black-formatter

按下Ctrl+Shift+P 选择 Preferences:OpenUserSettings(JSON)

添加

{
  "[python]": {
    "editor.defaultFormatter": "ms-python.black-formatter",
    "editor.formatOnSave": true
  }
}

2.3. 格式化python代码插件 #

VS Code 的 "Run Code"(由 Code Runner 扩展提供)可能未正确配置编码。

  1. 打开 VS Code 设置(Ctrl + ,),搜索Code-runner: Executor Map。
  2. 点击Edit in settings.json, 在 settings.json 中修改或添加:
    "code-runner.executorMap": {
     "python": "set PYTHONIOENCODING=utf8 && python -u $fullFileName"
    }
    如果想在Run Code时使用当前目录的虚拟环境可以配置
    "code-runner.executorMap": {
     "python": "set PYTHONIOENCODING=utf8 && $workspaceRoot/.venv/Scripts/python.exe -u $fullFileName",
    }
    (Windows 用户用 set,Mac/Linux 用 export)

3.安装类库 #

本节教你如何一键安装项目所需的所有依赖库。新手只需复制命令到终端执行即可。

pip install DrissionPage openai python-dotenv pypinyin cos_python_sdk_v5 opencv-python

4.编写入口文件 #

本节讲解如何编写和理解 main.py 入口文件及常用的日志模块。新手可以学会如何让项目跑起来。

4.1 main.py #

# 从common模块导入get_logger函数
from common import get_logger

# 创建当前模块的日志记录器实例
logger = get_logger(__name__)

# 定义主函数
def main():
    # 记录主流程开始的日志信息
    logger.info("主流程开始")

# 判断是否作为主程序运行
if __name__ == "__main__":
    # 调用主函数
    main()

4.2 common.py #

# 导入Python标准库中的日志模块
import logging
# 配置日志系统的基本设置:设置日志级别为INFO,定义日志格式包含时间戳、日志级别和消息内容
logging.basicConfig(level=logging.INFO, format='%(asctime)s [%(levelname)s] %(message)s')
# 定义获取日志记录器的函数,参数name用于指定日志记录器的名称
def get_logger(name):
  # 根据传入的名称创建或获取日志记录器实例
  logger = logging.getLogger(name)
  # 返回创建的日志记录器实例
  return logger

4.3 .env #

# 腾讯云 COS 配置
# 腾讯云访问密钥 ID,用于身份验证
TENCENT_SECRET_ID=AKIDYzEI9nDRgrxufDi5FB0ueLuJZo7ITyQm
# 腾讯云访问密钥 Key,用于身份验证
TENCENT_SECRET_KEY=HXmu11voYAdv483nS0OacVAg6Sj3GLFz
# 腾讯云 COS 存储桶所在地区
TENCENT_REGION=ap-beijing
# 腾讯云 COS 存储桶名称
TENCENT_BUCKET=xiaohongshu-1258145019

# 火山引擎语音识别配置
# 火山引擎应用 ID,用于语音识别服务
VOLC_APPID=8709644943
# 火山引擎访问令牌,用于 API 调用认证
VOLC_TOKEN=6X54d8pz6HcUijsC3BKKcuYlxxMqX4qy

# OpenAI/火山大模型 API 配置
# OpenAI API 密钥,用于调用大模型服务
OPENAI_API_KEY=d52e49a1-36ea-44bb-bc6e-65ce789a72f6
# OpenAI API 基础 URL 地址
OPENAI_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
# 视频存放目录
DOUYIN_SAVE_DIR=douyin_videos
# 图片保存目录路径
IMAGE_SAVE_DIR=images
# 默认生成图片数量
IMAGE_NUM_DEFAULT=3

5.打开网页 #

本节介绍如何用自动化浏览器打开网页,为后续自动化操作(如扫码登录、自动发文)做准备。

5.1. config.py #

config.py

# 导入操作系统相关的模块,用于处理文件和目录等操作
import os

# 从dotenv库中导入load_dotenv函数,用于加载.env文件中的环境变量
from dotenv import load_dotenv

# 加载.env文件中的所有环境变量到系统环境变量中
load_dotenv()
# 常量配置区
# 获取环境变量"DOUYIN_SAVE_DIR"的值,如果没有设置则默认使用"douyin_videos"
DOUYIN_SAVE_DIR = os.environ.get("DOUYIN_SAVE_DIR", "douyin_videos")

5.2. douyin_utils.py #

douyin_utils.py

# 导入os模块,用于文件和目录操作
import os

# 从common模块导入get_logger函数和browser对象
from common import get_logger, browser

# 从config模块导入抖音视频保存目录常量
from config import DOUYIN_SAVE_DIR

# 创建当前模块的日志记录器实例
logger = get_logger(__name__)


# 定义爬取抖音热门视频的函数,参数为搜索关键词
def crawl_douyin_videos(keyword):
    # 记录日志,表示开始爬取抖音热门视频
    logger.info("开始爬取抖音热门视频...")
    # 构造抖音搜索页面的URL,使用传入的关键词
    url = f"https://www.douyin.com/search/{keyword}?type=video"
    # 如果保存视频的目录不存在,则创建该目录
    if not os.path.exists(DOUYIN_SAVE_DIR):
        os.makedirs(DOUYIN_SAVE_DIR)
    # 使用browser对象打开新标签页,访问构造好的抖音搜索页面
    browser.new_tab(url)

5.3. common.py #

common.py

# 导入Python标准库中的日志模块
import logging

# 从DrissionPage库中导入Chromium和ChromiumOptions类
+from DrissionPage import Chromium, ChromiumOptions

# 配置日志系统的基本设置
# 设置日志级别为INFO,日志格式为"时间戳 [日志级别] 日志内容"
logging.basicConfig(
   level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s"
)


# 定义获取日志记录器的函数,参数name用于指定日志记录器的名称
def get_logger(name):
    # 根据传入的名称创建或获取日志记录器实例
    logger = logging.getLogger(name)
    # 返回创建的日志记录器实例
    return logger

# 创建Chromium浏览器的配置对象
+co = ChromiumOptions()
# 使用配置对象创建Chromium浏览器实例
+browser = Chromium(co)

5.5. main.py #

main.py

# 从common模块导入get_logger函数
from common import get_logger

# 从douyin_utils模块导入crawl_douyin_videos函数
+from douyin_utils import crawl_douyin_videos

# 创建当前模块的日志记录器实例
logger = get_logger(__name__)

+
# 定义主函数
def main():
    # 记录主流程开始的日志信息
    logger.info("主流程开始")
+   try:
        # 调用爬取抖音热门视频的函数,参数为"脆桃"
+       crawl_douyin_videos("脆桃")
+   except Exception as e:
        # 如果主流程执行出错,记录错误日志
+       logger.error(f"主流程执行出错: {e}")
+

# 判断当前脚本是否作为主程序运行
if __name__ == "__main__":
    # 调用主函数
    main()

6.判断是否登录 #

本节讲解如何自动检测抖音网页是否已登录,确保后续操作不会因未登录而失败。

6.1. douyin_utils.py #

douyin_utils.py

# 导入os模块,用于文件和目录操作
import os

# 从common模块导入get_logger函数和browser对象
from common import get_logger, browser

# 从config模块导入抖音视频保存目录常量
from config import DOUYIN_SAVE_DIR

# 创建当前模块的日志记录器实例
logger = get_logger(__name__)


+def is_logged_in_js(tab):
+   js = """if (document.querySelector('span.semi-avatar img')) {return true;}return false;"""
+   try:
+       return tab.run_js(js)
+   except Exception as e:
+       logger.error(f"检查登录状态时异常: {e}")
+       return False


# 定义爬取抖音热门视频的函数,参数为搜索关键词
def crawl_douyin_videos(keyword):
    # 记录日志,表示开始爬取抖音热门视频
    logger.info("开始爬取抖音热门视频...")
    # 构造抖音搜索页面的URL,使用传入的关键词
    url = f"https://www.douyin.com/search/{keyword}?type=video"
    # 如果保存视频的目录不存在,则创建该目录
    if not os.path.exists(DOUYIN_SAVE_DIR):
        os.makedirs(DOUYIN_SAVE_DIR)
    # 使用browser对象打开新标签页,访问构造好的抖音搜索页面
+   tab = browser.new_tab(url)

+   logger.info("请扫码登录抖音账号...")
+   while True:
+       try:
+           if is_logged_in_js(tab):
+               break
+           logger.info("等待登录中,请扫码登录抖音账号...")
+           tab.wait(2)
+       except Exception as e:
+           logger.error(f"等待登录时异常: {e}")
+           tab.wait(2)
+   logger.info("检测到已登录,继续执行...")

7.下载视频 #

本节介绍如何自动下载抖音视频,并用安全的文件名保存到本地,防止乱码和非法字符。

7.1. common.py #

common.py

# 导入Python标准库中的日志模块
import logging

# 导入正则表达式模块,用于处理字符串
+import re

# 从pypinyin库中导入lazy_pinyin函数,用于将汉字转为拼音
+from pypinyin import lazy_pinyin

# 导入requests库,用于网络请求
+import requests

# 从DrissionPage库中导入Chromium和ChromiumOptions类,用于浏览器自动化
from DrissionPage import Chromium, ChromiumOptions

# 配置日志系统的基本设置
# 设置日志级别为INFO,日志格式为"时间戳 [日志级别] 日志内容"
logging.basicConfig(
    level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s"
)


# 定义获取日志记录器的函数,参数name用于指定日志记录器的名称
def get_logger(name):
    # 根据传入的名称创建或获取日志记录器实例
    logger = logging.getLogger(name)
    # 返回创建的日志记录器实例
    return logger


# 创建Chromium浏览器的配置对象
co = ChromiumOptions()
# 使用配置对象创建Chromium浏览器实例
browser = Chromium(co)


# 定义安全文件名生成函数
+def safe_filename(idx, title, ext):
    # 汉字转拼音
+   pinyin_title = "".join(lazy_pinyin(title))
    # 替换所有 Windows 不支持的特殊字符为下划线
+   name = re.sub(r'[\\/:*?"<>|]', "_", f"{idx}_{pinyin_title}")
    # 只保留中英文、数字和下划线
+   name = re.sub(r"[^\w]", "_", name)
    # 限制文件名长度,最多50个字符
+   name = name[:50]
    # 返回最终的文件名,包含扩展名
+   return f"{name}{ext}"


# 创建一个requests的会话对象,用于复用连接
+session = requests.Session()

7.2. config.py #

config.py

# 导入操作系统相关的模块,用于处理文件和目录等操作
import os

# 从dotenv库中导入load_dotenv函数,用于加载.env文件中的环境变量
from dotenv import load_dotenv

# 加载.env文件中的所有环境变量到系统环境变量中
load_dotenv()

# ================== 常量配置区 ==================

# 获取环境变量"DOUYIN_SAVE_DIR"的值,如果没有设置则默认使用"douyin_videos"
DOUYIN_SAVE_DIR = os.environ.get("DOUYIN_SAVE_DIR", "douyin_videos")

# 设置用于请求抖音网页的User-Agent字符串,模拟浏览器访问
+DOUYIN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"

7.3. douyin_utils.py #

douyin_utils.py

# 导入os模块,用于文件和目录操作
import os

# 从common模块导入get_logger函数、browser对象、safe_filename函数和session对象
+from common import get_logger, browser, safe_filename, session

# 从config模块导入抖音视频保存目录常量和User-Agent字符串
+from config import DOUYIN_SAVE_DIR, DOUYIN_USER_AGENT

# 创建当前模块的日志记录器实例
logger = get_logger(__name__)


# 定义检查是否已登录的函数,参数为tab对象
def is_logged_in_js(tab):
    # JavaScript代码:判断页面上是否存在头像img元素
    js = """if (document.querySelector('span.semi-avatar img')) {return true;}return false;"""
    try:
        # 在tab页面中执行js代码,返回登录状态
        return tab.run_js(js)
    except Exception as e:
        # 如果执行js出错,记录错误日志
        logger.error(f"检查登录状态时异常: {e}")
        # 返回未登录
        return False


# 定义爬取抖音热门视频的函数,参数为搜索关键词
def crawl_douyin_videos(keyword):
    # 记录日志,表示开始爬取抖音热门视频
    logger.info("开始爬取抖音热门视频...")
    # 构造抖音搜索页面的URL,使用传入的关键词
    url = f"https://www.douyin.com/search/{keyword}?type=video"
    # 如果保存视频的目录不存在,则创建该目录
    if not os.path.exists(DOUYIN_SAVE_DIR):
        os.makedirs(DOUYIN_SAVE_DIR)
    # 使用browser对象打开新标签页,访问构造好的抖音搜索页面
    tab = browser.new_tab(url)

    # 提示用户扫码登录抖音账号
    logger.info("请扫码登录抖音账号...")
    # 循环等待用户扫码登录
    while True:
        try:
            # 检查是否已登录
            if is_logged_in_js(tab):
                # 已登录则跳出循环
                break
            # 未登录则提示继续等待
            logger.info("等待登录中,请扫码登录抖音账号...")
            # 等待2秒
            tab.wait(2)
        except Exception as e:
            # 捕获异常并记录日志
            logger.error(f"等待登录时异常: {e}")
            # 等待2秒后重试
            tab.wait(2)
    # 登录成功后记录日志
    logger.info("检测到已登录,继续执行...")
    # 获取所有视频卡片元素
+   video_cards = tab.eles(".search-result-card")
    # 记录找到的视频卡片数量
+   logger.info(f"共找到{len(video_cards)}个视频卡片")
    # 只处理前1个视频卡片
+   video_cards = video_cards[:1]
    # 记录实际处理的视频卡片数量
+   logger.info(f"先处理{len(video_cards)}个视频卡片")
    # 启动监听,捕获视频真实地址
+   tab.listen.start("/video/tos")
    # 初始化视频文件路径列表
+   video_files = []
    # 遍历每个视频卡片,idx为序号,card为卡片对象
+   for idx, card in enumerate(video_cards, 1):
+       try:
            # 鼠标悬停到视频卡片上
+           card.hover()
            # 获取视频标题
+           title = card.ele(".VDYK8Xd7").text
            # 生成安全的文件名
+           filename = safe_filename(idx, title, ".mp4")
            # 记录视频文件名
+           logger.info(f"视频文件名: {filename}")
            # 等待监听获取真实视频地址
+           real_video_url = tab.listen.wait().url
            # 记录视频真实地址
+           logger.info(f"视频地址: {real_video_url}")
            # 如果获取到真实视频地址
+           if real_video_url:
                # 发送GET请求下载视频数据
+               video_data = session.get(
+                   real_video_url,
+                   headers={
+                       "Referer": "https://www.douyin.com/",
+                       "User-Agent": DOUYIN_USER_AGENT,
+                   },
+                   timeout=10,
+               ).content
                # 拼接视频文件保存路径
+               file_path = os.path.join(DOUYIN_SAVE_DIR, filename)
                # 记录保存路径
+               logger.info(f"保存到: {file_path}")
                # 以二进制写入方式保存视频文件
+               with open(file_path, "wb") as f:
+                   f.write(video_data)
                # 记录保存成功
+               logger.info(f"已保存到: {file_path}")
                # 将文件路径添加到列表
+               video_files.append(file_path)
+           else:
                # 未获取到视频地址,记录警告
+               logger.warning("未找到视频地址,跳过。")
+       except Exception as e:
            # 捕获下载异常并记录日志
+           logger.error(f"下载失败: {e}")
            # 跳过当前循环,继续下一个
+           continue

8.视频转音频 #

本节讲解如何用 ffmpeg 自动提取视频中的音频,生成 mp3 文件,为后续语音识别做准备。

8.1. douyin_utils.py #

douyin_utils.py

# 导入os模块,用于文件和目录操作
import os

# 导入subprocess模块,用于调用外部命令行程序
+import subprocess

# 从common模块导入get_logger函数、browser对象、safe_filename函数和session对象
from common import get_logger, browser, safe_filename, session

# 从config模块导入抖音视频保存目录常量和User-Agent字符串
from config import DOUYIN_SAVE_DIR, DOUYIN_USER_AGENT

# 创建当前模块的日志记录器实例
logger = get_logger(__name__)


# 定义检查是否已登录的函数,参数为tab对象
def is_logged_in_js(tab):
    # JavaScript代码:判断页面上是否存在头像img元素
    js = """if (document.querySelector('span.semi-avatar img')) {return true;}return false;"""
    try:
        # 在tab页面中执行js代码,返回登录状态
        return tab.run_js(js)
    except Exception as e:
        # 如果执行js出错,记录错误日志
        logger.error(f"检查登录状态时异常: {e}")
        # 返回未登录
        return False


# 定义爬取抖音热门视频的函数,参数为搜索关键词
def crawl_douyin_videos(keyword):
    # 记录日志,表示开始爬取抖音热门视频
    logger.info("开始爬取抖音热门视频...")
    # 构造抖音搜索页面的URL,使用传入的关键词
    url = f"https://www.douyin.com/search/{keyword}?type=video"
    # 如果保存视频的目录不存在,则创建该目录
    if not os.path.exists(DOUYIN_SAVE_DIR):
        os.makedirs(DOUYIN_SAVE_DIR)
    # 使用browser对象打开新标签页,访问构造好的抖音搜索页面
    tab = browser.new_tab(url)

    # 提示用户扫码登录抖音账号
    logger.info("请扫码登录抖音账号...")
    # 循环等待用户扫码登录
    while True:
        try:
            # 检查是否已登录
            if is_logged_in_js(tab):
                # 已登录则跳出循环
                break
            # 未登录则提示继续等待
            logger.info("等待登录中,请扫码登录抖音账号...")
            # 等待2秒
            tab.wait(2)
        except Exception as e:
            # 捕获异常并记录日志
            logger.error(f"等待登录时异常: {e}")
            # 等待2秒后重试
            tab.wait(2)
    # 登录成功后记录日志
    logger.info("检测到已登录,继续执行...")
    # 获取所有视频卡片元素
    video_cards = tab.eles(".search-result-card")
    # 记录找到的视频卡片数量
    logger.info(f"共找到{len(video_cards)}个视频卡片")
    # 只处理前1个视频卡片
    video_cards = video_cards[:1]
    # 记录实际处理的视频卡片数量
    logger.info(f"先处理{len(video_cards)}个视频卡片")
    # 启动监听,捕获视频真实地址
    tab.listen.start("/video/tos")
    # 初始化视频文件路径列表
    video_files = []
    # 遍历每个视频卡片,idx为序号,card为卡片对象
    for idx, card in enumerate(video_cards, 1):
        try:
            # 鼠标悬停到视频卡片上
            card.hover()
            # 获取视频标题
            title = card.ele(".VDYK8Xd7").text
            # 生成安全的文件名
            filename = safe_filename(idx, title, ".mp4")
            # 记录视频文件名
            logger.info(f"视频文件名: {filename}")
            # 等待监听获取真实视频地址
            real_video_url = tab.listen.wait().url
            # 记录视频真实地址
            logger.info(f"视频地址: {real_video_url}")
            # 如果获取到真实视频地址
            if real_video_url:
                # 发送GET请求下载视频数据
                video_data = session.get(
                    real_video_url,
                    headers={
                        "Referer": "https://www.douyin.com/",
                        "User-Agent": DOUYIN_USER_AGENT,
                    },
                    timeout=10,
                ).content
                # 拼接视频文件保存路径
                file_path = os.path.join(DOUYIN_SAVE_DIR, filename)
                # 记录保存路径
                logger.info(f"保存到: {file_path}")
                # 以二进制写入方式保存视频文件
                with open(file_path, "wb") as f:
                    f.write(video_data)
                # 记录保存成功
                logger.info(f"已保存到: {file_path}")
                # 将文件路径添加到列表
                video_files.append(file_path)
            else:
                # 未获取到视频地址,记录警告
                logger.warning("未找到视频地址,跳过。")
        except Exception as e:
            # 捕获下载异常并记录日志
            logger.error(f"下载失败: {e}")
            # 跳过当前循环,继续下一个
            continue
    # 全部下载完成后记录日志
+   logger.info("全部下载完成!")
    # 记录本次下载的视频文件列表
+   logger.info(f"本次共下载视频文件: {video_files}")
    # 返回视频文件路径列表
+   return video_files


# 定义视频转音频的函数,参数为视频文件路径
+def extract_audio(video_path):
    # 记录准备转音频的日志
+   logger.info(f"准备将视频转音频: {video_path}")
    # 获取视频文件名(不含扩展名)
+   base = os.path.splitext(os.path.basename(video_path))[0]
    # 构造mp3音频文件的完整路径
+   mp3_path = os.path.join(os.path.dirname(video_path), f"{base}.mp3")
    # 构造ffmpeg命令行参数列表
+   cmd = [
+       "ffmpeg",
+       "-y",  # 覆盖输出文件
+       "-i",  # 输入文件
+       video_path,
+       "-vn",  # 不处理视频流
+       "-acodec",  # 指定音频编码器
+       "libmp3lame",
+       "-ar",  # 指定音频采样率
+       "44100",
+       "-ac",  # 指定音频声道数
+       "2",
+       "-ab",  # 指定音频比特率
+       "192k",
+       mp3_path,  # 输出文件路径
+   ]
+   try:
        # 记录正在提取音频的日志
+       logger.info(f"正在提取音频到: {mp3_path}")
        # 调用ffmpeg命令行进行音频提取
+       subprocess.run(cmd, check=True)
        # 记录音频提取完成的日志
+       logger.info(f"音频提取完成: {mp3_path}")
        # 返回mp3音频文件路径
+       return mp3_path
+   except Exception as e:
        # 记录音频提取失败的日志
+       logger.error(f"音频提取失败: {e}")
        # 返回None表示失败
+       return None

8.2. main.py #

main.py

# 从common模块导入get_logger函数
from common import get_logger

# 从douyin_utils模块导入crawl_douyin_videos函数
+from douyin_utils import crawl_douyin_videos, extract_audio

# 创建当前模块的日志记录器实例
logger = get_logger(__name__)


# 定义主函数
def main():
    # 记录主流程开始的日志信息
    logger.info("主流程开始")
    try:
        # 调用爬取抖音热门视频的函数,参数为"脆桃"
+       video_files = crawl_douyin_videos("脆桃")
+       for video_file in video_files:
+           logger.info(f"处理视频文件: {video_file}")
            # 2. 视频转音频
+           mp3_file = extract_audio(video_file)
+           if not mp3_file:
+               logger.warning(f"视频转音频失败,跳过: {video_file}")
+               continue
    except Exception as e:
        # 如果主流程执行出错,记录错误日志
        logger.error(f"主流程执行出错: {e}")


# 判断当前脚本是否作为主程序运行
if __name__ == "__main__":
    # 调用主函数
    main()

9.上传音频到腾讯云 #

本节介绍如何将音频文件上传到腾讯云COS,方便后续API在线访问和处理。

9.1. cos_utils.py #

cos_utils.py

# 导入操作系统相关的模块,用于处理文件和目录等操作
import os

# 从腾讯云COS SDK中导入配置类和客户端类
from qcloud_cos import CosConfig, CosS3Client

# 从common模块导入日志记录器函数
from common import get_logger

# 从config模块导入腾讯云相关的配置参数
from config import (
    TENCENT_SECRET_ID,  # 腾讯云访问密钥ID
    TENCENT_SECRET_KEY,  # 腾讯云访问密钥
    TENCENT_REGION,  # 腾讯云服务区域
    TENCENT_BUCKET,  # 腾讯云存储桶名称
    TENCENT_TOKEN,  # 腾讯云临时访问令牌
    TENCENT_SCHEME,  # 腾讯云服务协议
)

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义上传文件到腾讯云COS的函数,接收文件路径和可选的存储键名作为参数
def upload_to_cos(file_path, key=None):
    # 记录开始上传文件的日志信息
    logger.info(f"准备上传音频到腾讯云COS: {file_path}")
    # 如果没有指定存储键名,则使用文件名作为键名
    if key is None:
        key = os.path.basename(file_path)
    # 创建腾讯云COS配置对象,包含区域、密钥等认证信息
    config = CosConfig(
        Region=TENCENT_REGION,  # 设置服务区域
        SecretId=TENCENT_SECRET_ID,  # 设置访问密钥ID
        SecretKey=TENCENT_SECRET_KEY,  # 设置访问密钥
        Token=TENCENT_TOKEN,  # 设置临时访问令牌
        Scheme=TENCENT_SCHEME,  # 设置服务协议
    )
    # 使用配置对象创建COS客户端实例
    client = CosS3Client(config)
    # 以二进制读取模式打开文件
    with open(file_path, "rb") as fp:
        # 调用客户端方法上传文件到COS存储桶
        client.put_object(
            Bucket=TENCENT_BUCKET,  # 指定存储桶名称
            Body=fp,  # 文件内容
            Key=key,  # 存储键名
            StorageClass="STANDARD",  # 存储类型为标准存储
            EnableMD5=False,  # 禁用MD5校验
        )
    # 构建文件的访问URL地址
    url = f"https://{TENCENT_BUCKET}.cos.{TENCENT_REGION}.myqcloud.com/{key}"
    # 记录上传成功的日志信息,包含访问地址
    logger.info(f"文件已上传,访问地址: {url}")
    # 返回文件的访问URL地址
    return url

9.2. config.py #

config.py

# 导入操作系统相关的模块,用于处理文件和目录等操作
import os

# 从dotenv库中导入load_dotenv函数,用于加载.env文件中的环境变量
from dotenv import load_dotenv

# 加载.env文件中的所有环境变量到系统环境变量中
load_dotenv()

# ================== 常量配置区 ==================

# 获取环境变量"DOUYIN_SAVE_DIR"的值,如果没有设置则默认使用"douyin_videos"
DOUYIN_SAVE_DIR = os.environ.get("DOUYIN_SAVE_DIR", "douyin_videos")

# 设置用于请求抖音网页的User-Agent字符串,模拟浏览器访问
DOUYIN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"

# 腾讯云配置
+TENCENT_SECRET_ID = os.environ.get("TENCENT_SECRET_ID")
+TENCENT_SECRET_KEY = os.environ.get("TENCENT_SECRET_KEY")
+TENCENT_REGION = os.environ.get("TENCENT_REGION", "ap-beijing")
+TENCENT_BUCKET = os.environ.get("TENCENT_BUCKET", "xiaohongshu-1258145019")
+TENCENT_TOKEN = None
+TENCENT_SCHEME = "https"

9.3. main.py #

main.py

# 从common模块导入get_logger函数
from common import get_logger

# 从douyin_utils模块导入crawl_douyin_videos函数
from douyin_utils import crawl_douyin_videos, extract_audio

# 从cos_utils模块导入upload_to_cos函数
+from cos_utils import upload_to_cos

# 创建当前模块的日志记录器实例
logger = get_logger(__name__)


# 定义主函数
def main():
    # 记录主流程开始的日志信息
    logger.info("主流程开始")
    try:
        # 调用爬取抖音热门视频的函数,参数为"脆桃"
        video_files = crawl_douyin_videos("脆桃")
        for video_file in video_files:
            logger.info(f"处理视频文件: {video_file}")
            # 2. 视频转音频
            mp3_file = extract_audio(video_file)
            if not mp3_file:
                logger.warning(f"视频转音频失败,跳过: {video_file}")
                continue
            # 3. 上传音频到腾讯云
+           cos_url = upload_to_cos(mp3_file)
+           logger.info(f"上传音频到腾讯云成功: {cos_url}")
    except Exception as e:
        # 如果主流程执行出错,记录错误日志
        logger.error(f"主流程执行出错: {e}")


# 判断当前脚本是否作为主程序运行
if __name__ == "__main__":
    # 调用主函数
    main()

10.音频转文字 #

本节讲解如何调用火山引擎API,把音频自动转成文字,免去手动听写的麻烦。

10.1. asr_utils.py #

asr_utils.py

# 导入UUID模块,用于生成唯一标识符
import uuid

# 导入时间模块,用于实现延时功能
import time

# 导入JSON模块,用于处理JSON格式数据
import json

# 从common模块导入日志记录器和HTTP会话对象
from common import get_logger, session

# 从config模块导入火山引擎和音频相关的配置参数
from config import (
    VOLC_APPID,  # 火山引擎应用ID
    VOLC_TOKEN,  # 火山引擎访问令牌
    AUDIO_FORMAT,  # 音频文件格式
    AUDIO_CODEC,  # 音频编解码器
    AUDIO_SAMPLE_RATE,  # 音频采样率
    AUDIO_BITS,  # 音频位深度
    AUDIO_CHANNEL,  # 音频声道数
)

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义音频转文字的主函数,接收音频文件URL作为参数
def audio_to_text(file_url):
    # 记录开始处理音频转文字任务的日志信息
    logger.info(f"准备将音频转文字,音频URL: {file_url}")
    # 设置火山引擎语音识别服务的提交任务API地址
    submit_url = "https://openspeech.bytedance.com/api/v3/auc/bigmodel/submit"
    # 生成唯一的任务ID,用于标识本次识别任务
    task_id = str(uuid.uuid4())
    # 设置HTTP请求头,包含火山引擎API所需的认证和标识信息
    headers = {
        "X-Api-App-Key": VOLC_APPID,
        "X-Api-Access-Key": VOLC_TOKEN,
        "X-Api-Resource-Id": "volc.bigasr.auc",
        "X-Api-Request-Id": task_id,
        "X-Api-Sequence": "-1",
    }
    # 构建请求数据,包含用户信息、音频参数和识别请求配置
    request_data = {
        "user": {"uid": "fake_uid"},
        "audio": {
            "url": file_url,  # 音频文件URL地址
            "format": AUDIO_FORMAT,  # 音频文件格式
            "codec": AUDIO_CODEC,  # 音频编解码器
            "rate": AUDIO_SAMPLE_RATE,  # 音频采样率
            "bits": AUDIO_BITS,  # 音频位深度
            "channel": AUDIO_CHANNEL,  # 音频声道数
        },
        "request": {
            "model_name": "bigmodel",
            "show_utterances": True,
            "corpus": {"correct_table_name": "", "context": ""},
        },
    }
    # 记录提交任务的ID信息
    logger.info(f"Submit task id: {task_id}")
    # 向火山引擎API发送POST请求,提交音频转文字任务
    response = session.post(
        submit_url, data=json.dumps(request_data), headers=headers, timeout=10
    )
    # 检查响应头中的状态码,判断任务是否提交成功
    if (
        "X-Api-Status-Code" in response.headers
        and response.headers["X-Api-Status-Code"] == "20000000"
    ):
        # 从响应头中获取日志ID,用于后续查询任务结果
        x_tt_logid = response.headers.get("X-Tt-Logid", "")
        # 记录任务提交成功的日志信息
        logger.info("音频转文字任务提交成功,开始轮询结果...")
    else:
        # 记录任务提交失败的日志信息,包含响应头详情
        logger.error(
            f"Submit task failed and the response headers are: {response.headers}"
        )
        # 任务提交失败,返回None
        return None
    # 设置查询任务结果的API地址
    query_url = "https://openspeech.bytedance.com/api/v3/auc/bigmodel/query"
    # 复制请求头并添加日志ID
    headers_query = headers.copy()
    headers_query["X-Tt-Logid"] = x_tt_logid
    # 开始轮询任务结果,直到获得最终结果或失败
    while True:
        # 向查询API发送POST请求,获取任务处理状态
        query_response = session.post(
            query_url, json.dumps({}), headers=headers_query, timeout=10
        )
        # 从响应头中获取状态码
        code = query_response.headers.get("X-Api-Status-Code", "")
        # 如果状态码为20000000,表示任务处理完成
        if code == "20000000":
            # 从响应JSON中提取识别出的文字内容
            text = query_response.json()["result"]["text"]
            # 记录识别成功的日志信息
            logger.info(f"音频转文字结果:{text}")
            # 返回识别出的文字内容
            return text
        # 如果状态码不是20000001或20000002(处理中状态),表示任务失败
        elif code != "20000001" and code != "20000002":
            # 记录任务失败的日志信息
            logger.error("音频转文字失败!")
            # 返回None表示处理失败
            return None
        # 如果任务仍在处理中,等待1秒后继续轮询
        time.sleep(1)

10.2. config.py #

config.py

# 导入操作系统相关的模块,用于处理文件和目录等操作
import os

# 从dotenv库中导入load_dotenv函数,用于加载.env文件中的环境变量
from dotenv import load_dotenv

# 加载.env文件中的所有环境变量到系统环境变量中
load_dotenv()

# ================== 常量配置区 ==================

# 获取环境变量"DOUYIN_SAVE_DIR"的值,如果没有设置则默认使用"douyin_videos"
DOUYIN_SAVE_DIR = os.environ.get("DOUYIN_SAVE_DIR", "douyin_videos")

# 设置用于请求抖音网页的User-Agent字符串,模拟浏览器访问
DOUYIN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"

# 腾讯云配置
# 获取腾讯云访问密钥ID,用于身份验证
TENCENT_SECRET_ID = os.environ.get("TENCENT_SECRET_ID")
# 获取腾讯云访问密钥,用于身份验证
TENCENT_SECRET_KEY = os.environ.get("TENCENT_SECRET_KEY")
# 获取腾讯云服务区域,默认为北京
TENCENT_REGION = os.environ.get("TENCENT_REGION", "ap-beijing")
# 获取腾讯云存储桶名称,默认为小红书相关桶
TENCENT_BUCKET = os.environ.get("TENCENT_BUCKET", "xiaohongshu-1258145019")
# 腾讯云临时访问令牌,初始为空
TENCENT_TOKEN = None
# 腾讯云服务协议,使用HTTPS
TENCENT_SCHEME = "https"

# 火山引擎配置
# 获取火山引擎应用ID
+VOLC_APPID = os.environ.get("VOLC_APPID")
# 获取火山引擎访问令牌
+VOLC_TOKEN = os.environ.get("VOLC_TOKEN")

# 音频采样率,设置为16kHz
+AUDIO_SAMPLE_RATE = 16000
# 音频位深度,设置为16位
+AUDIO_BITS = 16
# 音频声道数,设置为单声道
+AUDIO_CHANNEL = 1
# 音频文件格式,设置为MP3
+AUDIO_FORMAT = "mp3"
# 音频编解码器,设置为原始格式
+AUDIO_CODEC = "raw"

10.3. main.py #

main.py

# 导入日志记录器,用于记录程序运行过程中的各种信息
from common import get_logger

# 导入抖音相关工具函数,包括爬取视频和提取音频功能
from douyin_utils import crawl_douyin_videos, extract_audio

# 导入腾讯云对象存储上传工具函数
from cos_utils import upload_to_cos

# 导入语音识别工具函数,用于将音频转换为文字
+from asr_utils import audio_to_text

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义程序的主入口函数
def main():
    # 在程序开始时记录一条信息日志,表示主流程已启动
    logger.info("主流程开始")
    try:
        # 使用关键词"脆桃"爬取抖音热门视频,返回视频文件列表
        video_files = crawl_douyin_videos("脆桃")
        # 遍历每个爬取到的视频文件进行处理
        for video_file in video_files:
            # 记录当前正在处理的视频文件名称
            logger.info(f"处理视频文件: {video_file}")
            # 将视频文件转换为MP3音频格式
            mp3_file = extract_audio(video_file)
            # 检查音频转换是否成功,如果失败则跳过当前文件
            if not mp3_file:
                # 记录警告日志,表示音频转换失败
                logger.warning(f"视频转音频失败,跳过: {video_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 将转换后的音频文件上传到腾讯云对象存储
            cos_url = upload_to_cos(mp3_file)
            # 记录上传成功的日志信息,包含返回的云存储URL
            logger.info(f"上传音频到腾讯云成功: {cos_url}")
            # 使用语音识别服务将音频转换为文字内容
+           douyin_text = audio_to_text(cos_url)
            # 记录语音识别成功的日志信息,包含识别出的文字内容
+           logger.info(f"音频转文字成功: {douyin_text}")
            # 检查语音识别是否成功,如果失败则跳过当前文件
+           if not douyin_text:
                # 记录警告日志,表示语音识别失败
+               logger.warning(f"音频转文字失败,跳过: {mp3_file}")
                # 跳过当前文件,继续处理下一个
+               continue
    except Exception as e:
        # 捕获并记录主流程执行过程中的任何异常错误
        logger.error(f"主流程执行出错: {e}")


# 检查当前脚本是否作为主程序直接运行(而不是被其他模块导入)
if __name__ == "__main__":
    # 如果作为主程序运行,则调用主函数开始执行
    main()

11.抖音文案转小红书文案 #

本节介绍如何用大模型自动改写文案,生成小红书风格的爆款内容、标题和标签。

11.1. xhs_utils.py #

xhs_utils.py

# 导入OpenAI客户端库,用于调用大语言模型API
from openai import OpenAI

# 导入日志记录器,用于记录程序运行过程中的各种信息
from common import get_logger

# 导入OpenAI相关的配置参数,包括API密钥和基础URL
from config import OPENAI_API_KEY, OPENAI_BASE_URL

# 导入时间模块,用于实现延时功能
import time

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义抖音文案转小红书文案的主函数,接收抖音文案文本作为参数
def douyin_to_xhs_text(douyin_text):
    # 记录开始处理抖音文案转小红书文案任务的日志信息
    logger.info(f"准备将抖音文案转为小红书文案,原文案:{douyin_text}")
    # 创建OpenAI客户端实例,使用配置中的基础URL和API密钥
    client = OpenAI(base_url=OPENAI_BASE_URL, api_key=OPENAI_API_KEY)

    # 记录开始调用大模型生成小红书正文的日志信息
    logger.info("调用大模型生成小红书正文...")
    # 向大模型发送请求,要求将抖音文案改写为小红书爆款正文
    resp_intro = client.chat.completions.create(
        # 指定使用的大模型名称
        model="doubao-seed-1-6-250615",
        # 构建请求消息,包含用户角色和内容
        messages=[
            {
                # 设置消息角色为用户
                "role": "user",
                # 设置消息内容,包含文本类型和具体的改写要求
                "content": [
                    {
                        # 指定内容类型为文本
                        "type": "text",
                        # 设置具体的改写提示词,要求将抖音文案改写为不超过1000字的小红书正文
                        "text": f"请将以下抖音文案改写为小红书爆款正文,正文描述不超过1000字:{douyin_text}",
                    },
                ],
            }
        ],
    )
    # 从大模型响应中提取生成的小红书正文内容
    xhs_intro = (
        # 如果响应对象有message属性,则获取其content内容
        resp_intro.choices[0].message.content
        if hasattr(resp_intro.choices[0], "message")
        # 否则直接使用响应对象本身
        else resp_intro.choices[0]
    )
    # 去除正文内容首尾的空白字符
    xhs_intro = xhs_intro.strip()
    # 记录生成的小红书正文内容的日志信息
    logger.info(f"小红书正文:{xhs_intro}")
    # 等待5秒,避免API调用过于频繁
    time.sleep(5)

    # 记录开始调用大模型生成小红书标题的日志信息
    logger.info("调用大模型生成小红书标题...")
    # 向大模型发送请求,要求根据小红书正文生成爆款标题
    resp_title = client.chat.completions.create(
        # 指定使用的大模型名称
        model="doubao-seed-1-6-250615",
        # 构建请求消息,包含用户角色和内容
        messages=[
            {
                # 设置消息角色为用户
                "role": "user",
                # 设置消息内容,包含文本类型和具体的标题生成要求
                "content": [
                    {
                        # 指定内容类型为文本
                        "type": "text",
                        # 设置具体的标题生成提示词,要求生成不超过20字的爆款标题
                        "text": f"请根据以下小红书正文生成一个不超过20字的爆款标题:{xhs_intro}",
                    },
                ],
            }
        ],
    )
    # 从大模型响应中提取生成的小红书标题内容
    xhs_title = (
        # 如果响应对象有message属性,则获取其content内容
        resp_title.choices[0].message.content
        if hasattr(resp_title.choices[0], "message")
        # 否则直接使用响应对象本身
        else resp_title.choices[0]
    )
    # 去除标题内容首尾的空白字符
    xhs_title = xhs_title.strip()
    # 记录生成的小红书标题内容的日志信息
    logger.info(f"小红书标题:{xhs_title}")
    # 等待5秒,避免API调用过于频繁
    time.sleep(5)
    # 记录开始调用大模型生成小红书标签的日志信息
    logger.info("调用大模型生成小红书标签...")
    # 向大模型发送请求,要求根据小红书正文生成相关标签
    resp_tags = client.chat.completions.create(
        # 指定使用的大模型名称
        model="doubao-seed-1-6-250615",
        # 构建请求消息,包含用户角色和内容
        messages=[
            {
                # 设置消息角色为用户
                "role": "user",
                # 设置消息内容,包含文本类型和具体的标签生成要求
                "content": [
                    {
                        # 指定内容类型为文本
                        "type": "text",
                        # 设置具体的标签生成提示词,要求生成10个相关标签,用逗号分隔,每个标签不超过10字
                        "text": f"请根据以下小红书正文生成10个相关标签,标签用逗号分隔,每个标签不超过10字:{xhs_intro}",
                    },
                ],
            }
        ],
    )
    # 从大模型响应中提取生成的原始标签内容
    tags_raw = (
        # 如果响应对象有message属性,则获取其content内容
        resp_tags.choices[0].message.content
        if hasattr(resp_tags.choices[0], "message")
        # 否则直接使用响应对象本身
        else resp_tags.choices[0]
    )
    # 去除原始标签内容首尾的空白字符
    tags_raw = tags_raw.strip()
    # 将原始标签字符串按逗号分割,去除每个标签的空白字符,过滤掉空标签,生成最终的标签列表
    xhs_tags = [tag.strip() for tag in tags_raw.split(",") if tag.strip()]
    # 记录生成的小红书标签列表的日志信息
    logger.info(f"小红书标签:{xhs_tags}")
    # 返回生成的小红书标题、正文和标签列表
    return xhs_title, xhs_intro, xhs_tags

11.2. config.py #

config.py

# 导入操作系统相关的模块,用于处理文件和目录等操作
import os

# 从dotenv库中导入load_dotenv函数,用于加载.env文件中的环境变量
from dotenv import load_dotenv

# 加载.env文件中的所有环境变量到系统环境变量中
load_dotenv()

# ================== 常量配置区 ==================

# 获取环境变量"DOUYIN_SAVE_DIR"的值,如果没有设置则默认使用"douyin_videos"
DOUYIN_SAVE_DIR = os.environ.get("DOUYIN_SAVE_DIR", "douyin_videos")

# 设置用于请求抖音网页的User-Agent字符串,模拟浏览器访问
DOUYIN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"

# 腾讯云配置
# 获取腾讯云访问密钥ID,用于身份验证
TENCENT_SECRET_ID = os.environ.get("TENCENT_SECRET_ID")
# 获取腾讯云访问密钥,用于身份验证
TENCENT_SECRET_KEY = os.environ.get("TENCENT_SECRET_KEY")
# 获取腾讯云服务区域,默认为北京
TENCENT_REGION = os.environ.get("TENCENT_REGION", "ap-beijing")
# 获取腾讯云存储桶名称,默认为小红书相关桶
TENCENT_BUCKET = os.environ.get("TENCENT_BUCKET", "xiaohongshu-1258145019")
# 腾讯云临时访问令牌,初始为空
TENCENT_TOKEN = None
# 腾讯云服务协议,使用HTTPS
TENCENT_SCHEME = "https"

# 火山引擎配置
# 获取火山引擎应用ID
VOLC_APPID = os.environ.get("VOLC_APPID")
# 获取火山引擎访问令牌
VOLC_TOKEN = os.environ.get("VOLC_TOKEN")

# 音频采样率,设置为16kHz
AUDIO_SAMPLE_RATE = 16000
# 音频位深度,设置为16位
AUDIO_BITS = 16
# 音频声道数,设置为单声道
AUDIO_CHANNEL = 1
# 音频文件格式,设置为MP3
AUDIO_FORMAT = "mp3"
# 音频编解码器,设置为原始格式
AUDIO_CODEC = "raw"

# 获取OpenAI API密钥,用于身份验证和访问OpenAI服务
+OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
# 获取OpenAI服务的基础URL地址,用于指定API请求的目标服务器
+OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL")

11.3. main.py #

main.py

# 导入日志记录器,用于记录程序运行过程中的各种信息
from common import get_logger

# 导入抖音相关工具函数,包括爬取视频和提取音频功能
from douyin_utils import crawl_douyin_videos, extract_audio

# 导入腾讯云对象存储上传工具函数
from cos_utils import upload_to_cos

# 导入语音识别工具函数,用于将音频转换为文字
from asr_utils import audio_to_text

# 导入抖音文案转小红书文案工具函数
+from xhs_utils import douyin_to_xhs_text

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义程序的主入口函数
def main():
    # 在程序开始时记录一条信息日志,表示主流程已启动
    logger.info("主流程开始")
    try:
        # 1.使用关键词"脆桃"爬取抖音热门视频,返回视频文件列表
        video_files = crawl_douyin_videos("脆桃")
        # 遍历每个爬取到的视频文件进行处理
        for video_file in video_files:
            # 记录当前正在处理的视频文件名称
            logger.info(f"处理视频文件: {video_file}")
            # 2.将视频文件转换为MP3音频格式
            mp3_file = extract_audio(video_file)
            # 检查音频转换是否成功,如果失败则跳过当前文件
            if not mp3_file:
                # 记录警告日志,表示音频转换失败
                logger.warning(f"视频转音频失败,跳过: {video_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 3.将转换后的音频文件上传到腾讯云对象存储
            cos_url = upload_to_cos(mp3_file)
            # 记录上传成功的日志信息,包含返回的云存储URL
            logger.info(f"上传音频到腾讯云成功: {cos_url}")
            # 4.使用语音识别服务将音频转换为文字内容
            douyin_text = audio_to_text(cos_url)
            # 记录语音识别成功的日志信息,包含识别出的文字内容
            logger.info(f"音频转文字成功: {douyin_text}")
            # 检查语音识别是否成功,如果失败则跳过当前文件
            if not douyin_text:
                # 记录警告日志,表示语音识别失败
                logger.warning(f"音频转文字失败,跳过: {mp3_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 5.抖音文案转小红书文案
+           xhs_title, xhs_intro, xhs_tags = douyin_to_xhs_text(douyin_text)
+           logger.info(
+               f"抖音文案转小红书文案成功: {xhs_title}, {xhs_intro}, {xhs_tags}"
+           )
    except Exception as e:
        # 捕获并记录主流程执行过程中的任何异常错误
        logger.error(f"主流程执行出错: {e}")


# 检查当前脚本是否作为主程序直接运行(而不是被其他模块导入)
if __name__ == "__main__":
    # 如果作为主程序运行,则调用主函数开始执行
    main()

12.根据文案生成图片 #

本节讲解如何用AI根据文案自动生成多张图片,并自动去除水印,适合内容创作和推广。

12.1. image_utils.py #

image_utils.py

# 导入OpenAI客户端库,用于调用AI图片生成服务
from openai import OpenAI

# 从common模块导入session会话对象和safe_filename安全文件名生成函数
from common import session, safe_filename

# 从config模块导入OpenAI相关配置参数
from config import (
    OPENAI_API_KEY,
    OPENAI_BASE_URL,
    IMAGE_SIZE,
    IMAGE_SAVE_DIR,
    IMAGE_NUM_DEFAULT,
)

# 导入操作系统相关功能模块
import os

# 从common模块导入日志记录器
from common import get_logger

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义根据文案生成图片的主函数,接收标题、正文和图片数量参数
def generate_images_by_text(title, intro, num_images=None):
    # 记录开始生成图片的日志信息,包含标题和正文内容
    logger.info(f"准备根据文案生成图片,标题:{title},正文:{intro}")
    # 创建OpenAI客户端实例,使用配置的API密钥和基础URL
    client = OpenAI(base_url=OPENAI_BASE_URL, api_key=OPENAI_API_KEY)
    # 初始化图片路径列表,用于存储生成的图片文件路径
    img_paths = []
    # 如果未指定图片数量,则使用默认配置的数量
    if num_images is None:
        num_images = IMAGE_NUM_DEFAULT
    # 检查图片保存目录是否存在,如果不存在则创建
    if not os.path.exists(IMAGE_SAVE_DIR):
        os.makedirs(IMAGE_SAVE_DIR)
    # 循环生成指定数量的图片
    for i in range(num_images):
        # 调用OpenAI图片生成API,使用豆包模型生成图片
        response = client.images.generate(
            model="doubao-seedream-3-0-t2i-250415",
            prompt=f"{title}\n{intro}",
            size=IMAGE_SIZE,
            response_format="url",
        )
        # 从API响应中提取图片URL
        url = response.data[0].url
        # 记录图片生成成功的日志信息
        logger.info(f"生成图片{i+1}:{url}")
        # 使用安全文件名生成函数创建文件名
        filename = safe_filename(i + 1, title, ".jpg")
        # 下载图片
        # 构建完整的图片保存路径
        img_path = os.path.join(IMAGE_SAVE_DIR, filename)
        # 以二进制写入模式打开文件,下载并保存图片内容
        with open(img_path, "wb") as f:
            f.write(session.get(url, timeout=10).content)
        # 将保存的图片路径添加到结果列表中
        img_paths.append(img_path)
    # 返回所有生成的图片文件路径列表
    return img_paths

12.2. config.py #

config.py

# 导入操作系统相关的模块,用于处理文件和目录等操作
import os

# 从dotenv库中导入load_dotenv函数,用于加载.env文件中的环境变量
from dotenv import load_dotenv

# 加载.env文件中的所有环境变量到系统环境变量中
load_dotenv()

# ================== 常量配置区 ==================

# 获取环境变量"DOUYIN_SAVE_DIR"的值,如果没有设置则默认使用"douyin_videos"
DOUYIN_SAVE_DIR = os.environ.get("DOUYIN_SAVE_DIR", "douyin_videos")

# 设置用于请求抖音网页的User-Agent字符串,模拟浏览器访问
DOUYIN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"

# 腾讯云配置
# 获取腾讯云访问密钥ID,用于身份验证
TENCENT_SECRET_ID = os.environ.get("TENCENT_SECRET_ID")
# 获取腾讯云访问密钥,用于身份验证
TENCENT_SECRET_KEY = os.environ.get("TENCENT_SECRET_KEY")
# 获取腾讯云服务区域,默认为北京
TENCENT_REGION = os.environ.get("TENCENT_REGION", "ap-beijing")
# 获取腾讯云存储桶名称,默认为小红书相关桶
TENCENT_BUCKET = os.environ.get("TENCENT_BUCKET", "xiaohongshu-1258145019")
# 腾讯云临时访问令牌,初始为空
TENCENT_TOKEN = None
# 腾讯云服务协议,使用HTTPS
TENCENT_SCHEME = "https"

# 火山引擎配置
# 获取火山引擎应用ID
VOLC_APPID = os.environ.get("VOLC_APPID")
# 获取火山引擎访问令牌
VOLC_TOKEN = os.environ.get("VOLC_TOKEN")

# 音频采样率,设置为16kHz
AUDIO_SAMPLE_RATE = 16000
# 音频位深度,设置为16位
AUDIO_BITS = 16
# 音频声道数,设置为单声道
AUDIO_CHANNEL = 1
# 音频文件格式,设置为MP3
AUDIO_FORMAT = "mp3"
# 音频编解码器,设置为原始格式
AUDIO_CODEC = "raw"

# 获取OpenAI API密钥,用于身份验证和访问OpenAI服务
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
# 获取OpenAI服务的基础URL地址,用于指定API请求的目标服务器
OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL")
# 设置图片尺寸,默认为1024x1024
+IMAGE_SIZE = "1024x1024"
# 设置图片保存目录,默认为"images"
+IMAGE_SAVE_DIR = os.environ.get("IMAGE_SAVE_DIR", "images")
# 设置默认的图片生成数量,默认为3张
+IMAGE_NUM_DEFAULT = int(os.environ.get("IMAGE_NUM_DEFAULT", "3"))

12.3. main.py #

main.py

# 导入日志记录器,用于记录程序运行过程中的各种信息
from common import get_logger

# 导入抖音相关工具函数,包括爬取视频和提取音频功能
from douyin_utils import crawl_douyin_videos, extract_audio

# 导入腾讯云对象存储上传工具函数
from cos_utils import upload_to_cos

# 导入语音识别工具函数,用于将音频转换为文字
from asr_utils import audio_to_text

# 导入抖音文案转小红书文案工具函数
from xhs_utils import douyin_to_xhs_text

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)

# 导入根据文案生成图片工具函数
+from image_utils import generate_images_by_text

# 导入图片生成配置参数
+from config import IMAGE_NUM_DEFAULT


# 定义程序的主入口函数
def main():
    # 在程序开始时记录一条信息日志,表示主流程已启动
    logger.info("主流程开始")
    try:
        # 1.使用关键词"脆桃"爬取抖音热门视频,返回视频文件列表
        video_files = crawl_douyin_videos("脆桃")
        # 遍历每个爬取到的视频文件进行处理
        for video_file in video_files:
            # 记录当前正在处理的视频文件名称
            logger.info(f"处理视频文件: {video_file}")
            # 2.将视频文件转换为MP3音频格式
            mp3_file = extract_audio(video_file)
            # 检查音频转换是否成功,如果失败则跳过当前文件
            if not mp3_file:
                # 记录警告日志,表示音频转换失败
                logger.warning(f"视频转音频失败,跳过: {video_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 3.将转换后的音频文件上传到腾讯云对象存储
            cos_url = upload_to_cos(mp3_file)
            # 记录上传成功的日志信息,包含返回的云存储URL
            logger.info(f"上传音频到腾讯云成功: {cos_url}")
            # 4.使用语音识别服务将音频转换为文字内容
            douyin_text = audio_to_text(cos_url)
            # 记录语音识别成功的日志信息,包含识别出的文字内容
            logger.info(f"音频转文字成功: {douyin_text}")
            # 检查语音识别是否成功,如果失败则跳过当前文件
            if not douyin_text:
                # 记录警告日志,表示语音识别失败
                logger.warning(f"音频转文字失败,跳过: {mp3_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 5.抖音文案转小红书文案
            xhs_title, xhs_intro, xhs_tags = douyin_to_xhs_text(douyin_text)
            logger.info(
                f"抖音文案转小红书文案成功: {xhs_title}, {xhs_intro}, {xhs_tags}"
            )
            # 6. 根据文案生成图片
+           img_paths = generate_images_by_text(
+               xhs_title, xhs_intro, num_images=IMAGE_NUM_DEFAULT
+           )
+           logger.info(f"根据文案生成图片成功: {img_paths}")
    except Exception as e:
        # 捕获并记录主流程执行过程中的任何异常错误
        logger.error(f"主流程执行出错: {e}")


# 检查当前脚本是否作为主程序直接运行(而不是被其他模块导入)
if __name__ == "__main__":
    # 如果作为主程序运行,则调用主函数开始执行
    main()

13.去掉图片水印 #

本节介绍如何用AI算法自动去除图片右下角的"AI生成"水印,让图片更美观。

13.1. config.py #

config.py

# 导入操作系统相关的模块,用于处理文件和目录等操作
import os

# 从dotenv库中导入load_dotenv函数,用于加载.env文件中的环境变量
from dotenv import load_dotenv

# 加载.env文件中的所有环境变量到系统环境变量中
load_dotenv()

# ================== 常量配置区 ==================

# 获取环境变量"DOUYIN_SAVE_DIR"的值,如果没有设置则默认使用"douyin_videos"
+DOUYIN_SAVE_DIR = os.environ.get("DOUYIN_SAVE_DIR", "douyin_videos")

# 设置用于请求抖音网页的User-Agent字符串,模拟浏览器访问
DOUYIN_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"

# 腾讯云配置
# 获取腾讯云访问密钥ID,用于身份验证
TENCENT_SECRET_ID = os.environ.get("TENCENT_SECRET_ID")
# 获取腾讯云访问密钥,用于身份验证
TENCENT_SECRET_KEY = os.environ.get("TENCENT_SECRET_KEY")
# 获取腾讯云服务区域,默认为北京
TENCENT_REGION = os.environ.get("TENCENT_REGION", "ap-beijing")
# 获取腾讯云存储桶名称,默认为小红书相关桶
TENCENT_BUCKET = os.environ.get("TENCENT_BUCKET", "xiaohongshu-1258145019")
# 腾讯云临时访问令牌,初始为空
TENCENT_TOKEN = None
# 腾讯云服务协议,使用HTTPS
TENCENT_SCHEME = "https"

# 火山引擎配置
# 获取火山引擎应用ID
VOLC_APPID = os.environ.get("VOLC_APPID")
# 获取火山引擎访问令牌
VOLC_TOKEN = os.environ.get("VOLC_TOKEN")

# 音频采样率,设置为16kHz
AUDIO_SAMPLE_RATE = 16000
# 音频位深度,设置为16位
AUDIO_BITS = 16
# 音频声道数,设置为单声道
AUDIO_CHANNEL = 1
# 音频文件格式,设置为MP3
AUDIO_FORMAT = "mp3"
# 音频编解码器,设置为原始格式
AUDIO_CODEC = "raw"

# 获取OpenAI API密钥,用于身份验证和访问OpenAI服务
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
# 获取OpenAI服务的基础URL地址,用于指定API请求的目标服务器
OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL")
# 设置图片尺寸,默认为1024x1024
IMAGE_SIZE = "1024x1024"
# 设置图片保存目录,默认为"images"
IMAGE_SAVE_DIR = os.environ.get("IMAGE_SAVE_DIR", "images")
# 设置默认的图片生成数量,默认为3张
IMAGE_NUM_DEFAULT = int(os.environ.get("IMAGE_NUM_DEFAULT", "3"))

13.2. image_utils.py #

image_utils.py

# 导入OpenAI客户端库,用于调用AI图片生成服务
from openai import OpenAI

# 从common模块导入session会话对象和safe_filename安全文件名生成函数
from common import session, safe_filename

# 从config模块导入OpenAI相关配置参数
from config import (
    OPENAI_API_KEY,
    OPENAI_BASE_URL,
    IMAGE_SIZE,
    IMAGE_SAVE_DIR,
    IMAGE_NUM_DEFAULT,
)

# 导入操作系统相关功能模块
import os
+import cv2
+import numpy as np

# 从common模块导入日志记录器
from common import get_logger

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义去除图片水印的函数,接收输入路径、输出路径和水印比例参数
+def remove_watermark(input_path, output_path=None, height_ratio=0.10, width_ratio=0.22):
+   """
+   去除图片右下角的水印

+   Args:
+       input_path (str): 输入图片路径
+       output_path (str): 输出图片路径,如果为None则自动生成
+       height_ratio (float): 水印高度占图片高度的比例,默认0.10
+       width_ratio (float): 水印宽度占图片宽度的比例,默认0.22

+   Returns:
+       str: 输出图片路径
+   """

    # 使用OpenCV读取输入图片文件
+   img = cv2.imread(input_path)
    # 检查图片是否成功读取,如果失败则抛出异常
+   if img is None:
+       raise ValueError(f"无法读取图片:{input_path}")

    # 获取图片的高度和宽度
+   h, w = img.shape[:2]

    # 根据比例计算水印区域的高度
+   watermark_height = int(h * height_ratio)
    # 根据比例计算水印区域的宽度
+   watermark_width = int(w * width_ratio)

    # 创建一个与图片大小相同的全零掩码数组
+   mask = np.zeros((h, w), np.uint8)
    # 在掩码的右下角区域设置255值,标记水印区域
+   mask[h - watermark_height : h, w - watermark_width : w] = 255

    # 使用OpenCV的inpaint算法去除水印,使用TELEA算法
+   result = cv2.inpaint(img, mask, 3, cv2.INPAINT_TELEA)

    # 如果没有指定输出路径,则自动生成输出文件名
+   if output_path is None:
        # 获取输入文件的基础名称(不含扩展名)
+       base_name = os.path.splitext(input_path)[0]
        # 构造去水印后的文件名
+       output_path = f"{base_name}_no_watermark.jpg"

    # 将处理后的图片保存到指定路径
+   cv2.imwrite(output_path, result)

    # 打印去水印完成的提示信息
+   print(f"去水印完成,已保存为 {output_path}")
    # 返回输出图片的路径
+   return output_path


# 定义根据文案生成图片的主函数,接收标题、正文和图片数量参数
def generate_images_by_text(title, intro, num_images=None):
    # 记录开始生成图片的日志信息,包含标题和正文内容
    logger.info(f"准备根据文案生成图片,标题:{title},正文:{intro}")
    # 创建OpenAI客户端实例,使用配置的API密钥和基础URL
    client = OpenAI(base_url=OPENAI_BASE_URL, api_key=OPENAI_API_KEY)
    # 初始化图片路径列表,用于存储生成的图片文件路径
    img_paths = []
    # 如果未指定图片数量,则使用默认配置的数量
    if num_images is None:
        num_images = IMAGE_NUM_DEFAULT
    # 检查图片保存目录是否存在,如果不存在则创建
    if not os.path.exists(IMAGE_SAVE_DIR):
        os.makedirs(IMAGE_SAVE_DIR)
    # 循环生成指定数量的图片
    for i in range(num_images):
        # 调用OpenAI图片生成API,使用豆包模型生成图片
        response = client.images.generate(
            model="doubao-seedream-3-0-t2i-250415",
            prompt=f"{title}\n{intro}",
            size=IMAGE_SIZE,
            response_format="url",
        )
        # 从API响应中提取图片URL
        url = response.data[0].url
        # 记录图片生成成功的日志信息
        logger.info(f"生成图片{i+1}:{url}")
        # 使用安全文件名生成函数创建文件名
        filename = safe_filename(i + 1, title, ".jpg")
        # 构建完整的图片保存路径
        img_path = os.path.join(IMAGE_SAVE_DIR, filename)
        # 以二进制写入模式打开文件,下载并保存图片内容
        with open(img_path, "wb") as f:
            f.write(session.get(url, timeout=10).content)
        # 调用去水印函数处理图片
+       img_path_no_watermark = remove_watermark(img_path)
        # 将去水印后的图片路径添加到结果列表中
+       img_paths.append(img_path_no_watermark)
    # 返回所有生成的图片文件路径列表
    return img_paths

14.发布小红书 #

本节讲解如何自动化发布内容到小红书,实现一键发文,省时省力。

14.1. xhs_publisher.py #

xhs_publisher.py

# 导入浏览器实例和日志记录器
from common import browser, get_logger

# 导入键盘按键常量,用于模拟键盘操作
from DrissionPage.common import Keys

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义发布到小红书的主函数,接收标题、正文、标签和图片路径参数
def post_to_xiaohongshu(title, intro, tags, img_paths):
    # 记录开始发布小红书的日志信息,包含标题和图片路径
    logger.info(f"准备自动发到小红书,标题:{title},图片:{img_paths}")
    # 打开小红书创作者平台的发布页面
    tab = browser.new_tab("https://creator.xiaohongshu.com/publish/publish")
    # 等待页面加载完成
    tab.wait(2)
    # 查找并点击"发布笔记"按钮
    publish_btn = tab("tag:span@text()=发布笔记", timeout=60)
    publish_btn.click()
    # 等待页面响应
    tab.wait(2)
    # 查找并点击"上传图文"按钮
    publish_btn = tab("tag:span@text()=上传图文", timeout=60)
    publish_btn.click()
    # 等待页面响应
    tab.wait(2)
    # 设置要上传的图片文件路径,将多个路径用换行符连接
    tab.set.upload_files("\n".join(img_paths))
    # 查找并点击"上传图片"按钮
    upload_img_btn = tab("tag:span@text()=上传图片", timeout=60)
    upload_img_btn.click()
    # 等待图片上传完成
    tab.wait.upload_paths_inputted()
    # 等待页面响应
    tab.wait(2)
    # 检查标题长度是否超过20个字符,如果超过则截取前20个字符
    if len(title) > 20:
        title = title[:20]
    # 查找标题输入框并输入标题
    title_input = tab("tag:input@@type=text@@class=d-text", timeout=60)
    title_input.input(title)
    # 等待页面响应
    tab.wait(2)
    # 检查正文长度是否超过250个字符,如果超过则截取前250个字符
    if len(intro) > 250:
        intro = intro[:250]
    # 查找富文本编辑器并输入正文内容
    editor = tab.ele(".:ql-editor", timeout=60)
    editor.input(intro)
    # 等待页面响应
    tab.wait(2)
    # 计算要添加的标签数量,最多10个标签
    tag_count = min(len(tags), 10)
    # 将光标移动到编辑器末尾
    editor.input(Keys.END)
    # 按回车键换行
    editor.input(Keys.ENTER, clear=False)
    # 循环添加标签
    for i in range(tag_count):
        # 输入标签,格式为"#标签名"
        editor.input("#" + tags[i], clear=False)
        # 等待输入完成
        editor.wait(1)
        # 按回车键确认标签
        editor.input(Keys.ENTER, clear=False)
        # 等待页面响应
        editor.wait(1)
    # 等待页面响应
    tab.wait(2)
    # 滚动到页面底部
    tab.scroll.to_bottom()
    # 等待页面响应
    tab.wait(2)
    # 查找并点击"发布"按钮
    tab("tag:span@text()=发布").click()
    # 等待发布完成
    tab.wait(2)
    # 记录发布成功的日志信息
    logger.info("小红书上传成功~~")

14.2. main.py #

main.py

# 导入日志记录器,用于记录程序运行过程中的各种信息
from common import get_logger

# 导入抖音相关工具函数,包括爬取视频和提取音频功能
from douyin_utils import crawl_douyin_videos, extract_audio

# 导入腾讯云对象存储上传工具函数
from cos_utils import upload_to_cos

# 导入语音识别工具函数,用于将音频转换为文字
from asr_utils import audio_to_text

# 导入抖音文案转小红书文案工具函数
from xhs_utils import douyin_to_xhs_text

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)

# 导入根据文案生成图片工具函数
from image_utils import generate_images_by_text

# 导入图片生成配置参数
from config import IMAGE_NUM_DEFAULT

# 导入发布小红书工具函数
+from xhs_publisher import post_to_xiaohongshu


# 定义程序的主入口函数
def main():
    # 在程序开始时记录一条信息日志,表示主流程已启动
    logger.info("主流程开始")
    try:
        # 1.使用关键词"脆桃"爬取抖音热门视频,返回视频文件列表
        video_files = crawl_douyin_videos("脆桃")
        # 遍历每个爬取到的视频文件进行处理
        for video_file in video_files:
            # 记录当前正在处理的视频文件名称
            logger.info(f"处理视频文件: {video_file}")
            # 2.将视频文件转换为MP3音频格式
            mp3_file = extract_audio(video_file)
            # 检查音频转换是否成功,如果失败则跳过当前文件
            if not mp3_file:
                # 记录警告日志,表示音频转换失败
                logger.warning(f"视频转音频失败,跳过: {video_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 3.将转换后的音频文件上传到腾讯云对象存储
            cos_url = upload_to_cos(mp3_file)
            # 记录上传成功的日志信息,包含返回的云存储URL
            logger.info(f"上传音频到腾讯云成功: {cos_url}")
            # 4.使用语音识别服务将音频转换为文字内容
            douyin_text = audio_to_text(cos_url)
            # 记录语音识别成功的日志信息,包含识别出的文字内容
            logger.info(f"音频转文字成功: {douyin_text}")
            # 检查语音识别是否成功,如果失败则跳过当前文件
            if not douyin_text:
                # 记录警告日志,表示语音识别失败
                logger.warning(f"音频转文字失败,跳过: {mp3_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 5.抖音文案转小红书文案
            xhs_title, xhs_intro, xhs_tags = douyin_to_xhs_text(douyin_text)
            logger.info(
                f"抖音文案转小红书文案成功: {xhs_title}, {xhs_intro}, {xhs_tags}"
            )
            # 6. 根据文案生成图片
            img_paths = generate_images_by_text(
                xhs_title, xhs_intro, num_images=IMAGE_NUM_DEFAULT
            )
            logger.info(f"根据文案生成图片成功: {img_paths}")
            # 7. 自动发到小红书
+           post_to_xiaohongshu(
+               title=xhs_title, intro=xhs_intro, tags=xhs_tags, img_paths=img_paths
+           )
    except Exception as e:
        # 捕获并记录主流程执行过程中的任何异常错误
        logger.error(f"主流程执行出错: {e}")


# 检查当前脚本是否作为主程序直接运行(而不是被其他模块导入)
if __name__ == "__main__":
    # 如果作为主程序运行,则调用主函数开始执行
    main()

15.发布知乎 #

本节介绍如何自动化发布内容到知乎专栏,适合多平台内容分发。

15.1. zhihu_publisher.py #

zhihu_publisher.py

# 导入浏览器实例和日志记录器
from common import browser, get_logger

# 导入键盘按键常量,用于模拟键盘操作
from DrissionPage.common import Keys

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)


# 定义发布到知乎的主函数,接收标题、正文、标签和图片路径参数
def post_to_zhihu(title, intro, img_paths):
    # 记录开始发布知乎的日志信息,包含标题和图片路径
    logger.info(f"准备自动发到知乎,标题:{title},图片:{img_paths}")
    # 打开知乎专栏写作页面
    tab = browser.new_tab("https://zhuanlan.zhihu.com/write")
    # 等待页面加载完成
    tab.wait(3)

    # 查找标题输入框元素,使用特定的CSS类选择器
    title_input = tab("tag:textarea@class:i7cW1UcwT6ThdhTakqFm", timeout=30)
    # 检查标题长度是否超过100个字符,如果超过则截取前100个字符
    if len(title) > 100:
        title = title[:100]
    # 在标题输入框中输入标题内容
    title_input.input(title)
    # 等待输入完成
    tab.wait(1)

    # 查找正文输入区域元素,使用DraftEditor-root类选择器
    intro_input = tab("tag:div@class:DraftEditor-root", timeout=30)
    # 在正文输入区域中输入正文内容
    intro_input.input(intro)
    # 按回车键换行
    intro_input.input(Keys.ENTER)
    # 等待输入完成
    tab.wait(1)

    # 查找并点击工具栏中的图片上传按钮
    uplload_img_btn = tab("tag:button@aria-label=图片", timeout=10)
    # 点击图片上传按钮
    uplload_img_btn.click()
    # 等待按钮响应
    tab.wait(1)
    # 设置要上传的图片文件路径,多个路径用换行符分隔
    tab.set.upload_files("\n".join(img_paths))
    # 查找图片上传确认按钮
    img_btn = tab("tag:div@class:css-8x20s2", timeout=10)
    # 点击图片上传确认按钮
    img_btn.click()
    # 等待图片上传完成
    tab.wait(3)
    # 查找图片上传完成后的确认按钮
    img_btn = tab("tag:button@class:css-owamhi", timeout=10)
    # 点击图片上传完成确认按钮
    img_btn.click()
    # 等待按钮响应
    tab.wait(1)

    # 查找发布按钮
    publish_btn = tab("tag:button@text()=发布", timeout=30)
    # 点击发布按钮
    publish_btn.click()
    # 等待发布完成
    tab.wait(3)
    # 记录发布成功的日志信息
    logger.info("知乎专栏上传成功~~")


# 测试函数调用,传入测试用的标题、正文和图片路径
#post_to_zhihu(
#    title="标题标题标题标题标题标题",
#    intro="正文正文正文正文正文正文",
#    img_paths=[
#        "D:/aprepare/douyintools3/images/1_jiuming_shanxicuimitaobaozhiwuhe_cuitianfengshen_no_watermark.jpg"
#    ],
#)

15.2. main.py #

main.py

# 导入日志记录器,用于记录程序运行过程中的各种信息
from common import get_logger

# 导入抖音相关工具函数,包括爬取视频和提取音频功能
from douyin_utils import crawl_douyin_videos, extract_audio

# 导入腾讯云对象存储上传工具函数
from cos_utils import upload_to_cos

# 导入语音识别工具函数,用于将音频转换为文字
from asr_utils import audio_to_text

# 导入抖音文案转小红书文案工具函数
from xhs_utils import douyin_to_xhs_text
+from zhihu_publisher import post_to_zhihu

# 为当前模块创建专用的日志记录器实例
logger = get_logger(__name__)

# 导入根据文案生成图片工具函数
from image_utils import generate_images_by_text

# 导入图片生成配置参数
from config import IMAGE_NUM_DEFAULT

# 导入发布小红书工具函数
from xhs_publisher import post_to_xiaohongshu


# 定义程序的主入口函数
def main():
    # 在程序开始时记录一条信息日志,表示主流程已启动
    logger.info("主流程开始")
    try:
        # 1.使用关键词"脆桃"爬取抖音热门视频,返回视频文件列表
        video_files = crawl_douyin_videos("脆桃")
        # 遍历每个爬取到的视频文件进行处理
        for video_file in video_files:
            # 记录当前正在处理的视频文件名称
            logger.info(f"处理视频文件: {video_file}")
            # 2.将视频文件转换为MP3音频格式
            mp3_file = extract_audio(video_file)
            # 检查音频转换是否成功,如果失败则跳过当前文件
            if not mp3_file:
                # 记录警告日志,表示音频转换失败
                logger.warning(f"视频转音频失败,跳过: {video_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 3.将转换后的音频文件上传到腾讯云对象存储
            cos_url = upload_to_cos(mp3_file)
            # 记录上传成功的日志信息,包含返回的云存储URL
            logger.info(f"上传音频到腾讯云成功: {cos_url}")
            # 4.使用语音识别服务将音频转换为文字内容
            douyin_text = audio_to_text(cos_url)
            # 记录语音识别成功的日志信息,包含识别出的文字内容
            logger.info(f"音频转文字成功: {douyin_text}")
            # 检查语音识别是否成功,如果失败则跳过当前文件
            if not douyin_text:
                # 记录警告日志,表示语音识别失败
                logger.warning(f"音频转文字失败,跳过: {mp3_file}")
                # 跳过当前文件,继续处理下一个
                continue
            # 5.抖音文案转小红书文案
            xhs_title, xhs_intro, xhs_tags = douyin_to_xhs_text(douyin_text)
            logger.info(
                f"抖音文案转小红书文案成功: {xhs_title}, {xhs_intro}, {xhs_tags}"
            )
            # 6. 根据文案生成图片
            img_paths = generate_images_by_text(
                xhs_title, xhs_intro, num_images=IMAGE_NUM_DEFAULT
            )
            logger.info(f"根据文案生成图片成功: {img_paths}")
            # 7. 自动发到小红书
            post_to_xiaohongshu(
                title=xhs_title, intro=xhs_intro, tags=xhs_tags, img_paths=img_paths
            )
            # 8. 自动发到知乎
+           post_to_zhihu(
+               title=xhs_title, intro=xhs_intro, tags=xhs_tags, img_paths=img_paths
+           )
    except Exception as e:
        # 捕获并记录主流程执行过程中的任何异常错误
        logger.error(f"主流程执行出错: {e}")


# 检查当前脚本是否作为主程序直接运行(而不是被其他模块导入)
if __name__ == "__main__":
    # 如果作为主程序运行,则调用主函数开始执行
    main()

16.作业 #

本节为进阶任务,鼓励你尝试多平台发布和用数据库管理内容,提升项目的实用性和可扩展性。

1.增加把此内容发布到今日头条或百度百家号等任意一个平台

2.把相关的内容存入数据库,数据库表名为media,表结构可以参考以下内容:

表结构设计说明

  • 每一步的产物(视频、音频、文本、图片、发布)都单独建表,方便追踪和溯源。
  • 通过外键关联,保证数据的完整性和可追溯性。
  • 发布记录表可记录每次自动发文的结果,便于后续统计和异常排查。
  • 标签建议用英文逗号分隔,方便后续检索和处理。

1. 视频信息表(douyin_videos)

字段名 类型 说明
id BIGINT PRIMARY KEY AUTO_INCREMENT 主键
video_path VARCHAR(255) 视频本地路径
title VARCHAR(255) 视频标题
create_time DATETIME 下载时间

2. 音频信息表(audio_files)

字段名 类型 说明
id BIGINT PRIMARY KEY AUTO_INCREMENT 主键
video_id BIGINT 外键,关联视频
audio_path VARCHAR(255) 音频本地路径
cos_url VARCHAR(255) 腾讯云COS地址
create_time DATETIME 创建时间

3. 文本内容表(text_contents)

字段名 类型 说明
id BIGINT PRIMARY KEY AUTO_INCREMENT 主键
audio_id BIGINT 外键,关联音频
raw_text TEXT 原始识别文本
xhs_title VARCHAR(100) 小红书标题
xhs_intro TEXT 小红书正文
xhs_tags VARCHAR(255) 小红书标签(逗号分隔)
create_time DATETIME 创建时间

4. 图片信息表(images)

字段名 类型 说明
id BIGINT PRIMARY KEY AUTO_INCREMENT 主键
text_id BIGINT 外键,关联文本内容
image_path VARCHAR(255) 图片本地路径
create_time DATETIME 创建时间

5. 发布记录表(publish_records)

字段名 类型 说明
id BIGINT PRIMARY KEY AUTO_INCREMENT 主键
text_id BIGINT 外键,关联文本内容
platform VARCHAR(50) 平台(如"知乎"、"小红书")
status VARCHAR(20) 发布状态(成功/失败)
publish_time DATETIME 发布时间
extra_info TEXT 额外信息(如返回的链接、错误信息等)

访问验证

请输入访问令牌

Token不正确,请重新输入