# coding: utf-8
""" Utility programs for Selenium WebDriver. See `1. Installation — Selenium Python Bindings 2 documentation <https://selenium-python.readthedocs.io/installation.html#drivers>`_ for more details."""
import time
import warnings
from selenium import webdriver
from selenium.webdriver import DesiredCapabilities
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
# from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from ._warnings import DriverNotFoundWarning
from ._path import GUMMY_DIR
from .coloring_utils import toGRAY, toBLUE, toGREEN, toRED, toACCENT
from .generic_utils import get_latest_filename, try_wrapper, handleKeyError
SUPPORTED_DRIVER_TYPES = ["local", "remote"]
def _print_driver_check_log(is_succeed, driver_type):
"""Print logs."""
if is_succeed:
state = toGREEN("[success]")
msg = "driver can be built."
else:
state = toRED("[failure]")
msg = "driver can't be built."
print(" ".join([state, driver_type, msg]))
[docs]def get_chrome_options(browser=False):
"""Get default chrome options.
Args:
browser (bool) : Whether you want to run Chrome with GUI browser. (default= ``False`` )
Examples:
>>> from gummy.utils import get_chrome_options
>>> # browser == False (default)
>>> op = get_chrome_options(browser=False)
>>> op.arguments
['--no-sandbox',
'--ignore-certificate-errors',
'--disable-dev-shm-usage',
'--headless']
>>> op.experimental_options
{}
>>> # browser == True
>>> op = get_chrome_options(browser=True)
>>> op.arguments
['--no-sandbox',
'--ignore-certificate-errors',
'--disable-dev-shm-usage',
'--kiosk-printing']
>>> op.experimental_options
{'prefs': {'profile.default_content_settings.popups': 1,
'download.default_directory': '/Users/iwasakishuto/.gummy',
'directory_upgrade': True}}
"""
chrome_options = Options()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--ignore-certificate-errors')
chrome_options.add_argument('--disable-dev-shm-usage')
if not browser:
chrome_options.add_argument('--headless')
else:
chrome_options.add_experimental_option("prefs", {
# "plugins.always_open_pdf_externally": True,
"profile.default_content_settings.popups": 1,
"download.default_directory": GUMMY_DIR,
"directory_upgrade": True,
})
chrome_options.add_argument('--kiosk-printing')
return chrome_options
def _check_driver(selenium_port="4444"):
"""Check the available drivers. (if one of the drivers is built, there is no problem)"""
DRIVER_TYPE = ""
chrome_options = get_chrome_options(browser=False)
for driver_type in SUPPORTED_DRIVER_TYPES:
try:
with eval(f"_get_driver_{driver_type}(chrome_options, selenium_port=selenium_port)") as driver:
DRIVER_TYPE = driver_type
_print_driver_check_log(is_succeed=True, driver_type=driver_type)
except Exception as e:
_print_driver_check_log(is_succeed=False, driver_type=driver_type)
return DRIVER_TYPE
def _get_driver_local(chrome_options, **kwargs):
driver = webdriver.Chrome(options=chrome_options)
return driver
def _get_driver_remote(chrome_options, **kwargs):
selenium_port = kwargs.get("selenium_port", "4444")
driver = webdriver.Remote(
command_executor=f'http://selenium:{selenium_port}/wd/hub',
desired_capabilities=DesiredCapabilities.CHROME.copy(),
options=chrome_options
)
return driver
############################
# START: Check driver
############################
try:
__DRIVER_SETUP__
except NameError:
DRIVER_TYPE = _check_driver()
print(f"DRIVER_TYPE: {toGREEN(DRIVER_TYPE)}")
__DRIVER_SETUP__ = True
if DRIVER_TYPE == "":
warnings.warn(message="Fails to launch all supported drivers.", category=DriverNotFoundWarning)
############################
# END: Check driver
############################
[docs]def get_driver(driver_type=DRIVER_TYPE, chrome_options=None, browser=False, selenium_port="4444"):
"""Get a driver that works in your current environment.
Args:
driver_type (str) : driver type. (default= ``DRIVER_TYPE``)
chrome_options (ChromeOptions) : Instance of ChromeOptions. If not specify, use :meth:`get_chrome_options() <gummy.utils.driver_utils.get_chrome_options>` to get default options.
browser (bool) : Whether you want to run Chrome with GUI browser. (default= ``False`` )
selenium_port (str) : selenium port number. This will be used when you run on `Docker <https://github.com/iwasakishuto/Translation-Gummy/tree/master/docker>`_
"""
handleKeyError(lst=SUPPORTED_DRIVER_TYPES, driver_type=driver_type)
if chrome_options is None: chrome_options = get_chrome_options(browser=browser)
return eval(f"_get_driver_{driver_type}(chrome_options, selenium_port=selenium_port)")
[docs]def try_find_element(driver, by, identifier, timeout=3, verbose=True):
"""Find an element given a By strategy and locator.
Wrap with :meth:`try_wrapper <gummy.utils.generic_utils.try_wrapper>`
to handle the error so that the programs does not stop in a series of processes.
Args:
driver (WebDriver) : Selenium WebDriver.
by (str) : Locator strategies. See `4. Locating Elements — Selenium Python Bindings 2 documentation <https://selenium-python.readthedocs.io/locating-elements.html>`_
identifier (str) : Identifier to find the element
timeout (int) : Number of seconds before timing out (default= ``3``)
verbose (bool) : Whether you want to print output or not. (default= ``True`` )
Examples:
>>> from gummy.utils import get_driver, try_find_element
>>> with get_driver() as driver:
... driver.get("https://www.google.com/")
... e = try_find_element(driver=driver, by="tag name", identifier="img")
Succeeded to locate element with tag name=img
"""
return try_wrapper(
func=WebDriverWait(driver=driver, timeout=timeout).until,
msg_=f"locate element with {toGREEN(by)}={toBLUE(identifier)}",
method=lambda x: x.find_element(by=by, value=identifier),
verbose_=verbose,
)
[docs]def try_find_element_send_keys(driver, by=None, identifier=None, values=(), target=None, timeout=3, verbose=True):
"""Find an element given a By strategy and locator, and Simulates typing into the element.
Wrap with :meth:`try_wrapper <gummy.utils.generic_utils.try_wrapper>`
to handle the error so that the programs does not stop in a series of processes.
Args:
driver (WebDriver) : Selenium WebDriver.
by (str) : Locator strategies. See `4. Locating Elements — Selenium Python Bindings 2 documentation <https://selenium-python.readthedocs.io/locating-elements.html>`_
identifier (str) : Identifier to find the element
values (tuple) : A string for typing, or setting form fields. For setting file inputs, this could be a local file path.
target (WebElement) : Represents a DOM element. (If you already find element)
timeout (int) : Number of seconds before timing out (default= ``3``)
verbose (bool) : Whether you want to print output or not. (default= ``True`` )
"""
if target is None:
target = try_find_element(driver=driver, identifier=identifier, by=by, timeout=timeout)
if target is not None:
try_wrapper(
target.send_keys,
*tuple(values),
msg_=f"fill {toBLUE(values)} in element with {toGREEN(by)}={toBLUE(identifier)}",
verbose_=verbose,
)
[docs]def try_find_element_click(driver, by=None, identifier=None, target=None, timeout=3, verbose=True):
"""Find an element given a By strategy and locator, and Clicks the element.
Wrap with :meth:`try_wrapper <gummy.utils.generic_utils.try_wrapper>`
to handle the error so that the programs does not stop in a series of processes.
Args:
driver (WebDriver) : Selenium WebDriver.
by (str) : Locator strategies. See `4. Locating Elements — Selenium Python Bindings 2 documentation <https://selenium-python.readthedocs.io/locating-elements.html>`_
identifier (str) : Identifier to find the element
target (WebElement) : Represents a DOM element. (If you already find element)
timeout (int) : Number of seconds before timing out (default= ``3``)
verbose (bool) : Whether you want to print output or not. (default= ``True`` )
"""
if target is None:
target = try_find_element(driver=driver, identifier=identifier, by=by, timeout=timeout)
if target is not None:
def element_click(driver, target):
try:
driver.execute_script("arguments[0].click();", target)
except StaleElementReferenceException:
target.click()
try_wrapper(
func=element_click,
msg_=f"click the element with {toGREEN(by)}={toBLUE(identifier)}",
verbose_=verbose,
driver=driver,
target=target,
)
[docs]def click():
""" function for differentiation """
[docs]def download_PDF_with_driver(url, dirname=".", verbose=True, timeout=3):
"""Download PDF file with GUI driver.
Args:
url (str) : File URL.
dirname (str) : The directory where downloaded data will be saved.
verbose (bool) : Whether print verbose or not.
timeout (int) : Number of seconds before timing out (default= ``3``)
Returns:
path (str) : path/to/downloaded_file
"""
chrome_options = get_chrome_options(browser=True)
if "prefs" not in chrome_options._experimental_options:
chrome_options._experimental_options["prefs"] = {}
chrome_options._experimental_options["prefs"]["download.default_directory"] = dirname
chrome_options._experimental_options["prefs"]["plugins.always_open_pdf_externally"] = True
if verbose: print(f"Downloading PDF from {toBLUE(url)}")
with get_driver(chrome_options=chrome_options) as driver:
driver.get(url)
for _ in range(timeout):
time.sleep(1)
path = get_latest_filename(dirname=dirname)
if not path.endswith(".crdownload"):
break
if verbose: print(f"Save PDF at {toBLUE(path)}")
return path
[docs]def wait_until_all_elements(driver, timeout, verbose=True):
"""Wait until all elements visible.
Args:
driver (WebDriver) : Selenium WebDriver.
timeout (int) : Number of seconds before timing out (default= ``3``)
verbose (bool) : Whether you want to print output or not. (default= ``True`` )
"""
if verbose: print(f"Wait up to {timeout}[s] for all page elements to load.")
WebDriverWait(driver=driver, timeout=timeout).until(EC.presence_of_all_elements_located)
time.sleep(timeout)
# driver.find_element_by_tag_name('body').click()
# driver.find_element_by_tag_name('body').send_keys(Keys.PAGE_DOWN)