File size: 6,016 Bytes
6790676
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# app.py
# pip install gradio==4.43.0 pandas requests unidecode

import requests
import re
import random
import gradio as gr
import pandas as pd
from difflib import SequenceMatcher
from unidecode import unidecode
import os

# -----------------------------
# 工具函数
# -----------------------------
def normalize(s: str) -> str:
    s = s.strip().lower()
    s = unidecode(s)
    s = re.sub(r"\s+", " ", s)
    return s

def dedup_words(words):
    out, seen = [], set()
    for w in words:
        k = normalize(w)
        if k not in seen:
            seen.add(k)
            out.append(w)
    return out

def similarity(a: str, b: str) -> float:
    return SequenceMatcher(None, normalize(a), normalize(b)).ratio()

# -----------------------------
# TikTok Cookie 支持
# -----------------------------
TIKTOK_COOKIE = ""

def set_cookie(cookie_text):
    global TIKTOK_COOKIE
    TIKTOK_COOKIE = cookie_text
    return "✅ Cookie 已更新"

# -----------------------------
# 1. TikTok 实时热搜抓取
# -----------------------------
def get_tiktok_trending_keywords(region="VN", query=None, topk=12):
    if query:
        url = f"https://www.tiktok.com/api/discover/search/?keyword={query}&region={region}"
    else:
        url = f"https://www.tiktok.com/api/discover/trending/?region={region}"

    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
        "Referer": "https://www.tiktok.com/",
    }
    if TIKTOK_COOKIE:
        headers["Cookie"] = TIKTOK_COOKIE

    try:
        r = requests.get(url, headers=headers, timeout=8)
        r.raise_for_status()
        data = r.json()
    except Exception as e:
        print("请求失败:", e)
        return []

    keywords = []
    for item in data.get("data", []):
        title = item.get("title") or item.get("challenge_info", {}).get("title")
        views = item.get("viewCount") or 0
        if title:
            keywords.append((title.strip(), views))

    keywords.sort(key=lambda x: x[1], reverse=True)
    return [kw for kw, _ in keywords[:topk]]

# -----------------------------
# 2. 热词建议
# -----------------------------
def suggest_hotwords(product_name: str, category: str, lang: str, topk: int = 12):
    region_map = {"zh": "CN", "fr": "FR", "en": "US", "vi": "VN"}
    region = region_map.get(lang, "US")
    return get_tiktok_trending_keywords(region=region, query=product_name, topk=topk)

# -----------------------------
# 3. 标题生成
# -----------------------------
def rank_keywords(core_words, hotwords):
    items = []
    for w in core_words:
        items.append((w, 1.0))
    for i, w in enumerate(hotwords):
        items.append((w, 0.9 - i * 0.02))
    uniq = {}
    for w, sc in items:
        k = normalize(w)
        if k not in uniq or sc > uniq[k]:
            uniq[k] = sc
    ranked = sorted([(w, sc) for w, sc in uniq.items()], key=lambda x: x[1], reverse=True)
    return [w for w, _ in ranked]

def build_title(core, ranked, max_len):
    segments = dedup_words(core + ranked)
    title = " ".join(segments)
    if len(title) > max_len:
        title = title[:max_len]
    return title

def generate_titles(product_name, category, lang, n_titles, hotwords_text, max_len):
    core_words = dedup_words(re.split(r"[,\|/;,、 ]+", product_name))
    hotwords = dedup_words(re.split(r"[,\|/;,、\n]+", hotwords_text))
    ranked = rank_keywords(core_words, hotwords)

    out = []
    for i in range(n_titles):
        random.shuffle(ranked)
        title = build_title(core_words, ranked, max_len)
        out.append(title)

    df = pd.DataFrame({"序号": list(range(1, len(out) + 1)), "标题": out})
    csv_bytes = df.to_csv(index=False).encode("utf-8-sig")
    return "\n".join(out), csv_bytes

# -----------------------------
# 4. Gradio UI
# -----------------------------
LANGS = [("中文", "zh"), ("Français", "fr"), ("English", "en"), ("Tiếng Việt", "vi")]

def ui_hotwords(product_name, category, lang, topk):
    if not product_name:
        return ""
    kws = suggest_hotwords(product_name, category, lang, int(topk))
    return ", ".join(kws)

with gr.Blocks(theme=gr.themes.Soft()) as demo:
    gr.Markdown("## TikTok 商品标题优化工具(实时热搜 + Cookie 支持)")

    with gr.Row():
        product_name = gr.Textbox(label="商品名称与关键词", placeholder="输入商品名,例如:阻力带", lines=2)
        category = gr.Textbox(label="类目", value="健身用品/健身器材")

    with gr.Row():
        lang = gr.Dropdown(LANGS, value="fr", label="语言")
        n_titles = gr.Slider(1, 10, value=3, step=1, label="生成数量")
        max_len = gr.Slider(30, 120, value=80, step=1, label="最大标题长度")
        topk = gr.Slider(6, 30, value=12, step=1, label="热门关键词数量")

    hotwords_box = gr.Textbox(label="热门关键词(实时获取)", lines=2, placeholder="将自动填充")
    btn_refresh = gr.Button("⟳ 获取实时热门关键词")
    btn_generate = gr.Button("⚡ 生成标题")

    cookie_input = gr.Textbox(label="TikTok Cookie(可选)", placeholder="可手动粘贴 Cookie", lines=2)
    btn_set_cookie = gr.Button("✅ 更新 Cookie")

    titles_out = gr.Textbox(label="生成结果", lines=10)
    csv_file = gr.File(label="下载 CSV", file_count="single")

    product_name.change(ui_hotwords, [product_name, category, lang, topk], [hotwords_box])
    lang.change(ui_hotwords, [product_name, category, lang, topk], [hotwords_box])
    topk.change(ui_hotwords, [product_name, category, lang, topk], [hotwords_box])
    btn_refresh.click(ui_hotwords, [product_name, category, lang, topk], [hotwords_box])

    btn_generate.click(
        generate_titles,
        [product_name, category, lang, n_titles, hotwords_box, max_len],
        [titles_out, csv_file]
    )

    btn_set_cookie.click(set_cookie, [cookie_input], [cookie_input])

if __name__ == "__main__":
    demo.launch()