Python 之自动下载更新 selenium 驱动 chromedriver

用 selenium 做过自动化的都知道,由于谷歌游览器经常自动更新导致 selenium 驱动失效需要重新下载解压替换(当然,我们也可以禁用浏览器的自动更新),但这个替换动作手动去做的话又十分麻烦。所以最好是在谷歌浏览器和 chromedriver 版本不匹配时自动下载匹配的 chromedriver.exe 就好了。

谷歌浏览器版本获取

selenium

我们可以先随意找一个 chromedriver,然后基于这个 chromedriver 使用 selenium 去尝试初始化 driver,如果版本不匹配的话,会有如下报错提示,然后我们就可以获取到当前谷歌浏览器的版本信息了。

python 复制代码
from selenium import webdriver

try:
    driver = webdriver.Chrome(executable_path="./driver/chromedriver.exe")
except Exception as e:
    print(str(e))
python 复制代码
Message: session not created: This version of ChromeDriver only supports Chrome version 140
Current browser version is 120.0.6099.130 with binary path C:\Program Files\Google\Chrome\Application\chrome.exe

chrome_version

上面的方法很直接,唯一的缺点是我必须要先有一个 chromedriver,假如我还没有现成的 chromedriver 但又想获取浏览器版本就不方便了。不过我们可以直接使用 chrome-version 来获取谷歌浏览器版本。

python 复制代码
pip install chrome-version

使用时直接调用 get_chrome_version() 函数即可。

python 复制代码
import chrome_version

version = chrome_version.get_chrome_version()
print(version)  # 120.0.6099.130

其源码也很简单,针对 Windows 系统,它从浏览器安装后的注册表中获取 DisplayVersion 版本并正则提取。

python 复制代码
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome
    DisplayName    REG_SZ    Google Chrome
    UninstallString    REG_SZ    "C:\Program Files\Google\Chrome\Application\120.0.6099.130\Installer\setup.exe" --uninstall --channel=stable --system-level --verbose-logging
    InstallLocation    REG_SZ    C:\Program Files\Google\Chrome\Application
    DisplayIcon    REG_SZ    C:\Program Files\Google\Chrome\Application\chrome.exe,0
    NoModify    REG_DWORD    0x1
    NoRepair    REG_DWORD    0x1
    Publisher    REG_SZ    Google LLC
    Version    REG_SZ    120.0.6099.130
    DisplayVersion    REG_SZ    120.0.6099.130
    InstallDate    REG_SZ    20231222
    VersionMajor    REG_DWORD    0x17d3
    VersionMinor    REG_DWORD    0x82

playwright

playwright 也可以在不使用 chromedriver 的情况下获取浏览器的版本信息。

python 复制代码
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(executable_path='C:\Program Files\Google\Chrome\Application\chrome.exe')
    print(browser.version)  # 120.0.6099.130

DrissionPage

DrissionPage 也可以在不使用 chromedriver 的情况下获取到浏览器的版本信息。

python 复制代码
from DrissionPage import ChromiumPage, ChromiumOptions

co = ChromiumOptions()
co.headless()
page = ChromiumPage(co)
print(page.browser_version)  # Chrome/120.0.6099.130

匹配版本最接近的 chromedriver

一般来说,谷歌浏览器的版本和 chromedriver 的版本并不完全是一对一的关系。浏览器先发布,后续才更新的 chromedriver 版本,一个版本的 chromedriver 可能支持多个浏览器版本。比如我浏览器版本是 120.0.6099.130,但是并没有这个版本的 chromedriver。

所以查找时,我们可以查找主版本与其一致,但子版本与之最接近的版本作为对应的 chromedriver 的版本。

python 复制代码
import re
import itertools
import requests
import chrome_version


def get_chromedriver_version(browser_version):
    res = requests.get("https://registry.npmmirror.com/-/binary/chrome-for-testing/")
    results = res.json()
    # 筛选满足要求的 version
    versions = [obj["name"][:-1] for obj in results if re.match("\d+", obj["name"]) and obj["name"].count(".") == 3]
    # 先排序,将相邻版本的数据放在一块
    versions = sorted(versions)
    versions = {key: list(versions_split) for key, versions_split in itertools.groupby(versions, key=lambda x: x[:x.rfind(".")])}
    # 取前3个版本作为必须匹配的主版本
    main_version = browser_version[:browser_version.rfind(".")]
    sub_version = browser_version[browser_version.rfind(".") + 1:]
    for version, version_list in versions.items():
        if main_version == version:
            # 在主版本完全匹配的情况下,匹配最接近的子版本并返回
            diff_dict = {abs(int(sub_version) - int(v[v.rfind(".") + 1:])): v for v in version_list}
            # print(diff_dict)
            min_diff = min(diff_dict.keys())
            return diff_dict[min_diff]
    return ''


