ZetCode

Python WebDriver

最后修改于 2025 年 3 月 6 日

在本文中,我们将展示如何使用 Python 中的 WebDriver 自动化 web 浏览器。WebDriver 是一个远程控制接口,可以对用户代理(即 web 浏览器)进行自省和控制。它提供了一个平台和语言无关的线路协议,供进程外的程序远程指示 web 浏览器的行为。

WebDriver 规范定义了一个类似 REST 的 API,用于与 web 浏览器交互。 此 REST API 使用标准的 HTTP 方法(GET、POST、DELETE)来对浏览器会话、网页上的元素和其他浏览器相关资源执行操作。 每个操作都与特定的 URL 端点相关联,WebDriver 客户端与 WebDriver 服务器通信以执行命令并检索结果。

ChromeDriver 是一个独立的服务器,它实现了 Google Chrome 的 WebDriver 协议。 它充当用 Python、Java 等语言编写的测试脚本和 Chrome 浏览器之间的桥梁。 当您运行自动化测试时,ChromeDriver 会与 Chrome 通信以执行您指定的命令和操作。 它支持各种功能,例如无头模式、移动设备模拟等。

安装 ChromeDriver

在使用 WebDriver 之前,您需要下载 ChromeDriver。 您可以从以下链接下载它

下载 ChromeDriver
https://googlechromelabs.github.io/chrome-for-testing/#stable

确保 ChromeDriver 的版本与您的 Chrome 浏览器版本匹配。 您可以在此处查看 ChromeDriver 的实施状态

ChromeDriver 状态
https://chromium.googlesource.com/chromium/src/+/master/docs/chromedriver_status.md

获取 ChromeDriver 状态

以下示例演示了如何使用 REST API 检查 ChromeDriver 的状态。

main.py
import requests

def get_chrome_driver_status():
    url = "https://:9515/status"

    try:
        response = requests.get(url)
        print(response)

        code = response.status_code

        if code == 200:
            print("OK - 200")
        else:
            print(f"Failed. HTTP Status Code: {code}")

    except requests.exceptions.RequestException as e:
        print(f"An error occurred while connecting to ChromeDriver: {e}")

if __name__ == "__main__":
    get_chrome_driver_status()

在此脚本中,我们使用 requests 库通过向 https://:9515/status/status 端点发送 HTTP GET 请求,直接与 ChromeDriver 的 REST API 交互。 此端点由 ChromeDriver 服务器提供,该服务器必须在本地端口 9515(默认端口)上运行才能使此脚本正常工作——通常,您会在执行此代码之前通过命令行手动启动 ChromeDriver(例如,chromedriver)。 response 对象包含服务器的回复,我们通过打印它来查看详细信息,例如标头和内容。

然后,我们使用 response.status_code 提取 HTTP 状态代码。状态代码 200 表示 ChromeDriver 正在运行并准备好接受命令,而其他代码(例如,404 或 500)表示服务器未运行或配置错误等问题。 try-except 块捕获潜在错误,例如网络故障或 ChromeDriver 未激活,并打印描述性消息。此示例是一个简单的诊断工具,用于在运行更复杂的自动化任务之前验证 ChromeDriver 的可用性。

获取网页标题

此示例演示了如何使用 WebDriver REST API 检索网页的标题。

main.py
import requests

def get_webpage_title(url):
    chromedriver_url = "https://:9515"

    try:
        session_response = requests.post(f"{chromedriver_url}/session", json={
            "capabilities": {
                "alwaysMatch": {
                    "browserName": "chrome",
                    "goog:chromeOptions": {
                        "args": ["--headless"]
                    }
                }
            }
        })
        session_response.raise_for_status()
        session_data = session_response.json()
        session_id = session_data["value"]["sessionId"]

        print(f"Session created with ID: {session_id}")

        navigate_response = requests.post(
            f"{chromedriver_url}/session/{session_id}/url",
            json={"url": url}
        )
        navigate_response.raise_for_status()

        print(f"Navigated to: {url}")

        title_resp = requests.get(
            f"{chromedriver_url}/session/{session_id}/title",
        )
        title_resp.raise_for_status()
        title_data = title_resp.json()
        title = title_data["value"]

        print(f"The title of the webpage is: {title}")

        delete_session_response = requests.delete(
            f"{chromedriver_url}/session/{session_id}")
        delete_session_response.raise_for_status()

        print("Session deleted successfully.")

    except requests.exceptions.RequestException as e:
        print(f"An error occurred while interacting with ChromeDriver: {e}")

if __name__ == "__main__":
    target_url = "https://example.com/"
    get_webpage_title(target_url)

