zangtuo 2 weeks ago
parent
commit
f9fafd7b3b

BIN
.DS_Store


+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 8 - 0
.idea/PythonProject.iml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="jdk" jdkName="django" jdkType="Python SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 98 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,98 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="ignoredPackages">
+        <value>
+          <list size="85">
+            <item index="0" class="java.lang.String" itemvalue="traitlets" />
+            <item index="1" class="java.lang.String" itemvalue="protobuf" />
+            <item index="2" class="java.lang.String" itemvalue="joblib" />
+            <item index="3" class="java.lang.String" itemvalue="threadpoolctl" />
+            <item index="4" class="java.lang.String" itemvalue="scikit-learn" />
+            <item index="5" class="java.lang.String" itemvalue="testpath" />
+            <item index="6" class="java.lang.String" itemvalue="PyYAML" />
+            <item index="7" class="java.lang.String" itemvalue="python-dateutil" />
+            <item index="8" class="java.lang.String" itemvalue="MarkupSafe" />
+            <item index="9" class="java.lang.String" itemvalue="mkl-random" />
+            <item index="10" class="java.lang.String" itemvalue="torchvision" />
+            <item index="11" class="java.lang.String" itemvalue="aiocontextvars" />
+            <item index="12" class="java.lang.String" itemvalue="easydict" />
+            <item index="13" class="java.lang.String" itemvalue="Pygments" />
+            <item index="14" class="java.lang.String" itemvalue="pyzmq" />
+            <item index="15" class="java.lang.String" itemvalue="bleach" />
+            <item index="16" class="java.lang.String" itemvalue="certifi" />
+            <item index="17" class="java.lang.String" itemvalue="docutils" />
+            <item index="18" class="java.lang.String" itemvalue="entrypoints" />
+            <item index="19" class="java.lang.String" itemvalue="pyparsing" />
+            <item index="20" class="java.lang.String" itemvalue="jsonschema" />
+            <item index="21" class="java.lang.String" itemvalue="notebook" />
+            <item index="22" class="java.lang.String" itemvalue="qtconsole" />
+            <item index="23" class="java.lang.String" itemvalue="terminado" />
+            <item index="24" class="java.lang.String" itemvalue="h5py" />
+            <item index="25" class="java.lang.String" itemvalue="cryptography" />
+            <item index="26" class="java.lang.String" itemvalue="typing-extensions" />
+            <item index="27" class="java.lang.String" itemvalue="jupyter-client" />
+            <item index="28" class="java.lang.String" itemvalue="pexpect" />
+            <item index="29" class="java.lang.String" itemvalue="tensorpack" />
+            <item index="30" class="java.lang.String" itemvalue="ipykernel" />
+            <item index="31" class="java.lang.String" itemvalue="nbconvert" />
+            <item index="32" class="java.lang.String" itemvalue="psutil" />
+            <item index="33" class="java.lang.String" itemvalue="jedi" />
+            <item index="34" class="java.lang.String" itemvalue="imageio" />
+            <item index="35" class="java.lang.String" itemvalue="PySocks" />
+            <item index="36" class="java.lang.String" itemvalue="contextvars" />
+            <item index="37" class="java.lang.String" itemvalue="iopath" />
+            <item index="38" class="java.lang.String" itemvalue="widgetsnbextension" />
+            <item index="39" class="java.lang.String" itemvalue="matplotlib" />
+            <item index="40" class="java.lang.String" itemvalue="dask" />
+            <item index="41" class="java.lang.String" itemvalue="msgpack" />
+            <item index="42" class="java.lang.String" itemvalue="idna" />
+            <item index="43" class="java.lang.String" itemvalue="scikit-image" />
+            <item index="44" class="java.lang.String" itemvalue="yacs" />
+            <item index="45" class="java.lang.String" itemvalue="rsa" />
+            <item index="46" class="java.lang.String" itemvalue="decorator" />
+            <item index="47" class="java.lang.String" itemvalue="jupyter-core" />
+            <item index="48" class="java.lang.String" itemvalue="networkx" />
+            <item index="49" class="java.lang.String" itemvalue="Pillow-SIMD" />
+            <item index="50" class="java.lang.String" itemvalue="cloudpickle" />
+            <item index="51" class="java.lang.String" itemvalue="awscli" />
+            <item index="52" class="java.lang.String" itemvalue="numpy" />
+            <item index="53" class="java.lang.String" itemvalue="glog" />
+            <item index="54" class="java.lang.String" itemvalue="importlib-metadata" />
+            <item index="55" class="java.lang.String" itemvalue="Jinja2" />
+            <item index="56" class="java.lang.String" itemvalue="mkl-fft" />
+            <item index="57" class="java.lang.String" itemvalue="fvcore" />
+            <item index="58" class="java.lang.String" itemvalue="tensorboard-plugin-wit" />
+            <item index="59" class="java.lang.String" itemvalue="pyOpenSSL" />
+            <item index="60" class="java.lang.String" itemvalue="PyWavelets" />
+            <item index="61" class="java.lang.String" itemvalue="prompt-toolkit" />
+            <item index="62" class="java.lang.String" itemvalue="urllib3" />
+            <item index="63" class="java.lang.String" itemvalue="Cython" />
+            <item index="64" class="java.lang.String" itemvalue="ipywidgets" />
+            <item index="65" class="java.lang.String" itemvalue="scipy" />
+            <item index="66" class="java.lang.String" itemvalue="thop" />
+            <item index="67" class="java.lang.String" itemvalue="tornado" />
+            <item index="68" class="java.lang.String" itemvalue="botocore" />
+            <item index="69" class="java.lang.String" itemvalue="asn1crypto" />
+            <item index="70" class="java.lang.String" itemvalue="opencv-python" />
+            <item index="71" class="java.lang.String" itemvalue="parso" />
+            <item index="72" class="java.lang.String" itemvalue="ruamel.yaml" />
+            <item index="73" class="java.lang.String" itemvalue="ipython" />
+            <item index="74" class="java.lang.String" itemvalue="torch" />
+            <item index="75" class="java.lang.String" itemvalue="prometheus-client" />
+            <item index="76" class="java.lang.String" itemvalue="mistune" />
+            <item index="77" class="java.lang.String" itemvalue="tqdm" />
+            <item index="78" class="java.lang.String" itemvalue="s3transfer" />
+            <item index="79" class="java.lang.String" itemvalue="toolz" />
+            <item index="80" class="java.lang.String" itemvalue="colorama" />
+            <item index="81" class="java.lang.String" itemvalue="jupyter-console" />
+            <item index="82" class="java.lang.String" itemvalue="tensorboardX" />
+            <item index="83" class="java.lang.String" itemvalue="grpcio" />
+            <item index="84" class="java.lang.String" itemvalue="Pillow" />
+          </list>
+        </value>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>

+ 6 - 0
.idea/inspectionProfiles/profiles_settings.xml

@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>

+ 7 - 0
.idea/misc.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Black">
+    <option name="sdkName" value="django" />
+  </component>
+  <component name="ProjectRootManager" version="2" project-jdk-name="django" project-jdk-type="Python SDK" />
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/PythonProject.iml" filepath="$PROJECT_DIR$/.idea/PythonProject.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$/auto_ui" vcs="Git" />
+  </component>
+</project>

