• 【Selenium】Python & Selenium 执行 CDP


    前言

    这篇文章来吧啦一下由Selenium中用到的cdpChrome DevTools Protocol 的调用
    用作于记录学习所得,无实质性意义。

    以下几篇文章有一点点关系,感兴趣的可以逐一审阅🔎🔎

    标题链接
    【Selenium】控制当前已经打开的 chrome浏览器窗口https://blog.csdn.net/weixin_45081575/article/details/112621581
    【Selenium】控制当前已经打开的 chrome浏览器窗口(高级篇)https://blog.csdn.net/weixin_45081575/article/details/126389273
    【Selenium】Selenium获取Network数据(高级版)https://blog.csdn.net/weixin_45081575/article/details/126551260
    【Selenium】Python & Selenium 执行 CDPhttps://blog.csdn.net/weixin_45081575/article/details/126556995

    本篇文章不讲CDP的原理,只讲它在Python 和 Selenium 中的实现。

    知识点📖

    Chrome DevTools Protocol 允许使用工具来检测、检查、调试和分析 Chromium、Chrome 和其他基于 Blink 的浏览器。

    Chrome DevTools Protocol,简称CDP

    看以下 Chrome DevTools Protocol官方文档 ,感兴趣的可以深入去学习了解。

    在这里插入图片描述


    Selenium的基础

    在Selenium中,执行下面的代码,便可以调用 cdp 命令~

    # selenium调用 cdp
    webdriver.execute_cdp_cmd(command, cmd_args)  
    
    • 1
    • 2

    翻看一下它的源码,

    • 该函数是执行 cdp 命令和 获取返回结果
    • 参考链接:https://chromedevtools.github.io/devtools-protocol/,使用示例 务必要参考这里使用,因为不用方法的传参不一样
    • 使用示例:driver.execute_cdp_cmd('Network.getResponseBody', {'requestId': requestId}),下面再解释它的意思

    \site-packages\selenium\webdriver\chromium\webdriver.py


    CDP操作

    下面介绍几种执行 CDP的方法

    Selenium 执行 CDP

    这里指定端口号为 9527

    # -*- coding: utf-8 -*-
    # @Time   : 2022-08-28 23:17
    # @Name   : selenium_cdp_test.py
    
    
    from selenium import webdriver
    from selenium.webdriver.chrome.options import Options
    
    options = Options()
    options.add_experimental_option("debuggerAddress", "127.0.0.1:9527")  # 指定端口为9527
    driver = webdriver.Chrome(options=options)  # 启动浏览器
    # 显示一个弹框,并输出 hello world
    result = driver.execute_cdp_cmd('Runtime.evaluate', {'expression': "alert('hello world')"})
    print(result)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    代码运行效果如下,

    • 显示一个弹框,并输出 hello world


    可能会有疑问,这行代码是什么意思?

    driver.execute_cdp_cmd('Runtime.evaluate', {'expression': "alert('hello world')"})
    
    • 1

    这个时候就需要用到上面的参考网站了,https://chromedevtools.github.io/devtools-protocol/

    • 左侧是对应的 域名 的一个大类
    • Runtime.evaluate 是 计算全局对象上的 expression
    • expression 是我们传进入的 js 语句(这里只有expression 是必选参数,其它的都是可选的~


    当然,也还有上面的 Network.getResponseBody

    • Network.getResponseBody 是 为给定 requestId 请求返回内容
    • requestId 是网络请求的唯一标识符

    更多的用法,可以通过官方文档学习~


    Python使用 requests操作 CDP

    这个方法是用过Selenium的源码中找到的,以调试模式执行Selenium,打上断点,一步步跟下去,找到了它发网络请求的地方~

    \site-packages\selenium\webdriver\remote\remote_connection.py

    再看调试的控制台的信息

    结合两图,总结一下

    • method:POST
    • url:http://localhost:55438(这里的端口号是随机的
    • path:/session/$sessionId/goog/cdp/execute
    • sessionId,service_url,(每启动Selenium都会有该参数,且唯一

    所以可以得出以下规律,

    • 请求连接:{service_url}/session/{session_id}/goog/cdp/execute(其中{xxx}为要填充的内容(这里的端口号是随机生成的,上一篇文章中有说~
    • data:就是我们传进去的表达式,是一个字典,cmd 为指定方法,params 为所需要携带的参数

    你执行以下代码时候,需要替换成自己的 service_urlsession_id

    # -*- coding: utf-8 -*-
    # @Time   : 2022-08-27 12:41
    # @Name   : selenium_cdp_2.py
    
    import time
    import json
    import requests
    
    # 'http://127.0.0.1:{port}/session/{session_id}/goog/cdp/execute'
    url = 'http://127.0.0.1:55623/session/0ce4027784730e1ffb42b334809ed427/goog/cdp/execute'
    # 导航当前页面到给定的URL
    data_1 = {"cmd": "Page.navigate", "params": {"url": "https://www.baidu.com"}}
    
    data_2 = {
        "cmd": "Runtime.evaluate",
        "params": {"expression": "alert('hello world')"}  # 出来一个弹框
    }
    resp = requests.post(url, data=json.dumps(data_1))
    print(resp)
    print(resp.json())
    
    time.sleep(2)
    resp = requests.post(url, data=json.dumps(data_2))
    print(resp)
    print(resp.json())
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    以上代码执行效果如下所示,

    • 先访问了 baidu
    • 然后 弹出一个 弹窗

    但是这个方法有点麻烦,因为需要以调试模式执行,才能知道当前的 session_idservice_url 。下面有个更好的方法~

    Python连接 WebSocket 执行 CDP

    这个方法就简单的多了,还记得前面的文章吗?

    讲到了这个链接:http://127.0.0.1:9527/json

    看图右边的 webSocketDebuggerUrl 参数,这是个 WebSocket 的链接,所以使用 WebSocket 进行连接后,我们也可以执行 CDP。


    这里值得注意是安装 websocket 模块,要按照这以下顺序

    1. pip install webscoket
    2. pip install websocket-client
    # 这里插入代码片
    # -*- coding: utf-8 -*-
    # @Time   : 2022-08-27 12:00
    # @Name   : py_cdp.py
    
    import json
    import requests
    import websocket
    
    
    def websocket_conn():
        # websocket_conn 连接
        resp = requests.get('http://127.0.0.1:9527/json')  # 有不懂的看上一篇文章
        assert resp.status_code == 200
        ws_url = resp.json()[0].get('webSocketDebuggerUrl')
        return websocket.create_connection(ws_url)
    
    
    def execute_cdp(conn: websocket, command: dict):
        # 执行  dp
        conn.send(json.dumps(command))
        # 接受websocket的响应,并将字符串转换为 dict()
        return json.loads(conn.recv())
    
    
    def main():
        conn = websocket_conn()
        # js = "alert('hello world')" # 弹窗 hello world
        # js = "console.log('hello world')" # 控制台打印 hello world
        js = "location.href='https://www.bilibili.com'"  # 页面跳转
        command = {
            'method': 'Runtime.evaluate',  # 处理 传进去的 expression
            'id': int(),	# id需要传一个整型,否则会报错
            'params': {'expression': js}
        }
        resp = execute_cdp(conn, command)
        print(resp)
    
    
    if __name__ == '__main__':
        main()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    运行效果看下面动图,js代码中指定页面跳转到 B站。


    后话

    本次的执行cdp操作就介绍到这里了,
    怎么样,是不是感觉没用的知识又增加了~🎃🎃
    see you.

  • 相关阅读:
    C语言——写一个函数,每调用一次这个函数,就会将num的值增加1
    Vuex使用和v-model语法详细教程
    MATLAB|热力日历图
    [buuctf.reverse] 126-130
    李沐论文精度系列之七:Two-Stream双流网络、I3D
    数据库应用:kylin 部署 达梦数据库DM8
    ol(openlayers)使用记录
    计算机网络篇之MAC地址
    Vue入门介绍
    HTTP详细讲解
  • 原文地址:https://blog.csdn.net/weixin_45081575/article/details/126556995