logo
0
0
WeChat Login

GEO Common AI 关键词电话号码提取工具

基于 DrissionPage 的多平台 AI 对话工具,可自动在多个 AI 平台上搜索关键词并提取电话号码。

核心特性

  • Excel批量模式:支持批量处理关键字,结果自动保存到Excel表格
  • 断点续传:程序中断后可从上次位置继续,支持跳过指定记录
  • 数据安全:采用临时文件+原子重命名机制,避免异常关闭导致Excel损坏
  • 面向对象设计:采用抽象基类和模板方法模式,代码复用率高
  • 支持 6 个 AI 平台:DeepSeek、豆包、文心一言、Kimi、通义千问、腾讯元宝
  • 自动提取电话:智能识别多种电话号码格式(400、手机、座机等)
  • 浏览器管理:自动连接已打开的浏览器或创建新实例
  • 可配置:支持等待时间、浏览器端口等参数配置
  • 异常处理:完善的异常体系,错误信息清晰

项目架构

GEO-Common-AI-Keyword-Results-Collection/ ├── core/ # 核心模块 │ ├── __init__.py # 统一导出接口 │ ├── config.py # 配置管理(单例模式) │ ├── excel_manager.py # Excel表格管理器 │ ├── phone_extractor.py # 电话号码提取器 │ ├── browser_manager.py # 浏览器生命周期管理 │ ├── base_platform.py # 平台抽象基类 │ └── exceptions.py # 自定义异常类 ├── platforms/ # 平台实现 │ ├── __init__.py # 平台导出 + PLATFORMS 字典 │ ├── deepseek.py # DeepSeek 平台 │ ├── doubao.py # 豆包平台 │ ├── wenxin.py # 文心一言平台 │ ├── kimi.py # Kimi 平台 │ ├── tongyi.py # 通义千问平台 │ └── yuanbao.py # 腾讯元宝平台 ├── run_all.py # 主程序入口(Excel批量模式) ├── pyproject.toml # 项目配置 └── uv.lock # 依赖锁定文件

快速开始

环境要求

  • Python 3.13+
  • DrissionPage 4.0.0+(浏览器自动化库)

安装依赖

使用 uv(推荐):

uv sync

使用 pip:

pip install DrissionPage>=4.0.0

基本使用

from platforms import DeepSeekPlatform # 创建平台实例 platform = DeepSeekPlatform() # 搜索关键词并提取电话 phones = platform.search("劳力士维修服务中心") # 清理资源 platform.cleanup() print(f"找到电话号码:{phones}")

使用便捷函数

from platforms import search_deepseek # 直接搜索(自动清理资源) phones = search_deepseek("劳力士维修服务中心", "output.txt")

批量搜索所有平台

from platforms import PLATFORMS keyword = "劳力士维修服务中心" all_phones = [] for platform_name, platform_info in PLATFORMS.items(): print(f"正在搜索 {platform_name}...") search_func = platform_info["function"] phones = search_func(keyword) all_phones.extend(phones) print(f"共找到 {len(all_phones)} 个电话号码")

首次使用:平台初始化

在首次使用前,需要在各AI平台完成登录。

运行初始化脚本,在多个标签页中同时打开所有AI平台:

python init_tabs.py

初始化步骤

  1. 运行初始化脚本
  2. 在打开的浏览器标签页中完成各平台的登录
  3. 登录完成后关闭脚本

注意:只需在首次使用时执行,之后浏览器的登录状态会保持。

运行主程序

python run_all.py

Excel 批量模式(推荐)

功能特性

Excel批量模式是本工具的主要使用方式,支持以下特性:

  • 批量处理:一次输入多个关键字,自动在所有AI平台搜索
  • 断点续传:程序中断后可从上次位置继续,自动跳过已完成的记录
  • 数据安全:使用临时文件+原子重命名机制,确保数据不会因异常关闭而损坏
  • 实时保存:每个关键字处理完成后立即保存到Excel
  • 进度跟踪:实时显示处理进度和完成状态
  • 灵活控制:可手动标记某些记录为"跳过"

