昨天在群里聊天时,有同学说 Appium 官方支持自动下载兼容的浏览器驱动,想来Selenium也有类似的方法,于是在网上搜索一番。参考了Medium上一篇文章的方法,对步骤进行改进,增加了对多浏览器的支持。

首先,先想好大致上的几个步骤

  1. 识别本地浏览器版本
  2. 下载对应浏览器版本的驱动
  3. 解压到对应文件夹
  4. 记录到mapping.json文件中

接下来就是撸起袖子开干

定义好目录结构

|— config

​ |— mapping.json: 浏览器驱动配置信息

|— driver: 存放浏览器驱动

|— utils

​ |— driver_util.py: 封装的工具包

|— test_search.py: 测试脚本

数据准备

导入第三方库,定义好路径名称等常量

import json
import os
import zipfile
import shutil
import requests
import pathlib
from win32com import client as win_client # 工作目录(当前路径调试时需加上.parent)
BASE_DIR = str(pathlib.Path.cwd())
# BASE_DIR = str(pathlib.Path.cwd().parent) CHROME_DRIVER_BASE_URL = "https://chromedriver.storage.googleapis.com"
EDGE_DRIVER_BASE_URL = "https://msedgedriver.azureedge.net"
CHROME_DRIVER_ZIP = "chromedriver_win32.zip"
EDGE_DRIVER_ZIP = "edgedriver_win64.zip"
CHROME_DRIVER = "chromedriver.exe"
EDGE_DRIVER = "msedgedriver.exe" BROWSER_DRIVER_DIR = str(pathlib.PurePath(BASE_DIR, "driver"))
DRIVER_MAPPING_FILE = os.path.join(BASE_DIR, "config", "mapping.json")

第一步,获取浏览器的版本

Chrome 浏览器有些小版本没有对应版本号的浏览器驱动,需要借助 Query API 查询对应大版本LATEST RELEASE版本,再根据查询对应的浏览器驱动

新版Edge 浏览器每个版本号官网都有对应的驱动下载

Latest Version API
https://chromedriver.storage.googleapis.com/LATEST_RELEASE_{version}Download Chrome Driver API
https://chromedriver.storage.googleapis.com/{version}/chromedriver_win32.zip
https://msedgedriver.azureedge.net/{version}/edgedriver_win64.zip

代码如下

def get_browser_version(file_path):
"""
获取浏览器版本
:param file_path: 浏览器文件路径
:return: 浏览器大版本号
"""
# 判断路径文件是否存在
if not os.path.isfile(file_path):
raise FileNotFoundError(f"{file_path} is not found.")
win_obj = win_client.Dispatch('Scripting.FileSystemObject')
version = win_obj.GetFileVersion(file_path) return version.strip() def get_browser_major_version(file_path):
"""
获取浏览器大版本号
:param file_path: 浏览器文件路径
:return: 浏览器大版本号
"""
browser_ver = get_browser_version(file_path)
browser_major_ver = browser_ver.split(".")[0] return browser_major_ver def get_latest_browser_version(browser_major_ver):
"""
获取匹配大版本的最新release版本
:param browser_major_ver: 浏览器大版本号
:return: 最新release版本号
"""
latest_api = f"{CHROME_DRIVER_BASE_URL}/LATEST_RELEASE_{browser_major_ver}"
resp = requests.get(latest_api)
latest_driver_version = resp.text.strip() return latest_driver_version

第二步,下载浏览器驱动

def download_browser_driver(latest_driver_version, browser_name):
"""
下载浏览器驱动压缩包
:param browser_name: 浏览器名称
:param latest_driver_version: 浏览器的版本号
"""
download_api = None
if browser_name == "Chrome":
download_api = f"{CHROME_DRIVER_BASE_URL}/{latest_driver_version}/{CHROME_DRIVER_ZIP}"
elif browser_name == "Edge":
download_api = f"{EDGE_DRIVER_BASE_URL}/{latest_driver_version}/{EDGE_DRIVER_ZIP}" download_dir = os.path.join(str(BROWSER_DRIVER_DIR), os.path.basename(download_api))
# 下载,设置超时时间20s
resp = requests.get(download_api, stream=True, timeout=20) if resp.status_code == 200:
with open(download_dir, 'wb') as fo:
fo.write(resp.content)
else:
raise Exception("Download chrome driver failed")

