Selenium 浏览器自动化案例:复用已登录会话批量查询专家信息

功能说明

本脚本通过 Chrome 远程调试模式,复用已登录的浏览器会话,避免重复登录,通过Selenium自动化操作浏览器,批量从指定网页中根据Excel名单查询专家姓名并获取对应的手机号,最终将结果保存到新Excel。


亮点与价值

  1. 浏览器复用机制(高效且贴近实战)
    • 避免重复启动浏览器的耗时和资源消耗,大幅提升批量操作效率;
    • 保留浏览器原有会话 / 登录状态(无需重复登录目标网站),贴合真实办公场景需求;
    • 调试时可直观看到浏览器操作过程,便于定位问题。
  2. 成熟的等待策略(稳定性拉满)
    • 相比纯固定等待,显式等待 “元素出现即执行”,既保证稳定性(避免元素未加载导致的定位失败),又不浪费多余时间;
    • 针对 iframe 加载、元素可见性等不同场景精准等待,覆盖网页加载的核心卡点。
  3. 完善的 iframe 切换逻辑
    • 精准解决网页嵌套 iframe 时 “元素明明存在却定位不到” 的核心痛点;
    • 切换逻辑闭环(操作完立即切回主文档),避免后续操作受 iframe 上下文影响。

完整代码

# 导入Selenium核心库:用于驱动浏览器进行自动化操作
from selenium import webdriver
# 导入Selenium浏览器驱动异常类:捕获浏览器连接相关错误
from selenium.common import WebDriverException
# 导入元素定位方式枚举类:提供标准化的元素定位策略(如ID、XPATH等)
from selenium.webdriver.common.by import By
# 导入Selenium异常类:专门捕获超时和元素不存在的异常
from selenium.common.exceptions import TimeoutException
# 显式等待模块:提供智能等待机制(等待元素出现/可点击再执行操作)
from selenium.webdriver.support.ui import WebDriverWait
# 显式等待条件:定义等待的触发条件(如元素可见、框架加载完成等)
from selenium.webdriver.support import expected_conditions as EC
# 导入time库:用于设置等待时间,确保页面元素加载完成
import time

# 导入pandas库:用于Excel文件的读取和写入操作(数据处理核心)
import pandas as pd
# 导入os库:用于文件路径检查、系统操作等
import os



# ==============================================
# 函数:读取Excel文件中的专家姓名列表
# 参数:file_path - Excel文件的完整路径
# 返回:成功返回姓名列表,失败返回空列表
# 异常处理:文件不存在、读取失败等场景
# ==============================================
def read_excel_data(file_path):
    try:
        # 检查文件是否存在(前置校验,避免文件不存在报错)
        if not os.path.exists(file_path):
            print(f"文件不存在: {file_path}")
            return []
        # 读取Excel文件(pandas默认读取第一个工作表)
        df = pd.read_excel(file_path)
        # 提取"姓名"列的数据并转换为列表(核心数据提取)
        name_list = df['姓名'].values.tolist()
        # 打印读取结果,便于调试
        print(f"找到 {len(name_list)}条数据")
        return name_list
    # 捕获所有读取过程中的异常(如文件损坏、列名错误等)
    except Exception as e:
        print(f"读取Excel文件时出错: {e}")
        return []

# ==============================================
# 函数:连接已启动调试模式的Chrome浏览器
# 参数:debug_address - 调试地址(格式:IP:端口,如127.0.0.1:9527)
# 返回:成功返回WebDriver对象,失败返回None
# 核心作用:复用已打开的浏览器,避免重复启动
# ==============================================
def connect_to_existing_chrome(debug_address):
    try:
        # 配置Chrome选项,指定调试地址
        options = webdriver.ChromeOptions()
        options.debugger_address = debug_address
        # 创建驱动对象,连接调试中的Chrome
        driver = webdriver.Chrome(options=options)
        # 打印连接成功信息,便于验证
        print("=" * 20)
        print(f"调试地址: {debug_address},连接成功!")
        print(f"当前窗口标题: {driver.title}")
        print(f"当前URL: {driver.current_url}")
        print("=" * 20 + "\n")
        return driver
    # 捕获浏览器连接异常(如端口错误、浏览器未启动调试模式等)
    except WebDriverException as e:
        print(f"调试地址: {debug_address}),连接失败: {str(e)[:100]}")
        return None

# ==============================================
# 函数:将查询结果保存到Excel文件
# 参数:
#   data - 待保存的数据(格式:[[姓名1,手机号1],[姓名2,手机号2]])
#   output_path - 保存文件的路径
# 核心作用:数据持久化,统计成功/总数量
# ==============================================
def save_to_excel(data, output_path):
    # 将列表数据转换为pandas的DataFrame(指定列名,便于阅读)
    df = pd.DataFrame(data, columns=["姓名", "手机号"])
    # 保存为Excel文件(index=False 不生成行号)
    df.to_excel(output_path, index=False)
    # 统计成功获取手机号的数量(过滤异常值)
    success_count = len([d for d in data if d[1] not in ["专家搜索不到"]])
    total_count = len(data)
    # 打印保存结果,便于核对
    print(f"数据已保存到: {output_path}")
    print(f"成功获取 {success_count}/{total_count} 条数据")

