seleniumUts 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- seleniumUts/__init__.py +697 -0
- seleniumUts/custom_driver.py +44 -0
- seleniumUts/seleniumuts.py +0 -0
- seleniumUts/webelement.py +43 -0
- seleniumuts-1.1.1.dist-info/METADATA +141 -0
- seleniumuts-1.1.1.dist-info/RECORD +8 -0
- seleniumuts-1.1.1.dist-info/WHEEL +5 -0
- seleniumuts-1.1.1.dist-info/top_level.txt +1 -0
seleniumUts/__init__.py
ADDED
|
@@ -0,0 +1,697 @@
|
|
|
1
|
+
from seleniumUts.custom_driver import CustomChromeDriver, CustomRemoteDriver
|
|
2
|
+
from selenium.webdriver.support import expected_conditions as EC
|
|
3
|
+
from selenium.webdriver.support.ui import WebDriverWait
|
|
4
|
+
from selenium.webdriver.chrome.service import Service
|
|
5
|
+
from selenium.webdriver.common.by import By
|
|
6
|
+
import undetected_chromedriver as uc
|
|
7
|
+
from selenium import webdriver
|
|
8
|
+
from time import time, sleep
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from glob import glob
|
|
11
|
+
import base64
|
|
12
|
+
|
|
13
|
+
CAPABILITIES = {
|
|
14
|
+
"browserName": "chrome",
|
|
15
|
+
"version": "110.0",
|
|
16
|
+
"name": "default",
|
|
17
|
+
"enableVNC": True,
|
|
18
|
+
"enableVideo": False,
|
|
19
|
+
"sessionTimeout": "30m",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
DEFAULT_OPTIONS = [
|
|
23
|
+
# "--disable-web-security",
|
|
24
|
+
"--verbose",
|
|
25
|
+
# "--no-sandbox",
|
|
26
|
+
"--disable-infobars",
|
|
27
|
+
"--disable-extensions",
|
|
28
|
+
"--disable-notifications",
|
|
29
|
+
"--disable-dev-shm-usage",
|
|
30
|
+
"--force-device-scale-factor=0.67",
|
|
31
|
+
"--disable-gpu",
|
|
32
|
+
"--start-maximized",
|
|
33
|
+
"--kiosk-printing",
|
|
34
|
+
"--disable-blink-features=AutomationControlled",
|
|
35
|
+
"user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SeleniumUts:
|
|
40
|
+
driver = None
|
|
41
|
+
|
|
42
|
+
default_download_path = None
|
|
43
|
+
current_download_path = None
|
|
44
|
+
|
|
45
|
+
def close(self):
|
|
46
|
+
"""
|
|
47
|
+
Desc:
|
|
48
|
+
Close the browser.\n
|
|
49
|
+
Args: None
|
|
50
|
+
"""
|
|
51
|
+
if self.driver:
|
|
52
|
+
self.driver.quit()
|
|
53
|
+
self.driver = None
|
|
54
|
+
|
|
55
|
+
def wait_loads(self, tm=5):
|
|
56
|
+
"""
|
|
57
|
+
Desc:
|
|
58
|
+
Wait for the page to load completely.\n
|
|
59
|
+
Args:
|
|
60
|
+
- ``tm`` - Additional time to wait after the page is loaded in seconds. Default is 5 seconds.
|
|
61
|
+
Returns: None
|
|
62
|
+
"""
|
|
63
|
+
wait = WebDriverWait(self.driver, tm)
|
|
64
|
+
wt = lambda a: self.driver.execute_script(
|
|
65
|
+
'return document.readyState=="complete";'
|
|
66
|
+
)
|
|
67
|
+
wait.until(wt)
|
|
68
|
+
|
|
69
|
+
def open_page(self, page):
|
|
70
|
+
"""
|
|
71
|
+
Desc:
|
|
72
|
+
Open a page in the browser.\n
|
|
73
|
+
and wait for it to load.\n
|
|
74
|
+
Args:
|
|
75
|
+
- ``page`` - The URL of the page to be opened.
|
|
76
|
+
Returns:
|
|
77
|
+
- The WebDriver instance.
|
|
78
|
+
"""
|
|
79
|
+
self.driver.get(page)
|
|
80
|
+
self.driver.implicitly_wait(2)
|
|
81
|
+
self.wait_loads()
|
|
82
|
+
|
|
83
|
+
return self.driver
|
|
84
|
+
|
|
85
|
+
def wait_xpath(self, path, time=20, multiple=False, throw=True, custom_error=None):
|
|
86
|
+
"""
|
|
87
|
+
Desc:
|
|
88
|
+
Wait for an element to be visible using its XPATH.\n
|
|
89
|
+
Args:
|
|
90
|
+
- ``path`` - The XPATH of the element to be waited for.
|
|
91
|
+
- ``time`` - Maximum time to wait for the element in seconds. Default is 20 seconds.
|
|
92
|
+
- ``throw`` - If True, raises an exception if the element is not found within the time limit.
|
|
93
|
+
Returns:
|
|
94
|
+
- The WebElement if found, otherwise None.
|
|
95
|
+
"""
|
|
96
|
+
try:
|
|
97
|
+
if multiple:
|
|
98
|
+
# Espera até que TODOS os elementos correspondentes estejam visíveis
|
|
99
|
+
elements = WebDriverWait(self.driver, time).until(
|
|
100
|
+
lambda d: self.driver.find_elements(
|
|
101
|
+
By.XPATH,
|
|
102
|
+
path,
|
|
103
|
+
selenium_uts=self,
|
|
104
|
+
time=time,
|
|
105
|
+
custom_error=custom_error,
|
|
106
|
+
)
|
|
107
|
+
)
|
|
108
|
+
return elements
|
|
109
|
+
else:
|
|
110
|
+
element = WebDriverWait(self.driver, time).until(
|
|
111
|
+
lambda d: self.driver.find_element(
|
|
112
|
+
By.XPATH,
|
|
113
|
+
path,
|
|
114
|
+
selenium_uts=self,
|
|
115
|
+
time=time,
|
|
116
|
+
custom_error=custom_error,
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
return element
|
|
120
|
+
except Exception:
|
|
121
|
+
if throw:
|
|
122
|
+
if custom_error:
|
|
123
|
+
raise Exception(custom_error)
|
|
124
|
+
else:
|
|
125
|
+
raise
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
def wait_css(self, selector, time=20, throw=True, custom_error=None):
|
|
129
|
+
"""
|
|
130
|
+
Desc:
|
|
131
|
+
Wait for an element to be visible using its CSS selector.\n
|
|
132
|
+
Args:
|
|
133
|
+
- ``selector`` - The CSS selector of the element to be waited for.
|
|
134
|
+
- ``time`` - Maximum time to wait for the element in seconds. Default is 20 seconds.
|
|
135
|
+
- ``throw`` - If True, raises an exception if the element is not found within the time limit.
|
|
136
|
+
Returns:
|
|
137
|
+
- The WebElement if found, otherwise None.
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
element = WebDriverWait(self.driver, time).until(
|
|
141
|
+
EC.visibility_of_element_located((By.CSS_SELECTOR, selector))
|
|
142
|
+
)
|
|
143
|
+
return element
|
|
144
|
+
except Exception:
|
|
145
|
+
if throw:
|
|
146
|
+
if custom_error:
|
|
147
|
+
raise Exception(custom_error)
|
|
148
|
+
else:
|
|
149
|
+
raise
|
|
150
|
+
return None
|
|
151
|
+
|
|
152
|
+
def wait_id(self, element_id, time=10, throw=True, custom_error=None):
|
|
153
|
+
"""
|
|
154
|
+
Desc:
|
|
155
|
+
Wait for an element to be present using its ID.\n
|
|
156
|
+
Args:
|
|
157
|
+
- ``element_id`` - The ID of the element to be waited for.
|
|
158
|
+
- ``time`` - Maximum time to wait for the element in seconds. Default is 10 seconds.
|
|
159
|
+
- ``throw`` - If True, raises an exception if the element is not found within the time limit.
|
|
160
|
+
Returns:
|
|
161
|
+
- The WebElement if found, otherwise None.
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
elem = WebDriverWait(self.driver, time).until(
|
|
165
|
+
lambda d: self.driver.find_element(
|
|
166
|
+
By.ID,
|
|
167
|
+
element_id,
|
|
168
|
+
selenium_uts=self,
|
|
169
|
+
time=time,
|
|
170
|
+
custom_error=custom_error,
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
return elem
|
|
174
|
+
except Exception:
|
|
175
|
+
if throw:
|
|
176
|
+
if custom_error:
|
|
177
|
+
raise Exception(custom_error)
|
|
178
|
+
else:
|
|
179
|
+
raise
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
def scroll_end(self):
|
|
183
|
+
"""
|
|
184
|
+
Desc:
|
|
185
|
+
Scroll to the end of the page.\n
|
|
186
|
+
Args: None
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
get_pos = lambda: self.driver.execute_script(
|
|
190
|
+
"return document.documentElement.scrollTop"
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
while True:
|
|
194
|
+
atual_pos = get_pos()
|
|
195
|
+
self.driver.execute_script(
|
|
196
|
+
"window.scrollTo(0, document.body.scrollHeight);"
|
|
197
|
+
)
|
|
198
|
+
sleep(2)
|
|
199
|
+
future_pos = get_pos()
|
|
200
|
+
if future_pos == atual_pos:
|
|
201
|
+
break
|
|
202
|
+
|
|
203
|
+
def accept_alert(self, time=10):
|
|
204
|
+
"""
|
|
205
|
+
Desc:
|
|
206
|
+
Wait for an alert to be present and accept it.\n
|
|
207
|
+
Args:
|
|
208
|
+
- ``time`` - Maximum time to wait for the alert in seconds. Default is 10 seconds.
|
|
209
|
+
Returns:
|
|
210
|
+
- The text of the alert.
|
|
211
|
+
"""
|
|
212
|
+
try:
|
|
213
|
+
WebDriverWait(self.driver, time).until(EC.alert_is_present())
|
|
214
|
+
alert = self.driver.switch_to.alert
|
|
215
|
+
txt = alert.text
|
|
216
|
+
alert.accept()
|
|
217
|
+
return txt
|
|
218
|
+
except Exception:
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
def disable_animations(self):
|
|
222
|
+
css_kill_animations = """
|
|
223
|
+
/* 1. Mata as durações */
|
|
224
|
+
*, *::before, *::after {
|
|
225
|
+
transition-duration: 0s !important;
|
|
226
|
+
transition-delay: 0s !important;
|
|
227
|
+
animation-duration: 0s !important;
|
|
228
|
+
animation-delay: 0s !important;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* 2. Força a visibilidade imediata de elementos que dependem de animação para aparecer */
|
|
232
|
+
[style*="opacity: 0"],
|
|
233
|
+
[style*="opacity:0"],
|
|
234
|
+
.fade, .collapse, .hidden {
|
|
235
|
+
opacity: 1 !important;
|
|
236
|
+
display: block !important;
|
|
237
|
+
visibility: visible !important;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/* 3. Reseta transformações que deixariam o elemento fora da tela (comum em slide-ins) */
|
|
241
|
+
* {
|
|
242
|
+
transform: none !important;
|
|
243
|
+
perspective: none !important;
|
|
244
|
+
}
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
self.add_style(css_kill_animations)
|
|
248
|
+
|
|
249
|
+
def new_tab(self):
|
|
250
|
+
"""
|
|
251
|
+
Desc:
|
|
252
|
+
Open a new browser tab and switch to it.\n
|
|
253
|
+
Returns: None
|
|
254
|
+
"""
|
|
255
|
+
self.driver.execute_script("window.open('');")
|
|
256
|
+
windows = self.driver.window_handles
|
|
257
|
+
self.driver.switch_to.window(windows[-1])
|
|
258
|
+
|
|
259
|
+
def switch_to_new_tab(self):
|
|
260
|
+
"""
|
|
261
|
+
Desc:
|
|
262
|
+
Switch to the most recently opened browser window.\n
|
|
263
|
+
Returns: None
|
|
264
|
+
"""
|
|
265
|
+
windows = self.driver.window_handles
|
|
266
|
+
self.driver.switch_to.window(windows[-1])
|
|
267
|
+
|
|
268
|
+
def close_current_tab(self):
|
|
269
|
+
"""
|
|
270
|
+
Desc:
|
|
271
|
+
Close the current browser tab and switch to the previous one.\n
|
|
272
|
+
Returns: None
|
|
273
|
+
"""
|
|
274
|
+
all_windows = self.driver.window_handles
|
|
275
|
+
|
|
276
|
+
if len(all_windows) > 1:
|
|
277
|
+
# 1. Identifica qual é a aba atual antes de fechar
|
|
278
|
+
current_window = self.driver.current_window_handle
|
|
279
|
+
|
|
280
|
+
# 2. Encontra o índice da aba atual na lista de abas
|
|
281
|
+
current_index = all_windows.index(current_window)
|
|
282
|
+
|
|
283
|
+
# 3. Fecha a aba atual
|
|
284
|
+
self.driver.close()
|
|
285
|
+
|
|
286
|
+
# 4. Define o destino: a aba anterior (index - 1)
|
|
287
|
+
# ou a primeira aba se a atual for a única restando
|
|
288
|
+
new_window = all_windows[current_index - 1]
|
|
289
|
+
|
|
290
|
+
self.driver.switch_to.window(new_window)
|
|
291
|
+
|
|
292
|
+
def back_main_tab(self):
|
|
293
|
+
"""
|
|
294
|
+
Desc:
|
|
295
|
+
Switch back to the first browser tab.\n
|
|
296
|
+
Returns: None
|
|
297
|
+
"""
|
|
298
|
+
windows = self.driver.window_handles
|
|
299
|
+
self.driver.switch_to.window(windows[0])
|
|
300
|
+
|
|
301
|
+
def go_to_tab(self, index):
|
|
302
|
+
"""
|
|
303
|
+
Desc:
|
|
304
|
+
Switch to a specific browser tab by index.\n
|
|
305
|
+
Args:
|
|
306
|
+
- ``index`` - The index of the tab to switch to (0-based).
|
|
307
|
+
Returns: None
|
|
308
|
+
"""
|
|
309
|
+
windows = self.driver.window_handles
|
|
310
|
+
if index < len(windows):
|
|
311
|
+
self.driver.switch_to.window(windows[index])
|
|
312
|
+
|
|
313
|
+
def element_exists_xpath(self, xpath, time=5):
|
|
314
|
+
"""
|
|
315
|
+
Desc:
|
|
316
|
+
Check if an element exists by XPATH.\n
|
|
317
|
+
Args:
|
|
318
|
+
- ``xpath`` - The XPATH of the element to be checked.
|
|
319
|
+
- ``time`` - Maximum time to wait for the element in seconds. Default is 5 seconds.
|
|
320
|
+
Returns:
|
|
321
|
+
- True if the element exists, False otherwise.
|
|
322
|
+
"""
|
|
323
|
+
try:
|
|
324
|
+
WebDriverWait(self.driver, time).until(
|
|
325
|
+
EC.visibility_of_element_located((By.XPATH, xpath))
|
|
326
|
+
)
|
|
327
|
+
return True
|
|
328
|
+
except Exception:
|
|
329
|
+
return False
|
|
330
|
+
|
|
331
|
+
def element_exists_css(self, selector, time=5):
|
|
332
|
+
"""
|
|
333
|
+
Desc:
|
|
334
|
+
Check if an element exists by CSS selector.\n
|
|
335
|
+
Args:
|
|
336
|
+
- ``selector`` - The CSS selector of the element to be checked.
|
|
337
|
+
- ``time`` - Maximum time to wait for the element in seconds. Default is 5 seconds.
|
|
338
|
+
Returns:
|
|
339
|
+
- True if the element exists, False otherwise.
|
|
340
|
+
"""
|
|
341
|
+
try:
|
|
342
|
+
WebDriverWait(self.driver, time).until(
|
|
343
|
+
EC.presence_of_element_located((By.CSS_SELECTOR, selector))
|
|
344
|
+
)
|
|
345
|
+
return True
|
|
346
|
+
except Exception:
|
|
347
|
+
return False
|
|
348
|
+
|
|
349
|
+
def element_exists(self, by, value, time=5):
|
|
350
|
+
"""
|
|
351
|
+
Desc:
|
|
352
|
+
Check if an element exists by a given locator strategy.\n
|
|
353
|
+
Args:
|
|
354
|
+
- ``by`` - The locator strategy (e.g., By.ID, By.XPATH, By.CSS_SELECTOR).
|
|
355
|
+
- ``value`` - The value of the locator.
|
|
356
|
+
- ``time`` - Maximum time to wait for the element in seconds. Default is 5 seconds.
|
|
357
|
+
Returns:
|
|
358
|
+
- True if the element exists, False otherwise.
|
|
359
|
+
"""
|
|
360
|
+
try:
|
|
361
|
+
WebDriverWait(self.driver, time).until(
|
|
362
|
+
EC.presence_of_element_located((by, value))
|
|
363
|
+
)
|
|
364
|
+
return True
|
|
365
|
+
except Exception:
|
|
366
|
+
return False
|
|
367
|
+
|
|
368
|
+
def change_download_path(self, download_path):
|
|
369
|
+
self.driver.execute_cdp_cmd(
|
|
370
|
+
"Page.setDownloadBehavior",
|
|
371
|
+
{"behavior": "allow", "downloadPath": str(Path(download_path).resolve())},
|
|
372
|
+
)
|
|
373
|
+
self.current_download_path = download_path
|
|
374
|
+
|
|
375
|
+
def restore_download_path(self):
|
|
376
|
+
self.driver.execute_cdp_cmd(
|
|
377
|
+
"Page.setDownloadBehavior",
|
|
378
|
+
{
|
|
379
|
+
"behavior": "allow",
|
|
380
|
+
"downloadPath": str(Path(self.default_download_path).resolve()),
|
|
381
|
+
},
|
|
382
|
+
)
|
|
383
|
+
self.current_download_path = self.default_download_path
|
|
384
|
+
|
|
385
|
+
def wait_downloads_done(
|
|
386
|
+
self, timeout_start=10, timeout_end=60, file_path=None, escape=False
|
|
387
|
+
):
|
|
388
|
+
temp_files = (
|
|
389
|
+
lambda: glob(f"{self.current_download_path}/*.crdownload")
|
|
390
|
+
+ glob(f"{self.current_download_path}/*.tmp")
|
|
391
|
+
+ glob(f"{self.current_download_path}/*.part")
|
|
392
|
+
)
|
|
393
|
+
|
|
394
|
+
scape_files = lambda: glob(
|
|
395
|
+
f"{self.current_download_path}/{glob.escape(file_path)}"
|
|
396
|
+
)
|
|
397
|
+
not_scape_files = lambda: glob(f"{self.current_download_path}/{file_path}")
|
|
398
|
+
|
|
399
|
+
if file_path:
|
|
400
|
+
start_time = time()
|
|
401
|
+
while not (scape_files() if escape else not_scape_files()):
|
|
402
|
+
if time() - start_time >= timeout_end:
|
|
403
|
+
raise Exception("Download file not detected")
|
|
404
|
+
|
|
405
|
+
while temp_files():
|
|
406
|
+
sleep(0.3)
|
|
407
|
+
return scape_files() if escape else not_scape_files()
|
|
408
|
+
|
|
409
|
+
# AGUARDA OS DOWNLOADS INICIAREM
|
|
410
|
+
start_time = time()
|
|
411
|
+
while not temp_files():
|
|
412
|
+
if time() - start_time >= timeout_start:
|
|
413
|
+
raise Exception("Download files not detected")
|
|
414
|
+
|
|
415
|
+
while temp_files():
|
|
416
|
+
sleep(0.3)
|
|
417
|
+
|
|
418
|
+
def get_last_download_file(self):
|
|
419
|
+
caminho_pasta = Path(self.current_download_path)
|
|
420
|
+
if not caminho_pasta.exists():
|
|
421
|
+
return None
|
|
422
|
+
|
|
423
|
+
arquivos = [
|
|
424
|
+
f
|
|
425
|
+
for f in caminho_pasta.iterdir()
|
|
426
|
+
if f.is_file() and not f.name.endswith((".crdownload", ".tmp", ".part"))
|
|
427
|
+
]
|
|
428
|
+
if not arquivos:
|
|
429
|
+
return None
|
|
430
|
+
|
|
431
|
+
# Ordenamos pelo mtime (Modification Time)
|
|
432
|
+
# No Windows e Linux, isso representa quando o download terminou de ser escrito.
|
|
433
|
+
ultimo_arquivo = max(arquivos, key=lambda f: f.stat().st_mtime)
|
|
434
|
+
|
|
435
|
+
return str(ultimo_arquivo.absolute())
|
|
436
|
+
|
|
437
|
+
def full_screenshot(self, path):
|
|
438
|
+
self.scroll_end()
|
|
439
|
+
# 1. Pegar a altura total do documento via JavaScript
|
|
440
|
+
total_height = self.driver.execute_script(
|
|
441
|
+
"return document.body.parentNode.scrollHeight"
|
|
442
|
+
)
|
|
443
|
+
total_width = self.driver.execute_script(
|
|
444
|
+
"return document.body.parentNode.scrollWidth"
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
self.driver.execute_cdp_cmd(
|
|
448
|
+
"Emulation.setDeviceMetricsOverride",
|
|
449
|
+
{
|
|
450
|
+
"width": total_width,
|
|
451
|
+
"height": total_height,
|
|
452
|
+
"deviceScaleFactor": 1,
|
|
453
|
+
"mobile": False,
|
|
454
|
+
},
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# 2. Redimensionar a janela para o tamanho total da página
|
|
458
|
+
sleep(1)
|
|
459
|
+
|
|
460
|
+
# 3. Tirar o print padrão
|
|
461
|
+
self.driver.save_screenshot(path)
|
|
462
|
+
|
|
463
|
+
def save_to_pdf(self, path, single_page=False):
|
|
464
|
+
# O comando retorna um dicionário com os dados em Base64
|
|
465
|
+
params = {"printBackground": True, "landscape": False}
|
|
466
|
+
if single_page:
|
|
467
|
+
# Dividimos por 96 porque o padrão de tela é 96 DPI
|
|
468
|
+
dimensions = self.driver.execute_script("""
|
|
469
|
+
return {
|
|
470
|
+
width: document.documentElement.offsetWidth / 96,
|
|
471
|
+
height: document.documentElement.scrollHeight / 96
|
|
472
|
+
};
|
|
473
|
+
""")
|
|
474
|
+
|
|
475
|
+
params = {
|
|
476
|
+
**params,
|
|
477
|
+
"paperHeight": dimensions["height"],
|
|
478
|
+
"marginTop": 0,
|
|
479
|
+
"marginBottom": 0,
|
|
480
|
+
"marginLeft": 0,
|
|
481
|
+
"marginRight": 0,
|
|
482
|
+
"pageRanges": "1", # Garante que ele foque na primeira (e única) página
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
result = self.driver.execute_cdp_cmd("Page.printToPDF", params)
|
|
486
|
+
|
|
487
|
+
# Salva o arquivo no diretório que você escolher agora
|
|
488
|
+
with open(path, "wb") as f:
|
|
489
|
+
f.write(base64.b64decode(result["data"]))
|
|
490
|
+
|
|
491
|
+
def add_print_style(self):
|
|
492
|
+
"""
|
|
493
|
+
Injeta regras de CSS para garantir que as cores e fundos
|
|
494
|
+
sejam preservados durante a impressão/PDF.
|
|
495
|
+
"""
|
|
496
|
+
css_code = """
|
|
497
|
+
var style = document.createElement('style');
|
|
498
|
+
style.type = 'text/css';
|
|
499
|
+
style.innerHTML = `
|
|
500
|
+
@media print {
|
|
501
|
+
body, .info-bar, .container, .section h3 {
|
|
502
|
+
-webkit-print-color-adjust: exact !important;
|
|
503
|
+
print-color-adjust: exact !important;
|
|
504
|
+
color-adjust: exact !important;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
`;
|
|
508
|
+
document.getElementsByTagName('head')[0].appendChild(style);
|
|
509
|
+
"""
|
|
510
|
+
self.driver.execute_script(css_code)
|
|
511
|
+
|
|
512
|
+
def add_style(self, css_content):
|
|
513
|
+
"""
|
|
514
|
+
Injeta um bloco de CSS dinamicamente na página atual.
|
|
515
|
+
"""
|
|
516
|
+
script = """
|
|
517
|
+
var style = document.createElement('style');
|
|
518
|
+
style.type = 'text/css';
|
|
519
|
+
if (style.styleSheet){
|
|
520
|
+
style.styleSheet.cssText = arguments[0];
|
|
521
|
+
} else {
|
|
522
|
+
style.appendChild(document.createTextNode(arguments[0]));
|
|
523
|
+
}
|
|
524
|
+
document.getElementsByTagName('head')[0].appendChild(style);
|
|
525
|
+
"""
|
|
526
|
+
self.driver.execute_script(script, css_content)
|
|
527
|
+
|
|
528
|
+
def startRemoteSelenium(
|
|
529
|
+
self,
|
|
530
|
+
host,
|
|
531
|
+
name="default",
|
|
532
|
+
cust_opt=[],
|
|
533
|
+
cust_prefs=[],
|
|
534
|
+
remove_default_options=False,
|
|
535
|
+
download_path=None,
|
|
536
|
+
selenoid_browser=("chrome", "128.0"),
|
|
537
|
+
profile=None,
|
|
538
|
+
) -> webdriver:
|
|
539
|
+
self.default_download_path = download_path
|
|
540
|
+
self.current_download_path = download_path
|
|
541
|
+
|
|
542
|
+
# ===================== OPTIONS ==============================
|
|
543
|
+
options = []
|
|
544
|
+
if not remove_default_options:
|
|
545
|
+
options = DEFAULT_OPTIONS
|
|
546
|
+
options += cust_opt
|
|
547
|
+
|
|
548
|
+
web_options = webdriver.ChromeOptions()
|
|
549
|
+
for op in options:
|
|
550
|
+
web_options.add_argument(op)
|
|
551
|
+
|
|
552
|
+
if profile:
|
|
553
|
+
web_options.add_argument(r"user-data-dir={}".format(profile))
|
|
554
|
+
|
|
555
|
+
web_options.headless = False
|
|
556
|
+
|
|
557
|
+
# ==================== PREFERENCES ===========================
|
|
558
|
+
prefs = {
|
|
559
|
+
"download.default_directory": str(Path(download_path).resolve()),
|
|
560
|
+
"savefile.default_directory": str(Path(download_path).resolve()),
|
|
561
|
+
"download.directory_upgrade": True,
|
|
562
|
+
"download.prompt_for_download": False,
|
|
563
|
+
"safebrowsing.enabled": False,
|
|
564
|
+
"download.directory_upgrade": True,
|
|
565
|
+
"credentials_enable_service": False,
|
|
566
|
+
"profile.password_manager_enabled": False,
|
|
567
|
+
"autofill.profile_enabled": False,
|
|
568
|
+
"plugins.always_open_pdf_externally": True,
|
|
569
|
+
"profile.password_manager_leak_detection": False,
|
|
570
|
+
"printing.print_preview_sticky_settings.appState": '{"recentDestinations":[{"id":"Save as PDF","origin":"local","account":""}],"selectedDestinationId":"Save as PDF","version":2}',
|
|
571
|
+
}
|
|
572
|
+
prefs = {**prefs, **cust_prefs}
|
|
573
|
+
web_options.add_experimental_option("prefs", prefs)
|
|
574
|
+
|
|
575
|
+
# ================== START BROWSER ===========================
|
|
576
|
+
|
|
577
|
+
web_options.add_experimental_option("useAutomationExtension", False)
|
|
578
|
+
web_options.add_experimental_option("excludeSwitches", ["enable-automation"])
|
|
579
|
+
|
|
580
|
+
CAPABILITIES["name"] = name
|
|
581
|
+
CAPABILITIES["browserName"] = selenoid_browser[0]
|
|
582
|
+
CAPABILITIES["version"] = selenoid_browser[1]
|
|
583
|
+
web_options.set_capability(name="selenoid:options", value=CAPABILITIES)
|
|
584
|
+
web_options.set_capability(
|
|
585
|
+
name="browserName", value=CAPABILITIES["browserName"]
|
|
586
|
+
)
|
|
587
|
+
|
|
588
|
+
self.driver = CustomRemoteDriver(command_executor=host, options=web_options)
|
|
589
|
+
self.driver.maximize_window()
|
|
590
|
+
self.driver.implicitly_wait(10)
|
|
591
|
+
|
|
592
|
+
return self.driver
|
|
593
|
+
|
|
594
|
+
def startUC(
|
|
595
|
+
self,
|
|
596
|
+
version_main: int = 142,
|
|
597
|
+
enable_cdp_events: bool = True,
|
|
598
|
+
download_path: str = None,
|
|
599
|
+
custom_prefs: dict = {},
|
|
600
|
+
custom_args: list = [],
|
|
601
|
+
):
|
|
602
|
+
self.default_download_path = download_path
|
|
603
|
+
self.current_download_path = download_path
|
|
604
|
+
options = uc.ChromeOptions()
|
|
605
|
+
|
|
606
|
+
# PREFS padrão
|
|
607
|
+
prefs = {
|
|
608
|
+
"credentials_enable_service": False,
|
|
609
|
+
"profile.password_manager_enabled": False,
|
|
610
|
+
"profile.password_manager_leak_detection": False,
|
|
611
|
+
"download.default_directory": str(Path(download_path).resolve()),
|
|
612
|
+
}
|
|
613
|
+
prefs.update(custom_prefs)
|
|
614
|
+
options.add_experimental_option("prefs", prefs)
|
|
615
|
+
|
|
616
|
+
# ARGUMENTOS padrão
|
|
617
|
+
default_args = DEFAULT_OPTIONS.copy()
|
|
618
|
+
default_args.extend(custom_args)
|
|
619
|
+
|
|
620
|
+
for arg in default_args:
|
|
621
|
+
options.add_argument(arg)
|
|
622
|
+
|
|
623
|
+
# REMOVIDO: options.add_experimental_option("detach", True)
|
|
624
|
+
# Isso QUEBRA o UC e gera o erro que você recebeu.
|
|
625
|
+
|
|
626
|
+
# start UC
|
|
627
|
+
driver = uc.Chrome(
|
|
628
|
+
options=options,
|
|
629
|
+
enable_cdp_events=enable_cdp_events,
|
|
630
|
+
version_main=version_main,
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
self.driver = driver
|
|
634
|
+
driver.implicitly_wait(10)
|
|
635
|
+
return driver
|
|
636
|
+
|
|
637
|
+
def startChrome(
|
|
638
|
+
self,
|
|
639
|
+
driver_path,
|
|
640
|
+
download_path: str = None,
|
|
641
|
+
custom_prefs: dict = {},
|
|
642
|
+
custom_options: list = [],
|
|
643
|
+
remove_default_options: bool = False,
|
|
644
|
+
profile: str = None,
|
|
645
|
+
binary_location: str = None,
|
|
646
|
+
):
|
|
647
|
+
self.default_download_path = download_path
|
|
648
|
+
self.current_download_path = download_path
|
|
649
|
+
|
|
650
|
+
# ===================== OPTIONS ==============================
|
|
651
|
+
options = []
|
|
652
|
+
if not remove_default_options:
|
|
653
|
+
options = DEFAULT_OPTIONS
|
|
654
|
+
options += custom_options
|
|
655
|
+
|
|
656
|
+
web_options = webdriver.ChromeOptions()
|
|
657
|
+
if binary_location:
|
|
658
|
+
web_options.binary_location = binary_location
|
|
659
|
+
web_options.set_capability("unhandledPromptBehavior", "accept")
|
|
660
|
+
for op in options:
|
|
661
|
+
web_options.add_argument(op)
|
|
662
|
+
|
|
663
|
+
if profile:
|
|
664
|
+
web_options.add_argument(
|
|
665
|
+
r"user-data-dir={}".format(str(Path(profile).resolve()))
|
|
666
|
+
)
|
|
667
|
+
|
|
668
|
+
web_options.headless = False
|
|
669
|
+
|
|
670
|
+
# ==================== PREFERENCES ===========================
|
|
671
|
+
prefs = {
|
|
672
|
+
"download.default_directory": (
|
|
673
|
+
str(Path(download_path).resolve()) if download_path else None
|
|
674
|
+
),
|
|
675
|
+
"savefile.default_directory": (
|
|
676
|
+
str(Path(download_path).resolve()) if download_path else None
|
|
677
|
+
),
|
|
678
|
+
"download.directory_upgrade": True,
|
|
679
|
+
"download.prompt_for_download": False,
|
|
680
|
+
"safebrowsing.enabled": False,
|
|
681
|
+
"download.directory_upgrade": True,
|
|
682
|
+
"credentials_enable_service": False,
|
|
683
|
+
"profile.password_manager_enabled": False,
|
|
684
|
+
"autofill.profile_enabled": False,
|
|
685
|
+
"plugins.always_open_pdf_externally": True,
|
|
686
|
+
"profile.password_manager_leak_detection": False,
|
|
687
|
+
"printing.print_preview_sticky_settings.appState": '{"recentDestinations":[{"id":"Save as PDF","origin":"local","account":""}],"selectedDestinationId":"Save as PDF","version":2}',
|
|
688
|
+
}
|
|
689
|
+
prefs = {**prefs, **custom_prefs}
|
|
690
|
+
web_options.add_experimental_option("prefs", prefs)
|
|
691
|
+
|
|
692
|
+
service = Service(executable_path=driver_path)
|
|
693
|
+
driver = CustomChromeDriver(service=service, options=web_options)
|
|
694
|
+
driver.maximize_window()
|
|
695
|
+
|
|
696
|
+
self.driver = driver
|
|
697
|
+
return driver
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver
|
|
2
|
+
from seleniumUts.webelement import CWebElement
|
|
3
|
+
from selenium.webdriver import Chrome
|
|
4
|
+
from selenium.webdriver.common.by import By
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CustomRemoteDriver(RemoteWebDriver):
|
|
8
|
+
def create_web_element(self, element_id):
|
|
9
|
+
return CWebElement(self, element_id)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CustomChromeDriver(Chrome):
|
|
13
|
+
def create_web_element(self, element_id):
|
|
14
|
+
return CWebElement(self, element_id)
|
|
15
|
+
|
|
16
|
+
def find_element(
|
|
17
|
+
self, by=By.ID, value=None, selenium_uts=None, time=None, custom_error=None
|
|
18
|
+
):
|
|
19
|
+
# Faz a busca normal
|
|
20
|
+
element = super().find_element(by, value)
|
|
21
|
+
|
|
22
|
+
# Injeta as informações de busca no objeto CWebElement recém-criado
|
|
23
|
+
if isinstance(element, CWebElement):
|
|
24
|
+
element._found_by = by
|
|
25
|
+
element._query_path = value
|
|
26
|
+
element.selenium_uts = selenium_uts
|
|
27
|
+
element.time = time
|
|
28
|
+
element.custom_error = custom_error
|
|
29
|
+
|
|
30
|
+
return element
|
|
31
|
+
|
|
32
|
+
# É importante fazer o mesmo para find_elements (lista)
|
|
33
|
+
def find_elements(
|
|
34
|
+
self, by=By.ID, value=None, selenium_uts=None, time=None, custom_error=None
|
|
35
|
+
):
|
|
36
|
+
elements = super().find_elements(by, value)
|
|
37
|
+
for el in elements:
|
|
38
|
+
if isinstance(el, CWebElement):
|
|
39
|
+
el._found_by = by
|
|
40
|
+
el._query_path = value
|
|
41
|
+
el.selenium_uts = selenium_uts
|
|
42
|
+
el.time = time
|
|
43
|
+
el.custom_error = custom_error
|
|
44
|
+
return elements
|
|
File without changes
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from selenium.webdriver.remote.webelement import WebElement
|
|
2
|
+
from selenium.webdriver.support.ui import Select
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CWebElement(WebElement):
|
|
7
|
+
def __init__(self, *args, **kwargs):
|
|
8
|
+
super().__init__(*args, **kwargs)
|
|
9
|
+
|
|
10
|
+
def delayed_send(self, word, delay=0.5):
|
|
11
|
+
"""
|
|
12
|
+
Desc:
|
|
13
|
+
Send keys to the element with a delay between each character.\n
|
|
14
|
+
Args:
|
|
15
|
+
- ``word`` - The string to be sent to the element.
|
|
16
|
+
- ``delay`` - The delay between each character in seconds.
|
|
17
|
+
Default: 0.5 seconds.
|
|
18
|
+
"""
|
|
19
|
+
for c in word:
|
|
20
|
+
self.send_keys(c)
|
|
21
|
+
time.sleep(delay)
|
|
22
|
+
|
|
23
|
+
def focus(self):
|
|
24
|
+
"""Foca no elemento"""
|
|
25
|
+
self.parent.execute_script("arguments[0].scrollIntoView(true);", self)
|
|
26
|
+
time.sleep(0.5)
|
|
27
|
+
self.parent.execute_script("arguments[0].focus();", self)
|
|
28
|
+
return self
|
|
29
|
+
|
|
30
|
+
def select_by_text(self, text):
|
|
31
|
+
"""Seleciona item em dropdown pelo texto visível"""
|
|
32
|
+
Select(self).select_by_visible_text(text)
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
def select_by_value(self, value):
|
|
36
|
+
"""Seleciona item em dropdown pelo valor"""
|
|
37
|
+
Select(self).select_by_value(value)
|
|
38
|
+
return self
|
|
39
|
+
|
|
40
|
+
def click_js(self):
|
|
41
|
+
"""Clica no elemento usando JavaScript (evita toggle indesejado)"""
|
|
42
|
+
self.driver.execute_script("arguments[0].click();", self)
|
|
43
|
+
return self
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: seleniumUts
|
|
3
|
+
Version: 1.1.1
|
|
4
|
+
Summary: Zdek Util libraries for Pythom coding
|
|
5
|
+
Home-page: https://github.com/ZdekPyPi/SeleniumUts
|
|
6
|
+
Author: Zdek Development team
|
|
7
|
+
License: MIT
|
|
8
|
+
Keywords: seleniumUts
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Education
|
|
11
|
+
Classifier: Operating System :: Microsoft :: Windows :: Windows 10
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
Requires-Dist: undetected-chromedriver>=3.5.5
|
|
17
|
+
Requires-Dist: selenium>=4.15.2
|
|
18
|
+
Dynamic: author
|
|
19
|
+
Dynamic: classifier
|
|
20
|
+
Dynamic: description
|
|
21
|
+
Dynamic: description-content-type
|
|
22
|
+
Dynamic: home-page
|
|
23
|
+
Dynamic: keywords
|
|
24
|
+
Dynamic: license
|
|
25
|
+
Dynamic: requires-dist
|
|
26
|
+
Dynamic: requires-python
|
|
27
|
+
Dynamic: summary
|
|
28
|
+
|
|
29
|
+
# SeleniumUts
|
|
30
|
+
|
|
31
|
+
Uma biblioteca Python que encapsula algumas funcionalidades do Selenium WebDriver, facilitando a automação de navegadores para testes e raspagem de dados. A biblioteca suporta o uso do `undetected_chromedriver` e integra-se facilmente com o Selenoid para execução de testes em ambientes distribuídos.
|
|
32
|
+
|
|
33
|
+
## Uso
|
|
34
|
+
|
|
35
|
+
### Importando a Biblioteca
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from seleniumUts import SeleniumUts
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Criando uma Instância de `SeleniumUts`
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
selenium_lib = SeleniumUts()
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Exemplos de Uso
|
|
48
|
+
|
|
49
|
+
#### Configurando o Selenium com ChromeDriver
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
# Configure o Selenium sem usar o Selenoid
|
|
53
|
+
selenium_lib.setupSelenium(host=None, use_selenoid=False)
|
|
54
|
+
|
|
55
|
+
# Abrir uma página web
|
|
56
|
+
driver = selenium_lib.open_page('https://www.example.com')
|
|
57
|
+
|
|
58
|
+
# Fechar o navegador
|
|
59
|
+
selenium_lib.close()
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
#### Configurando o Selenium com Selenoid
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
# Configure o Selenium usando o Selenoid
|
|
66
|
+
selenoid_host = 'http://your-selenoid-server.com/wd/hub'
|
|
67
|
+
selenium_lib.setupSelenium(host=selenoid_host, use_selenoid=True)
|
|
68
|
+
|
|
69
|
+
# Abrir uma página web
|
|
70
|
+
driver = selenium_lib.open_page('https://www.example.com')
|
|
71
|
+
|
|
72
|
+
# Fechar o navegador
|
|
73
|
+
selenium_lib.close()
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
#### Aguardando a Visibilidade de um Elemento
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
# Configure o Selenium
|
|
80
|
+
selenium_lib.setupSelenium(host=None, use_selenoid=False)
|
|
81
|
+
|
|
82
|
+
# Abrir uma página web
|
|
83
|
+
selenium_lib.open_page('https://www.example.com')
|
|
84
|
+
|
|
85
|
+
# Esperar até que o elemento esteja visível
|
|
86
|
+
element = selenium_lib.wait_xpath('//button[@id="submit"]', time=10)
|
|
87
|
+
element.click()
|
|
88
|
+
|
|
89
|
+
# Fechar o navegador
|
|
90
|
+
selenium_lib.close()
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### Envio de Texto com Atraso entre Caracteres
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
# Configure o Selenium
|
|
97
|
+
selenium_lib.setupSelenium(host=None, use_selenoid=False)
|
|
98
|
+
|
|
99
|
+
# Abrir uma página web
|
|
100
|
+
selenium_lib.open_page('https://www.example.com')
|
|
101
|
+
|
|
102
|
+
# Encontrar o campo de texto e enviar texto com atraso
|
|
103
|
+
element = selenium_lib.wait_xpath('//input[@id="search-box"]')
|
|
104
|
+
element.delayed_send('Python Selenium', delay=0.2)
|
|
105
|
+
|
|
106
|
+
# Fechar o navegador
|
|
107
|
+
selenium_lib.close()
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Rolagem até o Fim da Página
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
# Configure o Selenium
|
|
114
|
+
selenium_lib.setupSelenium(host=None, use_selenoid=False)
|
|
115
|
+
|
|
116
|
+
# Abrir uma página web
|
|
117
|
+
selenium_lib.open_page('https://www.example.com')
|
|
118
|
+
|
|
119
|
+
# Rolagem até o fim da página
|
|
120
|
+
selenium_lib.scroll_end()
|
|
121
|
+
|
|
122
|
+
# Fechar o navegador
|
|
123
|
+
selenium_lib.close()
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Métodos Disponíveis
|
|
127
|
+
|
|
128
|
+
- **`setupSelenium(host, name="default", use_selenoid=False, cust_opt=[], remove_default_options=False, download_path=None, selenoid_browser=("chrome","110.0"))`**: Configura o WebDriver do Selenium com opções personalizadas e preferências para o ChromeDriver. Suporta configuração para Selenoid.
|
|
129
|
+
- **`open_page(page)`**: Abre uma página web e espera até que ela seja totalmente carregada.
|
|
130
|
+
- **`wait_xpath(path, time=20, throw=True)`**: Aguarda até que um elemento, identificado por um caminho XPath, esteja visível no DOM.
|
|
131
|
+
- **`<el>.delayed_send(word, delay)`**: Envia texto para um elemento, inserindo um atraso especificado entre cada caractere.
|
|
132
|
+
- **`scroll_end()`**: Rola até o final da página atual.
|
|
133
|
+
- **`close()`**: Fecha o navegador e encerra a sessão do WebDriver.
|
|
134
|
+
|
|
135
|
+
## Contribuição
|
|
136
|
+
|
|
137
|
+
Contribuições são bem-vindas! Por favor, envie um pull request ou abra uma issue para quaisquer problemas ou melhorias.
|
|
138
|
+
|
|
139
|
+
## Licença
|
|
140
|
+
|
|
141
|
+
Este projeto está licenciado sob a licença MIT.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
seleniumUts/__init__.py,sha256=QzMr-N06WTh37AAdjgRc_Nw0ZWKn6X8fCNKgbz5TYu8,23984
|
|
2
|
+
seleniumUts/custom_driver.py,sha256=ffzeRl3Uhccfo47jj61DWMuPHSBMJwT-mAV0BaT0lr8,1536
|
|
3
|
+
seleniumUts/seleniumuts.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
seleniumUts/webelement.py,sha256=ccf-zTdipXfNiQyVlgJl17jLo9xe3pIrBQcBPy3jkr8,1408
|
|
5
|
+
seleniumuts-1.1.1.dist-info/METADATA,sha256=UjOD3xpkcfGCuO1z6fFY7GSloUxDXxP-TCRvjG68ZZA,4148
|
|
6
|
+
seleniumuts-1.1.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
7
|
+
seleniumuts-1.1.1.dist-info/top_level.txt,sha256=DSdtNU4ul1OiKP-wVWtW9-nNeK5jtIkp3bKq5xGO1Ns,12
|
|
8
|
+
seleniumuts-1.1.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
seleniumUts
|