第三步,解驱动压缩包

解压后将原压缩包删除

def unzip_driver(browser_major_ver, browser_name):
"""
解压驱动压缩包
:param browser_name: 浏览器名称
:param browser_major_ver: 浏览器大版本号
:return: 驱动文件路径
"""
file_path = None
driver_path = None if browser_name == "Chrome":
file_path = os.path.join(BROWSER_DRIVER_DIR, os.path.basename(CHROME_DRIVER_ZIP))
driver_path = os.path.join(BROWSER_DRIVER_DIR, browser_major_ver, CHROME_DRIVER)
elif browser_name == "Edge":
file_path = os.path.join(BROWSER_DRIVER_DIR, os.path.basename(EDGE_DRIVER_ZIP))
driver_path = os.path.join(BROWSER_DRIVER_DIR, browser_major_ver, EDGE_DRIVER)
browser_driver_dir = os.path.join(BROWSER_DRIVER_DIR, browser_major_ver) # 解压到指定目录
with zipfile.ZipFile(file_path, 'r') as zip_ref:
zip_ref.extractall(browser_driver_dir) return driver_path def remove_driver_zip(browser_name):
"""
删除下载的驱动压缩包
:param browser_name: 浏览器名称
"""
file_path = None
if browser_name == "Chrome":
file_path = os.path.join(BROWSER_DRIVER_DIR, os.path.basename(CHROME_DRIVER_ZIP))
elif browser_name == "Edge":
file_path = os.path.join(BROWSER_DRIVER_DIR, os.path.basename(EDGE_DRIVER_ZIP))
os.remove(file_path)

第四步,读写配置文件信息

def read_driver_mapping_json():
"""
读取 mapping_json
:return: 字典格式
"""
if os.path.exists(DRIVER_MAPPING_FILE):
with open(DRIVER_MAPPING_FILE) as fo:
try:
driver_mapping_dict = json.load(fo)
# mapping.json内容为空时,返回空字典
except json.decoder.JSONDecodeError:
driver_mapping_dict = {}
else:
raise FileNotFoundError(f"{DRIVER_MAPPING_FILE} is not found") return driver_mapping_dict def write_driver_mapping_json(browser_major_ver, latest_driver_version, driver_path, browser_name):
"""
写入 mapping_json
:param browser_major_ver: 浏览器大版本号
:param latest_driver_version: 浏览器驱动版本号
:param driver_path: 驱动存放路径
:param browser_name: 浏览器名称
"""
mapping_dict = read_driver_mapping_json()
# 版本号在dict中(浏览器名不在dict中)
if browser_major_ver in mapping_dict: mapping_dict[browser_major_ver][browser_name] = {
"driver_path": driver_path,
"driver_version": latest_driver_version
}
# 大版本号不在dict中,且字典不为空
elif browser_major_ver not in mapping_dict and mapping_dict:
mapping_dict[browser_major_ver] = {
browser_name:
{
"driver_path": driver_path,
"driver_version": latest_driver_version
}
}
# 字典为空
else:
mapping_dict = {
browser_major_ver:
{
browser_name:
{
"driver_path": driver_path,
"driver_version": latest_driver_version
}
}
}
mapping_dict.update(mapping_dict) with open(DRIVER_MAPPING_FILE, 'w') as fo:
json.dump(mapping_dict, fo)

综合

将以上步骤整合到automatic_discover_driver函数中,通过调用该函数返回浏览器驱动路径

