Python 之 selenium 打开浏览器指定端口进行接续操作

news2025/5/16 0:36:02

一般使用 selenium 进行数据爬取时,常用处理流程是让 selenium 从打开浏览器开始,完成全流程的所有操作。但是有时候,我们希望用户先自己打开浏览器进入指定网页,完成登录认证等一系列操作之后(比如用户、密码、短信验证码及各种难处理的图形验证码之类),再让 selenium 从登录后的页面进行接续操作爬取数据。那么如何才能将前后操作接续起来呢?

常规操作

常规操作一般使用下面的这种方式,设置初始参数后直接使用 get 方法去打开网页。

from selenium import webdriver


class DriverClass:
    def __init__(self):
        self.driver = self._init_driver()

    def _init_driver(self):
        try:
            option = webdriver.ChromeOptions()
            option.add_experimental_option('excludeSwitches', ['enable-automation'])
            option.add_experimental_option('useAutomationExtension', False)
            prefs = dict()
            prefs['credentials_enable_service'] = False
            prefs['profile.password_manager_enable'] = False
            prefs['profile.name'] = "Person 1"
            option.add_experimental_option('prefs', prefs)
            option.add_argument('--disable-gpu')
            option.add_argument("--disable-blink-features=AutomationControlled")
            option.add_argument('--user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"')
            option.add_argument('--no-sandbox')
            option.add_argument('ignore-certificate-errors')
            driver = webdriver.Chrome(r"./driver/chromedriver.exe", options=option)
            driver.implicitly_wait(2)
            driver.maximize_window()
            return driver
        except Exception as e:
            raise e

    def get_driver(self) -> webdriver.Chrome:
        if isinstance(self.driver, webdriver.Chrome):
            return self.driver
        raise Exception('初始化浏览器失败')


if __name__ == '__main__':
    dc = DriverClass()
    driver = dc.get_driver()
    print(driver)
    driver.get("https://www.baidu.com")

接续操作

接续操作主要通过在打开浏览器时,都设置相同的接口来完成前后的衔接(不然 selenium 不知道要从哪个浏览器页面进行接续)。

用户打开浏览器

用户手动打开浏览器时,指定对应的端口(这里设置的是 9527)及数据目录(自己自定义自定一个)。

C:\Program Files\Google\Chrome\Application>chrome.exe --remote-debugging-port=9527 --user-data-dir="E:\lky_project\tmp_project\handle_qcc_data\\chrome_user_data"

执行完上面的命令以后,会打开一个新的浏览器页面。

打开浏览器后,用户可以手动输入相应页面,完成相应的用户登录认证等操作。 

程序接续浏览器

selenium 通过增加下面的配置参数

option.add_experimental_option("debuggerAddress", "127.0.0.1:9527")

来打开并接续处理用户已经打开的指定端口的浏览器。之后,程序就可以通过浏览器句柄去接续处理后续的任务了。

driver_class.py

from selenium import webdriver


class DriverClass:
    def __init__(self):
        self.driver = self._init_driver()

    def _init_driver(self):
        try:
            option = webdriver.ChromeOptions()
            # option.add_experimental_option('excludeSwitches', ['enable-automation'])
            # option.add_experimental_option('useAutomationExtension', False)
            # prefs = dict()
            # prefs['credentials_enable_service'] = False
            # prefs['profile.password_manager_enable'] = False
            # prefs['profile.name'] = "Person 1"
            # option.add_experimental_option('prefs', prefs)
            option.add_argument('--disable-gpu')
            option.add_argument("--disable-blink-features=AutomationControlled")
            option.add_argument('--user-agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"')
            option.add_argument('--no-sandbox')
            option.add_argument('ignore-certificate-errors')
            option.add_experimental_option("debuggerAddress", "127.0.0.1:9527")
            driver = webdriver.Chrome(r"./driver/chromedriver.exe", options=option)
            driver.implicitly_wait(2)
            # driver.maximize_window()
            return driver
        except Exception as e:
            raise e

    def get_driver(self) -> webdriver.Chrome:
        if isinstance(self.driver, webdriver.Chrome):
            return self.driver
        raise Exception('初始化浏览器失败')


