子舒的博客

一个热爱生活的前端开发工程师。

通过 AI 实现自动生成 SEO TDK

Oct 60, 60612

感谢博友的文章,《AI 实践|零成本生成 SEO 友好的 TDK 落地方案》

昨天晚上刷到一篇文章,通过 AI 自动化生成网站 SEO 的 TDK,他这个方法有两点比较繁琐的地方。

  1. 文章内容需要手动替换到脚本中
  2. 生成的 TDK 需要手动加入数据库

我针对第一点略微做了一些调整,可以直接查找当前脚本目录下的文件夹,在文件的 62 行,稍后我会把源码贴在文章最下方。

https://github.com/dlzmoe/blog/blob/main/aitdk.py

# aitdk.py
def main(file_name, api_key, api_url, model, debug=False):
    current_dir = os.path.dirname(os.path.abspath(__file__))
    file_path = os.path.join(current_dir, 'content', 'blog', file_name)

这里的目录 /content/blog 可以自由修改,换成你本地的格式。

使用方法也很简单,在 aitdk.py 同级目录下新建一个 .env 环境变量文件,内容如下:

OPENAI_API_KEY=sk-xxx
OPENAI_API_URL=https://api.openai.com/v1
OPENAI_API_MODEL=gpt-4o-mini

然后安装一下依赖。

pip install markdown beautifulsoup4 openai==0.28 python-dotenv

运行命令,xxx.md 就是你的文件全名。

py aitdk.py xxx.md

生成的结果就如下,对于 SEO 质量还是不错的。

1741253061441

1741253117948

aitdk.py 源码
import os
import json
import markdown
from bs4 import BeautifulSoup
import openai
from dotenv import load_dotenv
import argparse

# 加载环境变量
load_dotenv()

def extract_text_from_markdown(file_path):
    with open(file_path, 'r', encoding='utf-8') as file:
        md_content = file.read()
    
    html_content = markdown.markdown(md_content)
    soup = BeautifulSoup(html_content, 'html.parser')
    text_content = soup.get_text()
    
    return text_content

def generate_seo_content(text, api_key, api_url, model, debug=False):
    openai.api_key = api_key
    if api_url:
        openai.api_base = api_url

    prompt = f"请根据文章内容从 SEO 友好的角度提取出标题、关键词和描述:\n\n{text}\n\n请以 JSON 格式输出,包含 slug、title、keywords 和 description 字段。"
    
    try:
        response = openai.ChatCompletion.create(
            model=model,
            messages=[
                {"role": "system", "content": "你是一个 SEO 专家,擅长提炼文章的核心内容并生成优化的元数据。"},
                {"role": "user", "content": prompt}
            ]
        )
        
        content = response.choices[0].message['content']
        # 只在 debug 模式下输出 API 响应
        if debug:
            print("API Response:", content)
        return content
    except Exception as e:
        print(f"Error calling OpenAI API: {e}")
        return None

def parse_seo_content(content):
    try:
        # 尝试直接解析 JSON
        return json.loads(content)
    except json.JSONDecodeError:
        # 如果直接解析失败,尝试提取 JSON 部分
        try:
            start = content.index('{')
            end = content.rindex('}') + 1
            json_str = content[start:end]
            return json.loads(json_str)
        except (ValueError, json.JSONDecodeError):
            print("无法解析 API 返回的内容为 JSON 格式")
            return None

def main(file_name, api_key, api_url, model, debug=False):
    current_dir = os.path.dirname(os.path.abspath(__file__))
    file_path = os.path.join(current_dir, 'content', 'blog', file_name)
    
    if not os.path.exists(file_path):
        print(f"错误:文件 '{file_path}' 不存在。")
        return

    text_content = extract_text_from_markdown(file_path)
    seo_content = generate_seo_content(text_content, api_key, api_url, model, debug)
    
    if seo_content:
        seo_data = parse_seo_content(seo_content)
        if seo_data:
            print(json.dumps(seo_data, ensure_ascii=False, indent=2))
        else:
            print("无法生成有效的 SEO 数据")
    else:
        print("生成 SEO 内容失败")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="从 Markdown 文件生成 SEO 内容")
    parser.add_argument("file_name", help="Markdown 文件名称 (位于 content/blog/目录下)")
    parser.add_argument("--api_key", default=os.getenv("OPENAI_API_KEY"), help="OpenAI API 密钥")
    parser.add_argument("--api_url", default=os.getenv("OPENAI_API_URL"), help="OpenAI API URL")
    parser.add_argument("--model", default=os.getenv("OPENAI_API_MODEL"), help="OpenAI 模型名称")
    parser.add_argument("--debug", action="store_true", help="开启调试模式,显示 API 响应")
    
    args = parser.parse_args()

    if not args.api_key:
        print("错误:未提供 API 密钥。请在命令行参数中指定或在.env 文件中设置 OPENAI_API_KEY。")
    else:
        main(args.file_name, args.api_key, args.api_url, args.model, args.debug)
昵称
邮箱
网址
0/500
  • OωO
  • |´・ω・)ノ
  • ヾ(≧∇≦*)ゝ
  • (☆ω☆)
  • (╯‵□′)╯︵┴─┴
  •  ̄﹃ ̄
  • (/ω\)
  • ∠( ᐛ 」∠)_
  • (๑•̀ㅁ•́ฅ)
  • →_→
  • ୧(๑•̀⌄•́๑)૭
  • ٩(ˊᗜˋ*)و
  • (ノ°ο°)ノ
  • (´இ皿இ`)
  • ⌇●﹏●⌇
  • (ฅ´ω`ฅ)
  • (╯°A°)╯︵○○○
  • φ( ̄∇ ̄o)
  • ヾ(´・ ・`。)ノ"
  • ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
  • (ó﹏ò。)
  • Σ(っ °Д °;)っ
  • ( ,,´・ω・)ノ"(´っω・`。)
  • ╮(╯▽╰)╭
  • o(*////▽////*)q
  • >﹏<
  • ( ๑´•ω•) "(ㆆᴗㆆ)
  • 😂
  • 😀
  • 😅
  • 😊
  • 🙂
  • 🙃
  • 😌
  • 😍
  • 😘
  • 😜
  • 😝
  • 😏
  • 😒
  • 🙄
  • 😳
  • 😡
  • 😔
  • 😫
  • 😱
  • 😭
  • 💩
  • 👻
  • 🙌
  • 🖕
  • 👍
  • 👫
  • 👬
  • 👭
  • 🌚
  • 🌝
  • 🙈
  • 💊
  • 😶
  • 🙏
  • 🍦
  • 🍉
  • 😣
  • 颜文字
  • Emoji
  • Bilibili
1 条评论
Deep Router

上次批量更新了TDK之后,Google直接给我丢到沙盒里面自生自灭了

子舒 博主
回复 @Deep Router :

估计被识别成农场了,自己玩的话就无所谓