def automatic_discover_driver(browser_path, browser_name="Chrome"):
"""
侦测浏览器驱动是否在mapping.json有记录,否则下载该驱动
:param browser_path: 浏览器路径
:param browser_name: 浏览器名称
"""
browser_maj_ver = get_browser_major_version(browser_path)
# Chrome需要获取大版本号对应的latest release version
# Edge 可直接用当前浏览器版本号
if browser_name == "Chrome":
latest_browser_ver = get_latest_browser_version(browser_maj_ver)
elif browser_name == "Edge":
latest_browser_ver = get_browser_version(browser_path)
else:
raise Exception(f"{browser_name} is not found") # 读取mapping.json内容
mapping_dict = read_driver_mapping_json() # json为空 或版本号不在mapping_dict中 或浏览器名不在mapping_dict中
if not mapping_dict or \
browser_maj_ver not in mapping_dict or \
browser_name not in mapping_dict[browser_maj_ver]: # 下载浏览器驱动压缩包
download_browser_driver(latest_browser_ver, browser_name)
# 解压浏览器驱动压缩包,并返回驱动路径
driver_path = unzip_driver(browser_maj_ver, browser_name)
# 将浏览器大版本号、浏览器名、驱动路径、对应的浏览器版本号信息写入到mapping.json中
write_driver_mapping_json(browser_maj_ver, latest_browser_ver, driver_path, browser_name) # 删除浏览器驱动压缩包
remove_driver_zip(browser_name) # 返回浏览器驱动的路径
mapping_dict = read_driver_mapping_json()
return mapping_dict[browser_maj_ver][browser_name]["driver_path"]

测试

创建一个test_search.py文件验证是否可以自动下载对应的浏览器驱动

import pytest
from time import sleep
from selenium import webdriver
from utils.driver_util import automatic_discover_driver as automatic class TestSearch:
_CHROME_PATH = r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
_EDGE_PATH = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
_browser = "Edge" def setup(self):
driver_path = automatic(self._EDGE_PATH, self._browser)
if self._browser == "Chrome":
self.driver = webdriver.Chrome(driver_path)
elif self._browser == "Edge":
self.driver = webdriver.Edge(driver_path) def teardown(self):
self.driver.close()
self.driver.quit() def test_search_bing(self):
self.driver.get("https://cn.bing.com/")
self.driver.find_element_by_id("sb_form_q").send_keys("selenium")
self.driver.find_element_by_id("sb_go_par").click()
sleep(3) if __name__ == '__main__':
pytest.main()

实测,成功打开浏览器!

详细代码:https://github.com/felixzfq/AutomaticDiscoverBrowserDriver

参考资料

[Selenium] 自动侦测浏览器版本并下载对应的浏览器驱动的更多相关文章

  1. selenium2 浏览器版本问题

    一.chrome浏览器 chrome浏览器与驱动版本对应关系 ----------ChromeDriver v2.26 (2016-12-09)---------- Supports Chrome v ...

  2. 这是一个用于判断IE浏览器版本的紧凑脚本

    这是一个用于判断IE浏览器版本的紧凑脚本IE浏览器,不管它们是什么版本,总是与Web标准有些不兼容.对于编码人员来说,这很困难.为了考虑IE的兼容性,不管它是写CSS还是写JS,IE通常都会被特殊处理 ...

  3. 【Selenium】之谷歌、IE、火狐浏览器各个版本的浏览器驱动下载地址

    地址:chromedriver官网下载地址: http://chromedriver.storage.googleapis.com/index.html(失效了) http://npm.taobao. ...

  4. webdriver浏览器版本驱动对应以及下载

    对于webdriver和各个浏览器的版本的对应,我最近发现浏览器驱动的对应在selenium库的源码里都有提及,路径是:python>site-packages>selenium>w ...

  5. 爬虫篇-如何下载selenium及其适配谷歌浏览器插件chromedriver(含chrome各版本及下载地址)

    最近换了电脑,练习爬虫时用到selenium,结果在重新安装chromedriver插件的时候发现原网址不能使用,找了好久终于找到了了新网址,顺便更一篇详细使用的文章,希望可以对屏幕前的你有所帮助.本 ...

  6. Google Chrome浏览器各版本直接下载地址

    Google Chrome浏览器各版本直接下载地址  2012.04.12珍藏软件  10161 Views  0 Comments 现在所用的主浏览器Google Chrome,在其官方主页上默认只 ...

  7. ASP.NET MVC中检测浏览器版本并提示下载更新

    如果网站使用html5.css3.自适应等新特性,可能有些浏览器版本不支持.这时候,需要提醒浏览者更新浏览器的版本到最新. 本篇用到的插件为:http://jreject.turnwheel.com/ ...

  8. 使用Selenium时,如何选择ChromeDriver驱动版本对应Chrome浏览器版本

      ChromeDriver版本 支持的Chrome版本 v2.46 v72-74 v2.45 v71-73 v2.44 v70-72 v2.43 v69-71 v2.42 v68-70 v2.41 ...

  9. selenium driver版本和Chrome浏览器版本对应关系

    ChromeDriver v2.41 (2018-07-27) ---- Chrome v67-69ChromeDriver v2.40 (2018-06-07) ---- Chrome v66-68 ...