if __name__ == '__main__':
    dc = DriverClass()
    driver = dc.get_driver()
    print(driver)
    # 程序使用接续后的浏览器句柄 driver 完成后续操作

注意事项

注意看,我上面的接续操作函数,有一部分的参数设置是注释掉的。这是因为接续是从已经打开的浏览器接收继续进行操作,有部分的参数在用户打开浏览器的时候就已经设定了,所以不再支持通过接续的方式继续重复设置。

实战示例

比如在手动打开指定 9527 端口的浏览器后,登录企查查进入高级搜索,然后使用程序获取具有相应资质的企业数目(操作太频繁可能触发校验或封号,请谨慎操作!),最后生成结果文件 data.json(中途可能会异常中断,可以做成下面这种利用 data.json 实现的断点续查的方式,这样,后续再次运行也只会查询未查询过的资质数据)。

driver_class.py 用上面的就可以。

main.py

import json
import re
import time

from selenium.webdriver.common.by import By
from driver_class import DriverClass

dc = DriverClass()
driver = dc.get_driver()
xpath_prefix = '//div/div/div/div/span[text()="资质证书"]/following-sibling::div'


def checkbox_select(element_checkbox):
    """复选框选中"""
    class_attribute = element_checkbox.get_attribute("class")
    if "checked" not in class_attribute:
        element_checkbox.find_element(By.XPATH, './span[@class="qccd-tree-checkbox-inner"]').click()


def checkbox_unselect(element_checkbox):
    """复选框取消选中"""
    class_attribute = element_checkbox.get_attribute("class")
    if "checked" in class_attribute:
        element_checkbox.find_element(By.XPATH, './span[@class="qccd-tree-checkbox-inner"]').click()


def get_amount(element_checkbox):
    """获取对应复选框对应的企业数目"""
    checkbox_select(element_checkbox)
    xpath_confirm = xpath_prefix + '/div/div/div/div/div[text()="确定"]'
    driver.find_element(By.XPATH, xpath_confirm).click()
    time.sleep(0.5)
    try:
        xpath_result = '//div/div/div[@class="search-btn limit-svip"]'
        result = str(driver.find_element(By.XPATH, xpath_result).text)
    except Exception as e:
        print(f"异常: {str(e)}")
        result = "0"
    result = result.replace(",", "")
    match_object = re.search("(\d+)", result)
    amount = match_object.group(1)
    print(f"数目:{amount}")
    # 清除结果,避免点击选择项时误点击关闭
    xpath_clear = '//div/div/a[contains(text(), "清除")]'
    try:
        driver.find_element(By.XPATH, xpath_clear).click()
    except:
        pass
    xpath_select = xpath_prefix + '[@class="trigger-container"]'
    driver.find_element(By.XPATH, xpath_select).click()
    time.sleep(0.2)
    checkbox_unselect(element_checkbox)
    return amount


