# -*- coding: utf-8 -*- import gradio as gr from urllib.parse import quote from datetime import date BIG_CUPS = ["F","G","H","I","J","K","L"] SEARCH_ENGINES = { "Google": "https://www.google.com/search?q=", "Bing": "https://www.bing.com/search?q=", "Yandex": "https://yandex.com/search/?text=", } def build_terms(cups, extra_cn, extra_jp, extra_en): cups = cups or [] cn = [f"{c}罩杯" for c in cups] + [f"{c}杯" for c in cups] en = [f"{c} cup" for c in cups] + [f"{c.lower()}cup" for c in cups] jp = [f"{c}カップ" for c in cups] base_terms = list(dict.fromkeys(cn + en + jp)) if extra_cn: base_terms += [t.strip() for t in extra_cn.split(",") if t.strip()] if extra_jp: base_terms += [t.strip() for t in extra_jp.split(",") if t.strip()] if extra_en: base_terms += [t.strip() for t in extra_en.split(",") if t.strip()] seen = set(); out = [] for t in base_terms: if t not in seen: seen.add(t); out.append(t) return out def needs_quotes(s: str) -> bool: try: s.encode('ascii'); ascii_only = True except UnicodeEncodeError: ascii_only = False return (not ascii_only) or (" " in s) def quote_term(t: str) -> str: return f'"{t}"' if needs_quotes(t) and not (t.startswith('"') and t.endswith('"')) else t def or_join(terms): grouped = [quote_term(t) for t in terms] if not grouped: return "" if len(grouped) == 1: return grouped[0] return "(" + " OR ".join(grouped) + ")" def build_filters(media, exclude_words, since, until, from_user, to_user, mention_user, hashtags): filters = [] if media == "只图片": filters.append("filter:images") elif media == "只视频": filters.append("filter:videos") elif media == "只含链接": filters.append("filter:links") if from_user: filters.append(f"from:{from_user.strip()}") if to_user: filters.append(f"to:{to_user.strip()}") if mention_user: filters.append(f"@{mention_user.strip()}") if hashtags: for tag in [t.strip().lstrip('#') for t in hashtags.split(",") if t.strip()]: filters.append(f"#{tag}") if exclude_words: for w in [w.strip() for w in exclude_words.split(",") if w.strip()]: filters.append(f'-"{w}"' if " " in w else f"-{w}") if since: filters.append(f"since:{since}") if until: filters.append(f"until:{until}") return " ".join(filters) def build_engine_link(engine, q): base = SEARCH_ENGINES[engine] return base + quote(q, safe="") def build_per_term_links(terms, engine, focus): """Return list of (term, url_x, url_twitter) pairs per engine (Bing has no inurl).""" focus_clause = "" if focus: fs = [quote_term(x) for x in [s.strip() for s in focus.split(",") if s.strip()]] if fs: focus_clause = " " + " ".join(fs) # 用空格相与,提升精度并避免垃圾判定 links = [] for t in [quote_term(x) for x in terms]: q = f'site:x.com inurl:/status/ {t}{focus_clause}' url1 = build_engine_link(engine, q) q2 = f'site:twitter.com inurl:/status/ {t}{focus_clause}' url2 = build_engine_link(engine, q2) links.append((t, url1, url2)) return links def build_query(cups, add_cn, add_jp, add_en, media, exclude, since, until, from_user, to_user, mention_user, hashtags, engine, focus): # 1) X 完整语法 terms = build_terms(cups, add_cn, add_jp, add_en) core = or_join(terms) flt = build_filters(media, exclude, since, until, from_user, to_user, mention_user, hashtags) raw = (core + " " + flt).strip() x_url = "https://x.com/search?q=" + quote(raw, safe="") + "&src=typed_query&f=live" # 2) 站内搜索(每词两条:x.com & twitter.com) per_term = build_per_term_links(terms, engine, focus) # 用 Markdown 列表渲染 if not per_term: md = f"[🔎 在 X 打开实时搜索]({x_url})\n\n_请至少选择一个关键词_" else: md = f"[🔎 在 X 打开实时搜索]({x_url})\n\n**{engine} 站内搜索(每个词两条:x.com / twitter.com)**\n" for i,(t,u1,u2) in enumerate(per_term,1): md += f"- {t} → [x.com]({u1}) | [twitter.com]({u2})\n" # 返回第一个链接方便复制 g_url_first = per_term[0][1] if per_term else "" return raw, md, x_url, g_url_first with gr.Blocks(title="Big Cup Twitter/X Deep Search Builder v3") as demo: gr.Markdown("# 大罩杯 X/Twitter 深度搜索构建器(v3)\n- X:完整语法(括号/OR/from/filter/since)\n- 站内:每个关键词单独生成 x.com / twitter.com 链接,规避 Google 风控;支持引擎切换 Google/Bing/Yandex") with gr.Row(): cups = gr.CheckboxGroup(choices=BIG_CUPS, value=["F","G","H","I","J","K","L"], label="选择罩杯(可多选)") with gr.Row(): add_cn = gr.Textbox(label="追加中文(逗号分隔)", placeholder="巨乳, 爆乳, 写真, 女優, 出道") add_jp = gr.Textbox(label="追加日文(逗号分隔)", placeholder="巨乳, 爆乳, グラビア, 女優") add_en = gr.Textbox(label="追加英文(逗号分隔)", placeholder="big boobs, huge breasts, gravure") with gr.Row(): media = gr.Dropdown(choices=["不限","只图片","只视频","只含链接"], value="不限", label="媒体过滤") hashtags = gr.Textbox(label="话题标签(逗号分隔,无需#)", placeholder="muteki, gravure") exclude = gr.Textbox(label="排除词(逗号分隔)", placeholder="广告, 出售, 推广") with gr.Row(): since = gr.Textbox(label="开始日期 since: (YYYY-MM-DD)", placeholder="2025-01-01") until = gr.Textbox(label="结束日期 until: (YYYY-MM-DD)", placeholder=str(date.today())) from_user = gr.Textbox(label="来自账号 from:", placeholder="username") to_user = gr.Textbox(label="给账号 to:", placeholder="username") mention_user = gr.Textbox(label="提及账号 @", placeholder="username") with gr.Row(): engine = gr.Radio(choices=list(SEARCH_ENGINES.keys()), value="Google", label="站内搜索引擎") focus = gr.Textbox(label="额外聚焦词(空格相与,逗号分隔)", placeholder="巨乳, 爆乳, グラビア, 女優") go = gr.Button("✅ 生成搜索", variant="primary") raw_out = gr.Textbox(label="原始搜索语句(给 X 用)", lines=3) links_md = gr.Markdown() x_url = gr.Textbox(label="X 搜索链接", interactive=False) g_url = gr.Textbox(label="首个站内链接(便于复制)", interactive=False) go.click( build_query, inputs=[cups, add_cn, add_jp, add_en, media, exclude, since, until, from_user, to_user, mention_user, hashtags, engine, focus], outputs=[raw_out, links_md, x_url, g_url] ) demo.launch()