随机推荐

  1. frame/iframe多表单切换

    应用场景: 在Web应用中经常会遇到frame/iframe表单嵌套页面的应用,WebDriver只能在一个页面上对元素识别与定位,对于frame/iframe表单内嵌页面上的元素无法直接定位.这时就 ...

  2. SpringCloud-Gateway 网关路由、断言、过滤

    Gateway 简介 是什么? Spring Cloud 全家桶中有个很重要的组件:网关.在 1.x 版本中使用的是 Zuul 网关,但是到了 2.x,由于Zuul的升级不断跳票,Spring Clo ...

  3. php环境兼容性问题---压缩格式及其配置简介

    php环境兼容性问题-- 内容编码错误 无法显示您尝试查看的页面,因为它使用了无效或者不支持的压缩格式. 请联系网站的所有者以告知此问题. 以前也遇到过同样的问题,记得是PHP代码ob_start(' ...

  4. Test Test...

    标题: Test(一级标题) Test(二级标题) Test(三级标题) 列表: test(列表) Alpha Beta Gamma test 2 Delte Epsilon 链接: 点兔成金斐波那契 ...

  5. [Qt] 文本文件读写, 摘自官方文档

    Reading Files Directly The following example reads a text file line by line: QFile file("in.txt ...

  6. 如何高效使用vim

    Vim 是一款文本编辑器,被称为编辑器之神,非常适合在shell 中编辑代码,熟练的使用Vim,可以让你高效的编写代码. Vim 是Vi 的增强版,所有的类Unix 系统,都自带这两个工具,这两个工具 ...

  7. Shutdown SpringBoot App

    文章目录 Shutdown Endpoint close Application Context 退出SpringApplication 从外部程序kill App Shutdown SpringBo ...

  8. WLAN 无线网络 03 - RF 基础

    射频(Radio frequency),又称无线电频率.无线射频.高周波,常被用来当成无线电的同义词,为在3 kHz至300 GHz这个范围内的震荡频率,这个频率相当于无线电波的频率,以及携带着无线电 ...

  9. Linux运维面试题:请简要说明Linux系统在目标板上的启动过程?

    Linux运维面试题:请简要说明Linux系统在目标板上的启动过程? 该问题是Linux运维面试最常见的问题之一,问题答案如下: 1.用户打开PC的电源,BIOS开机自检,按BIOS中设置的启动设备( ...

  10. [开发笔记]-unix时间戳、GMT时间与datetime类型时间之前的转换

    前段时间项目中涉及到了MySql和MsSql数据类型之间的转换,最近又在研究新浪微博的API,涉及到了带有时区的GMT时间类型的转换,所以,特记录于此,以备日后查询. 一:UNIX时间戳与dateti ...