def extend_options():
    """展开折叠项并获取数据,只展开三层"""
    # json.dump(data, open("data.json", 'w', encoding="utf-8"), indent=2, ensure_ascii=False)
    try:
        data = json.load(open("data.json", encoding="utf-8"))
    except:
        data = {}
    try:
        xpath_first_class = xpath_prefix + '//div/ul/li[@role="treeitem"]'
        # xpath_first_class = xpath_prefix + '//div/ul/li/span[contains(@class, "qccd-tree-switcher")]'
        first_item_list = driver.find_elements(By.XPATH, xpath_first_class)
        for item_li in first_item_list:
            text_dk1 = item_li.find_element(By.XPATH, './span/span/div/span[@class="text-dk"]').text
            data[text_dk1] = data.get(text_dk1, {})
            print(f"{text_dk1}")
            switcher = item_li.find_element(By.XPATH, './span[contains(@class, "qccd-tree-switcher")]')
            class_attribute = switcher.get_attribute("class")
            element_checkbox = item_li.find_element(By.XPATH, './span[contains(@class, "checkbox")]')
            if "close" in class_attribute:
                switcher.click()
                time.sleep(0.1)
            elif "noop" in class_attribute:
                # 当前节点没有子节点
                if not data[text_dk1]:
                    amount = get_amount(element_checkbox)
                    data[text_dk1] = amount
                continue
            # 点开以后,下层级的 ul/li 会展示出来
            second_item_list = item_li.find_elements(By.XPATH, "./ul/li")
            for second_item_li in second_item_list:
                text_dk2 = second_item_li.find_element(By.XPATH, './span/span/div/span[@class="text-dk"]').text
                data[text_dk1][text_dk2] = data[text_dk1].get(text_dk2, {})
                print(f"--{text_dk2}")
                switcher = second_item_li.find_element(By.XPATH, './span[contains(@class, "qccd-tree-switcher")]')
                class_attribute = switcher.get_attribute("class")
                element_checkbox = second_item_li.find_element(By.XPATH, './span[contains(@class, "checkbox")]')
                if "close" in class_attribute:
                    switcher.click()
                    time.sleep(0.1)
                elif "noop" in class_attribute:
                    # 当前节点没有子节点
                    if not data[text_dk1][text_dk2]:
                        amount = get_amount(element_checkbox)
                        data[text_dk1][text_dk2] = amount
                    continue
                # 点开以后,下层级的 ul/li 会展示出来
                third_item_list = second_item_li.find_elements(By.XPATH, "./ul/li")
                for third_item_li in third_item_list:
                    text_dk3 = third_item_li.find_element(By.XPATH, './span/span/div/span[@class="text-dk"]').text
                    data[text_dk1][text_dk2][text_dk3] = data[text_dk1][text_dk2].get(text_dk3, {})
                    print(f"----{text_dk3}")
                    switcher = third_item_li.find_element(By.XPATH, './span[contains(@class, "qccd-tree-switcher")]')
                    class_attribute = switcher.get_attribute("class")
                    # 到第三层时,不再展开,直接选择复选框
                    element_checkbox = third_item_li.find_element(By.XPATH, './span[contains(@class, "checkbox")]')
                    if not data[text_dk1][text_dk2][text_dk3]:
                        amount = get_amount(element_checkbox)
                        data[text_dk1][text_dk2][text_dk3] = amount
    except Exception as e:
        raise e
    finally:
        json.dump(data, open("data.json", 'w', encoding="utf-8"), indent=2, ensure_ascii=False)


def spider_data():
    # 尝试关闭资质证书选择框、清除所选项
    xpath_close = xpath_prefix + '/div/div/div/a[@class="nclose"]'
    xpath_clear = '//div/div/a[contains(text(), "清除")]'
    try:
        driver.find_element(By.XPATH, xpath_close).click()
    except:
        pass
    try:
        driver.find_element(By.XPATH, xpath_clear).click()
    except:
        pass
    # 点击资质证书选择框
    xpath_select = xpath_prefix + '[@class="trigger-container"]'
    driver.find_element(By.XPATH, xpath_select).click()
    time.sleep(2)
    extend_options()
    # 取消按钮
    xpath_cancel = xpath_prefix + '/div/div/div/div/div[text()="取消"]'
    # 确定按钮
    xpath_confirm = xpath_prefix + '/div/div/div/div/div[text()="确定"]'
    driver.find_element(By.XPATH, xpath_confirm).click()


if __name__ == '__main__':
    spider_data()

最后可以得到生成的 data.json 文件如下:

