availcheck.selenium
1# -*- coding: utf-8 -*- 2# Copyright (C) 2024 TU Dresden 3# ralf.klammer@tu-dresden.de 4 5import logging 6import pathlib 7import requests 8 9from selenium import webdriver 10from selenium.common import exceptions as selenium_exceptions 11from selenium.webdriver.common.by import By 12from selenium.webdriver.support import expected_conditions as EC 13from selenium.webdriver.support.ui import WebDriverWait 14 15log = logging.getLogger(__name__) 16 17 18class SeleniumAgent(object): 19 """ 20 A simple wrapper around the selenium webdriver to make it easier to use 21 in the context of the availability checker. 22 """ 23 24 def __init__( 25 self, 26 path_of_screenshots=None, 27 header_arguments=[], 28 implicit_wait=None, 29 browser_dir=None, 30 user_agent=""" 31 Mozilla/5.0 (X11; Linux x86_64) 32 AppleWebKit/537.36 (KHTML, like Gecko) 33 Chrome/130.0.0.0 Safari/537.36 34 """, 35 window_size=(1920, 1080), 36 ): 37 # set up the chrome options 38 chrome_options = webdriver.ChromeOptions() 39 chrome_options.add_argument("--headless") 40 chrome_options.add_argument("--no-sandbox") 41 chrome_options.add_argument("--disable-dev-shm-usage") 42 if browser_dir is not None: 43 chrome_options.add_argument(f"--user-data-dir={browser_dir}") 44 if user_agent: 45 chrome_options.add_argument("user-agent=%s" % user_agent) 46 47 for argument in header_arguments: 48 log.info("Add additional header arguments: %s" % argument) 49 chrome_options.add_argument(argument) 50 51 self.path_of_screenshots = path_of_screenshots 52 self.browser = webdriver.Chrome(chrome_options) 53 self.browser.set_window_size(*window_size) 54 55 # we need to add a script to the browser to make it work with shadow 56 # dom which is used by React.js 57 # see: https://stackoverflow.com/a/77243136 58 self.browser.execute_cdp_cmd( 59 "Page.addScriptToEvaluateOnNewDocument", 60 { 61 "source": """ 62 Element.prototype._attachShadow = Element.prototype.attachShadow; 63 Element.prototype.attachShadow = function () { 64 return this._attachShadow( { mode: "open" } ); 65 }; 66 """ 67 }, 68 ) 69 self.implicit_wait = implicit_wait 70 71 @property 72 def implicit_wait(self): 73 return self._implicit_wait 74 75 @implicit_wait.setter 76 def implicit_wait(self, value): 77 self._implicit_wait = value 78 if value is not None: 79 self.browser.implicitly_wait(value) 80 81 def get_shadow_root(self, shadow_root_css): 82 # get the shadow root of the element, this is necessary for 83 # elements that are hidden in the shadow dom 84 # see: https://stackoverflow.com/a/77243136 85 log.debug(f"######get_shadow_root: {shadow_root_css}#######") 86 closed_shadow_host = self.find_by_css(shadow_root_css) 87 log.debug(f"closed_shadow_host: {closed_shadow_host}") 88 shadow_root = self.browser.execute_script( 89 "return arguments[0].shadowRoot", closed_shadow_host 90 ) 91 log.debug(f"shadow_root: {shadow_root}") 92 return shadow_root 93 94 def get(self, url): 95 self.browser.get(url) 96 97 def find_by_tag(self, tag=None, elem=None, as_list=False): 98 if tag is None: 99 return None 100 if elem is None: 101 elem = self.browser 102 try: 103 if as_list: 104 return elem.find_elements(By.TAG_NAME, tag) 105 else: 106 return elem.find_element(By.TAG_NAME, tag) 107 except selenium_exceptions.WebDriverException as e: 108 log.info(f"Unable to find elemnt: {tag}") 109 log.debug(e) 110 111 def find_by_css(self, css=None, elem=None, as_list=False): 112 if css is None: 113 return None 114 if elem is None: 115 elem = self.browser 116 try: 117 if as_list: 118 return elem.find_elements(By.CSS_SELECTOR, css) 119 else: 120 return elem.find_element(By.CSS_SELECTOR, css) 121 except selenium_exceptions.WebDriverException as e: 122 log.info( 123 f"Unable to to find element: {css}", 124 ) 125 log.debug(e) 126 127 def find_by_text( 128 self, 129 text=None, 130 css=None, 131 elem=None, 132 as_list=False, 133 case_sensitive=False, 134 ): 135 if all([text, css]): 136 if elem is None: 137 elem = self.browser 138 result_as_list = [] 139 for item in self.find_by_css(css, elem=elem, as_list=True): 140 # convert to lower case if case insensitive is requested 141 item_text = item.text if case_sensitive else item.text.lower() 142 text = text if case_sensitive else text.lower() 143 if item_text == text: 144 if as_list: 145 result_as_list.append(item) 146 else: 147 return item 148 149 def wait_until(self, css=None, seconds=10, elem=None, want_bool=False): 150 if css is not None: 151 if elem is None: 152 elem = self.browser 153 try: 154 WebDriverWait(elem, seconds).until( 155 EC.presence_of_element_located((By.CSS_SELECTOR, css)) 156 ) 157 if want_bool: 158 return True 159 else: 160 return self.find_by_css(css=css, elem=elem) 161 except selenium_exceptions.TimeoutException: 162 log.info(f"Element not found: {css}") 163 return False 164 165 def save_screenshot(self, filename): 166 if self.path_of_screenshots: 167 self.path_of_screenshots = self.path_of_screenshots.rstrip("/") 168 filename = filename.lstrip("/") 169 filename = f"{self.path_of_screenshots}/{filename}" 170 171 if not pathlib.Path(filename).parent.exists(): 172 log.warning( 173 f"Destination for screenshots ({pathlib.Path(filename).parent}) does not exist." 174 ) 175 self.browser.save_screenshot(filename) 176 177 def zoom(self): 178 self.browser.execute_script("document.body.style.zoom='25%'") 179 180 def get_url(self): 181 current_url = self.browser.current_url 182 return current_url 183 184 def amount_of_elements(self, css=None, minimum=10): 185 if css is not None: 186 try: 187 count = self.browser.execute_script( 188 f'return document.querySelectorAll("{css}").length' 189 ) 190 if count >= minimum: 191 return True 192 else: 193 return False 194 except Exception as e: 195 log.error(f"Unable to find Element: {css}") 196 log.error(e) 197 return False 198 else: 199 log.error("CSS selector is None") 200 return False 201 202 def check_button_redirection(self, css=None, tag=None): 203 if css: 204 ankers = self.find_by_css(css=css, as_list=True) 205 elif tag: 206 ankers = self.find_by_tag(tag=tag) 207 else: 208 log.info("No elements found") 209 return False 210 if ankers == []: 211 log.warning("No elements found") 212 return False 213 checks = [] 214 for anker in ankers: 215 button_href = anker.get_attribute("href") 216 try: 217 response_status = requests.get(button_href).ok 218 except requests.RequestException as e: 219 print(f"Failed to fetch {button_href}, Error: {e}") 220 response_status = False 221 checks.append(response_status) 222 return all(checks) 223 224 def quit(self): 225 self.browser.quit()
log =
<Logger availcheck.selenium (WARNING)>
class
SeleniumAgent:
19class SeleniumAgent(object): 20 """ 21 A simple wrapper around the selenium webdriver to make it easier to use 22 in the context of the availability checker. 23 """ 24 25 def __init__( 26 self, 27 path_of_screenshots=None, 28 header_arguments=[], 29 implicit_wait=None, 30 browser_dir=None, 31 user_agent=""" 32 Mozilla/5.0 (X11; Linux x86_64) 33 AppleWebKit/537.36 (KHTML, like Gecko) 34 Chrome/130.0.0.0 Safari/537.36 35 """, 36 window_size=(1920, 1080), 37 ): 38 # set up the chrome options 39 chrome_options = webdriver.ChromeOptions() 40 chrome_options.add_argument("--headless") 41 chrome_options.add_argument("--no-sandbox") 42 chrome_options.add_argument("--disable-dev-shm-usage") 43 if browser_dir is not None: 44 chrome_options.add_argument(f"--user-data-dir={browser_dir}") 45 if user_agent: 46 chrome_options.add_argument("user-agent=%s" % user_agent) 47 48 for argument in header_arguments: 49 log.info("Add additional header arguments: %s" % argument) 50 chrome_options.add_argument(argument) 51 52 self.path_of_screenshots = path_of_screenshots 53 self.browser = webdriver.Chrome(chrome_options) 54 self.browser.set_window_size(*window_size) 55 56 # we need to add a script to the browser to make it work with shadow 57 # dom which is used by React.js 58 # see: https://stackoverflow.com/a/77243136 59 self.browser.execute_cdp_cmd( 60 "Page.addScriptToEvaluateOnNewDocument", 61 { 62 "source": """ 63 Element.prototype._attachShadow = Element.prototype.attachShadow; 64 Element.prototype.attachShadow = function () { 65 return this._attachShadow( { mode: "open" } ); 66 }; 67 """ 68 }, 69 ) 70 self.implicit_wait = implicit_wait 71 72 @property 73 def implicit_wait(self): 74 return self._implicit_wait 75 76 @implicit_wait.setter 77 def implicit_wait(self, value): 78 self._implicit_wait = value 79 if value is not None: 80 self.browser.implicitly_wait(value) 81 82 def get_shadow_root(self, shadow_root_css): 83 # get the shadow root of the element, this is necessary for 84 # elements that are hidden in the shadow dom 85 # see: https://stackoverflow.com/a/77243136 86 log.debug(f"######get_shadow_root: {shadow_root_css}#######") 87 closed_shadow_host = self.find_by_css(shadow_root_css) 88 log.debug(f"closed_shadow_host: {closed_shadow_host}") 89 shadow_root = self.browser.execute_script( 90 "return arguments[0].shadowRoot", closed_shadow_host 91 ) 92 log.debug(f"shadow_root: {shadow_root}") 93 return shadow_root 94 95 def get(self, url): 96 self.browser.get(url) 97 98 def find_by_tag(self, tag=None, elem=None, as_list=False): 99 if tag is None: 100 return None 101 if elem is None: 102 elem = self.browser 103 try: 104 if as_list: 105 return elem.find_elements(By.TAG_NAME, tag) 106 else: 107 return elem.find_element(By.TAG_NAME, tag) 108 except selenium_exceptions.WebDriverException as e: 109 log.info(f"Unable to find elemnt: {tag}") 110 log.debug(e) 111 112 def find_by_css(self, css=None, elem=None, as_list=False): 113 if css is None: 114 return None 115 if elem is None: 116 elem = self.browser 117 try: 118 if as_list: 119 return elem.find_elements(By.CSS_SELECTOR, css) 120 else: 121 return elem.find_element(By.CSS_SELECTOR, css) 122 except selenium_exceptions.WebDriverException as e: 123 log.info( 124 f"Unable to to find element: {css}", 125 ) 126 log.debug(e) 127 128 def find_by_text( 129 self, 130 text=None, 131 css=None, 132 elem=None, 133 as_list=False, 134 case_sensitive=False, 135 ): 136 if all([text, css]): 137 if elem is None: 138 elem = self.browser 139 result_as_list = [] 140 for item in self.find_by_css(css, elem=elem, as_list=True): 141 # convert to lower case if case insensitive is requested 142 item_text = item.text if case_sensitive else item.text.lower() 143 text = text if case_sensitive else text.lower() 144 if item_text == text: 145 if as_list: 146 result_as_list.append(item) 147 else: 148 return item 149 150 def wait_until(self, css=None, seconds=10, elem=None, want_bool=False): 151 if css is not None: 152 if elem is None: 153 elem = self.browser 154 try: 155 WebDriverWait(elem, seconds).until( 156 EC.presence_of_element_located((By.CSS_SELECTOR, css)) 157 ) 158 if want_bool: 159 return True 160 else: 161 return self.find_by_css(css=css, elem=elem) 162 except selenium_exceptions.TimeoutException: 163 log.info(f"Element not found: {css}") 164 return False 165 166 def save_screenshot(self, filename): 167 if self.path_of_screenshots: 168 self.path_of_screenshots = self.path_of_screenshots.rstrip("/") 169 filename = filename.lstrip("/") 170 filename = f"{self.path_of_screenshots}/{filename}" 171 172 if not pathlib.Path(filename).parent.exists(): 173 log.warning( 174 f"Destination for screenshots ({pathlib.Path(filename).parent}) does not exist." 175 ) 176 self.browser.save_screenshot(filename) 177 178 def zoom(self): 179 self.browser.execute_script("document.body.style.zoom='25%'") 180 181 def get_url(self): 182 current_url = self.browser.current_url 183 return current_url 184 185 def amount_of_elements(self, css=None, minimum=10): 186 if css is not None: 187 try: 188 count = self.browser.execute_script( 189 f'return document.querySelectorAll("{css}").length' 190 ) 191 if count >= minimum: 192 return True 193 else: 194 return False 195 except Exception as e: 196 log.error(f"Unable to find Element: {css}") 197 log.error(e) 198 return False 199 else: 200 log.error("CSS selector is None") 201 return False 202 203 def check_button_redirection(self, css=None, tag=None): 204 if css: 205 ankers = self.find_by_css(css=css, as_list=True) 206 elif tag: 207 ankers = self.find_by_tag(tag=tag) 208 else: 209 log.info("No elements found") 210 return False 211 if ankers == []: 212 log.warning("No elements found") 213 return False 214 checks = [] 215 for anker in ankers: 216 button_href = anker.get_attribute("href") 217 try: 218 response_status = requests.get(button_href).ok 219 except requests.RequestException as e: 220 print(f"Failed to fetch {button_href}, Error: {e}") 221 response_status = False 222 checks.append(response_status) 223 return all(checks) 224 225 def quit(self): 226 self.browser.quit()
A simple wrapper around the selenium webdriver to make it easier to use in the context of the availability checker.
SeleniumAgent( path_of_screenshots=None, header_arguments=[], implicit_wait=None, browser_dir=None, user_agent='\n Mozilla/5.0 (X11; Linux x86_64)\n AppleWebKit/537.36 (KHTML, like Gecko)\n Chrome/130.0.0.0 Safari/537.36\n ', window_size=(1920, 1080))
25 def __init__( 26 self, 27 path_of_screenshots=None, 28 header_arguments=[], 29 implicit_wait=None, 30 browser_dir=None, 31 user_agent=""" 32 Mozilla/5.0 (X11; Linux x86_64) 33 AppleWebKit/537.36 (KHTML, like Gecko) 34 Chrome/130.0.0.0 Safari/537.36 35 """, 36 window_size=(1920, 1080), 37 ): 38 # set up the chrome options 39 chrome_options = webdriver.ChromeOptions() 40 chrome_options.add_argument("--headless") 41 chrome_options.add_argument("--no-sandbox") 42 chrome_options.add_argument("--disable-dev-shm-usage") 43 if browser_dir is not None: 44 chrome_options.add_argument(f"--user-data-dir={browser_dir}") 45 if user_agent: 46 chrome_options.add_argument("user-agent=%s" % user_agent) 47 48 for argument in header_arguments: 49 log.info("Add additional header arguments: %s" % argument) 50 chrome_options.add_argument(argument) 51 52 self.path_of_screenshots = path_of_screenshots 53 self.browser = webdriver.Chrome(chrome_options) 54 self.browser.set_window_size(*window_size) 55 56 # we need to add a script to the browser to make it work with shadow 57 # dom which is used by React.js 58 # see: https://stackoverflow.com/a/77243136 59 self.browser.execute_cdp_cmd( 60 "Page.addScriptToEvaluateOnNewDocument", 61 { 62 "source": """ 63 Element.prototype._attachShadow = Element.prototype.attachShadow; 64 Element.prototype.attachShadow = function () { 65 return this._attachShadow( { mode: "open" } ); 66 }; 67 """ 68 }, 69 ) 70 self.implicit_wait = implicit_wait
def
get_shadow_root(self, shadow_root_css):
82 def get_shadow_root(self, shadow_root_css): 83 # get the shadow root of the element, this is necessary for 84 # elements that are hidden in the shadow dom 85 # see: https://stackoverflow.com/a/77243136 86 log.debug(f"######get_shadow_root: {shadow_root_css}#######") 87 closed_shadow_host = self.find_by_css(shadow_root_css) 88 log.debug(f"closed_shadow_host: {closed_shadow_host}") 89 shadow_root = self.browser.execute_script( 90 "return arguments[0].shadowRoot", closed_shadow_host 91 ) 92 log.debug(f"shadow_root: {shadow_root}") 93 return shadow_root
def
find_by_tag(self, tag=None, elem=None, as_list=False):
98 def find_by_tag(self, tag=None, elem=None, as_list=False): 99 if tag is None: 100 return None 101 if elem is None: 102 elem = self.browser 103 try: 104 if as_list: 105 return elem.find_elements(By.TAG_NAME, tag) 106 else: 107 return elem.find_element(By.TAG_NAME, tag) 108 except selenium_exceptions.WebDriverException as e: 109 log.info(f"Unable to find elemnt: {tag}") 110 log.debug(e)
def
find_by_css(self, css=None, elem=None, as_list=False):
112 def find_by_css(self, css=None, elem=None, as_list=False): 113 if css is None: 114 return None 115 if elem is None: 116 elem = self.browser 117 try: 118 if as_list: 119 return elem.find_elements(By.CSS_SELECTOR, css) 120 else: 121 return elem.find_element(By.CSS_SELECTOR, css) 122 except selenium_exceptions.WebDriverException as e: 123 log.info( 124 f"Unable to to find element: {css}", 125 ) 126 log.debug(e)
def
find_by_text( self, text=None, css=None, elem=None, as_list=False, case_sensitive=False):
128 def find_by_text( 129 self, 130 text=None, 131 css=None, 132 elem=None, 133 as_list=False, 134 case_sensitive=False, 135 ): 136 if all([text, css]): 137 if elem is None: 138 elem = self.browser 139 result_as_list = [] 140 for item in self.find_by_css(css, elem=elem, as_list=True): 141 # convert to lower case if case insensitive is requested 142 item_text = item.text if case_sensitive else item.text.lower() 143 text = text if case_sensitive else text.lower() 144 if item_text == text: 145 if as_list: 146 result_as_list.append(item) 147 else: 148 return item
def
wait_until(self, css=None, seconds=10, elem=None, want_bool=False):
150 def wait_until(self, css=None, seconds=10, elem=None, want_bool=False): 151 if css is not None: 152 if elem is None: 153 elem = self.browser 154 try: 155 WebDriverWait(elem, seconds).until( 156 EC.presence_of_element_located((By.CSS_SELECTOR, css)) 157 ) 158 if want_bool: 159 return True 160 else: 161 return self.find_by_css(css=css, elem=elem) 162 except selenium_exceptions.TimeoutException: 163 log.info(f"Element not found: {css}") 164 return False
def
save_screenshot(self, filename):
166 def save_screenshot(self, filename): 167 if self.path_of_screenshots: 168 self.path_of_screenshots = self.path_of_screenshots.rstrip("/") 169 filename = filename.lstrip("/") 170 filename = f"{self.path_of_screenshots}/{filename}" 171 172 if not pathlib.Path(filename).parent.exists(): 173 log.warning( 174 f"Destination for screenshots ({pathlib.Path(filename).parent}) does not exist." 175 ) 176 self.browser.save_screenshot(filename)
def
amount_of_elements(self, css=None, minimum=10):
185 def amount_of_elements(self, css=None, minimum=10): 186 if css is not None: 187 try: 188 count = self.browser.execute_script( 189 f'return document.querySelectorAll("{css}").length' 190 ) 191 if count >= minimum: 192 return True 193 else: 194 return False 195 except Exception as e: 196 log.error(f"Unable to find Element: {css}") 197 log.error(e) 198 return False 199 else: 200 log.error("CSS selector is None") 201 return False