|
|
@@ -0,0 +1,348 @@
|
|
|
+#!/usr/bin/env python
|
|
|
+# -*- encoding: utf-8 -*-
|
|
|
+"""
|
|
|
+@Contact : liuyuqi.gov@msn.cn
|
|
|
+@Time : 2024/12/19
|
|
|
+@License : Copyright © 2017-2022 liuyuqi. All Rights Reserved.
|
|
|
+@Desc : GUI界面入口
|
|
|
+"""
|
|
|
+
|
|
|
+import tkinter as tk
|
|
|
+from tkinter import ttk, scrolledtext, filedialog, messagebox
|
|
|
+import threading
|
|
|
+import sys
|
|
|
+import os
|
|
|
+from collections import OrderedDict
|
|
|
+from searchdomain import SearchDomain, GenerateDomain, GenerateEnDomain
|
|
|
+from searchdomain.utils.frozen_dir import get_app_path
|
|
|
+
|
|
|
+
|
|
|
+class SearchDomainGUI:
|
|
|
+ def __init__(self, root):
|
|
|
+ self.root = root
|
|
|
+ self.root.title("域名批量检索工具")
|
|
|
+ self.root.geometry("800x700")
|
|
|
+ self.root.resizable(True, True)
|
|
|
+
|
|
|
+ # 设置应用路径
|
|
|
+ self.app_path = get_app_path()
|
|
|
+
|
|
|
+ # 创建主界面
|
|
|
+ self.create_widgets()
|
|
|
+
|
|
|
+ # 运行状态
|
|
|
+ self.is_running = False
|
|
|
+
|
|
|
+ def create_widgets(self):
|
|
|
+ # 创建Notebook(标签页)
|
|
|
+ notebook = ttk.Notebook(self.root)
|
|
|
+ notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
|
|
+
|
|
|
+ # 生成域名标签页
|
|
|
+ self.generate_frame = ttk.Frame(notebook)
|
|
|
+ notebook.add(self.generate_frame, text="生成域名")
|
|
|
+ self.create_generate_tab()
|
|
|
+
|
|
|
+ # 搜索域名标签页
|
|
|
+ self.search_frame = ttk.Frame(notebook)
|
|
|
+ notebook.add(self.search_frame, text="搜索域名")
|
|
|
+ self.create_search_tab()
|
|
|
+
|
|
|
+ # 日志输出区域(共用)
|
|
|
+ log_frame = ttk.LabelFrame(self.root, text="运行日志")
|
|
|
+ log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
|
|
+
|
|
|
+ self.log_text = scrolledtext.ScrolledText(log_frame, height=10, wrap=tk.WORD)
|
|
|
+ self.log_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
|
|
+
|
|
|
+ # 状态栏
|
|
|
+ self.status_var = tk.StringVar(value="就绪")
|
|
|
+ status_bar = ttk.Label(self.root, textvariable=self.status_var, relief=tk.SUNKEN)
|
|
|
+ status_bar.pack(side=tk.BOTTOM, fill=tk.X)
|
|
|
+
|
|
|
+ def create_generate_tab(self):
|
|
|
+ # 语言选择
|
|
|
+ lang_frame = ttk.LabelFrame(self.generate_frame, text="语言设置")
|
|
|
+ lang_frame.pack(fill=tk.X, padx=10, pady=5)
|
|
|
+
|
|
|
+ self.lang_var = tk.StringVar(value="en")
|
|
|
+ ttk.Radiobutton(lang_frame, text="中文", variable=self.lang_var, value="zh").pack(side=tk.LEFT, padx=10, pady=5)
|
|
|
+ ttk.Radiobutton(lang_frame, text="英文", variable=self.lang_var, value="en").pack(side=tk.LEFT, padx=10, pady=5)
|
|
|
+
|
|
|
+ # 关键词输入
|
|
|
+ keyword_frame = ttk.LabelFrame(self.generate_frame, text="关键词(用逗号分隔)")
|
|
|
+ keyword_frame.pack(fill=tk.X, padx=10, pady=5)
|
|
|
+
|
|
|
+ self.keyword_entry = ttk.Entry(keyword_frame, width=50)
|
|
|
+ self.keyword_entry.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
|
|
+ self.keyword_entry.insert(0, "gpt,go,ai")
|
|
|
+
|
|
|
+ # 域名后缀
|
|
|
+ domain_frame = ttk.LabelFrame(self.generate_frame, text="域名后缀(用逗号分隔)")
|
|
|
+ domain_frame.pack(fill=tk.X, padx=10, pady=5)
|
|
|
+
|
|
|
+ self.domain_entry = ttk.Entry(domain_frame, width=50)
|
|
|
+ self.domain_entry.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
|
|
+ self.domain_entry.insert(0, "com,cn,io")
|
|
|
+
|
|
|
+ # 位置选择
|
|
|
+ position_frame = ttk.LabelFrame(self.generate_frame, text="关键词位置")
|
|
|
+ position_frame.pack(fill=tk.X, padx=10, pady=5)
|
|
|
+
|
|
|
+ self.position_var = tk.StringVar(value="prefix")
|
|
|
+ ttk.Radiobutton(position_frame, text="前缀", variable=self.position_var, value="prefix").pack(side=tk.LEFT, padx=10, pady=5)
|
|
|
+ ttk.Radiobutton(position_frame, text="后缀", variable=self.position_var, value="suffix").pack(side=tk.LEFT, padx=10, pady=5)
|
|
|
+
|
|
|
+ # 输出文件
|
|
|
+ output_frame = ttk.LabelFrame(self.generate_frame, text="输出文件")
|
|
|
+ output_frame.pack(fill=tk.X, padx=10, pady=5)
|
|
|
+
|
|
|
+ self.generate_output_entry = ttk.Entry(output_frame, width=40)
|
|
|
+ self.generate_output_entry.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
|
|
+ self.generate_output_entry.insert(0, "domain.txt")
|
|
|
+
|
|
|
+ ttk.Button(output_frame, text="浏览", command=self.browse_generate_output).pack(side=tk.LEFT, padx=5, pady=5)
|
|
|
+
|
|
|
+ # 生成按钮
|
|
|
+ button_frame = ttk.Frame(self.generate_frame)
|
|
|
+ button_frame.pack(fill=tk.X, padx=10, pady=10)
|
|
|
+
|
|
|
+ self.generate_button = ttk.Button(button_frame, text="开始生成", command=self.start_generate)
|
|
|
+ self.generate_button.pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ ttk.Button(button_frame, text="清空日志", command=self.clear_log).pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ def create_search_tab(self):
|
|
|
+ # 输入文件
|
|
|
+ input_frame = ttk.LabelFrame(self.search_frame, text="输入文件(域名列表)")
|
|
|
+ input_frame.pack(fill=tk.X, padx=10, pady=5)
|
|
|
+
|
|
|
+ self.input_entry = ttk.Entry(input_frame, width=40)
|
|
|
+ self.input_entry.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
|
|
+ self.input_entry.insert(0, "domain.txt")
|
|
|
+
|
|
|
+ ttk.Button(input_frame, text="浏览", command=self.browse_input_file).pack(side=tk.LEFT, padx=5, pady=5)
|
|
|
+
|
|
|
+ # 输出文件
|
|
|
+ output_frame = ttk.LabelFrame(self.search_frame, text="输出文件(结果)")
|
|
|
+ output_frame.pack(fill=tk.X, padx=10, pady=5)
|
|
|
+
|
|
|
+ self.search_output_entry = ttk.Entry(output_frame, width=40)
|
|
|
+ self.search_output_entry.pack(side=tk.LEFT, padx=5, pady=5, fill=tk.X, expand=True)
|
|
|
+ self.search_output_entry.insert(0, "result.txt")
|
|
|
+
|
|
|
+ ttk.Button(output_frame, text="浏览", command=self.browse_search_output).pack(side=tk.LEFT, padx=5, pady=5)
|
|
|
+
|
|
|
+ # 导出选项
|
|
|
+ export_frame = ttk.LabelFrame(self.search_frame, text="导出选项")
|
|
|
+ export_frame.pack(fill=tk.X, padx=10, pady=5)
|
|
|
+
|
|
|
+ self.export_all_var = tk.BooleanVar(value=False)
|
|
|
+ ttk.Checkbutton(export_frame, text="导出所有域名(包括不可用的)", variable=self.export_all_var).pack(side=tk.LEFT, padx=10, pady=5)
|
|
|
+
|
|
|
+ # 搜索按钮
|
|
|
+ button_frame = ttk.Frame(self.search_frame)
|
|
|
+ button_frame.pack(fill=tk.X, padx=10, pady=10)
|
|
|
+
|
|
|
+ self.search_button = ttk.Button(button_frame, text="开始搜索", command=self.start_search)
|
|
|
+ self.search_button.pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ ttk.Button(button_frame, text="清空日志", command=self.clear_log).pack(side=tk.LEFT, padx=5)
|
|
|
+
|
|
|
+ def browse_input_file(self):
|
|
|
+ filename = filedialog.askopenfilename(
|
|
|
+ title="选择输入文件",
|
|
|
+ filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
|
|
|
+ )
|
|
|
+ if filename:
|
|
|
+ self.input_entry.delete(0, tk.END)
|
|
|
+ self.input_entry.insert(0, filename)
|
|
|
+
|
|
|
+ def browse_search_output(self):
|
|
|
+ filename = filedialog.asksaveasfilename(
|
|
|
+ title="选择输出文件",
|
|
|
+ defaultextension=".txt",
|
|
|
+ filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
|
|
|
+ )
|
|
|
+ if filename:
|
|
|
+ self.search_output_entry.delete(0, tk.END)
|
|
|
+ self.search_output_entry.insert(0, filename)
|
|
|
+
|
|
|
+ def browse_generate_output(self):
|
|
|
+ filename = filedialog.asksaveasfilename(
|
|
|
+ title="选择输出文件",
|
|
|
+ defaultextension=".txt",
|
|
|
+ filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
|
|
|
+ )
|
|
|
+ if filename:
|
|
|
+ self.generate_output_entry.delete(0, tk.END)
|
|
|
+ self.generate_output_entry.insert(0, filename)
|
|
|
+
|
|
|
+ def log(self, message):
|
|
|
+ """添加日志"""
|
|
|
+ self.log_text.insert(tk.END, message + "\n")
|
|
|
+ self.log_text.see(tk.END)
|
|
|
+ self.root.update_idletasks()
|
|
|
+
|
|
|
+ def clear_log(self):
|
|
|
+ """清空日志"""
|
|
|
+ self.log_text.delete(1.0, tk.END)
|
|
|
+
|
|
|
+ def start_generate(self):
|
|
|
+ """开始生成域名"""
|
|
|
+ if self.is_running:
|
|
|
+ messagebox.showwarning("警告", "任务正在运行中,请等待完成")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 获取参数
|
|
|
+ lang = self.lang_var.get()
|
|
|
+ keyword = self.keyword_entry.get().strip()
|
|
|
+ domain = self.domain_entry.get().strip()
|
|
|
+ position = self.position_var.get()
|
|
|
+ output = self.generate_output_entry.get().strip()
|
|
|
+
|
|
|
+ if not keyword:
|
|
|
+ messagebox.showerror("错误", "请输入关键词")
|
|
|
+ return
|
|
|
+
|
|
|
+ if not domain:
|
|
|
+ messagebox.showerror("错误", "请输入域名后缀")
|
|
|
+ return
|
|
|
+
|
|
|
+ if not output:
|
|
|
+ messagebox.showerror("错误", "请输入输出文件名")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 准备参数
|
|
|
+ params = OrderedDict({
|
|
|
+ "lang": lang,
|
|
|
+ "keyword": keyword,
|
|
|
+ "domain": domain,
|
|
|
+ "position": position,
|
|
|
+ "app_path": self.app_path
|
|
|
+ })
|
|
|
+
|
|
|
+ # 更新输出文件路径为绝对路径
|
|
|
+ if not os.path.isabs(output):
|
|
|
+ output = os.path.join(self.app_path, output)
|
|
|
+ else:
|
|
|
+ params["app_path"] = os.path.dirname(output)
|
|
|
+
|
|
|
+ # 在后台线程中运行
|
|
|
+ self.is_running = True
|
|
|
+ self.generate_button.config(state=tk.DISABLED)
|
|
|
+ self.status_var.set("正在生成域名...")
|
|
|
+
|
|
|
+ thread = threading.Thread(target=self._generate_thread, args=(params, lang))
|
|
|
+ thread.daemon = True
|
|
|
+ thread.start()
|
|
|
+
|
|
|
+ def _generate_thread(self, params, lang):
|
|
|
+ """生成域名的后台线程"""
|
|
|
+ try:
|
|
|
+ self.log(f"开始生成域名...")
|
|
|
+ self.log(f"语言: {lang}, 关键词: {params['keyword']}, 域名后缀: {params['domain']}, 位置: {params['position']}")
|
|
|
+
|
|
|
+ if lang == "en":
|
|
|
+ generateDomain = GenerateEnDomain(params=params)
|
|
|
+ else:
|
|
|
+ generateDomain = GenerateDomain(params=params)
|
|
|
+
|
|
|
+ generateDomain.run()
|
|
|
+
|
|
|
+ output_file = params.get("output", "domain.txt")
|
|
|
+ self.log("域名生成完成!")
|
|
|
+ self.log(f"结果已保存到: {output_file}")
|
|
|
+ messagebox.showinfo("成功", f"域名生成完成!\n结果已保存到: {output_file}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ error_msg = f"生成域名时出错: {str(e)}"
|
|
|
+ self.log(error_msg)
|
|
|
+ messagebox.showerror("错误", error_msg)
|
|
|
+ finally:
|
|
|
+ self.is_running = False
|
|
|
+ self.root.after(0, lambda: self.generate_button.config(state=tk.NORMAL))
|
|
|
+ self.root.after(0, lambda: self.status_var.set("就绪"))
|
|
|
+
|
|
|
+ def start_search(self):
|
|
|
+ """开始搜索域名"""
|
|
|
+ if self.is_running:
|
|
|
+ messagebox.showwarning("警告", "任务正在运行中,请等待完成")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 获取参数
|
|
|
+ input_file = self.input_entry.get().strip()
|
|
|
+ output_file = self.search_output_entry.get().strip()
|
|
|
+ export_all = self.export_all_var.get()
|
|
|
+
|
|
|
+ if not input_file:
|
|
|
+ messagebox.showerror("错误", "请输入输入文件名")
|
|
|
+ return
|
|
|
+
|
|
|
+ if not output_file:
|
|
|
+ messagebox.showerror("错误", "请输入输出文件名")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 检查输入文件是否存在
|
|
|
+ if not os.path.isabs(input_file):
|
|
|
+ input_file = os.path.join(self.app_path, input_file)
|
|
|
+
|
|
|
+ if not os.path.exists(input_file):
|
|
|
+ messagebox.showerror("错误", f"输入文件不存在: {input_file}")
|
|
|
+ return
|
|
|
+
|
|
|
+ # 准备参数
|
|
|
+ if not os.path.isabs(output_file):
|
|
|
+ output_file = os.path.join(self.app_path, output_file)
|
|
|
+
|
|
|
+ params = OrderedDict({
|
|
|
+ "input": os.path.basename(input_file),
|
|
|
+ "output": os.path.basename(output_file),
|
|
|
+ "app_path": os.path.dirname(input_file)
|
|
|
+ })
|
|
|
+
|
|
|
+ # 在后台线程中运行
|
|
|
+ self.is_running = True
|
|
|
+ self.search_button.config(state=tk.DISABLED)
|
|
|
+ self.status_var.set("正在搜索域名...")
|
|
|
+
|
|
|
+ thread = threading.Thread(target=self._search_thread, args=(params, export_all))
|
|
|
+ thread.daemon = True
|
|
|
+ thread.start()
|
|
|
+
|
|
|
+ def _search_thread(self, params, export_all):
|
|
|
+ """搜索域名的后台线程"""
|
|
|
+ try:
|
|
|
+ self.log(f"开始搜索域名...")
|
|
|
+ self.log(f"输入文件: {params['input']}")
|
|
|
+ self.log(f"输出文件: {params['output']}")
|
|
|
+ self.log(f"导出所有: {export_all}")
|
|
|
+
|
|
|
+ searchdomain = SearchDomain(params=params, debug=True, export_all=export_all)
|
|
|
+ searchdomain.run()
|
|
|
+
|
|
|
+ self.log("域名搜索完成!")
|
|
|
+ output_path = os.path.join(params["app_path"], params["output"])
|
|
|
+ self.log(f"结果已保存到: {output_path}")
|
|
|
+ messagebox.showinfo("成功", f"域名搜索完成!\n结果已保存到: {output_path}")
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ error_msg = f"搜索域名时出错: {str(e)}"
|
|
|
+ self.log(error_msg)
|
|
|
+ import traceback
|
|
|
+ self.log(traceback.format_exc())
|
|
|
+ messagebox.showerror("错误", error_msg)
|
|
|
+ finally:
|
|
|
+ self.is_running = False
|
|
|
+ self.root.after(0, lambda: self.search_button.config(state=tk.NORMAL))
|
|
|
+ self.root.after(0, lambda: self.status_var.set("就绪"))
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ root = tk.Tk()
|
|
|
+ app = SearchDomainGUI(root)
|
|
|
+ root.mainloop()
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|