此脚本演示了如何使用 ChromeDriver 的 REST API 自动化浏览器会话并检索网页的标题。 它首先向 /session 发送 POST 请求以创建新的浏览器会话,指定 Chrome 作为浏览器并启用无头模式(通过 --headless)以在没有可见 UI 的情况下运行,这非常适合在服务器或 CI/CD 管道上进行自动化测试。 响应提供了一个 sessionId,它是此会话的唯一标识符,我们提取该标识符并在后续请求中使用。 接下来,向 /session/{session_id}/url 发送 POST 请求,将浏览器导航到目标 URL(例如,https://example.com/),模拟用户输入地址。

然后,向 /session/{session_id}/title 发送 GET 请求,获取页面的标题(例如,“Example Domain”),该标题以 JSON 格式返回并从 "value" 键中提取。 最后,向 /session/{session_id} 发送 DELETE 请求以终止会话,确保释放资源。 每个请求都使用 raise_for_status() 来检查 HTTP 错误(例如,404 或 500),并且 try-except 块捕获更广泛的问题,例如网络故障或 ChromeDriver 未运行。 中间打印语句提供有关每个步骤的反馈,使其成为通过 WebDriver 协议进行会话管理和基本页面交互的强大示例。

单击按钮

此示例演示了如何使用 WebDriver 单击网页上的按钮。

main.py
import requests

def click_button_on_page(url):
    chromedriver_url = "https://:9515"

    try:
        session_response = requests.post(f"{chromedriver_url}/session", json={
            "capabilities": {
                "alwaysMatch": {
                    "browserName": "chrome",
                    "goog:chromeOptions": {
                        "args": ["--headless"]
                    }
                }
            }
        })
        session_response.raise_for_status()
        session_data = session_response.json()
        session_id = session_data["value"]["sessionId"]

        print(f"Session created with ID: {session_id}")

        navigate_response = requests.post(
            f"{chromedriver_url}/session/{session_id}/url",
            json={"url": url}
        )
        navigate_response.raise_for_status()

        print(f"Navigated to: {url}")

        find_element_response = requests.post(
            f"{chromedriver_url}/session/{session_id}/element",
            json={"using": "tag name", "value": "button"}
        )
        find_element_response.raise_for_status()
        element_data = find_element_response.json()
        button_element_id = element_data["value"]["element-6066-11e4-a52e-4f735466cecf"]

        print(f"Button element found with ID: {button_element_id}")

        click_response = requests.post(
            f"{chromedriver_url}/session/{session_id}/element/{button_element_id}/click",
            json={}
        )
        click_response.raise_for_status()

        print("Button clicked successfully.")

        delete_session_response = requests.delete(
            f"{chromedriver_url}/session/{session_id}")
        delete_session_response.raise_for_status()

        print("Session deleted successfully.")

    except requests.exceptions.RequestException as e:
        print(f"An error occurred while interacting with ChromeDriver: {e}")

if __name__ == "__main__":
    target_url = "https://webcode.me/click.html"
    click_button_on_page(target_url)

此脚本展示了如何在无头模式下使用 ChromeDriver 的 REST API 在网页上查找和单击按钮。 它首先使用 POST 请求向 /session 创建一个新的 Chrome 会话,指定 --headless 以隐形运行,并检索 sessionId 以供后续命令使用。

然后,浏览器通过向 /url 发送 POST 请求导航到 https://webcode.me/click.html — 一个带有按钮的简单测试页面。 为了找到该按钮,向 /element 发送 POST 请求,使用“标签名称”定位器策略,其中 "button" 作为值,返回第一个 <button> 元素的唯一 ID(例如,element-6066-11e4-a52e-4f735466cecf),它遵循 WebDriver 的元素引用格式。 然后,向 /element/{button_element_id}/click 发送 POST 请求,模拟鼠标单击按钮,从而触发任何关联的操作(例如,测试页面上的 JavaScript 事件)。 会话通过向 /session/{session_id} 发送 DELETE 请求来清理。

通过 raise_for_status()try-except 块进行错误处理可确保可靠性,而打印语句可确认每个步骤。 此示例重点介绍元素交互,但在实践中,对于具有多个按钮的复杂页面,可能需要更具体的定位器(例如,ID 或 CSS 选择器)。

截取屏幕截图

此示例显示了如何使用 WebDriver 截取网页的屏幕截图。

main.py
import requests
import base64

def get_webpage_screenshot(url):
    chromedriver_url = "https://:9515"

    try:
        session_response = requests.post(f"{chromedriver_url}/session", json={
            "capabilities": {
                "alwaysMatch": {
                    "browserName": "chrome",
                    "goog:chromeOptions": {
                        "args": ["--headless"]
                    }
                }
            }
        })
        session_response.raise_for_status()
        session_data = session_response.json()
        session_id = session_data["value"]["sessionId"]

        print(f"Session created with ID: {session_id}")

        navigate_response = requests.post(
            f"{chromedriver_url}/session/{session_id}/url",
            json={"url": url}
        )
        navigate_response.raise_for_status()

        print(f"Navigated to: {url}")

        screenshot_response = requests.get(
            f"{chromedriver_url}/session/{session_id}/screenshot"
        )
        screenshot_response.raise_for_status()
        screenshot_data = screenshot_response.json()
        screenshot_base64 = screenshot_data["value"]

        with open("screenshot.png", "wb") as f:
            f.write(base64.b64decode(screenshot_base64))

        print("Screenshot saved as screenshot.png")

        delete_session_response = requests.delete(
            f"{chromedriver_url}/session/{session_id}")
        delete_session_response.raise_for_status()

        print("Session deleted successfully.")

    except requests.exceptions.RequestException as e:
        print(f"An error occurred while interacting with ChromeDriver: {e}")

if __name__ == "__main__":
    target_url = "https://example.com/"
    get_webpage_screenshot(target_url)

此脚本演示了如何在无头模式下使用 ChromeDriver 的 REST API 捕获网页屏幕截图。 它使用 POST 请求向 /session 启动会话,配置 Chrome 以无头方式运行,并检索 sessionId。 浏览器通过向 /url 发送 POST 请求导航到 https://example.com/,从而隐形地加载页面。 然后,向 /screenshot 发送 GET 请求,以捕获可见的视口作为 base64 编码的字符串,该字符串在 JSON 响应的 "value" 键中返回。

该脚本使用 base64.b64decode 解码此字符串,并以二进制模式 ("wb") 将其写入名为 screenshot.png 的文件中,从而在工作目录中创建可查看的 PNG 图像。 会话通过向 /session/{session_id} 发送 DELETE 请求来终止。 错误处理确保如果 ChromeDriver 未运行或出现网络问题,脚本会正常失败,而打印语句会跟踪进度。 此功能对于可视化测试、调试或存档网页状态特别有用,但屏幕截图的大小取决于无头浏览器的默认窗口尺寸。

最大化浏览器窗口

此示例演示了如何使用 WebDriver 最大化浏览器窗口。

main.py
import requests
import time

def maximize_browser_window(url):
    chromedriver_url = "https://:9515"

    try:
        session_response = requests.post(f"{chromedriver_url}/session", json={
            "capabilities": {
                "alwaysMatch": {
                    "browserName": "chrome",
                }
            }
        })
        session_response.raise_for_status()
        session_data = session_response.json()
        session_id = session_data["value"]["sessionId"]

        print(f"Session created with ID: {session_id}")

        navigate_response = requests.post(
            f"{chromedriver_url}/session/{session_id}/url",
            json={"url": url}
        )
        navigate_response.raise_for_status()

        print(f"Navigated to: {url}")

        time.sleep(5)

        maximize_response = requests.post(
            f"{chromedriver_url}/session/{session_id}/window/maximize",
            json={}
        )
        maximize_response.raise_for_status()

        print("Browser window maximized.")

        time.sleep(3)

        delete_session_response = requests.delete(
            f"{chromedriver_url}/session/{session_id}")
        delete_session_response.raise_for_status()

        print("Session deleted successfully.")

    except requests.exceptions.RequestException as e:
        print(f"An error occurred while interacting with ChromeDriver: {e}")

if __name__ == "__main__":
    target_url = "https://example.com/"
    maximize_browser_window(target_url)

此脚本演示了如何使用 WebDriver REST API 最大化 Chrome 浏览器窗口,该窗口在可见模式下运行(没有 --headless 标志)。 它首先向 /session 发送 POST 请求来创建会话,获取 sessionId,然后使用 POST 向 /url 导航到 https://example.com/

time.sleep(5) 暂停,使用户可以在最大化之前观察初始窗口大小 — 这对于演示很有用,但在生产环境中是可选的。 关键操作是向 /window/maximize 发送 POST 请求,该请求会调整浏览器大小以填充屏幕,从而模拟用户单击最大化按钮。

另一个 time.sleep(3) 允许在会话以向 /session/{session_id} 发送 DELETE 请求结束之前观察最大化状态。 通过 raise_for_status()try-except 进行错误处理可确保可靠性,而打印语句可确认每个步骤。 此功能有助于确保测试中一致的视口大小或捕获全页屏幕截图(如果在屏幕截图命令之后),尽管其效果仅在非无头模式下可见。

来源

WebDriver 文档

在本文中,我们探讨了使用 Python 中的 WebDriver 自动化 web 浏览器的基础知识。

作者

我叫 Jan Bodnar,是一位充满热情的程序员,拥有丰富的编程经验。 我从 2007 年开始撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 Python 教程