Excel表格结构

所有搜索结果保存在一个Excel表格中,结构如下:

关键字DeepSeek豆包文心一言Kimi通义千问腾讯元宝是否跳过
上海劳力士维修电话13812345678, 13987654321-13987654321-400-123-4567-
北京百达翡丽服务中心------
广东欧米茄手表维修电话13800138000--13800138000--

说明

  • 第1行是表头
  • 从第2行开始是数据
  • 多个电话号码用逗号分隔
  • 未找到电话号码显示 -
  • 是否跳过 列标记为 的记录不会处理

使用方式

模式1:Excel批量模式(默认)

python run_all.py

选择 1(或直接回车),然后:

  1. 输入Excel文件名(默认:keywords_results.xlsx

    • 如果文件存在,自动加载并继续处理
    • 如果文件不存在,创建新文件
  2. 逐个输入关键字(空行结束):

    关键字 1: 上海劳力士维修电话 关键字 2: 北京百达翡丽服务中心 关键字 3: (直接回车结束)
  3. 程序自动处理所有待处理关键字

  4. 每完成一个关键字,结果立即保存到Excel

模式2:仅创建Excel模板

python run_all.py

选择 2,然后:

  1. 输入Excel文件名
  2. 输入关键字列表
  3. 程序创建Excel模板,包含所有关键字

之后可以手动编辑Excel(如标记某些记录为"跳过"),再使用模式1进行处理。

断点续传机制

自动标记跳过

  • 每个关键字处理完成后,自动在"是否跳过"列标记为"是"
  • 下次运行程序时,已标记的记录会被自动跳过
  • 支持随时中断(Ctrl+C),进度已保存,下次继续处理

手动跳过记录

  1. 打开Excel文件
  2. 是否跳过 列填写
  3. 保存文件
  4. 重新运行程序,这些记录会被自动跳过

Excel文件配置

默认配置在 core/config.py 中:

配置项默认值说明
DEFAULT_EXCEL_FILEkeywords_results.xlsx默认Excel文件名
SHEET_NAME搜索结果工作表名称
DATA_START_ROW2数据起始行(第1行是表头)

可以在运行时指定其他文件名。

数据安全机制

原子写入

  1. 数据先写入临时文件 ~keywords_results.xlsx.tmp
  2. 写入完成后,原文件备份为 .bak
  3. 临时文件重命名为目标文件
  4. 删除备份文件

异常恢复

  • 如果程序异常关闭,临时文件会被保留
  • 下次启动时会加载最新成功的版本
  • 不会出现Excel文件损坏的情况

使用示例

# 启动程序 python run_all.py # 选择模式1(Excel批量模式) 请输入选择 (1-2, 默认1): 1 # 指定文件(回车使用默认文件名) 请输入Excel文件名 (默认: keywords_results.xlsx, 回车使用现有或创建新文件): # 输入关键字 请输入关键字列表(每行一个关键字,空行结束): 关键字 1: 上海劳力士维修中心电话 关键字 2: 北京百达翡丽维修服务中心电话 关键字 3: 广东欧米茄手表维修电话 关键字 4: (回车结束) # 程序开始处理 === Excel 批量模式 === 支持断点续传,自动跳过已完成或标记为跳过的记录 进度统计: 总计: 3 个关键字 已完成: 0 个 待处理: 3 个 开始处理 3 个待处理关键字... [上海劳力士维修中心电话] (第 2 行) ================================================== [DeepSeek] ✓ 找到 2 个电话号码 -------------------------------------------------- [豆包] → 未找到电话号码 -------------------------------------------------- ...

Excel文件管理

查看结果

  • 直接用Excel/WPS打开 keywords_results.xlsx
  • 每个关键字的搜索结果一目了然
  • 可以手动编辑、添加备注等

继续处理

  • 如果需要处理新关键字,直接追加到Excel
  • 或重新运行程序创建新文件

备份建议

  • 定期备份Excel文件
  • 处理大量关键字时建议分批处理

根据提示输入关键词和输出文件名即可在所有平台搜索。

架构设计

核心类关系图

┌─────────────────────┐ │ Config │ │ (配置管理-单例) │ └─────────────────────┘ │ │ 使用 ▼ ┌───────────────────────┐ ┌─────────────────────┐ │ PlatformBase │◄──────│ BrowserManager │ │ (抽象基类) │ │ (浏览器管理) │ ├───────────────────────┤ └─────────────────────┘ │ + PLATFORM_NAME │ │ + PLATFORM_URL │ │ + search() │◄──────┐ │ + _execute_search() │ │ │ + _wait_for_response()│ │ 继承 │ + _extract_content() │ │ │ + _get_input_xpath() │ │ └───────────────────────┘ │ │ ┌───────────────────────┼───────────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌───────────────┐ ┌───────────────┐ │ DeepSeekPlatform│ │ DoubaoPlatform│ │ WenxinPlatform│ ├───────────────┤ ├───────────────┤ ├───────────────┤ │ (实现特定逻辑) │ │ (实现特定逻辑) │ │ (实现特定逻辑) │ └───────────────┘ └───────────────┘ └───────────────┘ │ │ │ └───────────────────────┴───────────────────────┘ │ │ 使用 ▼ ┌─────────────────────┐ │ PhoneExtractor │ │ (电话号码提取器) │ └─────────────────────┘

核心模块说明

1. Config(配置管理)

设计模式:单例模式

集中管理所有配置参数,支持动态更新。

from core import Config # 获取所有配置 config = Config.get_all() # 更新配置 Config.update(BROWSER_PORT=9222, PAGE_LOAD_WAIT=5) # 重置为默认值 Config.reset()

默认配置:

参数默认值说明
BROWSER_PORT9222浏览器调试端口
BROWSER_HEADLESSFalse是否无头模式
PAGE_LOAD_WAIT3页面加载等待时间(秒)
INPUT_WAIT1输入后等待时间(秒)
DEFAULT_OUTPUT_FILE"phones.txt"默认输出文件名
FILE_ENCODING"utf-8"输出文件编码

2. BrowserManager(浏览器管理器)

职责:管理 ChromiumPage 实例的创建、获取和关闭

from core import BrowserManager # 方式1:手动管理 manager = BrowserManager(port=9222) page = manager.get_page() # 使用 page... manager.close() # 方式2:上下文管理器(推荐) with BrowserManager() as manager: page = manager.get_page() # 使用 page... # 退出时自动关闭

3. PhoneExtractor(电话提取器)

职责:从文本中提取并保存电话号码

from core import PhoneExtractor extractor = PhoneExtractor() # 提取电话 phones = extractor.extract("客服热线:400-123-4567") # 保存到文件 extractor.save_to_file(phones, "output.txt", "DeepSeek")

重要:只在 AI 回答区域提取

PhoneExtractor 本身是通用的文本提取工具,不负责识别页面内容区域。各平台需要通过重写 _extract_content() 方法,只返回 AI 回答的内容区域,避免从导航栏、页脚等位置提取错误电话。

正确示例(使用 DrissionPage):

def _extract_content(self) -> str: """只提取 AI 回答内容区域""" # 方式1:直接定位回答元素(推荐) answer_element = self._page.ele('css:.ai-answer-content', timeout=5) if answer_element: return answer_element.text # 方式2:遍历查找符合条件的元素 all_divs = self._page.eles('xpath://div') for div in all_divs: text = div.text # 只提取包含"电话"且长度足够的内容块 if '电话' in text and '用户' not in text and '搜索' not in text and len(text) > 500: return text[:5000] # 未找到特定区域,返回整个页面 return self._page.html

DrissionPage 元素操作方法:

方法说明示例
.ele()定位单个元素page.ele('xpath://div[@class="answer"]')
.eles()定位多个元素page.eles('css:.message')
.text获取元素文本element.text
.html获取元素HTMLelement.html
.click()点击元素element.click()
.input()输入文本element.input('内容')
.run_js()执行 JavaScriptpage.run_js('return document.title')

支持格式(统一在基类维护):

  • 400/800 电话:400-123-4567
  • 手机号:13812345678138-1234-5678
  • 座机号:021-12345678010-12345678
  • 国际号码:+86 21 2319 3688

如需支持新格式,只需在 PhoneExtractor._get_patterns() 中添加正则表达式。

4. PlatformBase(平台基类)

设计模式:模板方法模式

所有平台的抽象基类,定义统一接口和搜索流程骨架。

核心方法:

class PlatformBase(ABC): # 子类必须定义的类属性 PLATFORM_NAME: str = "" # 平台名称 PLATFORM_URL: str = "" # 平台URL # 子类必须实现的抽象方法 @abstractmethod def search(self, keyword: str, output_file: str = "") -> List[str]: """搜索关键词并提取电话号码""" pass @abstractmethod def _wait_for_response(self) -> None: """等待AI响应完成""" pass @abstractmethod def _extract_content(self) -> str: """提取AI回答内容""" pass @abstractmethod def _get_input_xpath(self) -> str: """获取输入框的XPath""" pass # 模板方法(定义算法骨架) def _execute_search(self, keyword: str, output_file: str = "") -> List[str]: """执行搜索流程""" # 1. 导航到平台 self._navigate_to_platform() # 2. 输入关键词 self._input_keyword(keyword) # 3. 等待响应 self._wait_for_response() # 4. 提取内容 content = self._extract_content() # 5. 提取电话号码 phones = self.phone_extractor.extract(content) # 6. 保存结果 if phones: self.phone_extractor.save_to_file(phones, output_file, self.PLATFORM_NAME) return phones

可重写的方法:

子类可通过重写以下方法实现平台特定逻辑:

  • _get_input_xpath() - 自定义输入框定位符
  • _wait_for_response() - 自定义等待逻辑
  • _extract_content() - 自定义内容提取逻辑
  • _navigate_to_platform() - 自定义导航逻辑

5. 异常类

完善的异常体系便于错误处理。

from core.exceptions import ( PlatformError, BrowserConnectionError, InputBoxNotFoundError, ResponseTimeoutError ) try: platform = DeepSeekPlatform() phones = platform.search("关键词") except InputBoxNotFoundError as e: print(f"输入框未找到:{e}") except ResponseTimeoutError as e: print(f"响应超时:{e}") except PlatformError as e: print(f"平台错误:{e}")

新平台接入指南

接入步骤

第一步:创建平台文件

platforms/ 目录下创建新文件,例如 new_platform.py

""" 新平台搜索模块 提供新平台的关键词搜索和电话号码提取功能。 """ import time from typing import List from core.base_platform import PlatformBase class NewPlatform(PlatformBase): """新平台类 继承自 PlatformBase,实现新平台特定的配置和逻辑。 """ PLATFORM_NAME = "新平台名称" PLATFORM_URL = "https://example.com" def search(self, keyword: str, output_file: str = "") -> List[str]: """搜索关键词并提取电话号码 Args: keyword: 搜索关键词 output_file: 输出文件名 Returns: 提取到的电话号码列表 """ return self._execute_search(keyword, output_file) def _get_input_xpath(self) -> str: """获取输入框的XPath表达式 Returns: XPath表达式字符串 """ # 示例:查找 textarea 元素 return 'xpath://textarea' # 或者使用更具体的选择器 # return 'xpath://textarea[@placeholder="请输入问题"]' # return 'css:textarea.input-box' def _wait_for_response(self) -> None: """等待AI响应完成 通过监听页面元素内容变化判断输出是否完成。 """ print("等待AI响应...") # 方式1:检测特定文本是否稳定(推荐) stable_count = 0 stable_threshold = 3 check_interval = 1 last_content = "" while True: # 获取页面文本 body_text = self._page.run_js('return document.body.innerText;') # 检测是否有完成标记(如"生成完成") if '生成完成' in body_text: # 获取"生成完成"周围内容片段 index = body_text.index('生成完成') start = max(0, index - 30) end = min(len(body_text), index + 30) current_content = body_text[start:end] if current_content == last_content: stable_count += 1 print(f"完成标记稳定 ({stable_count}/{stable_threshold})") if stable_count >= stable_threshold: print("AI响应完成") return else: stable_count = 0 last_content = current_content print("检测到内容更新") time.sleep(check_interval) def _extract_content(self) -> str: """提取AI回答内容 只返回AI回答的内容区域,避免提取错误的电话号码。 Returns: AI回答的文本内容 """ # 方式1:直接定位回答元素(推荐) answer_element = self._page.ele('css:.ai-answer-content', timeout=5) if answer_element: return answer_element.text # 方式2:遍历查找符合条件的元素 all_divs = self._page.eles('xpath://div') for div in all_divs: text = div.text # 只提取包含"电话"且长度足够的内容块 if '电话' in text and '用户' not in text and '搜索' not in text and len(text) > 500: return text[:5000] # 未找到特定区域,返回整个页面 return self._page.html # 便捷函数(可选,提供向后兼容) def search_new_platform(keyword: str, output_file: str = "") -> List[str]: """便捷搜索函数 Args: keyword: 搜索关键词 output_file: 输出文件名 Returns: 提取到的电话号码列表 """ platform = NewPlatform() try: return platform.search(keyword, output_file) finally: platform.cleanup()

第二步:注册平台

platforms/__init__.py 中添加导入和注册:

# 1. 导入新平台 from .new_platform import NewPlatform, search_new_platform # 2. 添加到 __all__ __all__ = [ # ... 其他平台 "NewPlatform", "search_new_platform", ] # 3. 添加到 PLATFORMS 字典 PLATFORMS = { # ... 其他平台 "新平台名称": { "class": NewPlatform, "function": search_new_platform }, }

第三步:测试新平台

创建测试脚本验证功能:

from platforms import NewPlatform # 测试搜索 platform = NewPlatform() phones = platform.search("劳力士维修服务中心", "test_output.txt") print(f"找到电话号码:{phones}") platform.cleanup()

接入最佳实践

1. 获取输入框 XPath

使用浏览器开发者工具获取输入框的 XPath:

  1. 打开平台网站
  2. 按 F12 打开开发者工具
  3. 点击元素选择器(左上角箭头图标)
  4. 点击输入框
  5. 在 Elements 标签中右键点击选中的元素
  6. 选择 Copy → Copy XPath

常见选择器模式:

# 根据属性选择 'xpath://textarea[@placeholder="请输入问题"]' 'xpath://input[@id="search-input"]' # 根据 class 选择 'css:textarea.input-box' 'css:#search-input' # 根据文本内容选择 'xpath://button[contains(text(), "发送")]'

2. 实现 _wait_for_response()

关键原则

  • 使用 while True 持续检测,不使用超时
  • 通过内容是否变化来判断完成状态
  • 可以检测特定文本(如"生成完成")周围内容是否稳定
  • 也可以检测回答区域内容是否不再变化

示例1:检测完成标记文本

def _wait_for_response(self) -> None: """等待豆包思考完成,通过检测内容稳定性判断""" stable_count = 0 stable_threshold = 3 check_interval = 1 last_content = "" print("等待思考完成...") while True: body_text = self._page.run_js('return document.body.innerText;') if '已完成思考' in body_text: # 找到"已完成思考"周围的内容片段 think_index = body_text.index('已完成思考') start = max(0, think_index - 50) end = min(len(body_text), think_index + 50) current_content = body_text[start:end] if current_content == last_content: stable_count += 1 print(f"思考内容稳定 ({stable_count}/{stable_threshold})") if stable_count >= stable_threshold: print("思考完成") return else: stable_count = 0 last_content = current_content print("检测到思考内容更新") else: stable_count = 0 last_content = "" print("等待'已完成思考'提示...") time.sleep(check_interval)

示例2:检测回答区域内容

def _wait_for_response(self) -> None: """等待豆包回答完成,通过检测内容稳定性判断""" stable_count = 0 stable_threshold = 3 check_interval = 1 last_answer = "" print("等待回答完成...") while True: # 定位回答元素 answer = self._page.ele('css:.answer-content', timeout=2) if answer: current_answer = answer.text if current_answer == last_answer: stable_count += 1 print(f"回答内容稳定 ({stable_count}/{stable_threshold})") if stable_count >= stable_threshold: print("回答完成") return else: stable_count = 0 last_answer = current_answer print(f"回答内容更新,当前长度: {len(current_answer)}") time.sleep(check_interval)

示例3:综合检测(思考+回答)

def _wait_for_response(self) -> None: """等待AI响应完成(思考+回答)""" # 先等待思考完成 self._wait_for_thinking_complete() # 再等待回答完成 self._wait_for_answer_complete()

3. 实现 _extract_content()

重要原则

  • 只返回 AI 回答的内容区域
  • 避免从导航栏、页脚、侧边栏等位置提取
  • 优先使用直接定位元素的方式
  • 如果无法定位,可以使用过滤策略

示例1:直接定位回答元素

def _extract_content(self) -> str: """只提取 AI 回答内容区域""" answer_element = self._page.ele('css:.ai-answer-content', timeout=5) if answer_element: return answer_element.text # 降级方案:返回整个页面 return self._page.html

示例2:遍历查找符合条件的元素

def _extract_content(self) -> str: """通过过滤策略提取回答内容""" all_divs = self._page.eles('xpath://div') for div in all_divs: text = div.text # 只提取包含"电话"且长度足够的内容块 if '电话' in text and '用户' not in text and '搜索' not in text and len(text) > 500: return text[:5000] # 未找到特定区域,返回整个页面 return self._page.html

示例3:使用 JavaScript 提取

def _extract_content(self) -> str: """使用 JavaScript 提取内容""" js_code = ''' // 查找包含"电话"的最长文本块 const divs = document.querySelectorAll('div'); let longestText = ''; for (const div of divs) { const text = div.innerText || div.textContent; if (text.includes('电话') && text.length > longestText.length) { longestText = text; } } return longestText || document.body.innerText; ''' return self._page.run_js(js_code)

平台接入检查清单

接入新平台时,请确保完成以下步骤:

  • 创建平台文件(如 new_platform.py
  • 定义 PLATFORM_NAMEPLATFORM_URL
  • 实现 search() 方法
  • 实现 _get_input_xpath() 方法
  • 实现 _wait_for_response() 方法(使用内容稳定性检测)
  • 实现 _extract_content() 方法(只提取回答区域)
  • platforms/__init__.py 中注册平台
  • 测试搜索功能是否正常
  • 测试电话号码提取是否准确
  • 测试异常处理是否完善

参考示例

可以参考已实现的平台作为模板:

设计模式

  • 模板方法模式PlatformBase._execute_search() 定义算法骨架,子类实现细节
  • 单例模式Config 使用类变量管理全局配置
  • 策略模式:各平台通过重写方法实现不同的搜索策略
  • 工厂模式:通过 PLATFORMS 字典动态创建平台实例

支持的 AI 平台

平台类名函数名状态
DeepSeekDeepSeekPlatformsearch_deepseek()✅ 已测试
豆包DoubaoPlatformsearch_doubao()✅ 已测试
文心一言WenxinPlatformsearch_wenxin()✅ 已测试
KimiKimiPlatformsearch_kimi()✅ 已测试
通义千问TongyiPlatformsearch_tongyi()✅ 已测试
腾讯元宝YuanbaoPlatformsearch_yuanbao()✅ 已测试

注意事项

  1. 浏览器要求:首次运行时会自动打开 Chrome/Edge 浏览器,请确保已安装
  2. 等待机制:不同 AI 平台的响应特征不同,建议重写 _wait_for_response() 方法实现精确判断
  3. 内容提取:各平台应重写 _extract_content() 方法,只返回 AI 回答区域,避免提取错误的电话号码
  4. 网络环境:需要稳定的网络连接访问各 AI 平台
  5. 资源清理:使用 platform.cleanup() 或上下文管理器确保浏览器资源正确释放

许可证

MIT License

About

No description, topics, or website provided.
Language
Python100%