from ..exceptions import UnknownOSException, ChromeError from .common import NO_CHROME_DRIVER from clint.textui import puts, colored from selenium import webdriver from selenium.common.exceptions import WebDriverException from shutil import which from subprocess import check_output from urllib.request import urlretrieve from appdirs import AppDirs from ..version import version from os.path import exists import os, sys, stat, platform import progressbar import re import zipfile import requests import pathlib cache_dir = AppDirs("DeleteFB", version=version).user_cache_dir try: pathlib.Path(cache_dir).mkdir(parents=True, exist_ok=True) except FileExistsError: pass def extract_zip(filename, chrome_maj_version): """ Uses zipfile package to extract a single zipfile :param filename: :return: new filename """ # Remove any leftover unversioned chromedriver try: os.remove(f"{cache_dir}/chromedriver") except FileNotFoundError: pass try: _file = zipfile.ZipFile(filename, 'r') except FileNotFoundError: puts(colored.red(f"{filename} Does not exist")) sys.exit(1) # Save the name of the new file new_file_name = f"{cache_dir}/{_file.namelist()[0] + chrome_maj_version}" # Extract the file and make it executable _file.extractall(path=cache_dir) # Rename the filename to a versioned one os.rename(f"{cache_dir}/chromedriver", f"{cache_dir}/chromedriver{chrome_maj_version}") driver_stat = os.stat(new_file_name) os.chmod(new_file_name, driver_stat.st_mode | stat.S_IEXEC) _file.close() os.remove(filename) return new_file_name def setup_selenium(options, chrome_binary_path): try: # try letting Selenium find the driver (in PATH) return webdriver.Chrome(options=options) except WebDriverException: # Configures selenium to use a custom path driver_path = get_webdriver(chrome_binary_path) return webdriver.Chrome(executable_path=driver_path, options=options) def parse_version(output): """ Attempt to extract version number from chrome version string. """ return [c for c in re.split('([0-9]+)\.?', output.decode("utf-8")) if all(d.isdigit() for d in c) and c][0] def get_chrome_version(chrome_binary_path=None): """ Extract the chrome major version. """ driver_locations = [which(loc) for loc in ["google-chrome", "google-chrome-stable", "chromium", "chromium-browser", "chrome.exe"]] for location in driver_locations: if location: return parse_version(check_output([location, "--version"]).strip()) return None def construct_driver_url(chrome_binary_path=None): """ Construct a URL to download the Chrome Driver. """ platform_string = platform.system() chrome_drivers = { "Windows" : "https://chromedriver.storage.googleapis.com/{0}/chromedriver_win32.zip", "Darwin" : "https://chromedriver.storage.googleapis.com/{0}/chromedriver_mac64.zip", "Linux" : "https://chromedriver.storage.googleapis.com/{0}/chromedriver_linux64.zip" } version = get_chrome_version() if version is None: raise ChromeError("Chrome version not found") latest_release_url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_{0}".format(version) return version, chrome_drivers.get(platform_string).format(requests.get(latest_release_url).text) # First, construct a LATEST_RELEASE URL using Chrome's major version number. # For example, with Chrome version 73.0.3683.86, use URL "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_73". # Try to download a small file from this URL. If it successful, the file contains the ChromeDriver version to use. # If the above step failed, reduce the Chrome major version by 1 and try again. # For example, with Chrome version 75.0.3745.4, use URL "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_74" # to download a small file, which contains the ChromeDriver version to use. # You can also use ChromeDriver Canary build. def get_webdriver(chrome_binary_path): """ Ensure a webdriver is available If Not, Download it. """ # Download it according to the current machine chrome_maj_version, chrome_webdriver = construct_driver_url(chrome_binary_path) driver_path = f"{cache_dir}/chromedriver{chrome_maj_version}" if exists(driver_path): return driver_path if not chrome_webdriver: raise UnknownOSException("Unknown Operating system platform") global total_size def show_progress(*res): global total_size pbar = None downloaded = 0 block_num, block_size, total_size = res if not pbar: pbar = progressbar.ProgressBar(maxval=total_size) pbar.start() downloaded += block_num * block_size if downloaded < total_size: pbar.update(downloaded) else: pbar.finish() puts(colored.yellow("Downloading Chrome Webdriver")) file_name = f"{cache_dir}/{chrome_webdriver.split('/')[-1]}" response = urlretrieve(chrome_webdriver, file_name, show_progress) if int(response[1].get("Content-Length")) == total_size: puts(colored.green("Completed downloading the Chrome Driver.")) return extract_zip(file_name, chrome_maj_version) else: puts(colored.red("An error Occurred While trying to download the driver.")) # remove the downloaded file and exit os.remove(file_name) sys.stderr.write(NO_CHROME_DRIVER) sys.exit(1)