Skip to content

Instantly share code, notes, and snippets.

@shakahl
Forked from jslay88/download_chromedriver.py
Created July 23, 2025 14:16
Show Gist options
  • Save shakahl/dde266032b8be1876b4ac33be4227ae3 to your computer and use it in GitHub Desktop.
Save shakahl/dde266032b8be1876b4ac33be4227ae3 to your computer and use it in GitHub Desktop.
Download Latest chromedriver. Win/Linux/Mac. 32/64bit
import os
import re
import zipfile
import logging
import requests
import xml.etree.ElementTree as ET
from io import BytesIO
logger = logging.getLogger(__name__)
def discover_system():
import sys
return {
'linux': 'linux',
'linux2': 'linux',
'darwin': 'mac',
'win32': 'win',
'cygwin': 'win'
}[sys.platform.lower()]
def discover_arch():
import platform
return '64' if platform.machine().endswith('64') else '32'
def discover_chrome_version():
import subprocess
system = discover_system()
version = None
if system == 'win':
cmd_args = [
'wmic',
'datafile', 'where',
'name="C:\\\\Program Files (x86)\\\\Google\\\\Chrome\\\\Application\\\\chrome.exe"',
'get', 'Version', '/value'
]
process = subprocess.run(cmd_args, capture_output=True)
pattern = re.compile(r'Version=(\d+\.\d+\.\d+)')
version_match = pattern.search(process.stdout.decode('utf-8'))
if version_match:
version = version_match.group(1)
elif args.system == 'linux':
pass
elif args.system == 'mac':
pass
if version is not None:
version = tuple((int(version_number) for version_number in version.split('.')[:3]))
return version
def discover_latest_chromedriver(system, arch='64'):
if system == 'win':
arch = '32'
download_filename = 'chromedriver_{system}{arch}.zip'.format(system=system, arch=arch)
logger.debug('Download Filename: {filename}'.format(filename=download_filename))
url = 'https://chromedriver.storage.googleapis.com/'
resp = requests.get(url)
root = ET.fromstring(resp.content)
namespace = {'default': re.match(r'^{([^}]*)}.*$', root.tag)[1]}
latest = None
version_pattern = re.compile(r'^(\d+)\.?(\d*)\.?(\d*)\.?(\d*)/' + download_filename + '$',
re.IGNORECASE)
for content in root.findall('default:Contents', namespace):
matches = version_pattern.match(content.find('default:Key', namespace).text)
if matches:
matches = (int(matches.group(1)) if matches.group(1) else 0,
int(matches.group(2)) if matches.group(2) else 0,
int(matches.group(3)) if matches.group(3) else 0,
int(matches.group(4)) if matches.group(4) else 0)
logger.debug('Discovered v{version}'.format(version='.'.join(str(match) for match in matches)))
if latest is None:
latest = (content, matches)
else:
if matches[0] < latest[1][0]:
continue
if matches[0] > latest[1][0]:
latest = (content, matches)
elif matches[1] < latest[1][1]:
continue
elif matches[1] > latest[1][1]:
latest = (content, matches)
elif matches[2] < latest[1][2]:
continue
elif matches[2] > latest[1][2]:
latest = (content, matches)
elif matches[3] < latest[1][3]:
continue
elif matches[3] > latest[1][3]:
latest = (content, matches)
return url + latest[0].find('default:Key', namespace).text, latest[1]
def download_chromedriver(system, arch='64', latest_version_version=None, extract_name=None, extract_path=None):
"""
Download the latest chromedriver binary for the specified system
:param system: win, linux, or mac
:type system: str
:param arch: 32 or 64
:type arch: str
:param latest_version_version: Grab a particular latest version (74,0,3729)
(Use first 3 version numbers of a Chrome build v70+)
:type latest_version_version: tuple of int
:param extract_name: Override binary name on extraction
:type extract_name: str
:param extract_path: Override extraction path. Default is current working directory.
:type extract_path: str
:return: Extracted binary path
:rtype: str
"""
if system == 'win':
arch = '32'
if extract_path is None:
extract_path = os.path.realpath(os.getcwd())
logger.info('Updating chromedriver...')
if latest_version_version is None:
download_url, download_version = discover_latest_chromedriver(system, arch)
else:
url = 'https://chromedriver.storage.googleapis.com/LATEST_RELEASE_'
resp = requests.get(url + '.'.join(str(v for v in latest_version_version[:3])))
if resp.status_code == 404:
resp = requests.get(url + str(latest_version_version[0]))
if resp.status_code == 404:
raise FileNotFoundError('Unable to find a suitable chromedriver for v{version}!'.format(
version='.'.join(str(v for v in latest_version_version))))
download_url = 'https://chromedriver.storage.googleapis.com/{version}/chromedriver_{system}{arch}.zip'.format(
version=resp.content.decode('utf-8'),
system=system,
arch=arch
)
download_version = tuple(int(v) for v in resp.content.decode('utf-8').split('.'))
logger.info('Downloading chromedriver v{version}...'.format(version=".".join((str(x) for x in download_version))))
resp = requests.get(download_url)
data = BytesIO(resp.content)
logger.info('Download Complete!')
logger.info('Extracting Archive...')
with zipfile.ZipFile(data) as chromedriver_zip:
file_info = chromedriver_zip.infolist()[0]
logger.info('Found {filename} in archive.'.format(filename=file_info.filename))
data = chromedriver_zip.read(file_info.filename)
if extract_name is None:
extract_path = os.path.join(extract_path, file_info.filename)
else:
extract_path = os.path.join(extract_path, extract_name)
logger.info('Extracting {filename} to {extract_path}'.format(filename=file_info.filename,
extract_path=extract_path))
with open(extract_path, 'wb') as extracted_file:
extracted_file.write(data)
logger.info('chromedriver v{version} downloaded and extracted successfully!'
.format(version=".".join((str(x) for x in download_version))))
return extract_path
if __name__ == '__main__':
import sys
import argparse
arg_parser = argparse.ArgumentParser('Chromedriver Downloader')
arg_parser.add_argument('-s', '--system', help='System build to download for', choices=['linux', 'mac', 'win'],
default=None)
arg_parser.add_argument('-a', '--arch', help='Arch build to download for (win is always 32).', choices=['32', '64'],
default=None, required=False)
arg_parser.add_argument('-v', '--version', help='Chrome version (First 3 version numbers) to download for. (v70+)',
default=None)
arg_parser.add_argument('-f', '--filename', help='Name for the extracted chromedriver file',
default=None, type=str, required=False)
arg_parser.add_argument('-p', '--path', help='Directory to extract chromedriver to.',
default=None, type=str, required=False)
arg_parser.add_argument('-l', '--log-file', help='Log File Path', default=None, required=False)
arg_parser.add_argument('-L', '--log-level', help='Log Level', choices=['DEBUG', 'INFO', 'WARNING', 'CRITICAL'],
default='INFO', required=False)
args = arg_parser.parse_args()
log_format = logging.Formatter('%(asctime)s :: %(levelname)s :: %(name)s : %(message)s',
datefmt='%m/%d/%Y %H:%M:%S')
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(log_format)
logger.addHandler(console_handler)
if args.system is None:
args.system = discover_system()
if args.arch is None:
args.arch = discover_arch()
if args.version is None:
args.version = discover_chrome_version()
if args.log_file:
from logging.handlers import RotatingFileHandler
file_handler = RotatingFileHandler(args.log_file, maxBytes=5 * (1024 ** 2), backupCount=5)
file_handler.setFormatter(log_format)
logger.addHandler(file_handler)
logger.setLevel(args.log_level)
download_chromedriver(args.system, args.arch, args.version, args.filename, args.path)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment