formharvester 2.2.2__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.
SeleniumBot.py ADDED
@@ -0,0 +1,631 @@
1
+ import json
2
+ import os
3
+ import random
4
+ import re
5
+ import sys
6
+ import time
7
+ from urllib.parse import urljoin, parse_qs, urlparse
8
+
9
+ import requests
10
+ from bs4 import BeautifulSoup
11
+ from selenium import webdriver
12
+ from selenium.common.exceptions import (
13
+ NoSuchElementException,
14
+ )
15
+ from selenium.common.exceptions import TimeoutException
16
+ from selenium.webdriver.common.by import By
17
+ from selenium.webdriver.common.keys import Keys
18
+ from selenium.webdriver.support import expected_conditions as EC
19
+ from selenium.webdriver.support.ui import WebDriverWait, Select
20
+ import html
21
+
22
+ if getattr(sys, 'frozen', False):
23
+ BASE_DIR = os.path.dirname(sys.executable)
24
+ elif __file__:
25
+ BASE_DIR = os.path.dirname(os.path.realpath(__file__))
26
+
27
+
28
+ class SeleniumBot:
29
+ driver = None
30
+ captcha_client = None
31
+
32
+ DEV_SETTINGS = None
33
+ COOKIES_PROFILE = None
34
+ HEADLESS = None
35
+ INCOGNITO = None
36
+ DISABLE_IMAGES = None
37
+ REMOTE_DEBUGGING = None
38
+ HIDE_EXTENSION = None
39
+ SINGLE_PROXY = None
40
+ PROXY_LIST = None
41
+ PROXY_AUTH = None
42
+ USERAGENT = None
43
+ FLASH = None
44
+ DATA_DIR = None
45
+ PROFILE = None
46
+
47
+ EXTENSIONS = []
48
+
49
+ WAIT = 99999
50
+
51
+ TIMEOUT = 5
52
+ MAX_SLEEP = 5
53
+
54
+ MIN_RAND = 0.64
55
+ MAX_RAND = 1.27
56
+ MIN_RAND_LONG = 4.78
57
+ MAX_RAND_LONG = 11.1
58
+
59
+ NUM_MOUSE_MOVES = 10
60
+
61
+ crawl = True
62
+
63
+ def get(self, page, pre_sleep=0, sleep=0, timeout=False, check=False):
64
+
65
+ if not self.crawl:
66
+ return
67
+
68
+ if check:
69
+ try:
70
+ requests.get(page, timeout=8)
71
+ except:
72
+ return
73
+
74
+ if timeout:
75
+ self.driver.set_page_load_timeout(timeout)
76
+
77
+ time.sleep(pre_sleep)
78
+ try:
79
+ self.driver.get(page)
80
+ time.sleep(sleep)
81
+ if timeout:
82
+ self.driver.set_page_load_timeout(-1)
83
+ return True
84
+ except Exception as e:
85
+ print(e)
86
+ if timeout:
87
+ self.driver.set_page_load_timeout(-1)
88
+ return False
89
+
90
+ def get_wait(self, url, selector, wait=9999):
91
+ self.get(url)
92
+ return self.wait_show_element(selector, wait=wait)
93
+
94
+ def reload(self):
95
+ self.driver.refresh()
96
+
97
+ def close(self):
98
+ if self.COOKIES_PROFILE:
99
+ self.save_cookies()
100
+ try:
101
+ self.driver.quit()
102
+ except:
103
+ pass
104
+
105
+ def script(self, script, *args):
106
+ return self.driver.execute_script(script, *args)
107
+
108
+ def css(self, selector, node=None, getall=False, attr=None,
109
+ wait=None, wait_for=None,
110
+ ):
111
+
112
+ if wait:
113
+ w = self.wait_show_element(selector, wait=wait)
114
+ if not w:
115
+ return None
116
+ if wait_for:
117
+ w = self.wait_for_element(selector, wait=wait_for)
118
+ if not w:
119
+ return None
120
+
121
+ if getall:
122
+ el = self.get_elements_from(node if node else self.driver, selector)
123
+ else:
124
+ el = self.get_element_from(node if node else self.driver, selector)
125
+
126
+ if attr and el:
127
+ el = self.extract_attributes(el, attr)
128
+
129
+ return el
130
+
131
+ def xpath(self, selector, node=None, getall=False, attr=None, wait=None):
132
+ if wait:
133
+ w = self.wait_show_element(selector, xpath=True, wait=wait)
134
+ if not w:
135
+ return None
136
+
137
+ if getall:
138
+ el = self.get_elements_from(node if node else self.driver, selector, xpath=True)
139
+ else:
140
+ el = self.get_element_from(node if node else self.driver, selector, xpath=True)
141
+
142
+ if attr and el:
143
+ el = self.extract_attributes(el, attr)
144
+
145
+ return el
146
+
147
+ def get_attr(self, el, attr):
148
+ if type(attr) == list:
149
+ output = []
150
+ for a in attr:
151
+ if a == 'text':
152
+ output.append(el.text)
153
+ else:
154
+ output.append(el.get_attribute(a))
155
+ else:
156
+ if attr == 'text':
157
+ output = el.text
158
+ else:
159
+ output = el.get_attribute(attr)
160
+ return output
161
+
162
+ def extract_attributes(self, el, attr):
163
+ if type(el) == list:
164
+ return [self.get_attr(i, attr) for i in el]
165
+ else:
166
+ return self.get_attr(el, attr)
167
+
168
+ def contains_text(self, text):
169
+ try:
170
+ return self.driver.find_element(By.XPATH, f'//*[contains(text(), "{text}")]')
171
+ except NoSuchElementException:
172
+ return None
173
+
174
+ def contains_regex(self, regex):
175
+ page_text = self.css('body').text
176
+ if re.findall(rf'{regex}', page_text):
177
+ return True
178
+ else:
179
+ return False
180
+
181
+ def submit_form(self, element):
182
+ try:
183
+ element.submit()
184
+ return True
185
+ except TimeoutException:
186
+ return False
187
+
188
+ def wait_page_load(self):
189
+ while True:
190
+ page_state = self.script(
191
+ 'return document.readyState;')
192
+ self.driver.implicitly_wait(1)
193
+ if page_state == 'complete':
194
+ break
195
+ return
196
+
197
+ def wait_for_text(self, text, wait=99999):
198
+ try:
199
+ wait = WebDriverWait(self.driver, wait)
200
+ element = wait.until(EC.presence_of_element_located((By.XPATH, f'//*[contains(text(), "{text}")]')))
201
+ return element
202
+ except:
203
+ return None
204
+
205
+ def wait_for_regex(self, regex):
206
+ while True:
207
+ if self.contains_regex(regex):
208
+ return True
209
+ time.sleep(.5)
210
+
211
+ def wait_for_element(self, selector, xpath=False, wait=99999):
212
+ try:
213
+ wait = WebDriverWait(self.driver, wait)
214
+ if xpath:
215
+ element = wait.until(EC.presence_of_element_located((By.XPATH, selector)))
216
+ else:
217
+ element = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, selector)))
218
+ return element
219
+ except:
220
+ return None
221
+
222
+ def wait_show_element(self, selector, xpath=False, wait=99999):
223
+ try:
224
+ wait = WebDriverWait(self.driver, wait)
225
+ if xpath:
226
+ element = wait.until(EC.visibility_of_element_located((By.XPATH, selector)))
227
+ else:
228
+ element = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, selector)))
229
+ return element
230
+ except:
231
+ return None
232
+
233
+ def wait_hide_element(self, selector, wait):
234
+ try:
235
+ wait = WebDriverWait(self.driver, wait)
236
+ element = wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR, selector)))
237
+ return element
238
+ except:
239
+ return None
240
+
241
+ def wait_click_element(self, selector, wait):
242
+ try:
243
+ wait = WebDriverWait(self.driver, wait)
244
+ element = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, selector)))
245
+ return element
246
+ except:
247
+ return None
248
+
249
+ def element_is_present(self, selector):
250
+ try:
251
+ self.driver.find_element(By.CSS_SELECTOR, selector)
252
+ return True
253
+ except NoSuchElementException:
254
+ return False
255
+
256
+ def verify_element_present(self, selector):
257
+ if not self.element_is_present(selector):
258
+ raise Exception('Element %s not found' % selector)
259
+
260
+ def get_element_from(self, fromObject, selector, xpath=False):
261
+ try:
262
+ if xpath:
263
+ return fromObject.find_element(By.XPATH, selector)
264
+ else:
265
+ return fromObject.find_element(By.CSS_SELECTOR, selector)
266
+ except NoSuchElementException:
267
+ return None
268
+
269
+ def get_elements_from(self, fromObject, selector, xpath=False):
270
+ try:
271
+ if xpath:
272
+ return fromObject.find_elements(By.XPATH, selector)
273
+ else:
274
+ return fromObject.find_elements(By.CSS_SELECTOR, selector)
275
+ except NoSuchElementException:
276
+ return []
277
+
278
+ def get_element_from_value(self, fromObject, selector):
279
+ element = self.get_element_from(fromObject, selector)
280
+ if element:
281
+ return element.text
282
+ return None
283
+
284
+ def get_element_value(self, selector):
285
+ element = self.css(selector)
286
+ if element:
287
+ return element.text
288
+ return None
289
+
290
+ def get_element_from_attribute(self, fromObject, selector, attribute):
291
+ element = self.get_element_from(fromObject, selector)
292
+ if element:
293
+ return element.get_attribute(attribute)
294
+ return None
295
+
296
+ def get_element_attribute(self, selector, attribute):
297
+ element = self.css(selector)
298
+ if element:
299
+ return element.get_attribute(attribute)
300
+ return None
301
+
302
+ def get_parent_levels(self, node, levels):
303
+ path = '..'
304
+ if levels > 1:
305
+ for i in range(1, levels):
306
+ path = path + '/..'
307
+ return node.find_element(By.XPATH, path)
308
+
309
+ def get_parent_node(self, node):
310
+ return node.find_element(By.XPATH, '..')
311
+
312
+ def get_child_nodes(self, node):
313
+ return node.find_elements(By.XPATH, './*')
314
+
315
+ def write(self, field, text,
316
+ css=False,
317
+ xpath=False,
318
+ name=False,
319
+ wait=False,
320
+ clear=False,
321
+ human=False,
322
+ submit=False,
323
+ ):
324
+
325
+ if wait:
326
+ self.wait_show_element(field, wait=self.WAIT, xpath=xpath)
327
+
328
+ if css:
329
+ field = self.css(field)
330
+ elif xpath:
331
+ field = self.xpath(field)
332
+ elif name:
333
+ field = self.driver.find_element(By.NAME, field)
334
+
335
+ if clear:
336
+ if human:
337
+ while field.get_attribute('value') != '':
338
+ field.send_keys(Keys.BACKSPACE)
339
+ time.sleep(random.uniform(.05, .1))
340
+ self.random_sleep()
341
+ else:
342
+ field.clear()
343
+
344
+ if human:
345
+ for letter in text:
346
+ field.send_keys(letter)
347
+ time.sleep(random.uniform(.05, .2))
348
+ else:
349
+ field.send_keys(text)
350
+
351
+ if submit:
352
+ try:
353
+ field.send_keys(text)
354
+ except:
355
+ self.driver.execute_script(f'arguments[0].value = "{text}"', field)
356
+
357
+ return field
358
+
359
+ def press_key(self, key):
360
+ webdriver.ActionChains(self.driver).send_keys(key).perform()
361
+
362
+ def press_enter(self, field_object):
363
+ field_object.send_keys(Keys.RETURN)
364
+ return field_object
365
+
366
+ def click(self,
367
+ element,
368
+ wait=False,
369
+ css=False,
370
+ xpath=False,
371
+ js_click=False,
372
+ sleep=False,
373
+ double=False):
374
+
375
+ if wait:
376
+ w = self.wait_show_element(element, wait=wait, xpath=xpath)
377
+ if not w:
378
+ return None
379
+
380
+ if sleep:
381
+ time.sleep(sleep)
382
+
383
+ if css:
384
+ element = self.css(element)
385
+ elif xpath:
386
+ element = self.xpath(element)
387
+
388
+ if double:
389
+ rng = 2
390
+ else:
391
+ rng = 1
392
+
393
+ for _ in range(rng):
394
+ try:
395
+ # use Selenium's built in click function
396
+ actions = webdriver.ActionChains(self.driver)
397
+ actions.move_to_element(element)
398
+ actions.click(element)
399
+ actions.perform()
400
+ return element
401
+ except:
402
+ try:
403
+ script = ("var viewPortHeight = Math.max("
404
+ "document.documentElement.clientHeight, window.innerHeight || 0);"
405
+ "var elementTop = arguments[0].getBoundingClientRect().top;"
406
+ "window.scrollBy(0, elementTop-(viewPortHeight/2));")
407
+ self.driver.execute_script(script) # parent = the webdriver
408
+ element.click()
409
+ except:
410
+ # try `execute_script` as a last resort
411
+ # print("attempting last ditch effort for click, `execute_script`")
412
+ self.script("arguments[0].click();", element)
413
+ return element
414
+
415
+ def select_checkbox(self, selector, name, deselect=False):
416
+ found_checkbox = False
417
+ checkboxes = self.css(selector, getall=True)
418
+ for checkbox in checkboxes:
419
+ if checkbox.get_attribute('name') == name:
420
+ found_checkbox = True
421
+ if not deselect and not checkbox.is_selected():
422
+ checkbox.click()
423
+ if deselect and checkbox.is_selected():
424
+ checkbox.click()
425
+ if not found_checkbox:
426
+ raise Exception('Checkbox %s not found.' % name)
427
+
428
+ def select_option(self, selector, value):
429
+ found_option = False
430
+ options = self.css(selector, getall=True)
431
+ for option in options:
432
+ if option.get_attribute('value') == str(value):
433
+ found_option = True
434
+ option.click()
435
+ if not found_option:
436
+ raise Exception('Option %s not found' % (value))
437
+
438
+ def select_dropdown(self, value, xpath=False):
439
+ if xpath:
440
+ elem = Select(self.driver.find_element(By.XPATH, value))
441
+ else:
442
+ elem = Select(self.driver.find_element(By.CSS_SELECTOR, value))
443
+ return elem
444
+
445
+ def get_selected_option(self, selector):
446
+ options = self.css(selector)
447
+ for option in options:
448
+ if option.is_selected():
449
+ return option.get_attribute('value')
450
+
451
+ def is_option_selected(self, selector, value):
452
+ options = self.css(selector)
453
+ for option in options:
454
+ if option.is_selected() != (value == option.get_attribute('value')):
455
+ print(option.get_attribute('value'))
456
+ return False
457
+ return True
458
+
459
+ def drag_drop(self, drag, drop):
460
+ drag_item = self.css(drag)
461
+ target_item = self.css(drop)
462
+ action_chains = webdriver.ActionChains(self.driver)
463
+ action_chains.drag_and_drop(drag_item, target_item).perform()
464
+
465
+ def move_to_element(self, element):
466
+ self.driver.execute_script("return arguments[0].scrollIntoView();", element)
467
+ actions = webdriver.ActionChains(self.driver)
468
+ actions.move_to_element(element)
469
+ actions.perform()
470
+
471
+ def check_title(self, title):
472
+ return self.driver.title == title
473
+
474
+ def save_screenshot(self, path=f"{str(int(time.time()))}.png"):
475
+ self.driver.save_screenshot(path)
476
+
477
+ def scroll_up(self, y=100):
478
+ scroll = self.driver.execute_script('return document.documentElement.scrollTop')
479
+ self.driver.execute_script(f'scrollTo(0, {scroll - y})')
480
+
481
+ def scroll_down(self, y=400):
482
+ scroll = self.driver.execute_script('return document.documentElement.scrollTop')
483
+ self.driver.execute_script(f'scrollTo(0, {scroll + y})')
484
+
485
+ def log(self, screenshot=False, error=None):
486
+ try:
487
+ timestamp = str(int(time.time()))
488
+ filename = os.path.join('log', timestamp)
489
+ output = ''
490
+
491
+ if not os.path.exists('log'):
492
+ os.mkdir('log')
493
+
494
+ if screenshot:
495
+ self.save_screenshot(f'{filename}.png')
496
+
497
+ output += str(self.driver.current_url) + '\n\n'
498
+
499
+ if error:
500
+ output += f'{error}\n\n'
501
+
502
+ output += str(self.driver.page_source)
503
+
504
+ soup = BeautifulSoup(self.driver.page_source, 'html.parser')
505
+ output += str(soup.prettify()) + '\n\n'
506
+
507
+ with open(f'{filename}.log', 'w', encoding='utf8') as f:
508
+ f.write(output)
509
+
510
+ except Exception as e:
511
+ print(e)
512
+
513
+ def highlight(self, element,
514
+ css=False):
515
+ if css:
516
+ element = self.css(element)
517
+ self.driver.execute_script("arguments[0].style.border='6px groove green'", element)
518
+
519
+ def check_captcha(self,
520
+ recaptcha=False,
521
+ image=False):
522
+
523
+ if image:
524
+ # Captcha 1 (image)
525
+ src = self.xpath('//img[contains(@class, "captcha") or contains(@src, "captcha")]',
526
+ attr='src')
527
+ if src:
528
+ return urljoin(self.driver.current_url, src)
529
+
530
+ if recaptcha:
531
+ # Captcha 1
532
+ site_key = self.css('.g-recaptcha', attr='data-sitekey')
533
+ if site_key:
534
+ return site_key
535
+
536
+ # Captcha 2
537
+ site_key = self.xpath('//*[contains(@class, "recaptcha") and @data-sitekey]', attr='data-sitekey')
538
+ if site_key:
539
+ return site_key
540
+
541
+ # Captcha 3
542
+ src = self.css('.grecaptcha-logo>iframe', attr='src')
543
+ if src:
544
+ src = html.unescape(src)
545
+ site_key = parse_qs(urlparse(src).query)['k'][0]
546
+ return site_key
547
+
548
+ # Captcha 4
549
+ src = self.xpath('//iframe[contains(@src, "recaptcha") and contains(@src, "k=")]',
550
+ attr='src')
551
+ if src:
552
+ src = html.unescape(src)
553
+ site_key = parse_qs(urlparse(src).query)['k'][0]
554
+ return site_key
555
+ return None
556
+
557
+ def solve_captcha(self, site_key, recaptcha=False, image=False):
558
+ if image:
559
+ captcha = self.captcha_client.decode(site_key)
560
+ if captcha:
561
+ return captcha.get('text')
562
+
563
+ if recaptcha:
564
+ payload = {
565
+ 'googlekey': site_key,
566
+ 'pageurl': self.driver.current_url,
567
+ }
568
+
569
+ captcha = self.captcha_client.decode(type=4, token_params=json.dumps(payload))
570
+ if captcha:
571
+ print(captcha)
572
+ captcha_response = self.css('#g-recaptcha-response')
573
+ if captcha_response:
574
+ self.script(f'''
575
+ arguments[0].innerHTML='{captcha['text']}'
576
+ ''', captcha_response)
577
+ else:
578
+ captcha_response = self.xpath(
579
+ '//input[contains(@class, "captcha") or contains(@name, "captcha") or contains(@id, "captcha")]'
580
+ )
581
+ if captcha_response:
582
+ self.script(f'''
583
+ arguments[0].innerHTML='{captcha['text']}'
584
+ ''', captcha_response)
585
+ return True
586
+
587
+ def check_solve_captchas(self, recaptcha=False, image=False):
588
+
589
+ if self.captcha_client:
590
+
591
+ if recaptcha:
592
+ site_key = self.check_captcha(recaptcha=True)
593
+ if site_key:
594
+ return self.solve_captcha(
595
+ site_key,
596
+ recaptcha=True,
597
+ )
598
+ else:
599
+ return None
600
+
601
+ if image:
602
+ img_url = self.check_captcha(image=True)
603
+ if img_url:
604
+ self.download_file(img_url, 'captcha.png')
605
+ return self.solve_captcha(
606
+ 'captcha.png',
607
+ image=True,
608
+ )
609
+ else:
610
+ return None
611
+
612
+ @staticmethod
613
+ def download_file(url, path=BASE_DIR):
614
+ with open(path, 'wb') as f:
615
+ r = requests.get(url)
616
+ for chunk in r.iter_content(1024):
617
+ f.write(chunk)
618
+
619
+ def random_sleep(self, *args, **kwargs):
620
+
621
+ if kwargs.get('long'):
622
+ time.sleep(random.uniform(self.MIN_RAND_LONG, self.MAX_RAND_LONG))
623
+
624
+ elif len(args) == 1:
625
+ time.sleep(random.uniform(1, args[0]))
626
+
627
+ elif len(args) == 2:
628
+ time.sleep(random.uniform(args[0], args[1]))
629
+
630
+ else:
631
+ time.sleep(random.uniform(self.MIN_RAND, self.MAX_RAND))