Selenium自动化测试实战|自动侦测浏览器版本并下载对应的浏览器驱动

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

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

识别本地浏览器版本
下载对应浏览器版本的驱动
解压到对应文件夹
记录到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()
实测,成功打开浏览器!

(文章来源于霍格沃兹测试学院)

更多技术文章http://qrcode.testing-studio.com/f?from=jianshu&url=https://ceshiren.com/t/topic/3822

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249