BIN
__pycache__/bit_api.cpython-310.pyc


BIN
__pycache__/bit_api.cpython-311.pyc


BIN
__pycache__/utils.cpython-310.pyc


BIN
__pycache__/utils.cpython-311.pyc


+ 142 - 0
allcode.py

@@ -0,0 +1,142 @@
+import requests
+import time
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.support.ui import Select
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.common.exceptions import TimeoutException
+from utils import abbreviation_to_index
+from bit_api import *
+
+from multiprocessing import Process  # 替换 threading 为 multiprocessing
+# PROFILE_ID为环境ID
+res = openBrowser("947c8036facb4e8a935296e57f36652b") # 窗口ID从窗口配置界面中复制,或者api创建后返回
+
+
+def start_Ads_data(PROFILE_ID, data:list):
+    BASE_URL = "http://127.0.0.1:54345"
+    API_KEY = "704daf420f7244d08be51f61c987a232"  # 替换为你的 API Key
+    # 启动 Adspower
+    start_profile_url = f"{BASE_URL}/api/v1/browser/start?user_id={PROFILE_ID}&api_key={API_KEY}"
+    response = requests.get(start_profile_url).json()
+    # 检查是否启动成功
+    if response["code"] == 0:
+        print(f"Adspower 启动成功,返回信息")
+        chrome_driver_path = response["data"]["webdriver"]  # 驱动地址
+        debugger_address = response["data"]["ws"]["selenium"]
+        print(chrome_driver_path)
+    else:
+        print(f"启动失败: {response['msg']}")
+        exit(1)
+    # 打开 Selenium -> Adspower
+    options = webdriver.ChromeOptions()
+    options.add_experimental_option("debuggerAddress", debugger_address)
+    web = webdriver.Chrome(service=Service(chrome_driver_path),options=options)
+    web.implicitly_wait(30)
+    # 访问页面
+    web.get("https://claimform.savingsclubsettlement.com/consumerb-claimants")
+    print("当前页面标题:", web.title)
+    time.sleep(10)
+    print("等待验证码识别")
+    try:
+        iframe = WebDriverWait(web, 10).until(
+            EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[src*='google.com/recaptcha']"))
+        )
+    except TimeoutException:
+        print("未找到reCAPTCHA iframe")
+        exit()
+    while True:
+        try:
+            # 等待直到响应值非空
+            WebDriverWait(web, 10).until(
+                lambda d: d.find_element(By.ID, 'g-recaptcha-response').get_attribute('value') != ''
+            )
+            print("reCAPTCHA验证成功")
+            break
+        except TimeoutException:
+            print("验证未完成或失败")
+        finally:
+            web.switch_to.default_content()  # 切回主页面
+
+    web.switch_to.default_content()
+    # 姓名
+    web.find_element(By.XPATH, "//input[@type='text']").send_keys(data[1])
+    # 删除
+    # web.find_element(By.XPATH, "//div[3]/div/div/input").send_keys("测试数据")
+    # street
+    web.find_element(By.ID,"street1").send_keys(data[2])
+    # city
+    web.find_element(By.ID, "city").send_keys(data[3])
+    time.sleep(1)
+    # 选择州
+
+
+    Select(web.find_element(By.XPATH, "(.//*[normalize-space(text()) and normalize-space(.)='*'])[5]/preceding::select[1]")).select_by_index(abbreviation_to_index[data[4]])
+    # 选择code
+    web.find_element(By.ID, "zip").send_keys(data[5])
+    time.sleep(1)
+    # web.find_element(By.XPATH, "//div[6]/div/div/input").send_keys("测试数据")
+    # 邮箱
+    web.find_element(By.ID, "email").send_keys(data[0])
+    # 确认
+    web.find_element(By.XPATH, "//input[@type='checkbox']").click()
+    # 金额
+    web.find_element(By.XPATH, "//input[@value='$501-$1,000']").click()
+    # 签名
+    web.find_element(By.ID, "signature").send_keys(data[1])
+    submit_button = WebDriverWait(web, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[@type='submit']")))
+    submit_button.click()
+    time.sleep(3)
+    web.delete_all_cookies()
+    # 清除 LocalStorage 和 SessionStorage
+    web.execute_script("window.localStorage.clear();")
+    web.execute_script("window.sessionStorage.clear();")
+    web.quit()
+    time.sleep(3)
+    print("完成一次")
+
+
+def read_txt_file(file_path):
+
+    data = []
+
+    try:
+        with open(file_path, 'r', encoding='utf-8') as file:
+            for line in file:
+                # 移除首尾空白字符并按制表符分割
+                cleaned_line = line.strip()
+                if not cleaned_line:  # 跳过空行
+                    continue
+
+                fields = cleaned_line.split('\t')
+
+                # 验证字段数量(示例数据每行6个字段)
+                if len(fields) != 6:
+                    print(f"警告:第 {len(data) + 1} 行字段数量异常: {len(fields)}")
+                    continue
+
+                data.append(fields)
+
+    except FileNotFoundError:
+        print(f"错误:文件 {file_path} 未找到")
+        return None
+    except Exception as e:
+        print(f"读取文件时发生错误: {str(e)}")
+        return None
+
+    return data
+
+
+def cont():
+    datas = read_txt_file("generated_data.txt")
+    # TODO:进程池
+    for data in datas:
+        print(data)
+        start_Ads_data('kvgnckt', data)
+        print("进入下次循环")
+    return 0
+
+if __name__ == '__main__':
+    cont()

+ 422 - 0
allcode_by.py

@@ -0,0 +1,422 @@
+import requests
+import time
+import tkinter as tk
+from tkinter import ttk, filedialog, messagebox
+import json
+import os
+import threading
+import logging
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.support.ui import Select
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.common.exceptions import TimeoutException
+from utils import abbreviation_to_index
+from bit_api import *
+from multiprocessing import Process
+
+# 设置日志
+logging.basicConfig(level=logging.INFO)
+logger = logging.getLogger(__name__)
+
+# 自定义日志处理器,用于将日志输出到 GUI 的文本框
+class TextHandler(logging.Handler):
+    def __init__(self, text_widget, adjust_height_callback):
+        super().__init__()
+        self.text_widget = text_widget
+        self.adjust_height_callback = adjust_height_callback
+
+    def emit(self, record):
+        msg = self.format(record)
+        self.text_widget.insert(tk.END, msg + '\n')
+        self.text_widget.see(tk.END)
+        self.adjust_height_callback()
+
+# GUI 类
+class AdsDataGUI:
+    def __init__(self, root):
+        self.root = root
+        self.root.title("自动化注册 - BitBrowser")
+
+        # 初始化运行状态
+        self.running = False
+        self.current_web = None  # 用于存储当前 Selenium WebDriver 实例
+
+        # 加载保存的配置
+        self.config_file = "config.json"
+        self.load_config()
+
+        # 参数输入框
+        self.params = {
+            "WINDOW_ID": tk.StringVar(value=self.config.get("WINDOW_ID", "947c8036facb4e8a935296e57f36652b")),
+            "BASE_URL": tk.StringVar(value=self.config.get("BASE_URL", "http://127.0.0.1:54345")),
+            "API_KEY": tk.StringVar(value=self.config.get("API_KEY", "704daf420f7244d08be51f61c987a232")),
+            "FILE_PATH": tk.StringVar(value=self.config.get("FILE_PATH", "generated_data.txt")),
+            "AMOUNT": tk.StringVar(value=self.config.get("AMOUNT", "$501-$1,000")),
+            "SLEEP_TIME_1": tk.DoubleVar(value=self.config.get("SLEEP_TIME_1", 10.0)),  # 验证码等待时间
+            "SLEEP_TIME_2": tk.DoubleVar(value=self.config.get("SLEEP_TIME_2", 3.0)),   # 提交后等待时间
+            "SLEEP_TIME_3": tk.DoubleVar(value=self.config.get("SLEEP_TIME_3", 10.0)),  # 关闭后等待时间
+            "NO_SANDBOX": tk.BooleanVar(value=self.config.get("NO_SANDBOX", True)),
+            "DISABLE_DEV_SHM": tk.BooleanVar(value=self.config.get("DISABLE_DEV_SHM", True)),
+            "START_MAXIMIZED": tk.BooleanVar(value=self.config.get("START_MAXIMIZED", True))
+        }
+
+        # 创建主框架
+        self.main_frame = ttk.Frame(self.root)
+        self.main_frame.pack(fill=tk.BOTH, expand=True)
+
+        # 创建画布和滚动条
+        self.canvas = tk.Canvas(self.main_frame)
+        self.scrollbar = ttk.Scrollbar(self.main_frame, orient=tk.VERTICAL, command=self.canvas.yview)
+        self.scrollable_frame = ttk.Frame(self.canvas)
+
+        self.scrollable_frame.bind(
+            "<Configure>",
+            lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
+        )
+
+        self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
+        self.canvas.configure(yscrollcommand=self.scrollbar.set)
+
+        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
+
+        # 创建 GUI 元素(中文界面)
+        row = 0
+        tk.Label(self.scrollable_frame, text="自动化注册 - BitBrowser", font=("Arial", 16)).grid(row=row, column=0, columnspan=3, pady=10)
+        row += 1
+
+        # 参数输入框
+        labels = [
+            ("窗口 ID:", self.params["WINDOW_ID"]),
+            ("API 地址:", self.params["BASE_URL"]),
+            ("API 密钥:", self.params["API_KEY"]),
+            ("文件路径:", self.params["FILE_PATH"]),
+            ("金额范围:", self.params["AMOUNT"]),
+            ("等待时间 1 (验证码, 秒):", self.params["SLEEP_TIME_1"]),
+            ("等待时间 2 (提交后, 秒):", self.params["SLEEP_TIME_2"]),
+            ("等待时间 3 (关闭后, 秒):", self.params["SLEEP_TIME_3"])
+        ]
+
+        for label_text, var in labels:
+            tk.Label(self.scrollable_frame, text=label_text).grid(row=row, column=0, sticky="e", padx=5, pady=5)
+            if label_text == "金额范围:":
+                amount_options = ["$0-$500", "$501-$1,000", "$1,001-$5,000", "超过 $5,000"]
+                ttk.Combobox(self.scrollable_frame, textvariable=var, values=amount_options, state="readonly").grid(row=row, column=1, padx=5, pady=5, sticky="ew")
+            else:
+                tk.Entry(self.scrollable_frame, textvariable=var).grid(row=row, column=1, padx=5, pady=5, sticky="ew")
+            if label_text == "文件路径:":
+                tk.Button(self.scrollable_frame, text="浏览", command=self.browse_file).grid(row=row, column=2, padx=5, pady=5)
+            row += 1
+
+        tk.Checkbutton(self.scrollable_frame, text="无沙盒模式", variable=self.params["NO_SANDBOX"]).grid(row=row, column=0, columnspan=3, pady=5)
+        row += 1
+        tk.Checkbutton(self.scrollable_frame, text="禁用 Dev SHM", variable=self.params["DISABLE_DEV_SHM"]).grid(row=row, column=0, columnspan=3, pady=5)
+        row += 1
+        tk.Checkbutton(self.scrollable_frame, text="启动时最大化", variable=self.params["START_MAXIMIZED"]).grid(row=row, column=0, columnspan=3, pady=5)
+        row += 1
+
+        self.status_label = tk.Label(self.scrollable_frame, text="状态: 就绪", fg="blue")
+        self.status_label.grid(row=row, column=0, columnspan=3, pady=10)
+        row += 1
+
+        tk.Label(self.scrollable_frame, text="日志输出:").grid(row=row, column=0, columnspan=3, pady=5)
+        row += 1
+
+        # 日志窗口,初始高度为 0,动态调整
+        self.log_frame = ttk.Frame(self.scrollable_frame)
+        self.log_frame.grid(row=row, column=0, columnspan=3, padx=5, pady=5, sticky="nsew")
+        self.log_text = tk.Text(self.log_frame, height=0, width=60, wrap=tk.WORD)
+        self.log_scrollbar = ttk.Scrollbar(self.log_frame, orient=tk.VERTICAL, command=self.log_text.yview)
+        self.log_text.configure(yscrollcommand=self.log_scrollbar.set)
+        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
+        self.log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
+        row += 1
+
+        # 按钮框架
+        button_frame = ttk.Frame(self.scrollable_frame)
+        button_frame.grid(row=row, column=0, columnspan=3, pady=10)
+        tk.Button(button_frame, text="开始", command=self.start_script).pack(side=tk.LEFT, padx=5)
+        tk.Button(button_frame, text="停止", command=self.stop_script).pack(side=tk.LEFT, padx=5)
+        tk.Button(button_frame, text="清除日志", command=self.clear_log).pack(side=tk.LEFT, padx=5)
+        tk.Button(button_frame, text="保存参数", command=self.save_config).pack(side=tk.LEFT, padx=5)
+        row += 1
+
+        # 设置列权重,使输入框宽度自适应
+        for i in range(3):
+            self.scrollable_frame.grid_columnconfigure(i, weight=1)
+
+        # 设置日志处理器
+        self.log_handler = TextHandler(self.log_text, self.adjust_log_height)
+        logger.addHandler(self.log_handler)
+
+        # 保存配置时绑定关闭事件
+        self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
+
+        # 调整窗口大小以适应内容
+        self.root.update_idletasks()
+        self.root.minsize(self.scrollable_frame.winfo_reqwidth(), self.scrollable_frame.winfo_reqheight())
+
+    def adjust_log_height(self):
+        """动态调整日志框高度"""
+        content = self.log_text.get("1.0", tk.END).strip()
+        if not content:
+            self.log_text.configure(height=0)
+        else:
+            lines = content.count('\n') + 1
+            max_height = 20
+            new_height = min(lines, max_height)
+            self.log_text.configure(height=new_height)
+
+    def load_config(self):
+        """加载配置文件"""
+        self.config = {}
+        if os.path.exists(self.config_file):
+            try:
+                with open(self.config_file, 'r', encoding='utf-8') as f:
+                    self.config = json.load(f)
+            except Exception as e:
+                logger.error(f"无法加载配置文件: {str(e)}")
+
+    def save_config(self):
+        """保存配置"""
+        config = {
+            "WINDOW_ID": self.params["WINDOW_ID"].get(),
+            "BASE_URL": self.params["BASE_URL"].get(),
+            "API_KEY": self.params["API_KEY"].get(),
+            "FILE_PATH": self.params["FILE_PATH"].get(),
+            "AMOUNT": self.params["AMOUNT"].get(),
+            "SLEEP_TIME_1": self.params["SLEEP_TIME_1"].get(),
+            "SLEEP_TIME_2": self.params["SLEEP_TIME_2"].get(),
+            "SLEEP_TIME_3": self.params["SLEEP_TIME_3"].get(),
+            "NO_SANDBOX": self.params["NO_SANDBOX"].get(),
+            "DISABLE_DEV_SHM": self.params["DISABLE_DEV_SHM"].get(),
+            "START_MAXIMIZED": self.params["START_MAXIMIZED"].get()
+        }
+        try:
+            with open(self.config_file, 'w', encoding='utf-8') as f:
+                json.dump(config, f, indent=4, ensure_ascii=False)
+            messagebox.showinfo("提示", "参数已保存!")
+        except Exception as e:
+            logger.error(f"无法保存配置文件: {str(e)}")
+            messagebox.showerror("错误", f"无法保存参数: {str(e)}")
+
+    def on_closing(self):
+        """关闭窗口时保存配置"""
+        self.save_config()
+        self.root.destroy()
+
+    def browse_file(self):
+        """文件选择器"""
+        file_path = filedialog.askopenfilename(filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")])
+        if file_path:
+            self.params["FILE_PATH"].set(file_path)
+
+    def clear_log(self):
+        """清除日志"""
+        self.log_text.delete(1.0, tk.END)
+        self.adjust_log_height()
+
+    def start_script(self):
+        if self.running:
+            messagebox.showinfo("提示", "脚本已经在运行!")
+            return
+        self.running = True
+        self.status_label.config(text="状态: 运行中", fg="green")
+        threading.Thread(target=self.run_script, daemon=True).start()
+
+    def stop_script(self):
+        self.running = False
+        self.status_label.config(text="状态: 已停止", fg="red")
+        # 立即关闭当前浏览器
+        if self.current_web:
+            try:
+                self.current_web.quit()
+                logger.info("因用户停止脚本,关闭 Selenium 驱动...")
+            except Exception as e:
+                logger.error(f"关闭 Selenium 驱动失败: {str(e)}")
+            self.current_web = None
+
+    def run_script(self):
+        try:
+            logger.info("开始运行脚本...")
+            datas = read_txt_file(self.params["FILE_PATH"].get())
+            if not datas:
+                self.status_label.config(text="状态: 错误 - 文件未找到", fg="red")
+                logger.error("文件未找到或为空")
+                return
+
+            logger.info(f"从文件中读取了 {len(datas)} 条记录")
+            for data in datas:
+                if not self.running:
+                    logger.info("用户停止了脚本")
+                    break
+                self.status_label.config(text=f"状态: 正在处理 {data[0]}", fg="green")
+                logger.info(f"处理记录: {data}")
+                # 添加重试机制
+                max_retries = 3
+                for attempt in range(max_retries):
+                    if not self.running:
+                        logger.info("用户停止了脚本")
+                        break
+                    try:
+                        start_Ads_data(
+                            self,
+                            data,
+                            window_id=self.params["WINDOW_ID"].get(),
+                            sleep_times={
+                                "sleep1": self.params["SLEEP_TIME_1"].get(),
+                                "sleep2": self.params["SLEEP_TIME_2"].get(),
+                                "sleep3": self.params["SLEEP_TIME_3"].get()
+                            },
+                            chrome_options={
+                                "no_sandbox": self.params["NO_SANDBOX"].get(),
+                                "disable_dev_shm": self.params["DISABLE_DEV_SHM"].get(),
+                                "start_maximized": self.params["START_MAXIMIZED"].get()
+                            },
+                            amount=self.params["AMOUNT"].get()
+                        )
+                        break  # 成功则跳出重试循环
+                    except Exception as e:
+                        if not self.running:
+                            logger.info("用户停止了脚本")
+                            break
+                        if attempt < max_retries - 1:
+                            logger.warning(f"尝试 {attempt + 1} 失败: {str(e)},等待 10 秒后重试...")
+                            time.sleep(10)
+                        else:
+                            raise e
+                logger.info("完成一条记录")
+            self.status_label.config(text="状态: 已完成", fg="blue")
+            logger.info("脚本运行完成")
+        except Exception as e:
+            self.status_label.config(text=f"状态: 错误 - {str(e)}", fg="red")
+            logger.error(f"运行脚本时出错: {str(e)}")
+        finally:
+            self.running = False
+            self.current_web = None
+
+def start_Ads_data(gui, data, window_id, sleep_times, chrome_options, amount):
+    # 使用 BitBrowser 窗口 ID
+    res = openBrowser(window_id)
+    
+    # 调试:打印 openBrowser 的返回值
+    logger.info(f"openBrowser Response: {res}")
+    
+    # 检查返回值是否包含 'data' 键
+    if 'data' not in res:
+        logger.error("BitBrowser API 返回中没有 'data' 键")
+        raise Exception("BitBrowser API 返回中没有 'data' 键")
+    
+    driverPath = res['data']['driver']
+    debuggerAddress = res['data']['http']
+    
+    if res["success"] is True:
+        logger.info("启动成功,返回信息")
+    else:
+        logger.error("启动失败")
+        raise Exception("启动失败")
+    
+    chrome_opts = webdriver.ChromeOptions()
+    chrome_opts.add_experimental_option("debuggerAddress", debuggerAddress)
+    
+    if chrome_options["no_sandbox"]:
+        chrome_opts.add_argument('--no-sandbox')
+    if chrome_options["disable_dev_shm"]:
+        chrome_opts.add_argument('--disable-dev-shm-usage')
+    if chrome_options["start_maximized"]:
+        chrome_opts.add_argument('--start-maximized')
+    
+    chrome_service = Service(driverPath)
+    web = webdriver.Chrome(service=chrome_service, options=chrome_opts)
+    web.implicitly_wait(30)
+    
+    # 将 web 实例保存到 GUI 对象,以便在停止时关闭
+    gui.current_web = web
+    
+    web.get("https://claimform.savingsclubsettlement.com/consumerb-claimants")
+    
+    original_window = web.current_window_handle
+    logger.info(f"当前页面标题: {web.title}")
+    
+    time.sleep(sleep_times["sleep1"])
+    logger.info("等待验证码识别")
+    
+    try:
+        iframe = WebDriverWait(web, 10).until(
+            EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[src*='google.com/recaptcha']"))
+        )
+    except TimeoutException:
+        logger.error("未找到reCAPTCHA iframe")
+        raise Exception("未找到reCAPTCHA iframe")
+    
+    while True:
+        try:
+            WebDriverWait(web, 10).until(
+                lambda d: d.find_element(By.ID, 'g-recaptcha-response').get_attribute('value') != ''
+            )
+            logger.info("reCAPTCHA验证成功")
+            break
+        except TimeoutException:
+            logger.warning("验证未完成或失败")
+        finally:
+            web.switch_to.default_content()
+    
+    web.switch_to.default_content()
+    web.find_element(By.XPATH, "//input[@type='text']").send_keys(data[1])
+    web.find_element(By.ID, "street1").send_keys(data[2])
+    web.find_element(By.ID, "city").send_keys(data[3])
+    time.sleep(1)
+    Select(web.find_element(By.XPATH, "(.//*[normalize-space(text()) and normalize-space(.)='*'])[5]/preceding::select[1]")).select_by_index(abbreviation_to_index[data[4]])
+    web.find_element(By.ID, "zip").send_keys(data[5])
+    time.sleep(1)
+    web.find_element(By.ID, "email").send_keys(data[0])
+    web.find_element(By.XPATH, "//input[@type='checkbox']").click()
+    web.find_element(By.XPATH, f"//input[@value='{amount}']").click()
+    web.find_element(By.ID, "signature").send_keys(data[1])
+    
+    submit_button = WebDriverWait(web, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[@type='submit']")))
+    submit_button.click()
+    time.sleep(sleep_times["sleep2"])
+    web.delete_all_cookies()
+    web.execute_script("window.localStorage.clear();")
+    web.execute_script("window.sessionStorage.clear();")
+    web.quit()
+    time.sleep(sleep_times["sleep3"])
+    
+    # 使用 BitBrowser 窗口 ID
+    closeBrowser(window_id)
+    
+    # 添加延迟,确保窗口完全关闭
+    logger.info("等待窗口完全关闭...")
+    time.sleep(sleep_times["sleep3"])
+    
+    logger.info("完成一次")
+
+def read_txt_file(file_path):
+    data = []
+    try:
+        with open(file_path, 'r', encoding='utf-8') as file:
+            for line in file:
+                cleaned_line = line.strip()
+                if not cleaned_line:
+                    continue
+                fields = cleaned_line.split('\t')
+                if len(fields) != 6:
+                    logger.warning(f"第 {len(data) + 1} 行字段数量异常: {len(fields)}")
+                    continue
+                data.append(fields)
+    except FileNotFoundError:
+        logger.error(f"文件 {file_path} 未找到")
+        return None
+    except Exception as e:
+        logger.error(f"读取文件时发生错误: {str(e)}")
+        return None
+    return data
+
+if __name__ == '__main__':
+    root = tk.Tk()
+    app = AdsDataGUI(root)
+    root.mainloop()

+ 70 - 0
bit_api.py

@@ -0,0 +1,70 @@
+import requests
+import json
+import time
+
+# 官方文档地址
+# https://doc2.bitbrowser.cn/jiekou/ben-di-fu-wu-zhi-nan.html
+
+# 此demo仅作为参考使用,以下使用的指纹参数仅是部分参数,完整参数请参考文档
+
+url = "http://127.0.0.1:54345"
+headers = {
+    'Content-Type': 'application/json',
+    'X-API-KEY': '704daf420f7244d08be51f61c987a232'  # 添加 API Token
+}
+
+def createBrowser():  # 创建或者更新窗口,指纹参数 browserFingerPrint 如没有特定需求,只需要指定下内核即可,如果需要更详细的参数,请参考文档
+    json_data = {
+        'name': 'google',  # 窗口名称
+        'remark': '',  # 备注
+        'proxyMethod': 2,  # 代理方式 2自定义 3 提取IP
+        # 代理类型  ['noproxy', 'http', 'https', 'socks5', 'ssh']
+        'proxyType': 'noproxy',
+        'host': '',  # 代理主机
+        'port': '',  # 代理端口
+        'proxyUserName': '',  # 代理账号
+        "browserFingerPrint": {  # 指纹对象
+            'coreVersion': '124'  # 内核版本,注意,win7/win8/winserver 2012 已经不支持112及以上内核了,无法打开
+        }
+    }
+
+    res = requests.post(f"{url}/browser/update",
+                        data=json.dumps(json_data), headers=headers).json()
+    browserId = res['data']['id']
+    print(browserId)
+    return browserId
+
+def updateBrowser():  # 更新窗口,支持批量更新和按需更新,ids 传入数组,单独更新只传一个id即可,只传入需要修改的字段即可,比如修改备注,具体字段请参考文档,browserFingerPrint指纹对象不修改,则无需传入
+    json_data = {'ids': ['5b9597e911284a23a2dca5caa2a658c0'],
+                 'remark': '我是一个备注', 'browserFingerPrint': {}}
+    res = requests.post(f"{url}/browser/update/partial",
+                        data=json.dumps(json_data), headers=headers).json()
+    print(res)
+
+def openBrowser(id):  # 直接指定ID打开窗口,也可以使用 createBrowser 方法返回的ID
+    json_data = {"id": f'{id}'}
+    res = requests.post(f"{url}/browser/open",
+                        data=json.dumps(json_data), headers=headers).json()
+    return res
+
+def closeBrowser(id):  # 关闭窗口
+    json_data = {'id': f'{id}'}
+    requests.post(f"{url}/browser/close",
+                  data=json.dumps(json_data), headers=headers).json()
+
+def deleteBrowser(id):  # 删除窗口
+    json_data = {'id': f'{id}'}
+    print(requests.post(f"{url}/browser/delete",
+          data=json.dumps(json_data), headers=headers).json())
+
+if __name__ == '__main__':
+    browser_id = createBrowser()
+    openBrowser(browser_id)
+
+    time.sleep(10)  # 等待10秒自动关闭窗口
+
+    closeBrowser(browser_id)
+
+    time.sleep(10)  # 等待10秒自动删掉窗口
+
+    deleteBrowser(browser_id)

+ 13 - 0
config.json

@@ -0,0 +1,13 @@
+{
+    "WINDOW_ID": "947c8036facb4e8a935296e57f36652b",
+    "BASE_URL": "http://127.0.0.1:54345",
+    "API_KEY": "704daf420f7244d08be51f61c987a232",
+    "FILE_PATH": "generated_data.txt",
+    "AMOUNT": "$501-$1,000",
+    "SLEEP_TIME_1": 10.0,
+    "SLEEP_TIME_2": 3.0,
+    "SLEEP_TIME_3": 10.0,
+    "NO_SANDBOX": true,
+    "DISABLE_DEV_SHM": true,
+    "START_MAXIMIZED": true
+}

BIN
ext.crx


+ 25 - 0
generated_data.txt

@@ -0,0 +1,25 @@
+ambertaylor@gmail.com	Amber Taylor	492 NE Cedar Ln	Denver	WA	33707
+emmasmith@ebaysee.com	Emma Smith	315 NE Oak Ave	Seattle	CA	90743
+sophiabrown@ebaysee.com	Sophia Brown	242 SE Main St	Denver	WA	98879
+johnsmith@gmail.com	John Smith	833 SE Oak Ave	Miami	OR	97881
+sophiawilson@ebaysee.com	Sophia Wilson	1030 NE Savannah Dr	Miami	FL	78289
+michaeltaylor@gmail.com	Michael Taylor	822 NE Pine Rd	Seattle	CO	33248
+sophiabrown@ebaysee.com	Sophia Brown	1021 SE Main St	Denver	WA	98334
+oliviataylor@yahoo.com	Olivia Taylor	443 SE Maple St	Denver	CO	90846
+emmabrown@yahoo.com	Emma Brown	711 NW Main St	Portland	WA	80196
+sophiabrown@gmail.com	Sophia Brown	1096 NE Pine Rd	Portland	WA	33126
+jamestaylor@yahoo.com	James Taylor	530 NE Main St	Seattle	CA	80104
+jamesdavis@hotmail.com	James Davis	250 NW Cedar Ln	Miami	TX	80439
+sophiasmith@gmail.com	Sophia Smith	318 SW Oak Ave	Bend	CA	80024
+oliviawilson@hotmail.com	Olivia Wilson	561 NE Pine Rd	Bend	TX	98925
+johntaylor@hotmail.com	John Taylor	306 NE Cedar Ln	Portland	TX	98728
+amberjohnson@hotmail.com	Amber Johnson	147 SE Savannah Dr	Denver	CA	80197
+jamesclark@ebaysee.com	James Clark	393 SW Main St	Austin	OR	33866
+sophiaclark@hotmail.com	Sophia Clark	1046 NE Cedar Ln	Portland	WA	80610
+emmadavis@yahoo.com	Emma Davis	1065 NE Pine Rd	Denver	WA	90191
+sophiabrown@hotmail.com	Sophia Brown	1041 SW Savannah Dr	Denver	FL	80917
+emmadavis@ebaysee.com	Emma Davis	461 SE Savannah Dr	Miami	TX	98325
+jameswilson@ebaysee.com	James Wilson	558 SE Oak Ave	Denver	OR	78799
+sophiaclark@gmail.com	Sophia Clark	832 NE Pine Rd	Miami	OR	78160
+williamtaylor@hotmail.com	William Taylor	637 NE Savannah Dr	Portland	CO	90150
+johnclark@gmail.com	John Clark	511 SW Maple St	Miami	TX	78254

+ 330 - 0
main.py

@@ -0,0 +1,330 @@
+import requests
+import time
+from selenium import webdriver
+from selenium.webdriver.common.by import By
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.support.ui import Select
+from selenium.webdriver.support import expected_conditions as EC
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.common.exceptions import TimeoutException
+from multiprocessing import Process  # 替换 threading 为 multiprocessing
+# PROFILE_ID为环境ID
+
+def start_Ads(PROFILE_ID):
+    BASE_URL = "http://127.0.0.1:54345"
+    API_KEY = "dc954750b89edd7e421639832bff3151"  # 替换为你的 API Key
+    # 启动 Adspower
+    start_profile_url = f"{BASE_URL}/api/v1/browser/start?user_id={PROFILE_ID}&api_key={API_KEY}"
+    response = requests.get(start_profile_url).json()
+
+    # 检查是否启动成功
+    if response["code"] == 0:
+        print(f"Adspower 启动成功,返回信息")
+        chrome_driver_path = response["data"]["webdriver"]
+        debugger_address = response["data"]["ws"]["selenium"]
+        print(chrome_driver_path)
+    else:
+        print(f"启动失败: {response['msg']}")
+        exit(1)
+    # 打开 Selenium -> Adspower
+    options = webdriver.ChromeOptions()
+    options.add_experimental_option("debuggerAddress", debugger_address)
+    web = webdriver.Chrome(service=Service("/Users/zangtuo/Library/Application Support/adspower_global/cwd_global/chrome_131/chromedriver.app/Contents/MacOS/chromedriver"),options=options)
+    web.implicitly_wait(30)
+    # 访问页面
+    web.get("https://claimform.savingsclubsettlement.com/consumerb-claimants")
+    print("当前页面标题:", web.title)
+    time.sleep(10)
+    print("等待验证码识别")
+    # while True:
+    #     try:
+    #         WebDriverWait(web, 5).until(
+    #             EC.frame_to_be_available_and_switch_to_it(
+    #                 (By.XPATH, "//iframe[contains(@title, 'recaptcha challenge')]"))
+    #         )
+    #         # 切换回主界面
+    #         # web.switch_to.default_content()
+    #         print("等待 10 秒后再次尝试")
+    #         time.sleep(10)
+    #     except TimeoutException:
+    #         print("验证码识别成功")
+    #         break  # 超时后退出循环,继续执行后续代码
+    try:
+        iframe = WebDriverWait(web, 10).until(
+            EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[src*='google.com/recaptcha']"))
+        )
+    except TimeoutException:
+        print("未找到reCAPTCHA iframe")
+        exit()
+    while True:
+        try:
+            # 等待直到响应值非空
+            WebDriverWait(web, 10).until(
+                lambda d: d.find_element(By.ID, 'g-recaptcha-response').get_attribute('value') != ''
+            )
+            print("reCAPTCHA验证成功")
+            break
+        except TimeoutException:
+            print("验证未完成或失败")
+        finally:
+            web.switch_to.default_content()  # 切回主页面
+    web.switch_to.default_content()
+    # 姓名
+    web.find_element(By.XPATH, "//input[@type='text']").send_keys("测试数据")  # Claimants Name: *
+    # 删除
+    web.find_element(By.XPATH, "//div[3]/div/div/input").send_keys("测试数据")  # Claimants Name: *
+    # street
+    web.find_element(By.ID,"street1").send_keys("测试数据")
+    # city
+    web.find_element(By.ID, "city").send_keys("测试数据")
+    # 选择州
+    Select(web.find_element(By.XPATH, "(.//*[normalize-space(text()) and normalize-space(.)='*'])[5]/preceding::select[1]")).select_by_index(37)
+    # 选择code
+    web.find_element(By.ID, "zip").send_keys("测试数据")
+    #
+    web.find_element(By.XPATH, "//div[6]/div/div/input").send_keys("测试数据")
+    # 邮箱
+    web.find_element(By.ID, "email").send_keys("8888888888@gmail.com")
+    # 确认
+    web.find_element(By.XPATH, "//input[@type='checkbox']").click()
+    # 金额
+    web.find_element(By.XPATH, "//input[@value='$501-$1,000']").click()
+    # 签名
+    web.find_element(By.ID, "signature").send_keys("测试数据")
+    # 切换frame,点击验证按钮
+    # WebDriverWait(web, 10).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH, "//iframe[contains(@title, 'reCAPTCHA')]")))
+    # recaptcha_anchor = WebDriverWait(web, 10).until(
+    #     EC.element_to_be_clickable((By.XPATH, "//span[@id='recaptcha-anchor']/div"))
+    # )
+    # recaptcha_anchor.click()
+    # # 验证码处理逻辑,切换页面
+    # EC.frame_to_be_available_and_switch_to_it((By.XPATH, "//iframe[contains(@title, 'reCAPTCHA')]"))
+    # # 测试
+    # EC.frame_to_be_available_and_switch_to_it((By.XPATH, "/html/body/div/div[4]/iframe"))
+    # # 验证码指令
+    # dataw = web.find_element(By.XPATH,"//*[@id='rc-imageselect']/div[2]/div[1]/div[1]/div/text()[1]")
+    # # 加载图片
+    # img = web.find_element(By.XPATH, "//div[@id='rc-imageselect-target']/table/tbody/tr/td[2]/div/div/img")
+    # 点击提交按钮
+    submit_button = WebDriverWait(web, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[@type='submit']")))
+    submit_button.click()
+    time.sleep(3)
+    web.quit()
+    print("完成一次")
+
+def start_Ads_data(PROFILE_ID, data:list):
+    BASE_URL = "http://127.0.0.1:54345"
+    API_KEY = "dc954750b89edd7e421639832bff3151"  # 替换为你的 API Key
+    # 启动 Adspower
+    start_profile_url = f"{BASE_URL}/api/v1/browser/start?user_id={PROFILE_ID}&api_key={API_KEY}"
+    response = requests.get(start_profile_url).json()
+    # 检查是否启动成功
+    if response["code"] == 0:
+        print(f"Adspower 启动成功,返回信息")
+        chrome_driver_path = response["data"]["webdriver"]
+        debugger_address = response["data"]["ws"]["selenium"]
+        print(chrome_driver_path)
+    else:
+        print(f"启动失败: {response['msg']}")
+        exit(1)
+    # 打开 Selenium -> Adspower
+    options = webdriver.ChromeOptions()
+    options.add_experimental_option("debuggerAddress", debugger_address)
+    web = webdriver.Chrome(service=Service("/Users/zangtuo/Library/Application Support/adspower_global/cwd_global/chrome_131/chromedriver.app/Contents/MacOS/chromedriver"),options=options)
+    web.implicitly_wait(30)
+    # 访问页面
+    web.get("https://claimform.savingsclubsettlement.com/consumerb-claimants")
+    print("当前页面标题:", web.title)
+    time.sleep(10)
+    print("等待验证码识别")
+    try:
+        iframe = WebDriverWait(web, 10).until(
+            EC.frame_to_be_available_and_switch_to_it((By.CSS_SELECTOR, "iframe[src*='google.com/recaptcha']"))
+        )
+    except TimeoutException:
+        print("未找到reCAPTCHA iframe")
+        exit()
+    while True:
+        try:
+            # 等待直到响应值非空
+            WebDriverWait(web, 10).until(
+                lambda d: d.find_element(By.ID, 'g-recaptcha-response').get_attribute('value') != ''
+            )
+            print("reCAPTCHA验证成功")
+            break
+        except TimeoutException:
+            print("验证未完成或失败")
+        finally:
+            web.switch_to.default_content()  # 切回主页面
+    web.switch_to.default_content()
+    # 姓名
+    web.find_element(By.XPATH, "//input[@type='text']").send_keys(data[1])
+    # 删除
+    # web.find_element(By.XPATH, "//div[3]/div/div/input").send_keys("测试数据")
+    # street
+    web.find_element(By.ID,"street1").send_keys(data[2])
+    # city
+    web.find_element(By.ID, "city").send_keys(data[3])
+    # 选择州
+    # TODO: 缺个逻辑根据州名判断点击哪个按钮
+    Select(web.find_element(By.XPATH, "(.//*[normalize-space(text()) and normalize-space(.)='*'])[5]/preceding::select[1]")).select_by_index(37)
+    # 选择code
+    web.find_element(By.ID, "zip").send_keys(data[5])
+    # web.find_element(By.XPATH, "//div[6]/div/div/input").send_keys("测试数据")
+    # 邮箱
+    web.find_element(By.ID, "email").send_keys(data[0])
+    # 确认
+    web.find_element(By.XPATH, "//input[@type='checkbox']").click()
+    # 金额
+    web.find_element(By.XPATH, "//input[@value='$501-$1,000']").click()
+    # 签名
+    web.find_element(By.ID, "signature").send_keys(data[1])
+    submit_button = WebDriverWait(web, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[@type='submit']")))
+    submit_button.click()
+    time.sleep(3)
+    web.quit()
+    time.sleep(3)
+    print("完成一次")
+
+
+def read_data(path):
+    records = []
+    with open(path, 'r', encoding='utf-8') as f:
+        lines = [line.strip() for line in f if line.strip()]
+
+    for i in range(0, len(lines), 2):
+        if i + 1 >= len(lines):
+            break
+        email_line = lines[i]
+        data_line = lines[i + 1]
+
+        # 处理电子邮件
+        email = email_line.replace('\t', '').strip()
+
+        # 分割数据行字段
+        fields = data_line.split('\t')
+        fields = [f.strip() for f in fields if f.strip()]
+
+        # 确保字段足够
+        if len(fields) < 5:
+            print(f"跳过字段不足的行: {data_line}")
+            continue
+        # 提取姓名等字段
+        name_parts = fields[:len(fields) - 5]
+        name = ' '.join(name_parts)
+        try:
+            address = fields[-5]
+            city = fields[-4]
+            state = fields[-3]
+            zip_code = fields[-2]
+            phone = fields[-1]
+        except IndexError as e:
+            print(f"字段提取错误: {e}, 行内容: {data_line}")
+            continue
+
+        records.append({
+            'email': email,
+            'name': name,
+            'address': address,
+            'city': city,
+            'state': state,
+            'zip': zip_code,
+            'phone': phone
+        })
+    return records
+
+def read_data(path):
+    records = []
+    with open(path, 'r', encoding='utf-8') as f:
+        lines = [line.strip() for line in f if line.strip()]
+
+    for i in range(0, len(lines), 2):
+        if i + 1 >= len(lines):
+            break
+        email_line = lines[i]
+        data_line = lines[i + 1]
+
+        # 处理电子邮件
+        email = email_line.replace('\t', '').strip()
+
+        # 分割数据行字段
+        fields = data_line.split('\t')
+        fields = [f.strip() for f in fields if f.strip()]
+
+        # 确保字段足够
+        if len(fields) < 5:
+            print(f"跳过字段不足的行: {data_line}")
+            continue
+        # 提取姓名等字段
+        name_parts = fields[:len(fields) - 5]
+        name = ' '.join(name_parts)
+        try:
+            address = fields[-5]
+            city = fields[-4]
+            state = fields[-3]
+            zip_code = fields[-2]
+            phone = fields[-1]
+        except IndexError as e:
+            print(f"字段提取错误: {e}, 行内容: {data_line}")
+            continue
+
+        records.append({
+            'email': email,
+            'name': name,
+            'address': address,
+            'city': city,
+            'state': state,
+            'zip': zip_code,
+            'phone': phone
+        })
+    return records
+def read_txt_file(file_path):
+    """
+    读取并解析制表符分隔的文本文件
+    :param file_path: 文件路径(例如:data.txt)
+    :return: 包含所有记录的列表,每个记录是字段列表
+    """
+    data = []
+
+    try:
+        with open(file_path, 'r', encoding='utf-8') as file:
+            for line in file:
+                # 移除首尾空白字符并按制表符分割
+                cleaned_line = line.strip()
+                if not cleaned_line:  # 跳过空行
+                    continue
+
+                fields = cleaned_line.split('\t')
+
+                # 验证字段数量(示例数据每行6个字段)
+                if len(fields) != 6:
+                    print(f"警告:第 {len(data) + 1} 行字段数量异常: {len(fields)}")
+                    continue
+
+                data.append(fields)
+
+    except FileNotFoundError:
+        print(f"错误:文件 {file_path} 未找到")
+        return None
+    except Exception as e:
+        print(f"读取文件时发生错误: {str(e)}")
+        return None
+
+    return data
+
+# start_Ads('kvavcrf')
+# path = '模拟数据.txt'
+#
+# records = read_data(path)
+# for record in records:
+#     start_Ads_data('kvavcrf',record)
+# print("全部完成")
+def cont():
+    datas = read_txt_file("generated_data.txt")
+    for data in datas:
+        start_Ads_data('kvavcrf', data)
+        print("进入下次循环")
+    return 0
+
+if __name__ == '__main__':
+    cont()

BIN
python-demo/.DS_Store


+ 73 - 0
python-demo/bit_api.py

@@ -0,0 +1,73 @@
+import requests
+import json
+import time
+
+# 官方文档地址
+# https://doc2.bitbrowser.cn/jiekou/ben-di-fu-wu-zhi-nan.html
+
+# 此demo仅作为参考使用,以下使用的指纹参数仅是部分参数,完整参数请参考文档
+
+url = "http://127.0.0.1:54345"
+headers = {'Content-Type': 'application/json'}
+
+
+def createBrowser():  # 创建或者更新窗口,指纹参数 browserFingerPrint 如没有特定需求,只需要指定下内核即可,如果需要更详细的参数,请参考文档
+    json_data = {
+        'name': 'google',  # 窗口名称
+        'remark': '',  # 备注
+        'proxyMethod': 2,  # 代理方式 2自定义 3 提取IP
+        # 代理类型  ['noproxy', 'http', 'https', 'socks5', 'ssh']
+        'proxyType': 'noproxy',
+        'host': '',  # 代理主机
+        'port': '',  # 代理端口
+        'proxyUserName': '',  # 代理账号
+        "browserFingerPrint": {  # 指纹对象
+            'coreVersion': '124'  # 内核版本,注意,win7/win8/winserver 2012 已经不支持112及以上内核了,无法打开
+        }
+    }
+
+    res = requests.post(f"{url}/browser/update",
+                        data=json.dumps(json_data), headers=headers).json()
+    browserId = res['data']['id']
+    print(browserId)
+    return browserId
+
+
+def updateBrowser():  # 更新窗口,支持批量更新和按需更新,ids 传入数组,单独更新只传一个id即可,只传入需要修改的字段即可,比如修改备注,具体字段请参考文档,browserFingerPrint指纹对象不修改,则无需传入
+    json_data = {'ids': ['93672cf112a044f08b653cab691216f0'],
+                 'remark': '我是一个备注', 'browserFingerPrint': {}}
+    res = requests.post(f"{url}/browser/update/partial",
+                        data=json.dumps(json_data), headers=headers).json()
+    print(res)
+
+
+def openBrowser(id):  # 直接指定ID打开窗口,也可以使用 createBrowser 方法返回的ID
+    json_data = {"id": f'{id}'}
+    res = requests.post(f"{url}/browser/open",
+                        data=json.dumps(json_data), headers=headers).json()
+    return res
+
+
+def closeBrowser(id):  # 关闭窗口
+    json_data = {'id': f'{id}'}
+    requests.post(f"{url}/browser/close",
+                  data=json.dumps(json_data), headers=headers).json()
+
+
+def deleteBrowser(id):  # 删除窗口
+    json_data = {'id': f'{id}'}
+    print(requests.post(f"{url}/browser/delete",
+          data=json.dumps(json_data), headers=headers).json())
+
+
+if __name__ == '__main__':
+    browser_id = createBrowser()
+    openBrowser(browser_id)
+
+    time.sleep(10)  # 等待10秒自动关闭窗口
+
+    closeBrowser(browser_id)
+
+    time.sleep(10)  # 等待10秒自动删掉窗口
+
+    deleteBrowser(browser_id)

+ 37 - 0
python-demo/bit_playwright.py

@@ -0,0 +1,37 @@
+from bit_api import *
+import time
+import asyncio
+from playwright.async_api import async_playwright, Playwright
+
+
+
+async def run(playwright: Playwright):
+
+  # /browser/open 接口会返回 selenium使用的http地址,以及webdriver的path,直接使用即可
+  browser_id = "1ce676a2ae0a41bcaf73c6934d8ff230" # 窗口ID从窗口配置界面中复制,或者api创建后返回
+  res = openBrowser(browser_id)
+  ws = res['data']['ws']
+  print("ws address ==>>> ", ws)
+
+  chromium = playwright.chromium
+  browser = await chromium.connect_over_cdp(ws)
+  default_context = browser.contexts[0]
+
+  print('new page and goto baidu')
+
+  page = await default_context.new_page()
+  await page.goto('https://baidu.com')
+
+  time.sleep(2)
+
+  print('clsoe page and browser')
+  await page.close()
+
+  time.sleep(2)
+  closeBrowser(browser_id)
+
+async def main():
+    async with async_playwright() as playwright:
+      await run(playwright)
+
+asyncio.run(main())

+ 36 - 0
python-demo/bit_selenium.py

@@ -0,0 +1,36 @@
+from selenium import webdriver
+from selenium.common.exceptions import TimeoutException
+from selenium.webdriver.common.keys import Keys
+from selenium.webdriver.chrome.options import Options
+from bit_api import *
+from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
+from selenium.webdriver.chrome.service import Service
+from selenium.webdriver.common.by import By
+
+# /browser/open 接口会返回 selenium使用的http地址,以及webdriver的path,直接使用即可
+res = openBrowser("947c8036facb4e8a935296e57f36652b") # 窗口ID从窗口配置界面中复制,或者api创建后返回
+
+print(res)
+
+driverPath = res['data']['driver']
+debuggerAddress = res['data']['http']
+
+# selenium 连接代码
+chrome_options = webdriver.ChromeOptions()
+chrome_options.add_experimental_option("debuggerAddress", debuggerAddress)
+
+chrome_service = Service(driverPath)
+driver = webdriver.Chrome(service=chrome_service, options=chrome_options)
+
+# 以下为PC模式下,打开baidu,输入 BitBrowser,点击搜索的案例
+driver.get('https://www.baidu.com/')
+
+input = driver.find_element(By.CLASS_NAME, 's_ipt')
+input.send_keys('BitBrowser')
+
+print('before click...')
+
+btn = driver.find_element(By.CLASS_NAME, 's_btn')
+btn.click()
+
+print('after click')

+ 0 - 0
test.py


+ 15 - 0
utils.py

@@ -0,0 +1,15 @@
+abbreviation_to_index = {
+    "AL": 1, "AK": 2, "AZ": 3, "AR": 4, "CA": 5,
+    "CO": 6, "CT": 7, "DE": 8, "FL": 9, "GA": 10,
+    "HI": 11, "ID": 12, "IL": 13, "IN": 14, "IA": 15,
+    "KS": 16, "KY": 17, "LA": 18, "ME": 19, "MD": 20,
+    "MA": 21, "MI": 22, "MN": 23, "MS": 24, "MO": 25,
+    "MT": 26, "NE": 27, "NV": 28, "NH": 29, "NJ": 30,
+    "NM": 31, "NY": 32, "NC": 33, "ND": 34, "OH": 35,
+    "OK": 36, "OR": 37, "PA": 38, "RI": 39, "SC": 40,
+    "SD": 41, "TN": 42, "TX": 43, "UT": 44, "VT": 45,
+    "VA": 46, "WA": 47, "WV": 48, "WI": 49, "WY": 50,
+    "DC": 51, "AS": 52, "FM": 53, "GU": 54, "MH": 55,
+    "MP": 56, "PR": 57, "PW": 58, "VI": 59
+}
+