{
  "建筑业资质": {
    "工程设计资质证书": {
      "工程设计专项资质": "26329",
      "建筑工程设计事务所": "356",
      "工程设计行业资质": "4487",
      "工程设计专业资质": "19902",
      "工程设计综合资质": "98"
    },
    "工程勘察资质证书": {
      "工程勘察综合资质": "377",
      "工程勘察专业资质": "7464",
      "工程勘察劳务资质": "3019"
    },
...
  },
  "食品农产品认证": {
    "有机产品(OGA)": "49868",
    "良好农业规范(GAP)": "6449",
    "食品质量认证(酒类)": "151",
    "绿色食品认证": "34723",
    "绿色市场认证": "318",
    "无公害农产品": "31067",
    "食品安全管理体系认证": "72075",
    "危害分析与关键控制点认证": "51844",
    "乳制品生产企业良好生产规范认证": "445",
    "乳制品生产企业危害分析与关键控制点(HACCP)体系认证": "570",
    "饲料产品": "85"
  },
  "其他资质": {
    "办学许可证": "192010",
    "代理记账许可证书": "34588",
    "会计师事务所执业证书": "12252",
    "DOC证书": "982",
    "SMC证书": "1886",
    "名特优新农产品证书": "1818",
    "招投标类综合资质": "36317",
    "区块链信息服务备案": "2765",
    "医疗机构执业许可证": "570877",
    "CCC工厂认证": "16154",
    "卫生许可证": "3244"
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2376460.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

不用服务器转码,Web端如何播放RTSP视频流?

在物联网、智慧城市、工业互联网等新兴技术浪潮下,实时视频流(如RTSP协议)作为安防监控、生产巡检、远程协作等场景的核心数据载体,其价值愈发凸显。然而,一个长期困扰行业的痛点始终存在——‌如何在Web浏览器中直接播…

如何开发一款 Chrome 浏览器插件

Chrome是由谷歌开发的网页浏览器,基于开源软件(包括WebKit和Mozilla)开发,任何人都可以根据自己需要使用、修改或增强它的功能。Chrome凭借着其优秀的性能、出色的兼容性以及丰富的扩展程序,赢得了广大用户的信任。市场…

GitHub打开缓慢甚至失败的解决办法

在C:\Windows\System32\drivers\etc的hosts中增加如下内容: 20.205.243.166 github.com 199.59.149.236 github.global.ssl.fastly.net185.199.109.153 http://assets-cdn.github.com 185.199.108.153 http://assets-cdn.github.com 185.199.110.153 http://asset…

18前端项目----Vue项目收尾优化|重要知识

收尾/知识点汇总 项目收尾二级路由未登录全局路由守卫路由独享守卫图片懒加载路由懒加载打包上线 重要知识点汇总组件通信方式1. props2. 自定义事件3. 全局事件总线4. 订阅与发布pubsub5. Vuex6. 插槽 sync修饰符attrs和listeners属性children和parent属性mixin混入作用域插槽…

仿RabbitMQ 模拟实现消息队列

文章目录 项目项目介绍开发环境技术选型 开始项目前第三方框架内容介绍muduo搭建服务端,客户端服务端:客户端:makefile muduo库protobuf通信服务端:客户端 sqlitegtest线程池future 认识,async使用promis使用package_t…

基于Qt的app开发第八天

写在前面 笔者是一个大一下计科生,本学期的课程设计自命题完成一个督促学生自律的打卡软件,目前已经完成了待办和打卡部分功能,本篇要完成规划板块不需要存储就能实现的功能 需求分析 这一板块内容相比前两个板块还有一些特殊,因…

数据分析-图2-图像对象设置参数与子图

from matplotlib import pyplot as mp mp.figure(A figure,facecolorgray) mp.plot([0,1],[1,2]) mp.figure(B figure,facecolorlightgray) mp.plot([1,2],[2,1]) #如果figure中标题已创建,则不会新建窗口, #而是将旧窗口设置为当前窗口 mp.figure(A fig…

查询公网IP地址的方法:查看自己是不是公网ip,附内网穿透外网域名访问方案

本地搭建服务并提供互联网连接时,较为传统的方法是使用公网IP地址。因此,如何查询本地自己是不是公网IP,是必须要掌握的一种技巧。当面对确实无公网IP时,则可以通过内网穿透方案,如nat123网络映射工具,将本…

Redis学习打卡-Day1-SpringDataRedis、有状态无状态

Redis的Java客户端 Jedis 以 Redis 命令作为方法名称,学习成本低,简单实用。Jedis 是线程不安全的,并且频繁的创建和销毁连接会有性能损耗,因此推荐使用 Jedis 连接池代替Jedis的直连方式。 lettuce Lettuce是基于Netty实现的&am…

Spring Spring Boot 常用注解整理

Spring & Spring Boot 常用注解整理 先理解核心概念:什么是注解(Annotation)?第一部分:IOC(控制反转)和 DI(依赖注入)1. Component2. Service, Repository, Controll…

c#建筑行业财务流水账系统软件可上传记账凭证财务管理系统签核功能

# financial_建筑行业 建筑行业财务流水账系统软件可上传记账凭证财务管理系统签核功能 # 开发背景 软件是给岳阳客户定制开发一款建筑行业流水账财务软件。提供工程签证单、施工日志、人员出勤表等信息记录。 # 财务管理系统功能描述 1.可以自行设置记账科目,做凭…

让 Cursor 教我写 MCP Client

文章目录 1. 写在最前面2. 动手实现一个 MCP Client2.1 How 天气查询 Client2.1.1 向 Cursor 提问的艺术2.1.2 最终成功展示2.1.3 client 的代码 3. MCP 协议核心之一总结3.1 SSE vs WebSocket 4. 碎碎念5. 参考资料 1. 写在最前面 学习了 MCP Server 的实现后,刚好…

反射, 注解, 动态代理

文章目录 单元测试什么是单元测试咱们之前是如何进行单元测试的? 有啥问题 ?现在使用方法进行测试优点Junit单元测试的使用步骤删除不需要的jar包总结 反射认识反射、获取类什么是反射反射具体学什么?反射第一步:或者Class对象 获…

vue vite 无法热更新问题

一、在vue页面引入组件CustomEmployeesDialog,修改组件CustomEmployeesDialog无法热更新 引入方式: import CustomEmployeesDialog from ../dialog/customEmployeesDialog.vue 目录结构: 最后发现是引入import时,路径大小写与目…

深度学习中的查全率与查准率:如何实现有效权衡

📌 友情提示: 本文内容由银河易创AI(https://ai.eaigx.com)创作平台的gpt-4-turbo模型辅助生成,旨在提供技术参考与灵感启发。文中观点或代码示例需结合实际情况验证,建议读者通过官方文档或实践进一步确认…

Windows玩游戏的时候,一按字符键就显示桌面

最近打赛伯朋克 2077 的时候,不小心按错键了,导致一按字符键就显示桌面。如下: 一开始我以为是输入法的问题(相信打游戏的人都知道输入法和奔跑键冲突的时候有多烦),但是后来解决半天发现并不是。在网上搜…

Gemini 2.5 Flash和Pro预览版价格以及上下文缓存的理解

Gemini 2.5 Flash和Pro预览版价格 Gemini 2.5 Flash 预览版就是 Google 的最新 AI 大模型,能处理巨量内容。可以免费体验,但有次数和功能上的限制;付费层级才开放全部高级功能。价格也比传统 API 略有不同,尤其在“思考预算”“上…

vue2 头像上传+裁剪组件封装

背景:最近在进行公司业务开发时,遇到了头像上传限制尺寸的需求,即限制为一寸证件照(宽295像素,高413像素)。 用到的第三方库: "vue-cropper": "^0.5.5" 完整组件代码&…

AI-02a5a5.神经网络-与学习相关的技巧-权重初始值

权重的初始值 在神经网络的学习中,权重的初始值特别重要。实际上,设定什么样的权重初始值,经常关系到神经网络的学习能否成功。 不要将权重初始值设为 0 权值衰减(weight decay):抑制过拟合、提高泛化能…

【springcloud学习(dalston.sr1)】Eureka单个服务端的搭建(含源代码)(三)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍(含源代码)(一) 这篇文章主要介绍单个eureka服务端的集群环境是如何搭建的。 通过前面的文章【springcloud学习(dalston.sr1)】…