# ==============================================
# 函数:核心自动化操作逻辑
# 参数:
#   driver - 已连接的WebDriver对象
#   name_list - 待查询的专家姓名列表
# 返回:查询结果列表(格式同save_to_excel的data参数)
# 核心流程:输入姓名→搜索→查看详情→提取手机号→处理异常
# ==============================================
def automate_baidu(driver,name_list):
    zj_data = [] # 初始化结果列表,存储每一条查询结果
    try:
        # 遍历姓名列表,逐个查询
        for name in name_list:
            wait.until(EC.frame_to_be_available_and_switch_to_it((By.ID, "tab-content-95280013")))
            wait.until(EC.frame_to_be_available_and_switch_to_it((By.ID, "main1")))
            # 定位专家姓名输入框
            input_element = wait.until(
                    EC.visibility_of_element_located((By.ID, "zjxm$text"))
                )
            input_element.clear()
            input_element.send_keys(name)
            # 定位搜索按钮
            search_button = wait.until(
                    EC.visibility_of_element_located((By.XPATH, "/html/body/div[2]/a"))
                )
            search_button.click()
            time.sleep(1.5)  # 等待结果加载
            try:
                # 定位查看图标
                element = wait.until(
                    EC.visibility_of_element_located(
                        (By.XPATH, "/html/body/div[3]/div/div/div[2]/div[4]/div[2]/div/table/tbody/tr[3]/td[10]/div/i"))
                )
                element.click()
            # 捕获超时异常(表示未找到该专家,无查看图标)
            except TimeoutException as e:
                zj_data.append([name, "专家搜索不到"]) # 记录失败结果
                print(f"{len(zj_data)}.{name},无")
                # 切回主文档,避免框架嵌套问题
                driver.switch_to.default_content()
                continue # 跳过当前姓名,处理下一个
            # 回到主文档开始
            driver.switch_to.default_content()
            # 切换到专家信息弹窗的iframe(通过模糊匹配name属性定位)
            wait.until(EC.frame_to_be_available_and_switch_to_it((By.XPATH, "//iframe[contains(@name, 'mini-iframe-')]")))
            # 点击"查看隐私信息"按钮(显示手机号)
            element = wait.until(
                    EC.visibility_of_element_located((By.ID, 'datamasking'))
                )
            element.click()
            time.sleep(3)  # 等待隐私信息加载(可能延迟显示)
            # 定位并提取手机号和姓名(通过ID定位元素,获取文本内容)
            mobile = driver.find_element(By.ID, "mobile").text
            pwname = driver.find_element(By.ID, "pwname").text
            # 记录成功结果
            zj_data.append([pwname, mobile])
            print(f"{len(zj_data)}.{pwname},{mobile}")


            # 回归主文档
            driver.switch_to.default_content()

            # 关闭详情弹窗(ID: 4的关闭按钮)
            element = driver.find_element(by=By.ID, value='4')
            element.click()
            time.sleep(1.5)  # 等待弹窗关闭,避免影响下一次操作
        # 返回所有查询结果
        return zj_data
    # 捕获所有未预期的异常,避免程序崩溃
    except Exception as e:
        print(f"操作过程中发生如下错误:\n {e}")
        return zj_data

# ==============================================
# 主程序入口(脚本执行起点)
# ==============================================
if __name__ == "__main__":
    # 配置项(根据实际需求修改)
    file_path = "E:\\Project\\demo\\名单.xlsx"  # 源Excel文件路径(专家名单)
    output_path = "专家信息结果.xlsx"  # 结果Excel保存路径
    port = 9527 # Chrome调试端口(需与启动浏览器时的端口一致)
    debug_address = f"127.0.0.1:{port}" # 调试地址格式:IP:端口
    user_address = "D:\\浏览器\\Google\\selenium" # Chrome用户数据目录(避免多用户冲突)

    # (可选)自动启动调试模式的Chrome(取消注释即可使用)
    # os.popen(f'start chrome --remote-debugging-port={port} --user-data-dir={user_address}')

    # 读取Excel中的专家名单
    name_list = read_excel_data(file_path)

    # 连接已打开的Chrome浏览器
    driver = connect_to_existing_chrome(debug_address)
    try:
        # 初始化显式等待对象(最大等待10秒,全局生效)
        wait = WebDriverWait(driver, 10)
        # 执行核心自动化操作,获取查询结果
        zj_data = automate_baidu(driver,name_list)

        # 将结果保存到Excel
        save_to_excel(zj_data,output_path)
    # 最终执行:无论是否出错,都处理浏览器驱动
    finally:
        # 等待用户按回车后关闭驱动(保留浏览器窗口,便于调试)
        input("\n按Enter键关闭驱动连接(浏览器保持打开):")
        driver.quit()

效果

已上传哔哩哔哩:https://www.bilibili.com/video/BV1T6PQzQErT/?vd_source=8ce685a3116f22c81f52864beda2d692

posted @ 2026-02-11 19:57  克峰同学  阅读(18)  评论(0)    收藏  举报