if __name__ == '__main__':
    # browser_version = "120.0.6099.130"
    browser_version = chrome_version.get_chrome_version()
    print(f"browser_version: {browser_version}")
    chromedriver_version = get_chromedriver_version(browser_version)
    print(f"chromedriver_version: {chromedriver_version}")
python 复制代码
browser_version: 120.0.6099.130
chromedriver_version: 120.0.6099.109

下载对应版本的 chromedriver

查到对应的版本后,我们就可以从镜像地址 https://registry.npmmirror.com/binary.html?path=chrome-for-testing 下载 chromedriver 了。

由于下载的文件是 zip 形式的压缩包,所以还需要我们提取到指定的目录。

python 复制代码
import os
import re
import itertools
import zipfile
import shutil
import requests
import chrome_version


def get_chromedriver_version(browser_version):
    res = requests.get("https://registry.npmmirror.com/-/binary/chrome-for-testing/")
    results = res.json()
    # 筛选满足要求的 version
    versions = [obj["name"][:-1] for obj in results if re.match("\d+", obj["name"]) and obj["name"].count(".") == 3]
    # 先排序,将相邻版本的数据放在一块
    versions = sorted(versions)
    versions = {key: list(versions_split) for key, versions_split in itertools.groupby(versions, key=lambda x: x[:x.rfind(".")])}
    # 取前3个版本作为必须匹配的主版本
    main_version = browser_version[:browser_version.rfind(".")]
    sub_version = browser_version[browser_version.rfind(".") + 1:]
    for version, version_list in versions.items():
        if main_version == version:
            # 在主版本完全匹配的情况下,匹配最接近的子版本并返回
            diff_dict = {abs(int(sub_version) - int(v[v.rfind(".") + 1:])): v for v in version_list}
            # print(diff_dict)
            min_diff = min(diff_dict.keys())
            return diff_dict[min_diff]
    return ''


def download_chromedriver(chromedriver_version, output_dir):
    try:
        os.makedirs(output_dir, exist_ok=True)
        if os.path.exists(os.path.join(output_dir, "chromedriver.exe")):
            os.remove(os.path.join(output_dir, "chromedriver.exe"))
        file = f"chromedriver-win64.zip"
        url = f"https://cdn.npmmirror.com/binaries/chrome-for-testing/{chromedriver_version}/win64/{file}"
        res = requests.get(url)
        with open(file, 'wb') as f:
            f.write(res.content)
        with zipfile.ZipFile(file, 'r') as zipf:
            zipf.extract("chromedriver-win64/chromedriver.exe", output_dir)
        shutil.move(os.path.join(output_dir, "chromedriver-win64/chromedriver.exe"), output_dir)
        shutil.rmtree(os.path.join(output_dir, "chromedriver-win64"))
        return True
    except Exception as e:
        print(f"文件下载和提取失败:{str(e)}")
        return False



if __name__ == '__main__':
    # browser_version = "120.0.6099.130"
    browser_version = chrome_version.get_chrome_version()
    print(f"browser_version: {browser_version}")
    chromedriver_version = get_chromedriver_version(browser_version)
    print(f"chromedriver_version: {chromedriver_version}")
    output_dir = "./driver"
    download_chromedriver(chromedriver_version, output_dir)

校验下载的 chromedriver

我们可以尝试使用新更新的 chromedriver.exe 来打开浏览器并访问网页,看是否还会报错。

python 复制代码
import time

from selenium import webdriver

driver = webdriver.Chrome(executable_path="./driver/chromedriver.exe")
try:
    driver.get("https://www.baidu.com/")
    time.sleep(10)
except Exception as e:
    print(str(e))
finally:
    driver.quit()

发现能正常打开百度页面,说明当前的 chromedriver 版本是和浏览器匹配的。

注意: 校验结束后记得一定要 driver.quit() 退出 chromedriver,否则可能会出现进程残留导致后续无法删除和替换 chromedriver.exe 文件。

python 复制代码
文件下载和提取失败:[WinError 5] 拒绝访问。: './driver\\chromedriver.exe'
相关推荐
yzx9910132 小时前
使用Python构建交易回撤分析器:Trad与Claw模块实战
开发语言·python
一晌小贪欢2 小时前
PyQt5 + Pandas 打造常见的表格(Excel/CSV)读取与处理工具
python·qt·excel·pandas·python办公·excel处理
小龙在山东2 小时前
基于 ahocorasick 实现 多模式字符串匹配
python
疋瓞2 小时前
C\C++\python对比_概览(1)
c语言·c++·python
不光头强2 小时前
Java网络爬虫
java·爬虫·python
2401_891482172 小时前
用Python批量处理Excel和CSV文件
jvm·数据库·python
m0_743297422 小时前
使用Flask快速搭建轻量级Web应用
jvm·数据库·python
技术工小李2 小时前
多人平板答题系统护航第24届汉语桥比赛
python
董可伦2 小时前
Flink DataStream2Table 总结
服务器·python·flink