-
-
Save shakahl/dde266032b8be1876b4ac33be4227ae3 to your computer and use it in GitHub Desktop.
Download Latest chromedriver. Win/Linux/Mac. 32/64bit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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