From 52448cc97c98e53e9ed70eae5988a3d0235d5ff7 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Thu, 9 Feb 2023 14:02:02 +0100 Subject: [PATCH 01/22] removed redunand project iterations --- 1/main.py | 9 -- 2/main.py | 20 ---- 3/main.py | 23 ---- .../booking/booking.py | 19 ---- .../04 - Structure a Bot Project/run.py | 6 - .../booking/__init__.py | 0 .../booking/booking.py | 43 -------- .../booking/constants.py | 1 - Bot Project/05 - Deal Searching Part 1/run.py | 7 -- .../booking/__init__.py | 0 .../booking/booking.py | 88 --------------- .../booking/constants.py | 1 - Bot Project/06 - Deal Searching Part 2/run.py | 12 -- .../booking/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 128 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 160 -> 0 bytes .../__pycache__/booking.cpython-38.pyc | Bin 3156 -> 0 bytes .../__pycache__/booking.cpython-39.pyc | Bin 127 -> 0 bytes .../booking_filtration.cpython-38.pyc | Bin 1219 -> 0 bytes .../__pycache__/constants.cpython-38.pyc | Bin 165 -> 0 bytes .../__pycache__/constants.cpython-39.pyc | Bin 138 -> 0 bytes .../booking/booking.py | 94 ---------------- .../booking/constants.py | 1 - Bot Project/07 - Booking Filtrations/run.py | 13 --- .../booking/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 128 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 160 -> 0 bytes .../__pycache__/booking.cpython-38.pyc | Bin 3280 -> 0 bytes .../__pycache__/booking.cpython-39.pyc | Bin 127 -> 0 bytes .../booking_filtration.cpython-38.pyc | Bin 1219 -> 0 bytes .../__pycache__/constants.cpython-38.pyc | Bin 165 -> 0 bytes .../__pycache__/constants.cpython-39.pyc | Bin 138 -> 0 bytes .../booking/booking.py | 96 ---------------- .../booking/booking_filtration.py | 24 ---- .../booking/constants.py | 1 - Bot Project/08 - Execution from a CLI/run.py | 25 ----- .../booking/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 128 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 160 -> 0 bytes .../__pycache__/booking.cpython-38.pyc | Bin 3562 -> 0 bytes .../__pycache__/booking.cpython-39.pyc | Bin 127 -> 0 bytes .../booking_filtration.cpython-38.pyc | Bin 1219 -> 0 bytes .../__pycache__/booking_report.cpython-38.pyc | Bin 1056 -> 0 bytes .../__pycache__/constants.cpython-38.pyc | Bin 165 -> 0 bytes .../__pycache__/constants.cpython-39.pyc | Bin 138 -> 0 bytes .../booking/booking.py | 103 ------------------ .../booking/booking_filtration.py | 24 ---- .../booking/booking_report.py | 21 ---- .../booking/constants.py | 1 - Bot Project/09 - Deal Reporting Part 1/run.py | 27 ----- .../booking/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 128 -> 0 bytes .../__pycache__/__init__.cpython-39.pyc | Bin 160 -> 0 bytes .../__pycache__/booking.cpython-38.pyc | Bin 3742 -> 0 bytes .../__pycache__/booking.cpython-39.pyc | Bin 127 -> 0 bytes .../booking_filtration.cpython-38.pyc | Bin 1219 -> 0 bytes .../__pycache__/booking_report.cpython-38.pyc | Bin 1229 -> 0 bytes .../__pycache__/constants.cpython-38.pyc | Bin 165 -> 0 bytes .../__pycache__/constants.cpython-39.pyc | Bin 138 -> 0 bytes .../booking/booking_filtration.py | 24 ---- .../booking/constants.py | 1 - .../booking => booking}/__init__.py | 0 .../booking => booking}/booking.py | 0 .../booking => booking}/booking_filtration.py | 0 .../booking => booking}/booking_report.py | 0 .../booking => booking}/constants.py | 0 .../run.py => run.py | 0 67 files changed, 684 deletions(-) delete mode 100644 1/main.py delete mode 100644 2/main.py delete mode 100644 3/main.py delete mode 100644 Bot Project/04 - Structure a Bot Project/booking/booking.py delete mode 100644 Bot Project/04 - Structure a Bot Project/run.py delete mode 100644 Bot Project/05 - Deal Searching Part 1/booking/__init__.py delete mode 100644 Bot Project/05 - Deal Searching Part 1/booking/booking.py delete mode 100644 Bot Project/05 - Deal Searching Part 1/booking/constants.py delete mode 100644 Bot Project/05 - Deal Searching Part 1/run.py delete mode 100644 Bot Project/06 - Deal Searching Part 2/booking/__init__.py delete mode 100644 Bot Project/06 - Deal Searching Part 2/booking/booking.py delete mode 100644 Bot Project/06 - Deal Searching Part 2/booking/constants.py delete mode 100644 Bot Project/06 - Deal Searching Part 2/run.py delete mode 100644 Bot Project/07 - Booking Filtrations/booking/__init__.py delete mode 100644 Bot Project/07 - Booking Filtrations/booking/__pycache__/__init__.cpython-38.pyc delete mode 100644 Bot Project/07 - Booking Filtrations/booking/__pycache__/__init__.cpython-39.pyc delete mode 100644 Bot Project/07 - Booking Filtrations/booking/__pycache__/booking.cpython-38.pyc delete mode 100644 Bot Project/07 - Booking Filtrations/booking/__pycache__/booking.cpython-39.pyc delete mode 100644 Bot Project/07 - Booking Filtrations/booking/__pycache__/booking_filtration.cpython-38.pyc delete mode 100644 Bot Project/07 - Booking Filtrations/booking/__pycache__/constants.cpython-38.pyc delete mode 100644 Bot Project/07 - Booking Filtrations/booking/__pycache__/constants.cpython-39.pyc delete mode 100644 Bot Project/07 - Booking Filtrations/booking/booking.py delete mode 100644 Bot Project/07 - Booking Filtrations/booking/constants.py delete mode 100644 Bot Project/07 - Booking Filtrations/run.py delete mode 100644 Bot Project/08 - Execution from a CLI/booking/__init__.py delete mode 100644 Bot Project/08 - Execution from a CLI/booking/__pycache__/__init__.cpython-38.pyc delete mode 100644 Bot Project/08 - Execution from a CLI/booking/__pycache__/__init__.cpython-39.pyc delete mode 100644 Bot Project/08 - Execution from a CLI/booking/__pycache__/booking.cpython-38.pyc delete mode 100644 Bot Project/08 - Execution from a CLI/booking/__pycache__/booking.cpython-39.pyc delete mode 100644 Bot Project/08 - Execution from a CLI/booking/__pycache__/booking_filtration.cpython-38.pyc delete mode 100644 Bot Project/08 - Execution from a CLI/booking/__pycache__/constants.cpython-38.pyc delete mode 100644 Bot Project/08 - Execution from a CLI/booking/__pycache__/constants.cpython-39.pyc delete mode 100644 Bot Project/08 - Execution from a CLI/booking/booking.py delete mode 100644 Bot Project/08 - Execution from a CLI/booking/booking_filtration.py delete mode 100644 Bot Project/08 - Execution from a CLI/booking/constants.py delete mode 100644 Bot Project/08 - Execution from a CLI/run.py delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__init__.py delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/__init__.cpython-38.pyc delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/__init__.cpython-39.pyc delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/booking.cpython-38.pyc delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/booking.cpython-39.pyc delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/booking_filtration.cpython-38.pyc delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/booking_report.cpython-38.pyc delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/constants.cpython-38.pyc delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/constants.cpython-39.pyc delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/booking.py delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/booking_filtration.py delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/booking_report.py delete mode 100644 Bot Project/09 - Deal Reporting Part 1/booking/constants.py delete mode 100644 Bot Project/09 - Deal Reporting Part 1/run.py delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__init__.py delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/__init__.cpython-38.pyc delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/__init__.cpython-39.pyc delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/booking.cpython-38.pyc delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/booking.cpython-39.pyc delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/booking_filtration.cpython-38.pyc delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/booking_report.cpython-38.pyc delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/constants.cpython-38.pyc delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/constants.cpython-39.pyc delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/booking_filtration.py delete mode 100644 Bot Project/10 - Deal Reporting Part 2/booking/constants.py rename {Bot Project/04 - Structure a Bot Project/booking => booking}/__init__.py (100%) rename {Bot Project/10 - Deal Reporting Part 2/booking => booking}/booking.py (100%) rename {Bot Project/07 - Booking Filtrations/booking => booking}/booking_filtration.py (100%) rename {Bot Project/10 - Deal Reporting Part 2/booking => booking}/booking_report.py (100%) rename {Bot Project/04 - Structure a Bot Project/booking => booking}/constants.py (100%) rename Bot Project/10 - Deal Reporting Part 2/run.py => run.py (100%) diff --git a/1/main.py b/1/main.py deleted file mode 100644 index 2472655..0000000 --- a/1/main.py +++ /dev/null @@ -1,9 +0,0 @@ -import os -from selenium import webdriver - -os.environ['PATH'] += r"C:/SeleniumDrivers" -driver = webdriver.Chrome() -driver.get("https://www.seleniumeasy.com/test/jquery-download-progress-bar-demo.html") -driver.implicitly_wait(30) -my_element = driver.find_element_by_id('downloadButton') -my_element.click() \ No newline at end of file diff --git a/2/main.py b/2/main.py deleted file mode 100644 index 6ddba56..0000000 --- a/2/main.py +++ /dev/null @@ -1,20 +0,0 @@ -import os -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC - - -os.environ['PATH'] += r"C:/SeleniumDrivers" -driver = webdriver.Chrome() -driver.get("https://www.seleniumeasy.com/test/jquery-download-progress-bar-demo.html") -driver.implicitly_wait(8) -my_element = driver.find_element_by_id('downloadButton') -my_element.click() - -WebDriverWait(driver, 30).until( - EC.text_to_be_present_in_element( - (By.CLASS_NAME, 'progress-label') , # Element filtration - 'Complete!'# The expected text - ) -) \ No newline at end of file diff --git a/3/main.py b/3/main.py deleted file mode 100644 index 9c1d921..0000000 --- a/3/main.py +++ /dev/null @@ -1,23 +0,0 @@ -import os -from selenium import webdriver -from selenium.webdriver.common.keys import Keys - -os.environ['PATH'] += r"C:/SeleniumDrivers" -driver = webdriver.Chrome() - -driver.get('https://www.seleniumeasy.com/test/basic-first-form-demo.html') -driver.implicitly_wait(5) -try: - no_button = driver.find_element_by_class_name('at-cm-no-button') - no_button.click() -except: - print('No element with this class name. Skipping ....') - -sum1 = driver.find_element_by_id('sum1') -sum2 = driver.find_element_by_id('sum2') - -sum1.send_keys(Keys.NUMPAD1, Keys.NUMPAD5) -sum2.send_keys(15) - -btn = driver.find_element_by_css_selector('button[onclick="return total()"]') -btn.click() \ No newline at end of file diff --git a/Bot Project/04 - Structure a Bot Project/booking/booking.py b/Bot Project/04 - Structure a Bot Project/booking/booking.py deleted file mode 100644 index 609929d..0000000 --- a/Bot Project/04 - Structure a Bot Project/booking/booking.py +++ /dev/null @@ -1,19 +0,0 @@ -import booking.constants as const -import os -from selenium import webdriver - - -class Booking(webdriver.Chrome): - def __init__(self, driver_path=r"C:\SeleniumDrivers", - teardown=False): - self.driver_path = driver_path - self.teardown = teardown - os.environ['PATH'] += self.driver_path - super(Booking, self).__init__() - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.teardown: - self.quit() - - def land_first_page(self): - self.get(const.BASE_URL) diff --git a/Bot Project/04 - Structure a Bot Project/run.py b/Bot Project/04 - Structure a Bot Project/run.py deleted file mode 100644 index 3b05541..0000000 --- a/Bot Project/04 - Structure a Bot Project/run.py +++ /dev/null @@ -1,6 +0,0 @@ -from booking.booking import Booking - - -with Booking() as bot: - bot.land_first_page() - diff --git a/Bot Project/05 - Deal Searching Part 1/booking/__init__.py b/Bot Project/05 - Deal Searching Part 1/booking/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Bot Project/05 - Deal Searching Part 1/booking/booking.py b/Bot Project/05 - Deal Searching Part 1/booking/booking.py deleted file mode 100644 index ead8325..0000000 --- a/Bot Project/05 - Deal Searching Part 1/booking/booking.py +++ /dev/null @@ -1,43 +0,0 @@ -import booking.constants as const -import os -from selenium import webdriver - - -class Booking(webdriver.Chrome): - def __init__(self, driver_path=r"C:\SeleniumDrivers", - teardown=False): - self.driver_path = driver_path - self.teardown = teardown - os.environ['PATH'] += self.driver_path - super(Booking, self).__init__() - self.implicitly_wait(15) - self.maximize_window() - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.teardown: - self.quit() - - def land_first_page(self): - self.get(const.BASE_URL) - - def change_currency(self, currency=None): - currency_element = self.find_element_by_css_selector( - 'button[data-tooltip-text="Choose your currency"]' - ) - currency_element.click() - - selected_currency_element = self.find_element_by_css_selector( - f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' - ) - selected_currency_element.click() - - - def select_place_to_go(self, place_to_go): - search_field = self.find_element_by_id('ss') - search_field.clear() - search_field.send_keys(place_to_go) - - first_result = self.find_element_by_css_selector( - 'li[data-i="0"]' - ) - first_result.click() diff --git a/Bot Project/05 - Deal Searching Part 1/booking/constants.py b/Bot Project/05 - Deal Searching Part 1/booking/constants.py deleted file mode 100644 index 5ea7013..0000000 --- a/Bot Project/05 - Deal Searching Part 1/booking/constants.py +++ /dev/null @@ -1 +0,0 @@ -BASE_URL = "https://www.booking.com" \ No newline at end of file diff --git a/Bot Project/05 - Deal Searching Part 1/run.py b/Bot Project/05 - Deal Searching Part 1/run.py deleted file mode 100644 index 0400fc3..0000000 --- a/Bot Project/05 - Deal Searching Part 1/run.py +++ /dev/null @@ -1,7 +0,0 @@ -from booking.booking import Booking - - -with Booking() as bot: - bot.land_first_page() - bot.change_currency(currency='USD') - bot.select_place_to_go('New York') diff --git a/Bot Project/06 - Deal Searching Part 2/booking/__init__.py b/Bot Project/06 - Deal Searching Part 2/booking/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Bot Project/06 - Deal Searching Part 2/booking/booking.py b/Bot Project/06 - Deal Searching Part 2/booking/booking.py deleted file mode 100644 index bcf8a15..0000000 --- a/Bot Project/06 - Deal Searching Part 2/booking/booking.py +++ /dev/null @@ -1,88 +0,0 @@ -import booking.constants as const -import os -from selenium import webdriver -from booking.booking_filtration import BookingFiltration - - -class Booking(webdriver.Chrome): - def __init__(self, driver_path=r"C:\SeleniumDrivers", - teardown=False): - self.driver_path = driver_path - self.teardown = teardown - os.environ['PATH'] += self.driver_path - super(Booking, self).__init__() - self.implicitly_wait(15) - self.maximize_window() - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.teardown: - self.quit() - - def land_first_page(self): - self.get(const.BASE_URL) - - def change_currency(self, currency=None): - currency_element = self.find_element_by_css_selector( - 'button[data-tooltip-text="Choose your currency"]' - ) - currency_element.click() - - selected_currency_element = self.find_element_by_css_selector( - f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' - ) - selected_currency_element.click() - - - def select_place_to_go(self, place_to_go): - search_field = self.find_element_by_id('ss') - search_field.clear() - search_field.send_keys(place_to_go) - - first_result = self.find_element_by_css_selector( - 'li[data-i="0"]' - ) - first_result.click() - - def select_dates(self, check_in_date, check_out_date): - check_in_element = self.find_element_by_css_selector( - f'td[data-date="{check_in_date}"]' - ) - check_in_element.click() - - check_out_element = self.find_element_by_css_selector( - f'td[data-date="{check_out_date}"]' - ) - check_out_element.click() - - def select_adults(self, count=1): - selection_element = self.find_element_by_id('xp__guests__toggle') - selection_element.click() - - while True: - decrease_adults_element = self.find_element_by_css_selector( - 'button[aria-label="Decrease number of Adults"]' - ) - decrease_adults_element.click() - #If the value of adults reaches 1, then we should get out - #of the while loop - adults_value_element = self.find_element_by_id('group_adults') - adults_value = adults_value_element.get_attribute( - 'value' - ) # Should give back the adults count - - if int(adults_value) == 1: - break - - increase_button_element = self.find_element_by_css_selector( - 'button[aria-label="Increase number of Adults"]' - ) - - for _ in range(count - 1): - increase_button_element.click() - - def click_search(self): - search_button = self.find_element_by_css_selector( - 'button[type="submit"]' - ) - search_button.click() - diff --git a/Bot Project/06 - Deal Searching Part 2/booking/constants.py b/Bot Project/06 - Deal Searching Part 2/booking/constants.py deleted file mode 100644 index 5ea7013..0000000 --- a/Bot Project/06 - Deal Searching Part 2/booking/constants.py +++ /dev/null @@ -1 +0,0 @@ -BASE_URL = "https://www.booking.com" \ No newline at end of file diff --git a/Bot Project/06 - Deal Searching Part 2/run.py b/Bot Project/06 - Deal Searching Part 2/run.py deleted file mode 100644 index 6801fcc..0000000 --- a/Bot Project/06 - Deal Searching Part 2/run.py +++ /dev/null @@ -1,12 +0,0 @@ -from booking.booking import Booking - - -with Booking() as bot: - bot.land_first_page() - bot.change_currency(currency='USD') - bot.select_place_to_go('New York') - bot.select_dates(check_in_date='2021-05-19', - check_out_date='2021-05-25') - bot.select_adults(1) - bot.click_search() - diff --git a/Bot Project/07 - Booking Filtrations/booking/__init__.py b/Bot Project/07 - Booking Filtrations/booking/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Bot Project/07 - Booking Filtrations/booking/__pycache__/__init__.cpython-38.pyc b/Bot Project/07 - Booking Filtrations/booking/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 55b93e9228fb09b34d02ce61cc81fa58c9abb9ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmWIL<>g`k0*yX&Yn! diff --git a/Bot Project/07 - Booking Filtrations/booking/__pycache__/__init__.cpython-39.pyc b/Bot Project/07 - Booking Filtrations/booking/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 0d5aa5aaaa300303a88507caf15f44da91fb2a15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmYe~<>g`kg6TO^6QqIkV-N=!FabFZKwQiNBvKht7@8Rw8B!R788n%yggh0>Gjnnj z3W_rGN)*yEi;7G9G#PKPg83^Mia>h7#4lB6tC)bwl8pSkn3B?xn529V&d<)wOOJ_< e&&%q0a#Rm(VM14RK!1Z5+D$QIJxN5%5=FiqSjjO zQZuubNYGa{kgGgFP}IKkv-A!2wpV%u-z4YEaA}%y7ud7&b7to3cfNDxeBbT12|W70 z|M}PF9YX$xo#iKh&gbyzdmsc6G$SL*JnGs$^I^=(f>FpqJLYGNQIj<(`GSZ*gfEB) zW%wj|?z5Jlk4V391^bYrKCN08av)UtR4V9f-Ybf;G#@@pGoxaY7I}ht%MV(Dj~;-K zgi%45C$@hiCsg<%c+mn|LeY3ZSRk4rf^R5pi?&#ML0Lm|L>ESyVqI*&HxfHyQ*6OZ zOMD=1!bn@(5+A~M?MD(2)&Wa*tLEIOH(U4r`uLH|WS*9z2i9c$F!8N5PGB4D!>@h- zVn%0#lbHvS8t;iOXh?R*j65NK2Bpu42~0R6&%Kw8SA@d&%ck&NQQL}eL=dpRKdORn z?tb^>)duX?Z&hm!EgKsA0 zw8$Tu(mWm%_*$RU--Gc)bzvoLp@B<;cn#X1H|aWU)9H;lmwTRRK`N8{a8=(WWQ9|{y0|g=nscIpps0C6ue$~_{QrU zzxJw+PJnJ4W=AqNd@$h&(93BT3eW?Ro-G_(FJ?FDR=tw#Tsxtjx(0P}8s|f~u;eyi zrO}Us2Gsi{@FOpvZTP+ger3fq+!u8C)BADvUIOa#X!bF#E%HH4WQc3#X+2%fQU_*w z*t^p^?uXUZDmtk^Od!x%OG^OmOir}=2u@YM1u+MTt&Oudk=zt~Sg?);_2d-DB{RV~ zj$=yd5)$i5JZw1-`O@-_VOx!1MIdG)+6HV8qv>u<%IizsNrXtr1uZ{?j-#a|q4zEc z=QAGA)6E34V2Pgo(RAGi2N3X69`@8O?5BPMq93}@?s^4?JIAVXk>biP zVq2GE&WEMc1~R!RhQmxw59W~{t2Exv;(^Q#dk-Swy<^rH zs-hh8*!HUsl58o}C-6h>-RZ0R|L)Xps6AL_`D>ZXx{x<{Y>Z05CRzDu4rx?j%=RPa zW=N`KZrG-C4b%p8tg~$~w|Q>aV&bN=6mNr4E=D`cqij2!o5>v*wOU2_Dl58kR=D+l zfgjL45H?8sc^dKCE};Y+8o00tZqVti6{5Hu-nn5gtuILOA#@fbp)xt3T4Q85S}w>O6w8G*Yxqh0_JCm({Kri+7K`cMt;u z<_h`~eP)dnp738mbqI!gi+mSAxd$bfEu5}!@fD_szOV4F*=9T*Ls_CttT>i_`H*ev zLcztTQpjpqaRJPAcO~5an44JpWKkxr{@{GKw+i|nutGOLptm~Sbf@-+J9kMFwnxJr z_My_Cmfig>77ckklAKp<&f(fCGi-M_KQH6V&Dg3(q36^Nir=9?Y}D^j{1L@I3cRPQ z?!2l>WR~$|reT$*uu!qpTN4@@_lzjigTJh|q9F3yov0nHwRfY2)#n(NuWZDJ<|=gbc>|RUzHjZoO>&1$^tm+j*sN|5OztX^&aL*xn$nnEK3t@i$C* NmPH-7Fgmp3{|axKEl&Ud diff --git a/Bot Project/07 - Booking Filtrations/booking/__pycache__/booking.cpython-39.pyc b/Bot Project/07 - Booking Filtrations/booking/__pycache__/booking.cpython-39.pyc deleted file mode 100644 index 9c264d79911eb533b08541e583b349ddebd1c67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127 zcmYe~<>g`kf~%QR6F~H15P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HDvsFw$Wl2VU nUQ9`8Nla2c2ih@!0eC&G^mCObywkzJGoH%|Yk~94435p+}a~ z4a*o}I7J;CVT@K0Bg{EO%sC(tVeTswdOz(O6k=0-%iAkb>~fiaW^$eSTe4b?BFs=k zxWn*gT>5h(m$}S4M3KjQReX z?VEs7kqJ#Hn5!}cJeZ$Kvw89?!S>zB@~r^0YDvActUw>o5Hmc&`=lU4!ibfe0^OM?WTQNK7rxLHlgvVZW+RLTq6Mv@B$tzPIqU@@~<{QS7Bz$ zVY+68i8kQ^4+#KXf=yb3xm3Jkv5s#G_Mp*~BH@ko&>5q9K*~$jb4W_8Oy!&! zuc};XTDqlazQ>ie^C*?RVr>*=3hbv5}<;HD_{Y>=mfP=*9sl82xF6pUZ=Uo+v9)Rl)RH` Zex?`)0x$n_Z-V>d{2sz5E`MB|`~h-iKGpyL diff --git a/Bot Project/07 - Booking Filtrations/booking/__pycache__/constants.cpython-38.pyc b/Bot Project/07 - Booking Filtrations/booking/__pycache__/constants.cpython-38.pyc deleted file mode 100644 index 16e8a32ebd9e03dc2c26f2398f9dd2b37e59ef7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWIL<>g`kf|T@W2`WJPF^Gc<7=auIATH(r5-AK(3@MDk44O<;;u$3+1;tkS`sL;2 zdP({D*_nCiddc~@ewvK8IGh}VUE@Q8d{#0PF#)xKiC=2YRxtsUB^mj7F(suXF-iF# k4AvKuoS#=*l9*RgtXEKZi^C>2KczG$)edC!XCP((0O}tpd;kCd diff --git a/Bot Project/07 - Booking Filtrations/booking/__pycache__/constants.cpython-39.pyc b/Bot Project/07 - Booking Filtrations/booking/__pycache__/constants.cpython-39.pyc deleted file mode 100644 index c3597929a68106e92fae2abb8c443918d45d9349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmYe~<>g`kg6TO^6Ig-tV-N=!FakLaKwQiLBvKfn7*ZI688n$*vH-b$nvAy?6IU`6 zF#|bZ;+LAURZKu-Nk)ENOi5`;Oj14w=Vxc;rN<=a=M|SE=9Lud6;$5hu*uC&Da}c> K1DXFBh#3Hhq8~Q^ diff --git a/Bot Project/07 - Booking Filtrations/booking/booking.py b/Bot Project/07 - Booking Filtrations/booking/booking.py deleted file mode 100644 index e6ce8b7..0000000 --- a/Bot Project/07 - Booking Filtrations/booking/booking.py +++ /dev/null @@ -1,94 +0,0 @@ -import booking.constants as const -import os -from selenium import webdriver -from booking.booking_filtration import BookingFiltration - - -class Booking(webdriver.Chrome): - def __init__(self, driver_path=r"C:\SeleniumDrivers", - teardown=False): - self.driver_path = driver_path - self.teardown = teardown - os.environ['PATH'] += self.driver_path - super(Booking, self).__init__() - self.implicitly_wait(15) - self.maximize_window() - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.teardown: - self.quit() - - def land_first_page(self): - self.get(const.BASE_URL) - - def change_currency(self, currency=None): - currency_element = self.find_element_by_css_selector( - 'button[data-tooltip-text="Choose your currency"]' - ) - currency_element.click() - - selected_currency_element = self.find_element_by_css_selector( - f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' - ) - selected_currency_element.click() - - - def select_place_to_go(self, place_to_go): - search_field = self.find_element_by_id('ss') - search_field.clear() - search_field.send_keys(place_to_go) - - first_result = self.find_element_by_css_selector( - 'li[data-i="0"]' - ) - first_result.click() - - def select_dates(self, check_in_date, check_out_date): - check_in_element = self.find_element_by_css_selector( - f'td[data-date="{check_in_date}"]' - ) - check_in_element.click() - - check_out_element = self.find_element_by_css_selector( - f'td[data-date="{check_out_date}"]' - ) - check_out_element.click() - - def select_adults(self, count=1): - selection_element = self.find_element_by_id('xp__guests__toggle') - selection_element.click() - - while True: - decrease_adults_element = self.find_element_by_css_selector( - 'button[aria-label="Decrease number of Adults"]' - ) - decrease_adults_element.click() - #If the value of adults reaches 1, then we should get out - #of the while loop - adults_value_element = self.find_element_by_id('group_adults') - adults_value = adults_value_element.get_attribute( - 'value' - ) # Should give back the adults count - - if int(adults_value) == 1: - break - - increase_button_element = self.find_element_by_css_selector( - 'button[aria-label="Increase number of Adults"]' - ) - - for _ in range(count - 1): - increase_button_element.click() - - def click_search(self): - search_button = self.find_element_by_css_selector( - 'button[type="submit"]' - ) - search_button.click() - - def apply_filtrations(self): - filtration = BookingFiltration(driver=self) - filtration.apply_star_rating(4, 5) - - filtration.sort_price_lowest_first() - diff --git a/Bot Project/07 - Booking Filtrations/booking/constants.py b/Bot Project/07 - Booking Filtrations/booking/constants.py deleted file mode 100644 index 5ea7013..0000000 --- a/Bot Project/07 - Booking Filtrations/booking/constants.py +++ /dev/null @@ -1 +0,0 @@ -BASE_URL = "https://www.booking.com" \ No newline at end of file diff --git a/Bot Project/07 - Booking Filtrations/run.py b/Bot Project/07 - Booking Filtrations/run.py deleted file mode 100644 index a1c52bb..0000000 --- a/Bot Project/07 - Booking Filtrations/run.py +++ /dev/null @@ -1,13 +0,0 @@ -from booking.booking import Booking - - -with Booking() as bot: - bot.land_first_page() - bot.change_currency(currency='USD') - bot.select_place_to_go('New York') - bot.select_dates(check_in_date='2021-05-19', - check_out_date='2021-05-25') - bot.select_adults(1) - bot.click_search() - bot.apply_filtrations() - diff --git a/Bot Project/08 - Execution from a CLI/booking/__init__.py b/Bot Project/08 - Execution from a CLI/booking/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Bot Project/08 - Execution from a CLI/booking/__pycache__/__init__.cpython-38.pyc b/Bot Project/08 - Execution from a CLI/booking/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 55b93e9228fb09b34d02ce61cc81fa58c9abb9ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmWIL<>g`k0*yX&Yn! diff --git a/Bot Project/08 - Execution from a CLI/booking/__pycache__/__init__.cpython-39.pyc b/Bot Project/08 - Execution from a CLI/booking/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 0d5aa5aaaa300303a88507caf15f44da91fb2a15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmYe~<>g`kg6TO^6QqIkV-N=!FabFZKwQiNBvKht7@8Rw8B!R788n%yggh0>Gjnnj z3W_rGN)*yEi;7G9G#PKPg83^Mia>h7#4lB6tC)bwl8pSkn3B?xn529V&d<)wOOJ_< e&&||Q6PTIf9Wsa>puA}w>F(+w^o3u%+e_Gt--V2L+ z!aMb!J9LecM^@0dhJCC-K+3fXG2}9SDkOBa?q%6ooQ@vGiISm;vou1zx~VZ5V0s+x&g_uK#Gc7Hxy2du6jS>UH=2U!FV?iAdvO{6L$WKa3o0jS*-YzBrbD z4`NQ{7PICyNTTdhhZE1*wdU5T^=DA}(o*hA@5*}S%$=7FZgXcut^iZjRQ}w0ZlA8r z;Y2?X0xMp&xOZh*>sJ>Afc-a9b&{n|RH2>>Gki5y>hHm1DmP%I z4{v@8gk?8KgKQI@Y>+N#k=d>4A`fcoqMA&ojcq*eeR%UO2)rh_0nn2g@Kbww{IwZRnw)#Ep!2Pir#34uQd5u73|UE?Z`%BLegFgKeag zwhT`OHd%_C+DHf_g4~9|z$u-PP^A~aEi3BZyZh)L?D2p89T08gswJCAm~wU;%Ul5m zMq&rH$npFZneEg-t>_FQt$<4Kp!FWS+N?X!K(jcxBG2r(Z9vl__MH4|apxBYW{~L% zI0JVuyE815%F^$6sKUO=vP8v`z7m(}uy_9?%W|FpKnM!zqh^Vn#cY zfF7{*Y;m#mVtT7?)hp@ljX%^=H=vGA!gM4Smh1smhM~kI_HVA^+b(Ir_YLqXD{kPv zpu?Zujl2C4P_N>J!+|IodABBV;0&mpLzqZn17>{KyVE-gyt2EBPRtP#2oH2E7XaLu znC9|#@K^bJ5EW2#eUgNcU@BvyjJ9)7k4}JGBH^@cI3`72Ku}-thpqvUEiL~H(LqPH zz-N8Z0&EbY*-a3Z?r%n|it;9Nl`OQ4OtP0`MVq^#(cm)q*R? zkp)+_SMb39=hql3-eL=e!v$L=(0Qkq+6#_o@6nIRP8b^%B3BU2738Eu%nmBg4`m$o zlW-`K!`=fCNfCn6r^R?EBeMXKnQxf;l$wXf41UfEn(wwTy9 zmSQ$2#A39)JW6-s%1ma!sMSisR$0-hSYg)x6%LU1Kxij%sxacTOh9ql#5Hc?-XgQx zD?~9nyp>@vtS?CNK6Dl&A#<|i5%yqqySg?^cMxTZ;W$>kBjXdc28)8J+W(E_>OtB% z!a~GFoyYL694Ts5p>VdK^|IOxV=?#Wyn`4hFqe~`$TMv$)=^kRf#LQd-nmd}LiwkQ zwrgB`jUgg%B%YdXg_8-?C%FnG!?H6S(VaY#kQikQQ7y?XfVn1DLjK2;Pg}=}5_R=} z)oyPT^6y|p-T;BO)wXB5^^KVSE@{H{XxPI5Dh;C99dxjoO2e^WtZXp`X|G7I-Dd2( z2op1-D<6qDTw->S`zSs^f#Fp4Q5>N7BZ#t7RaTKm61L1VEG{ILCc2EnIxI&v_yj7> z-!|5L*LPZNzvZvD_WXv{30ib(XbQ0dU3i44%IVsoDR*(Vbr94?24n4P>dL)(S@i{c k>!RP>O2ZVM`Zr4$>kMLwsV@x}f5V`sS=5Gv(I#!@f5Z}0#sB~S diff --git a/Bot Project/08 - Execution from a CLI/booking/__pycache__/booking.cpython-39.pyc b/Bot Project/08 - Execution from a CLI/booking/__pycache__/booking.cpython-39.pyc deleted file mode 100644 index 9c264d79911eb533b08541e583b349ddebd1c67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127 zcmYe~<>g`kf~%QR6F~H15P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HDvsFw$Wl2VU nUQ9`8Nla2c2ih@!0eC&G^mCObywkzJGoH%|Yk~94435p+}a~ z4a*o}I7J;CVT@K0Bg{EO%sC(tVeTswdOz(O6k=0-%iAkb>~fiaW^$eSTe4b?BFs=k zxWn*gT>5h(m$}S4M3KjQReX z?VEs7kqJ#Hn5!}cJeZ$Kvw89?!S>zB@~r^0YDvActUw>o5Hmc&`=lU4!ibfe0^OM?WTQNK7rxLHlgvVZW+RLTq6Mv@B$tzPIqU@@~<{QS7Bz$ zVY+68i8kQ^4+#KXf=yb3xm3Jkv5s#G_Mp*~BH@ko&>5q9K*~$jb4W_8Oy!&! zuc};XTDqlazQ>ie^C*?RVr>*=3hbv5}<;HD_{Y>=mfP=*9sl82xF6pUZ=Uo+v9)Rl)RH` Zex?`)0x$n_Z-V>d{2sz5E`MB|`~h-iKGpyL diff --git a/Bot Project/08 - Execution from a CLI/booking/__pycache__/constants.cpython-38.pyc b/Bot Project/08 - Execution from a CLI/booking/__pycache__/constants.cpython-38.pyc deleted file mode 100644 index 16e8a32ebd9e03dc2c26f2398f9dd2b37e59ef7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWIL<>g`kf|T@W2`WJPF^Gc<7=auIATH(r5-AK(3@MDk44O<;;u$3+1;tkS`sL;2 zdP({D*_nCiddc~@ewvK8IGh}VUE@Q8d{#0PF#)xKiC=2YRxtsUB^mj7F(suXF-iF# k4AvKuoS#=*l9*RgtXEKZi^C>2KczG$)edC!XCP((0O}tpd;kCd diff --git a/Bot Project/08 - Execution from a CLI/booking/__pycache__/constants.cpython-39.pyc b/Bot Project/08 - Execution from a CLI/booking/__pycache__/constants.cpython-39.pyc deleted file mode 100644 index c3597929a68106e92fae2abb8c443918d45d9349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmYe~<>g`kg6TO^6Ig-tV-N=!FakLaKwQiLBvKfn7*ZI688n$*vH-b$nvAy?6IU`6 zF#|bZ;+LAURZKu-Nk)ENOi5`;Oj14w=Vxc;rN<=a=M|SE=9Lud6;$5hu*uC&Da}c> K1DXFBh#3Hhq8~Q^ diff --git a/Bot Project/08 - Execution from a CLI/booking/booking.py b/Bot Project/08 - Execution from a CLI/booking/booking.py deleted file mode 100644 index 795c461..0000000 --- a/Bot Project/08 - Execution from a CLI/booking/booking.py +++ /dev/null @@ -1,96 +0,0 @@ -import booking.constants as const -import os -from selenium import webdriver -from booking.booking_filtration import BookingFiltration - - -class Booking(webdriver.Chrome): - def __init__(self, driver_path=r"C:\SeleniumDrivers", - teardown=False): - self.driver_path = driver_path - self.teardown = teardown - os.environ['PATH'] += self.driver_path - options = webdriver.ChromeOptions() - options.add_experimental_option('excludeSwitches', ['enable-logging']) - super(Booking, self).__init__(options=options) - self.implicitly_wait(15) - self.maximize_window() - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.teardown: - self.quit() - - def land_first_page(self): - self.get(const.BASE_URL) - - def change_currency(self, currency=None): - currency_element = self.find_element_by_css_selector( - 'button[data-tooltip-text="Choose your currency"]' - ) - currency_element.click() - - selected_currency_element = self.find_element_by_css_selector( - f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' - ) - selected_currency_element.click() - - - def select_place_to_go(self, place_to_go): - search_field = self.find_element_by_id('ss') - search_field.clear() - search_field.send_keys(place_to_go) - - first_result = self.find_element_by_css_selector( - 'li[data-i="0"]' - ) - first_result.click() - - def select_dates(self, check_in_date, check_out_date): - check_in_element = self.find_element_by_css_selector( - f'td[data-date="{check_in_date}"]' - ) - check_in_element.click() - - check_out_element = self.find_element_by_css_selector( - f'td[data-date="{check_out_date}"]' - ) - check_out_element.click() - - def select_adults(self, count=1): - selection_element = self.find_element_by_id('xp__guests__toggle') - selection_element.click() - - while True: - decrease_adults_element = self.find_element_by_css_selector( - 'button[aria-label="Decrease number of Adults"]' - ) - decrease_adults_element.click() - #If the value of adults reaches 1, then we should get out - #of the while loop - adults_value_element = self.find_element_by_id('group_adults') - adults_value = adults_value_element.get_attribute( - 'value' - ) # Should give back the adults count - - if int(adults_value) == 1: - break - - increase_button_element = self.find_element_by_css_selector( - 'button[aria-label="Increase number of Adults"]' - ) - - for _ in range(count - 1): - increase_button_element.click() - - def click_search(self): - search_button = self.find_element_by_css_selector( - 'button[type="submit"]' - ) - search_button.click() - - def apply_filtrations(self): - filtration = BookingFiltration(driver=self) - filtration.apply_star_rating(4, 5) - - filtration.sort_price_lowest_first() - diff --git a/Bot Project/08 - Execution from a CLI/booking/booking_filtration.py b/Bot Project/08 - Execution from a CLI/booking/booking_filtration.py deleted file mode 100644 index e352aba..0000000 --- a/Bot Project/08 - Execution from a CLI/booking/booking_filtration.py +++ /dev/null @@ -1,24 +0,0 @@ -#This file will include a class with instance methods. -#That will be responsible to interact with our website -#After we have some results, to apply filtrations. -from selenium.webdriver.remote.webdriver import WebDriver - -class BookingFiltration: - def __init__(self, driver:WebDriver): - self.driver = driver - - def apply_star_rating(self, *star_values): - star_filtration_box = self.driver.find_element_by_id('filter_class') - star_child_elements = star_filtration_box.find_elements_by_css_selector('*') - - for star_value in star_values: - for star_element in star_child_elements: - if str(star_element.get_attribute('innerHTML')).strip() == f'{star_value} stars': - star_element.click() - - - def sort_price_lowest_first(self): - element = self.driver.find_element_by_css_selector( - 'li[data-id="price"]' - ) - element.click() \ No newline at end of file diff --git a/Bot Project/08 - Execution from a CLI/booking/constants.py b/Bot Project/08 - Execution from a CLI/booking/constants.py deleted file mode 100644 index 5ea7013..0000000 --- a/Bot Project/08 - Execution from a CLI/booking/constants.py +++ /dev/null @@ -1 +0,0 @@ -BASE_URL = "https://www.booking.com" \ No newline at end of file diff --git a/Bot Project/08 - Execution from a CLI/run.py b/Bot Project/08 - Execution from a CLI/run.py deleted file mode 100644 index 00785da..0000000 --- a/Bot Project/08 - Execution from a CLI/run.py +++ /dev/null @@ -1,25 +0,0 @@ -from booking.booking import Booking - -try: - with Booking() as bot: - bot.land_first_page() - bot.change_currency(currency='USD') - bot.select_place_to_go('New York') - bot.select_dates(check_in_date='2021-05-19', - check_out_date='2021-05-25') - bot.select_adults(1) - bot.click_search() - bot.apply_filtrations() - -except Exception as e: - if 'in PATH' in str(e): - print( - 'You are trying to run the bot from command line \n' - 'Please add to PATH your Selenium Drivers \n' - 'Windows: \n' - ' set PATH=%PATH%;C:path-to-your-folder \n \n' - 'Linux: \n' - ' PATH=$PATH:/path/toyour/folder/ \n' - ) - else: - raise \ No newline at end of file diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/__init__.py b/Bot Project/09 - Deal Reporting Part 1/booking/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/__init__.cpython-38.pyc b/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 55b93e9228fb09b34d02ce61cc81fa58c9abb9ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmWIL<>g`k0*yX&Yn! diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/__init__.cpython-39.pyc b/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 0d5aa5aaaa300303a88507caf15f44da91fb2a15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmYe~<>g`kg6TO^6QqIkV-N=!FabFZKwQiNBvKht7@8Rw8B!R788n%yggh0>Gjnnj z3W_rGN)*yEi;7G9G#PKPg83^Mia>h7#4lB6tC)bwl8pSkn3B?xn529V&d<)wOOJ_< e&&ld9)(}Cyq zU;q7&|8yMZzxc8E`0(*3_^AO1?r@SgV?teG+aC3x&rSSsjn+(`m(<4%+AwWD3C1gQ zg*bn4c#YTJIJ_=uhs|@3wm5n2^c$D3rZec1V&y~(xr|>42_I{pXW3Dlj-JJdmZ6Tb z6goR)=htGANgd(D#RtvAM;(F?4kg^7F2Dc1vro9k{WmLMs4;>1yupJv4z2Mm-sG(} zgw}bRcc7=iyL=Vi0sn}v@pTwk;kWq)^fdV$eiz;?zRB;wyZyc6JG28XJSZBaVei+U z{Ql*0k%%RIF&tE@m9@rQltI}kH6bC@%8K@#mAdYsgp`_9Zcbbbd)pE=rp zTf1;xdo%BCox9u{kqZD`H*_#_&fUY68F=;sAu!`@lh-b6t99WZ_+VRoQ21Xx{^kq1 zDo&#$=i>QEtfK>=rd^SS!$fQ+*=Phn1H6rFfPGij& zU5&?+B#vU8oUxNI)^v3op2p*ND%eS!!j^r%@RdmRC9+huPy`^}0c9AAl29qe-Z}3- z2JF8&(+64lQs?^RFvFiAQT5)LoJsTzXRBKv9JfyDWP=2xOV&w~OmCG3xl>seRir{~ zY@@?Z;HTC>;4#TAKu;>b-;qJzH8vLhaUScwFL9$H0Qi|cn+V##)~hg~b<;JJw_)6h z4Wr2bI|K&%mDwI_T(ry%M+C;J2ir&~V;OD;HaVIcx=08lg1iHreXsCFLKn3NPFd67 z^T*Hs%wBx`w>~jOE?csigehnHu~Zs3FcO=vghKxvGTp3zTGAOrMgf)JLF@6fjaiSN zfo5@XL0-Ev*Meqx+!^_&!@YlbFoH~n#12hdSKWS(fN{vaQ9b-t9d( z$TB4!o@Kdw80AumG&<|;O+O8-@;KvRvV9;zF64Ho&eCW*m&x`dl;QYSyFK8Ph_v9W zn!5Ym?d`pDiwFC_I|idjUp72r5m3x(XA;l@wjRw7wwg?DRjq0!y?k(LtDw z#N3iCz{)a|_{9Cu_X5`^O?Y1czck|p?sGc)=EJzVZvpi(Uw9mdVvzSMBKN%kbrqzE zB(`A2yS+!fy?(7&Uq&b9hzXh-_i{Cx}iLgaa`fkS1V*7)`e-Qr?_$EeRtf=d}D3J}fN_ z3Ek@?EJs|RrycQU{sKL{!L+Nn1qk>lc6;&xtS5g0qF=M2-+^=+9YMBZs8Mw7XO?U8 z4Ya9-OTy8$D~+;i854jHscRNc7*}(yAV=n0*;v8@_g9ygE3UBx)8U*g6ZrUGl)7_{ znCLV*7_SHj+(FQ)X@6EbC%ks#%Jt(fj@LoQ<#iaT>)H(Z?>Xn>sk7I9-I&!ah{xZV zTdFaj>!%aOM!8TLvblykDG}41GV((ihucXw6v=MysfeTqA?VY5JQVU_w*T-k2imCK z9&L|gmQPq{zKa?p*<8q9fC5#0uGFu~9!xX*HB6=*$eS$GTE<|LD7-j@G%7J> z`++@XNUC|N>6$$nxJRp4r<=UAxwLFPuwgC5+aMS7-u7ZI-Hb~!nFXU#D+pU=MW)xJ?3DjijR#9NMJI{AM)SyrXnxgFzCtqTU=z9{qrfcD30`-Z~p=4N(rX#wk zpa^9X8ADb}vJ+sgy({7V#~YY7_vf|ivV-MnR|@J|n4#(*@U+_Q^nP_BcHaffYmTCA z^Haf;dvz)rTs^lJ?Uj@6fUX{7S|mvf=Ei9X#kOa;jxIxz&Xa^`sKtbW0pmx@Okl(8 zR47`PruJTTsB62KP2nnpwUqgvqGVe@rgtlkuB!rC==QTd6i&nhYrl)dVH%DFV?~oO zxW)4X+ik{<^Dwa^rjV2OV53Q!tmNtsg(Lfc+8kyg7t6+>vek GbN>gWYMizJ diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/booking.cpython-39.pyc b/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/booking.cpython-39.pyc deleted file mode 100644 index 9c264d79911eb533b08541e583b349ddebd1c67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127 zcmYe~<>g`kf~%QR6F~H15P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HDvsFw$Wl2VU nUQ9`8Nla2c2ih@!0eC&G^mCObywkzJGoH%|Yk~94435p+}a~ z4a*o}I7J;CVT@K0Bg{EO%sC(tVeTswdOz(O6k=0-%iAkb>~fiaW^$eSTe4b?BFs=k zxWn*gT>5h(m$}S4M3KjQReX z?VEs7kqJ#Hn5!}cJeZ$Kvw89?!S>zB@~r^0YDvActUw>o5Hmc&`=lU4!ibfe0^OM?WTQNK7rxLHlgvVZW+RLTq6Mv@B$tzPIqU@@~<{QS7Bz$ zVY+68i8kQ^4+#KXf=yb3xm3Jkv5s#G_Mp*~BH@ko&>5q9K*~$jb4W_8Oy!&! zuc};XTDqlazQ>ie^C*?RVr>*=3hbv5}<;HD_{Y>=mfP=*9sl82xF6pUZ=Uo+v9)Rl)RH` Zex?`)0x$n_Z-V>d{2sz5E`MB|`~h-iKGpyL diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/booking_report.cpython-38.pyc b/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/booking_report.cpython-38.pyc deleted file mode 100644 index 285ffb7bce2a05b3fc0009123c49688669cedfaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1056 zcmZuw&2G~`5Z+zeiIXOUiXRCOhyxc>$O9mR5Cwq*M2H_DSwTyj-L|HV?e1=XMCqx0 z3=Ro5UTLqKcm)o?%sPRjVx<|6$FnovH#2_IZZ`>>b5Fm%Z+L`!$CuRuK5`#KUjrqG zpcxs`m{RhX2qwG}BD`Z3GvU7?QSbv|NJMS(txO(eawu~%K-cQ|oA4%hC5fpZF_WI4 zU#Lsy$G-4Ia6;lh)I=SgH4#GchHXD6ii0%YdnwC887OlhDLzZhwH%nV$hllpWGm&+ zM%e+;=Rr;BlyEX7Gdc&&nIMpxb|X*u@YS9vM_I;&>}T91x6OZc#H_Dnw(BCl5f#FP zpW8ckdN0OizsP%LWO_+~;$SYHTsgd*a%@A+(>yht*AWgDB(z2+7uSe&mO%rG0gHn+ zw$2f0fiWc?fP|oP3Itd;-qMPfswky0W1eKi;2>fZrz^W@E|%TVJQ?#r*4LWn{h^Gg zLM)C;r5gAefZ71jHBg9={hnN1M|rAIYhV#5vU6Z>fq`Hjl6TjS=#*BVXH2j;oq2O| z#Ed^>$FNd|H8%~fJK5Cg_I_bx#vMxANb_8(C$FDBivoq1?B!Kb>zr(RPa57gMy1Kf zNE>LdN*k0a%?%713CH1bu5q&}gxxsR2D{-%n$%=cpM_g`6BMCM>O(_(#(ss~Vr*Rl+MPsa++^Z^OG4z5mn3k1CwI` AtN;K2 diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/constants.cpython-38.pyc b/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/constants.cpython-38.pyc deleted file mode 100644 index 16e8a32ebd9e03dc2c26f2398f9dd2b37e59ef7a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 165 zcmWIL<>g`kf|T@W2`WJPF^Gc<7=auIATH(r5-AK(3@MDk44O<;;u$3+1;tkS`sL;2 zdP({D*_nCiddc~@ewvK8IGh}VUE@Q8d{#0PF#)xKiC=2YRxtsUB^mj7F(suXF-iF# k4AvKuoS#=*l9*RgtXEKZi^C>2KczG$)edC!XCP((0O}tpd;kCd diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/constants.cpython-39.pyc b/Bot Project/09 - Deal Reporting Part 1/booking/__pycache__/constants.cpython-39.pyc deleted file mode 100644 index c3597929a68106e92fae2abb8c443918d45d9349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmYe~<>g`kg6TO^6Ig-tV-N=!FakLaKwQiLBvKfn7*ZI688n$*vH-b$nvAy?6IU`6 zF#|bZ;+LAURZKu-Nk)ENOi5`;Oj14w=Vxc;rN<=a=M|SE=9Lud6;$5hu*uC&Da}c> K1DXFBh#3Hhq8~Q^ diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/booking.py b/Bot Project/09 - Deal Reporting Part 1/booking/booking.py deleted file mode 100644 index f3eb503..0000000 --- a/Bot Project/09 - Deal Reporting Part 1/booking/booking.py +++ /dev/null @@ -1,103 +0,0 @@ -import booking.constants as const -import os -from selenium import webdriver -from booking.booking_filtration import BookingFiltration -from booking.booking_report import BookingReport - -class Booking(webdriver.Chrome): - def __init__(self, driver_path=r"C:\SeleniumDrivers", - teardown=False): - self.driver_path = driver_path - self.teardown = teardown - os.environ['PATH'] += self.driver_path - options = webdriver.ChromeOptions() - options.add_experimental_option('excludeSwitches', ['enable-logging']) - super(Booking, self).__init__(options=options) - self.implicitly_wait(15) - self.maximize_window() - - def __exit__(self, exc_type, exc_val, exc_tb): - if self.teardown: - self.quit() - - def land_first_page(self): - self.get(const.BASE_URL) - - def change_currency(self, currency=None): - currency_element = self.find_element_by_css_selector( - 'button[data-tooltip-text="Choose your currency"]' - ) - currency_element.click() - - selected_currency_element = self.find_element_by_css_selector( - f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' - ) - selected_currency_element.click() - - - def select_place_to_go(self, place_to_go): - search_field = self.find_element_by_id('ss') - search_field.clear() - search_field.send_keys(place_to_go) - - first_result = self.find_element_by_css_selector( - 'li[data-i="0"]' - ) - first_result.click() - - def select_dates(self, check_in_date, check_out_date): - check_in_element = self.find_element_by_css_selector( - f'td[data-date="{check_in_date}"]' - ) - check_in_element.click() - - check_out_element = self.find_element_by_css_selector( - f'td[data-date="{check_out_date}"]' - ) - check_out_element.click() - - def select_adults(self, count=1): - selection_element = self.find_element_by_id('xp__guests__toggle') - selection_element.click() - - while True: - decrease_adults_element = self.find_element_by_css_selector( - 'button[aria-label="Decrease number of Adults"]' - ) - decrease_adults_element.click() - #If the value of adults reaches 1, then we should get out - #of the while loop - adults_value_element = self.find_element_by_id('group_adults') - adults_value = adults_value_element.get_attribute( - 'value' - ) # Should give back the adults count - - if int(adults_value) == 1: - break - - increase_button_element = self.find_element_by_css_selector( - 'button[aria-label="Increase number of Adults"]' - ) - - for _ in range(count - 1): - increase_button_element.click() - - def click_search(self): - search_button = self.find_element_by_css_selector( - 'button[type="submit"]' - ) - search_button.click() - - def apply_filtrations(self): - filtration = BookingFiltration(driver=self) - filtration.apply_star_rating(4, 5) - - filtration.sort_price_lowest_first() - - def report_results(self): - hotel_boxes = self.find_element_by_id( - 'hotellist_inner' - ) - - report = BookingReport(hotel_boxes) - report.pull_titles() \ No newline at end of file diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/booking_filtration.py b/Bot Project/09 - Deal Reporting Part 1/booking/booking_filtration.py deleted file mode 100644 index e352aba..0000000 --- a/Bot Project/09 - Deal Reporting Part 1/booking/booking_filtration.py +++ /dev/null @@ -1,24 +0,0 @@ -#This file will include a class with instance methods. -#That will be responsible to interact with our website -#After we have some results, to apply filtrations. -from selenium.webdriver.remote.webdriver import WebDriver - -class BookingFiltration: - def __init__(self, driver:WebDriver): - self.driver = driver - - def apply_star_rating(self, *star_values): - star_filtration_box = self.driver.find_element_by_id('filter_class') - star_child_elements = star_filtration_box.find_elements_by_css_selector('*') - - for star_value in star_values: - for star_element in star_child_elements: - if str(star_element.get_attribute('innerHTML')).strip() == f'{star_value} stars': - star_element.click() - - - def sort_price_lowest_first(self): - element = self.driver.find_element_by_css_selector( - 'li[data-id="price"]' - ) - element.click() \ No newline at end of file diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/booking_report.py b/Bot Project/09 - Deal Reporting Part 1/booking/booking_report.py deleted file mode 100644 index 40b3599..0000000 --- a/Bot Project/09 - Deal Reporting Part 1/booking/booking_report.py +++ /dev/null @@ -1,21 +0,0 @@ -# This file is going to include method that will parse -# The specific data that we need from each one of the deal boxes. -from selenium.webdriver.remote.webelement import WebElement - - -class BookingReport: - def __init__(self, boxes_section_element:WebElement): - self.boxes_section_element = boxes_section_element - self.deal_boxes = self.pull_deal_boxes() - - def pull_deal_boxes(self): - return self.boxes_section_element.find_elements_by_class_name( - 'sr_property_block' - ) - - def pull_titles(self): - for deal_box in self.deal_boxes: - hotel_name = deal_box.find_element_by_class_name( - 'sr-hotel__name' - ).get_attribute('innerHTML').strip() - print(hotel_name) \ No newline at end of file diff --git a/Bot Project/09 - Deal Reporting Part 1/booking/constants.py b/Bot Project/09 - Deal Reporting Part 1/booking/constants.py deleted file mode 100644 index 5ea7013..0000000 --- a/Bot Project/09 - Deal Reporting Part 1/booking/constants.py +++ /dev/null @@ -1 +0,0 @@ -BASE_URL = "https://www.booking.com" \ No newline at end of file diff --git a/Bot Project/09 - Deal Reporting Part 1/run.py b/Bot Project/09 - Deal Reporting Part 1/run.py deleted file mode 100644 index b1754fc..0000000 --- a/Bot Project/09 - Deal Reporting Part 1/run.py +++ /dev/null @@ -1,27 +0,0 @@ -from booking.booking import Booking - -try: - with Booking() as bot: - bot.land_first_page() - bot.change_currency(currency='USD') - bot.select_place_to_go('New York') - bot.select_dates(check_in_date='2021-05-19', - check_out_date='2021-05-25') - bot.select_adults(1) - bot.click_search() - bot.apply_filtrations() - bot.refresh() # A workaround to let our bot to grab the data properly - bot.report_results() - -except Exception as e: - if 'in PATH' in str(e): - print( - 'You are trying to run the bot from command line \n' - 'Please add to PATH your Selenium Drivers \n' - 'Windows: \n' - ' set PATH=%PATH%;C:path-to-your-folder \n \n' - 'Linux: \n' - ' PATH=$PATH:/path/toyour/folder/ \n' - ) - else: - raise \ No newline at end of file diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/__init__.py b/Bot Project/10 - Deal Reporting Part 2/booking/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/__init__.cpython-38.pyc b/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/__init__.cpython-38.pyc deleted file mode 100644 index 55b93e9228fb09b34d02ce61cc81fa58c9abb9ee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 128 zcmWIL<>g`k0*yX&Yn! diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/__init__.cpython-39.pyc b/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/__init__.cpython-39.pyc deleted file mode 100644 index 0d5aa5aaaa300303a88507caf15f44da91fb2a15..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmYe~<>g`kg6TO^6QqIkV-N=!FabFZKwQiNBvKht7@8Rw8B!R788n%yggh0>Gjnnj z3W_rGN)*yEi;7G9G#PKPg83^Mia>h7#4lB6tC)bwl8pSkn3B?xn529V&d<)wOOJ_< e&&H;LnH+8Waq?G$jDwm`cm;-cB4X#x~(u-WYe8!xL7>9K5$ zG$yIo9!#zrAXobYMS=J-uh2))ZLjhQxk=97&4sY=0Ylk;QaM(HbXqS`c&LDgb`#ZY>Qm&kcA(zQZA>m=|t2{qSveDxt z)iTmap21+Z8vI&Ja;agU{Y(n2&%TLfVI4)uAHhj0)0ZQkPT*Mv5Bhj(G5$$NYi?jiqzukm%5S>Zq6*I}f^Z}6LNZ}Sa) z3+|maj_=SeSahdsR(gI|fB1(N&qXS-q!>Rkrm4rVXSA{n>WF_FQ{O=|BQu9NGZ$K- z-9wL)z}a+W&Y|;r5W4SZ|7~#Lyz*w=+Xi>JHzF5sCc3G^nRD(QuFSx)?+JkwZ(BUL zu)X$$gJ%J{HFit?*@JJM&{c67rv(?!PZAv;2sQ1A3{l)p^U(-!2XLGD1aVjIS3$2q zr)=B9VUtK7l%W=p~gvhan8i)M95?;G99JN zZd?YcfI-=SHD$(Jma$sRyYYQW!--GOU#EdApm(F0$ufnXT| z{7j!s1Z`sPWt7r}85+v#FmIb1Mv?(`2n>!Z^Lo&6Q8POo6PT|r>?5U&W@s7c6eie9_Kttw+}?bh1`zR zSr%^>GToj;G8+GKrw^PGu@;=wOLsoF{k?Z?d1oJZ$6yxe%Z6tx28vnfTmpK))}#5s z){E)2x>v8Ho1eU)p85pp_#nzgVy?*-fR$w^@rnD<_d?evEx2C-zqH~L+~;)o&F67< z-va7Yyzn>>#USt3L>_p%)Kw5B(!_$9?DX&T_Xa_^zKl-75fcaxbVUgO?ns;|`D1vi z+=8Y8inb?d6bq(vHp*#7fp~lXkXva{Z?Agb>(B>IvQ%_ffqidHE)zC5~03TAhx%LWv`LwxHt{xveau|yQR z!<7t!VSP>#OeS-ZkQrI<2zPgSqdGQBcMxTZ;W*L#J?j&$0gHmF#{aG4vLPK4VIkt8 z%-`W%3MuMTp>R5<^`h7(%3|($h-(o84eE08J$Yr6#X1VBC{Wy;$2%WNQYZsW(e@q} zzsC?U@FZGI*P_V;>Jz0S$*?@lMs!0#P0A)RfvA?|C%{~rDFwwSA1Z9Mx4QW0AXrQrVT}HEK zXPM0(Y6veh)mcRtKO`;*KQ_D`ncl2ny}AUBtO=_g51@J@CSnYFSfQddjFm0Mpp+<5 z>~|PDE~3=Vm}*jPqM3KFL8{50Ve<=YeuWK|J@U8M{0^G3Ta~Oapv)Fs80$%i6`-k) zk?;x&u8=?}|FPE&{m^T5!dBRB-3}XJ!$=K!bZuyB+`Fcxi!!a~%DgMFj*wkwXo$f9 zKTB-Me!E^)-GRFGcb6sn&1C_Ln8Y)MTy8$Y>}i+-ZrD!%qt(Lsm|Skl+WQv5KVte( NSj2ZAb$9H~{U2?I)M@|# diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/booking.cpython-39.pyc b/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/booking.cpython-39.pyc deleted file mode 100644 index 9c264d79911eb533b08541e583b349ddebd1c67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 127 zcmYe~<>g`kf~%QR6F~H15P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HDvsFw$Wl2VU nUQ9`8Nla2c2ih@!0eC&G^mCObywkzJGoH%|Yk~94435p+}a~ z4a*o}I7J;CVT@K0Bg{EO%sC(tVeTswdOz(O6k=0-%iAkb>~fiaW^$eSTe4b?BFs=k zxWn*gT>5h(m$}S4M3KjQReX z?VEs7kqJ#Hn5!}cJeZ$Kvw89?!S>zB@~r^0YDvActUw>o5Hmc&`=lU4!ibfe0^OM?WTQNK7rxLHlgvVZW+RLTq6Mv@B$tzPIqU@@~<{QS7Bz$ zVY+68i8kQ^4+#KXf=yb3xm3Jkv5s#G_Mp*~BH@ko&>5q9K*~$jb4W_8Oy!&! zuc};XTDqlazQ>ie^C*?RVr>*=3hbv5}<;HD_{Y>=mfP=*9sl82xF6pUZ=Uo+v9)Rl)RH` Zex?`)0x$n_Z-V>d{2sz5E`MB|`~h-iKGpyL diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/booking_report.cpython-38.pyc b/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/booking_report.cpython-38.pyc deleted file mode 100644 index dd8a593529f610c4ed003bd34dff026d31883030..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1229 zcmaJ>&2G~`5Z+zesq6kgB|sGjPN>I-yAqtTf}9$?VKGvom?Sw&oEypSQn%_+b4s2ly=adAFXo)5GwuB48m+Q5MS#}tw{Z}&2Q~_zOM%gE+xt6^m&QdO? z8QEMql#zCT^c7$gtq3O-8Pf@H&IEzjwCh_6swefOyi5`xBGHf>}Tn2Q5L&VhGbyQzFZl+oqSlkoX2TgaK40aFd?BWI=Z${SZC(c zqv$X_C}ZnPA*&!%=v!fuy$Y*$ugS56=NC^_;fEZymj*0pNyXN63-si|I$ z(^RUbZ(ckfZAN9>&Q;u#Z4v7{35T2?he;_%o(PMut$Uf0zM~LreSN-Bi+!rs`m*3* zQK&d73t2lFq`Yp0c`j4ow@eoUua_lBGmU{e8=SyvuwlD{l}0v*W{SaZ;RWi=h5nvr z)|UaP-U3FbO+9AA)7XstajiwkwQcI2wXD(Bf1YhFY}a4H@kEfE*Bg`kf|T@W2`WJPF^Gc<7=auIATH(r5-AK(3@MDk44O<;;u$3+1;tkS`sL;2 zdP({D*_nCiddc~@ewvK8IGh}VUE@Q8d{#0PF#)xKiC=2YRxtsUB^mj7F(suXF-iF# k4AvKuoS#=*l9*RgtXEKZi^C>2KczG$)edC!XCP((0O}tpd;kCd diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/constants.cpython-39.pyc b/Bot Project/10 - Deal Reporting Part 2/booking/__pycache__/constants.cpython-39.pyc deleted file mode 100644 index c3597929a68106e92fae2abb8c443918d45d9349..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 138 zcmYe~<>g`kg6TO^6Ig-tV-N=!FakLaKwQiLBvKfn7*ZI688n$*vH-b$nvAy?6IU`6 zF#|bZ;+LAURZKu-Nk)ENOi5`;Oj14w=Vxc;rN<=a=M|SE=9Lud6;$5hu*uC&Da}c> K1DXFBh#3Hhq8~Q^ diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/booking_filtration.py b/Bot Project/10 - Deal Reporting Part 2/booking/booking_filtration.py deleted file mode 100644 index e352aba..0000000 --- a/Bot Project/10 - Deal Reporting Part 2/booking/booking_filtration.py +++ /dev/null @@ -1,24 +0,0 @@ -#This file will include a class with instance methods. -#That will be responsible to interact with our website -#After we have some results, to apply filtrations. -from selenium.webdriver.remote.webdriver import WebDriver - -class BookingFiltration: - def __init__(self, driver:WebDriver): - self.driver = driver - - def apply_star_rating(self, *star_values): - star_filtration_box = self.driver.find_element_by_id('filter_class') - star_child_elements = star_filtration_box.find_elements_by_css_selector('*') - - for star_value in star_values: - for star_element in star_child_elements: - if str(star_element.get_attribute('innerHTML')).strip() == f'{star_value} stars': - star_element.click() - - - def sort_price_lowest_first(self): - element = self.driver.find_element_by_css_selector( - 'li[data-id="price"]' - ) - element.click() \ No newline at end of file diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/constants.py b/Bot Project/10 - Deal Reporting Part 2/booking/constants.py deleted file mode 100644 index 5ea7013..0000000 --- a/Bot Project/10 - Deal Reporting Part 2/booking/constants.py +++ /dev/null @@ -1 +0,0 @@ -BASE_URL = "https://www.booking.com" \ No newline at end of file diff --git a/Bot Project/04 - Structure a Bot Project/booking/__init__.py b/booking/__init__.py similarity index 100% rename from Bot Project/04 - Structure a Bot Project/booking/__init__.py rename to booking/__init__.py diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/booking.py b/booking/booking.py similarity index 100% rename from Bot Project/10 - Deal Reporting Part 2/booking/booking.py rename to booking/booking.py diff --git a/Bot Project/07 - Booking Filtrations/booking/booking_filtration.py b/booking/booking_filtration.py similarity index 100% rename from Bot Project/07 - Booking Filtrations/booking/booking_filtration.py rename to booking/booking_filtration.py diff --git a/Bot Project/10 - Deal Reporting Part 2/booking/booking_report.py b/booking/booking_report.py similarity index 100% rename from Bot Project/10 - Deal Reporting Part 2/booking/booking_report.py rename to booking/booking_report.py diff --git a/Bot Project/04 - Structure a Bot Project/booking/constants.py b/booking/constants.py similarity index 100% rename from Bot Project/04 - Structure a Bot Project/booking/constants.py rename to booking/constants.py diff --git a/Bot Project/10 - Deal Reporting Part 2/run.py b/run.py similarity index 100% rename from Bot Project/10 - Deal Reporting Part 2/run.py rename to run.py From 8a844f2aedbc64ec07baa7884c39502e54198711 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Thu, 9 Feb 2023 14:02:41 +0100 Subject: [PATCH 02/22] refactored imports in booking.py --- booking/booking.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/booking/booking.py b/booking/booking.py index 041fdca..43ebbad 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,8 +1,7 @@ -import booking.constants as const import os from selenium import webdriver -from booking.booking_filtration import BookingFiltration -from booking.booking_report import BookingReport +from booking.booking import BookingFiltration +from booking.booking import BookingReport from prettytable import PrettyTable class Booking(webdriver.Chrome): From 23bb2d796edccf423816487c85db6dcc9c2d9e51 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Thu, 9 Feb 2023 14:05:37 +0100 Subject: [PATCH 03/22] some extra refactoring to fit my style --- booking/booking.py | 10 ++++++---- booking/{booking_filtration.py => filtration.py} | 2 +- booking/{booking_report.py => report.py} | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) rename booking/{booking_filtration.py => filtration.py} (97%) rename booking/{booking_report.py => report.py} (98%) diff --git a/booking/booking.py b/booking/booking.py index 43ebbad..a1ad818 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,9 +1,11 @@ import os from selenium import webdriver -from booking.booking import BookingFiltration -from booking.booking import BookingReport +from filtration import Filtration +import constants as const +from report import Report from prettytable import PrettyTable + class Booking(webdriver.Chrome): def __init__(self, driver_path=r"C:\SeleniumDrivers", teardown=False): @@ -89,7 +91,7 @@ def click_search(self): search_button.click() def apply_filtrations(self): - filtration = BookingFiltration(driver=self) + filtration = Filtration(driver=self) filtration.apply_star_rating(4, 5) filtration.sort_price_lowest_first() @@ -99,7 +101,7 @@ def report_results(self): 'hotellist_inner' ) - report = BookingReport(hotel_boxes) + report = Report(hotel_boxes) table = PrettyTable( field_names=["Hotel Name", "Hotel Price", "Hotel Score"] ) diff --git a/booking/booking_filtration.py b/booking/filtration.py similarity index 97% rename from booking/booking_filtration.py rename to booking/filtration.py index e352aba..4e165e1 100644 --- a/booking/booking_filtration.py +++ b/booking/filtration.py @@ -3,7 +3,7 @@ #After we have some results, to apply filtrations. from selenium.webdriver.remote.webdriver import WebDriver -class BookingFiltration: +class Filtration: def __init__(self, driver:WebDriver): self.driver = driver diff --git a/booking/booking_report.py b/booking/report.py similarity index 98% rename from booking/booking_report.py rename to booking/report.py index aedb64a..c21b5c8 100644 --- a/booking/booking_report.py +++ b/booking/report.py @@ -3,7 +3,7 @@ from selenium.webdriver.remote.webelement import WebElement -class BookingReport: +class Report: def __init__(self, boxes_section_element:WebElement): self.boxes_section_element = boxes_section_element self.deal_boxes = self.pull_deal_boxes() From df53c54855967758b6eb341e3351c004012bf99c Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Thu, 9 Feb 2023 14:13:13 +0100 Subject: [PATCH 04/22] readme, requirements, gitignore --- .gitignore | 135 +++++++++++++++++++++++++++++++++++++++++++++++ README.md | 37 ++----------- requirements.txt | 23 ++++++++ 3 files changed, 163 insertions(+), 32 deletions(-) create mode 100644 .gitignore create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b9d21dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,135 @@ +## My IDE + +.idea/ + +## Python .gitignore template from GitHub + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ diff --git a/README.md b/README.md index a777f28..81d7dc8 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,6 @@ -# Selenium Series by JimShapedCoding +# Updated booking.com bot -## Prerequisites: - - Python Installed (Recommended version 3.8 or above) - - IDE or Text Editor configured with Python Installed (PyCharm/ Visual Code/ Sublime Text) - - Pip Package Manager (use `pip install selenium`) - - Driver for launching the automation (We will use chromedriver.exe) - - Be sure to match the version of Chrome you have - - [Download From this URL](https://chromedriver.storage.googleapis.com/index.html) - -## What you will learn ? - - - Selenium with Python Basics - - Best practices for element identification on websites - - The most useful methods - - Explicit wait vs Implicit wait - - Selenium Booking Project (Online Bot) - - OOP Project, how to maintain code in Selenium Projects - - Context Manager in Selenium Projects - - Using different arguments to launch different bots - - Selenium Unittest Project (Web Application Testing) - - What is Unittest? Why we need to test our applications? - - Testcase writing, reporting - - Deciding how to fail/pass a test - -## What you should know before starting this series ? - - [Python Basics Full Course](https://www.youtube.com/watch?v=m0LdKZ-prto) - - [Python Context Managers](https://www.youtube.com/watch?v=9TRKdYVzXA) - -

-

- Start here! -

+Jim from JimShapedCoding made this as an example for his Selenium tutorials +(featured on FreeCodeCamp's YT channel). The bot no longer works, +so I decided to fork and update it. This fork only features the final iteration +of the project. diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..661a87d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,23 @@ +async-generator==1.10 +attrs==22.2.0 +certifi==2022.12.7 +charset-normalizer==3.0.1 +exceptiongroup==1.1.0 +h11==0.14.0 +idna==3.4 +outcome==1.2.0 +packaging==23.0 +prettytable==3.6.0 +PySocks==1.7.1 +python-dotenv==0.21.1 +requests==2.28.2 +selenium==4.8.0 +sniffio==1.3.0 +sortedcontainers==2.4.0 +tqdm==4.64.1 +trio==0.22.0 +trio-websocket==0.9.2 +urllib3==1.26.14 +wcwidth==0.2.6 +webdriver-manager==3.8.5 +wsproto==1.2.0 From 036a7a6256599ec37a393f77e2e7f5aedcf433f1 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Thu, 9 Feb 2023 14:39:25 +0100 Subject: [PATCH 05/22] implemented webdriver manager --- booking/booking.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/booking/booking.py b/booking/booking.py index a1ad818..76c5839 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,20 +1,19 @@ -import os from selenium import webdriver -from filtration import Filtration -import constants as const -from report import Report +from booking.filtration import Filtration +import booking.constants as const +from booking.report import Report from prettytable import PrettyTable +from selenium.webdriver.chrome.service import Service as ChromeService +from webdriver_manager.chrome import ChromeDriverManager + class Booking(webdriver.Chrome): - def __init__(self, driver_path=r"C:\SeleniumDrivers", - teardown=False): - self.driver_path = driver_path + def __init__(self, teardown=False): self.teardown = teardown - os.environ['PATH'] += self.driver_path options = webdriver.ChromeOptions() options.add_experimental_option('excludeSwitches', ['enable-logging']) - super(Booking, self).__init__(options=options) + super(Booking, self).__init__(options=options, service=ChromeService(ChromeDriverManager().install())) self.implicitly_wait(15) self.maximize_window() From 424a94a9e24bc8c6a6090e5c2c483b8bd4716794 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Thu, 9 Feb 2023 15:19:30 +0100 Subject: [PATCH 06/22] removed redundant try/catch block --- run.py | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/run.py b/run.py index e3a34c2..79fda8f 100644 --- a/run.py +++ b/run.py @@ -1,27 +1,13 @@ from booking.booking import Booking -try: - with Booking() as bot: - bot.land_first_page() - bot.change_currency(currency='USD') - bot.select_place_to_go(input("Where you want to go ?")) - bot.select_dates(check_in_date=input("What is the check in date ?"), - check_out_date=input("What is the check out date ?")) - bot.select_adults(int(input("How many people ?"))) - bot.click_search() - bot.apply_filtrations() - bot.refresh() # A workaround to let our bot to grab the data properly - bot.report_results() - -except Exception as e: - if 'in PATH' in str(e): - print( - 'You are trying to run the bot from command line \n' - 'Please add to PATH your Selenium Drivers \n' - 'Windows: \n' - ' set PATH=%PATH%;C:path-to-your-folder \n \n' - 'Linux: \n' - ' PATH=$PATH:/path/toyour/folder/ \n' - ) - else: - raise \ No newline at end of file +with Booking() as bot: + bot.land_first_page() + bot.change_currency(currency='USD') + bot.select_place_to_go(input("Where you want to go ?")) + bot.select_dates(check_in_date=input("What is the check in date ?"), + check_out_date=input("What is the check out date ?")) + bot.select_adults(int(input("How many people ?"))) + bot.click_search() + bot.apply_filtrations() + bot.refresh() # A workaround to let our bot to grab the data properly + bot.report_results() \ No newline at end of file From 2742b12bd2861dc758fe0838769be70b138821a7 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Thu, 9 Feb 2023 15:28:56 +0100 Subject: [PATCH 07/22] replaced deprecated find__by syntax, some pep --- booking/booking.py | 26 ++++++++++++++------------ booking/filtration.py | 23 ++++++++++++----------- booking/report.py | 23 ++++++++++++----------- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/booking/booking.py b/booking/booking.py index 76c5839..2a3b6be 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,4 +1,6 @@ from selenium import webdriver +from selenium.webdriver.common.by import By + from booking.filtration import Filtration import booking.constants as const from booking.report import Report @@ -25,50 +27,50 @@ def land_first_page(self): self.get(const.BASE_URL) def change_currency(self, currency=None): - currency_element = self.find_element_by_css_selector( + currency_element = self.find_element(By.CSS_SELECTOR, 'button[data-tooltip-text="Choose your currency"]' ) currency_element.click() - selected_currency_element = self.find_element_by_css_selector( + selected_currency_element = self.find_element(By.CSS_SELECTOR, f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' ) selected_currency_element.click() def select_place_to_go(self, place_to_go): - search_field = self.find_element_by_id('ss') + search_field = self.find_element(By.ID, 'ss') search_field.clear() search_field.send_keys(place_to_go) - first_result = self.find_element_by_css_selector( + first_result = self.find_element(By.CSS_SELECTOR, 'li[data-i="0"]' ) first_result.click() def select_dates(self, check_in_date, check_out_date): - check_in_element = self.find_element_by_css_selector( + check_in_element = self.find_element(By.CSS_SELECTOR, f'td[data-date="{check_in_date}"]' ) check_in_element.click() - check_out_element = self.find_element_by_css_selector( + check_out_element = self.find_element(By.CSS_SELECTOR, f'td[data-date="{check_out_date}"]' ) check_out_element.click() def select_adults(self, count=1): - selection_element = self.find_element_by_id('xp__guests__toggle') + selection_element = self.find_element(By.ID, 'xp__guests__toggle') selection_element.click() while True: - decrease_adults_element = self.find_element_by_css_selector( + decrease_adults_element = self.find_element(By.CSS_SELECTOR, 'button[aria-label="Decrease number of Adults"]' ) decrease_adults_element.click() #If the value of adults reaches 1, then we should get out #of the while loop - adults_value_element = self.find_element_by_id('group_adults') + adults_value_element = self.find_element(By.ID, 'group_adults') adults_value = adults_value_element.get_attribute( 'value' ) # Should give back the adults count @@ -76,7 +78,7 @@ def select_adults(self, count=1): if int(adults_value) == 1: break - increase_button_element = self.find_element_by_css_selector( + increase_button_element = self.find_element(By.CSS_SELECTOR, 'button[aria-label="Increase number of Adults"]' ) @@ -84,7 +86,7 @@ def select_adults(self, count=1): increase_button_element.click() def click_search(self): - search_button = self.find_element_by_css_selector( + search_button = self.find_element(By.CSS_SELECTOR, 'button[type="submit"]' ) search_button.click() @@ -96,7 +98,7 @@ def apply_filtrations(self): filtration.sort_price_lowest_first() def report_results(self): - hotel_boxes = self.find_element_by_id( + hotel_boxes = self.find_element(By.ID, 'hotellist_inner' ) diff --git a/booking/filtration.py b/booking/filtration.py index 4e165e1..86e6763 100644 --- a/booking/filtration.py +++ b/booking/filtration.py @@ -1,24 +1,25 @@ -#This file will include a class with instance methods. -#That will be responsible to interact with our website -#After we have some results, to apply filtrations. +# This file will include a class with instance methods. +# That will be responsible to interact with our website +# After we have some results, to apply filtrations. +from selenium.webdriver.common.by import By from selenium.webdriver.remote.webdriver import WebDriver + class Filtration: - def __init__(self, driver:WebDriver): + def __init__(self, driver: WebDriver): self.driver = driver def apply_star_rating(self, *star_values): - star_filtration_box = self.driver.find_element_by_id('filter_class') - star_child_elements = star_filtration_box.find_elements_by_css_selector('*') + star_filtration_box = self.driver.find_element(By.ID, 'filter_class') + star_child_elements = star_filtration_box.find_elements(By.CSS_SELECTOR, '*') for star_value in star_values: for star_element in star_child_elements: if str(star_element.get_attribute('innerHTML')).strip() == f'{star_value} stars': star_element.click() - def sort_price_lowest_first(self): - element = self.driver.find_element_by_css_selector( - 'li[data-id="price"]' - ) - element.click() \ No newline at end of file + element = self.driver.find_element(By.CSS_SELECTOR, + 'li[data-id="price"]' + ) + element.click() diff --git a/booking/report.py b/booking/report.py index c21b5c8..d1edde7 100644 --- a/booking/report.py +++ b/booking/report.py @@ -1,28 +1,29 @@ # This file is going to include method that will parse # The specific data that we need from each one of the deal boxes. +from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement class Report: - def __init__(self, boxes_section_element:WebElement): + def __init__(self, boxes_section_element: WebElement): self.boxes_section_element = boxes_section_element self.deal_boxes = self.pull_deal_boxes() def pull_deal_boxes(self): - return self.boxes_section_element.find_elements_by_class_name( - 'sr_property_block' - ) + return self.boxes_section_element.find_elements(By.CLASS_NAME, + 'sr_property_block' + ) def pull_deal_box_attributes(self): collection = [] for deal_box in self.deal_boxes: # Pulling the hotel name - hotel_name = deal_box.find_element_by_class_name( - 'sr-hotel__name' - ).get_attribute('innerHTML').strip() - hotel_price = deal_box.find_element_by_class_name( - 'bui-price-display__value' - ).get_attribute('innerHTML').strip() + hotel_name = deal_box.find_element(By.CLASS_NAME, + 'sr-hotel__name' + ).get_attribute('innerHTML').strip() + hotel_price = deal_box.find_element(By.CLASS_NAME, + 'bui-price-display__value' + ).get_attribute('innerHTML').strip() hotel_score = deal_box.get_attribute( 'data-score' ).strip() @@ -30,4 +31,4 @@ def pull_deal_box_attributes(self): collection.append( [hotel_name, hotel_price, hotel_score] ) - return collection \ No newline at end of file + return collection From 3167293adf0656f7460cb06bf26dfee311f8767e Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 07:59:11 +0100 Subject: [PATCH 08/22] temp. disabled currency and inputs to debug stuff --- run.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/run.py b/run.py index 79fda8f..e1fcde3 100644 --- a/run.py +++ b/run.py @@ -2,11 +2,10 @@ with Booking() as bot: bot.land_first_page() - bot.change_currency(currency='USD') - bot.select_place_to_go(input("Where you want to go ?")) - bot.select_dates(check_in_date=input("What is the check in date ?"), - check_out_date=input("What is the check out date ?")) - bot.select_adults(int(input("How many people ?"))) + # bot.change_currency(currency='USD') + bot.select_place_to_go("warsaw") + bot.select_dates(check_in_date="2023-02-20", check_out_date="2023-02-23") + bot.select_adults(3) bot.click_search() bot.apply_filtrations() bot.refresh() # A workaround to let our bot to grab the data properly From 5583c1c9ff9f437282d4831b70f2518e61959b90 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 08:00:05 +0100 Subject: [PATCH 09/22] mostly working on styles and updating locators --- booking/booking.py | 60 ++++++++++++++++++++++------------------------ 1 file changed, 29 insertions(+), 31 deletions(-) diff --git a/booking/booking.py b/booking/booking.py index 2a3b6be..3ffe1a7 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,5 +1,6 @@ from selenium import webdriver from selenium.webdriver.common.by import By +from selenium.webdriver.support.relative_locator import locate_with from booking.filtration import Filtration import booking.constants as const @@ -13,11 +14,10 @@ class Booking(webdriver.Chrome): def __init__(self, teardown=False): self.teardown = teardown - options = webdriver.ChromeOptions() - options.add_experimental_option('excludeSwitches', ['enable-logging']) - super(Booking, self).__init__(options=options, service=ChromeService(ChromeDriverManager().install())) - self.implicitly_wait(15) - self.maximize_window() + # options = webdriver.ChromeOptions() + # options.add_experimental_option('excludeSwitches', ['enable-logging']) + super(Booking, self).__init__(service=ChromeService(ChromeDriverManager().install())) + self.implicitly_wait(2) def __exit__(self, exc_type, exc_val, exc_tb): if self.teardown: @@ -27,36 +27,34 @@ def land_first_page(self): self.get(const.BASE_URL) def change_currency(self, currency=None): - currency_element = self.find_element(By.CSS_SELECTOR, - 'button[data-tooltip-text="Choose your currency"]' - ) + currency_element = self.find_element(By.CSS_SELECTOR, 'button[data-tooltip-text="Choose your currency"]') currency_element.click() selected_currency_element = self.find_element(By.CSS_SELECTOR, - f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' - ) + f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' + ) selected_currency_element.click() - def select_place_to_go(self, place_to_go): - search_field = self.find_element(By.ID, 'ss') + search_field = self.find_element(By.NAME, 'ss') + search_field.click() search_field.clear() search_field.send_keys(place_to_go) - first_result = self.find_element(By.CSS_SELECTOR, - 'li[data-i="0"]' - ) + # todo this dict seems weird + first_result_locator = locate_with(By.XPATH, "//li[1]").below({By.NAME: 'ss'}) + first_result = self.find_element(first_result_locator) first_result.click() def select_dates(self, check_in_date, check_out_date): check_in_element = self.find_element(By.CSS_SELECTOR, - f'td[data-date="{check_in_date}"]' - ) + f'td[data-date="{check_in_date}"]' + ) check_in_element.click() check_out_element = self.find_element(By.CSS_SELECTOR, - f'td[data-date="{check_out_date}"]' - ) + f'td[data-date="{check_out_date}"]' + ) check_out_element.click() def select_adults(self, count=1): @@ -65,30 +63,30 @@ def select_adults(self, count=1): while True: decrease_adults_element = self.find_element(By.CSS_SELECTOR, - 'button[aria-label="Decrease number of Adults"]' - ) + 'button[aria-label="Decrease number of Adults"]' + ) decrease_adults_element.click() - #If the value of adults reaches 1, then we should get out - #of the while loop + # If the value of adults reaches 1, then we should get out + # of the while loop adults_value_element = self.find_element(By.ID, 'group_adults') adults_value = adults_value_element.get_attribute( 'value' - ) # Should give back the adults count + ) # Should give back the adults count if int(adults_value) == 1: break increase_button_element = self.find_element(By.CSS_SELECTOR, - 'button[aria-label="Increase number of Adults"]' - ) + 'button[aria-label="Increase number of Adults"]' + ) for _ in range(count - 1): increase_button_element.click() def click_search(self): search_button = self.find_element(By.CSS_SELECTOR, - 'button[type="submit"]' - ) + 'button[type="submit"]' + ) search_button.click() def apply_filtrations(self): @@ -99,12 +97,12 @@ def apply_filtrations(self): def report_results(self): hotel_boxes = self.find_element(By.ID, - 'hotellist_inner' - ) + 'hotellist_inner' + ) report = Report(hotel_boxes) table = PrettyTable( field_names=["Hotel Name", "Hotel Price", "Hotel Score"] ) table.add_rows(report.pull_deal_box_attributes()) - print(table) \ No newline at end of file + print(table) From 893c6d83c31b0da6eef28b309d315417d0626e2e Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 08:33:56 +0100 Subject: [PATCH 10/22] more styles (line breaks) and locators (xpath) --- booking/booking.py | 43 ++++++++++++++++--------------------------- 1 file changed, 16 insertions(+), 27 deletions(-) diff --git a/booking/booking.py b/booking/booking.py index 3ffe1a7..2c2a782 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -14,10 +14,10 @@ class Booking(webdriver.Chrome): def __init__(self, teardown=False): self.teardown = teardown - # options = webdriver.ChromeOptions() - # options.add_experimental_option('excludeSwitches', ['enable-logging']) - super(Booking, self).__init__(service=ChromeService(ChromeDriverManager().install())) - self.implicitly_wait(2) + options = webdriver.ChromeOptions() + options.add_experimental_option('excludeSwitches', ['enable-logging']) + super(Booking, self).__init__(options=options, service=ChromeService(ChromeDriverManager().install())) + # self.implicitly_wait(10) def __exit__(self, exc_type, exc_val, exc_tb): if self.teardown: @@ -30,12 +30,12 @@ def change_currency(self, currency=None): currency_element = self.find_element(By.CSS_SELECTOR, 'button[data-tooltip-text="Choose your currency"]') currency_element.click() - selected_currency_element = self.find_element(By.CSS_SELECTOR, - f'a[data-modal-header-async-url-param*="selected_currency={currency}"]' - ) + selected_currency_element = self.find_element( + By.CSS_SELECTOR, f'a[data-modal-header-async-url-param*="selected_currency={currency}"]') selected_currency_element.click() def select_place_to_go(self, place_to_go): + # todo explicit wait here search_field = self.find_element(By.NAME, 'ss') search_field.click() search_field.clear() @@ -47,14 +47,10 @@ def select_place_to_go(self, place_to_go): first_result.click() def select_dates(self, check_in_date, check_out_date): - check_in_element = self.find_element(By.CSS_SELECTOR, - f'td[data-date="{check_in_date}"]' - ) - check_in_element.click() - - check_out_element = self.find_element(By.CSS_SELECTOR, - f'td[data-date="{check_out_date}"]' - ) + check_in_date_element = self.find_element(By.XPATH, f'//span[@data-date="{check_in_date}"]') + check_in_date_element.click() + + check_out_element = self.find_element(By.XPATH, f'//span[@data-date="{check_out_date}"]') check_out_element.click() def select_adults(self, count=1): @@ -62,9 +58,8 @@ def select_adults(self, count=1): selection_element.click() while True: - decrease_adults_element = self.find_element(By.CSS_SELECTOR, - 'button[aria-label="Decrease number of Adults"]' - ) + decrease_adults_element = self.find_element( + By.CSS_SELECTOR, 'button[aria-label="Decrease number of Adults"]') decrease_adults_element.click() # If the value of adults reaches 1, then we should get out # of the while loop @@ -76,17 +71,13 @@ def select_adults(self, count=1): if int(adults_value) == 1: break - increase_button_element = self.find_element(By.CSS_SELECTOR, - 'button[aria-label="Increase number of Adults"]' - ) + increase_button_element = self.find_element(By.CSS_SELECTOR, 'button[aria-label="Increase number of Adults"]') for _ in range(count - 1): increase_button_element.click() def click_search(self): - search_button = self.find_element(By.CSS_SELECTOR, - 'button[type="submit"]' - ) + search_button = self.find_element(By.CSS_SELECTOR, 'button[type="submit"]') search_button.click() def apply_filtrations(self): @@ -96,9 +87,7 @@ def apply_filtrations(self): filtration.sort_price_lowest_first() def report_results(self): - hotel_boxes = self.find_element(By.ID, - 'hotellist_inner' - ) + hotel_boxes = self.find_element(By.ID, 'hotellist_inner') report = Report(hotel_boxes) table = PrettyTable( From d888c39ee4f0a6c6137fb2aa050974106378ef5a Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 08:34:42 +0100 Subject: [PATCH 11/22] added config and helpers for conditional waits --- config.py | 7 +++++++ helpers.py | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 config.py create mode 100644 helpers.py diff --git a/config.py b/config.py new file mode 100644 index 0000000..f6ad090 --- /dev/null +++ b/config.py @@ -0,0 +1,7 @@ +from enum import Enum + + +class Timeout(Enum): + SHORT = 0.5 + MEDIUM = 2. + LONG = 5. diff --git a/helpers.py b/helpers.py new file mode 100644 index 0000000..a56b357 --- /dev/null +++ b/helpers.py @@ -0,0 +1,7 @@ +from selenium.webdriver.support.wait import WebDriverWait + +from config import Timeout + + +def get_webdriver_wait(driver, timeout: Timeout) -> WebDriverWait: + return WebDriverWait(driver, timeout.value) From b1d09a77d26f073c485d68e6d5262d25e5b0ccfd Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 10:20:19 +0100 Subject: [PATCH 12/22] moved helpers,major changes in select_place_to_go --- booking/booking.py | 12 ++++++++---- config.py => booking/config.py | 2 +- helpers.py => booking/helpers.py | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) rename config.py => booking/config.py (82%) rename helpers.py => booking/helpers.py (83%) diff --git a/booking/booking.py b/booking/booking.py index 2c2a782..379c9a2 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,15 +1,19 @@ from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.relative_locator import locate_with +from selenium.webdriver.support import expected_conditions as EC from booking.filtration import Filtration import booking.constants as const +from booking.helpers import get_webdriver_wait from booking.report import Report from prettytable import PrettyTable from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager +from config import Timeout + class Booking(webdriver.Chrome): def __init__(self, teardown=False): @@ -35,16 +39,16 @@ def change_currency(self, currency=None): selected_currency_element.click() def select_place_to_go(self, place_to_go): - # todo explicit wait here search_field = self.find_element(By.NAME, 'ss') search_field.click() search_field.clear() + + popular_destinations_locator = (By.XPATH, "//span[contains(text(), 'Popular destinations nearby')]") search_field.send_keys(place_to_go) + get_webdriver_wait(self, Timeout.MEDIUM).until(EC.invisibility_of_element_located(popular_destinations_locator)) - # todo this dict seems weird first_result_locator = locate_with(By.XPATH, "//li[1]").below({By.NAME: 'ss'}) - first_result = self.find_element(first_result_locator) - first_result.click() + self.find_element(first_result_locator).click() def select_dates(self, check_in_date, check_out_date): check_in_date_element = self.find_element(By.XPATH, f'//span[@data-date="{check_in_date}"]') diff --git a/config.py b/booking/config.py similarity index 82% rename from config.py rename to booking/config.py index f6ad090..40b4908 100644 --- a/config.py +++ b/booking/config.py @@ -3,5 +3,5 @@ class Timeout(Enum): SHORT = 0.5 - MEDIUM = 2. + MEDIUM = 5. LONG = 5. diff --git a/helpers.py b/booking/helpers.py similarity index 83% rename from helpers.py rename to booking/helpers.py index a56b357..5902015 100644 --- a/helpers.py +++ b/booking/helpers.py @@ -1,6 +1,6 @@ from selenium.webdriver.support.wait import WebDriverWait -from config import Timeout +from booking.config import Timeout def get_webdriver_wait(driver, timeout: Timeout) -> WebDriverWait: From b02de51032eeb9d95bc447a9545ac1c891826577 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 10:45:34 +0100 Subject: [PATCH 13/22] stable first result locator at last --- booking/booking.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/booking/booking.py b/booking/booking.py index 379c9a2..61c6ba4 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -43,22 +43,26 @@ def select_place_to_go(self, place_to_go): search_field.click() search_field.clear() + results_list_locator = (By.XPATH, "//ul") popular_destinations_locator = (By.XPATH, "//span[contains(text(), 'Popular destinations nearby')]") search_field.send_keys(place_to_go) + # search_field.click() + get_webdriver_wait(self, Timeout.MEDIUM).until(EC.visibility_of_element_located(results_list_locator)) get_webdriver_wait(self, Timeout.MEDIUM).until(EC.invisibility_of_element_located(popular_destinations_locator)) first_result_locator = locate_with(By.XPATH, "//li[1]").below({By.NAME: 'ss'}) self.find_element(first_result_locator).click() def select_dates(self, check_in_date, check_out_date): - check_in_date_element = self.find_element(By.XPATH, f'//span[@data-date="{check_in_date}"]') + check_in_date_element = get_webdriver_wait(self, Timeout.MEDIUM).until( + EC.visibility_of_element_located((By.XPATH, f'//span[@data-date="{check_in_date}"]'))) check_in_date_element.click() check_out_element = self.find_element(By.XPATH, f'//span[@data-date="{check_out_date}"]') check_out_element.click() def select_adults(self, count=1): - selection_element = self.find_element(By.ID, 'xp__guests__toggle') + selection_element = self.find_element(By.XPATH, "//button[@data-testid='occupancy-config']") selection_element.click() while True: From a4de160523c981dcd41e25ddf5e36a29c5785ab3 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 13:19:45 +0100 Subject: [PATCH 14/22] bot sequence complete, misc ad hoc fixes --- booking/booking.py | 39 +++++++++++---------------------------- booking/filtration.py | 20 +++++++++++++++----- booking/helpers.py | 12 ++++++++++++ booking/report.py | 35 +++++++++++++---------------------- run.py | 2 +- 5 files changed, 52 insertions(+), 56 deletions(-) diff --git a/booking/booking.py b/booking/booking.py index 61c6ba4..a6e141c 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -16,13 +16,17 @@ class Booking(webdriver.Chrome): + # todo specify input or manual config def __init__(self, teardown=False): self.teardown = teardown options = webdriver.ChromeOptions() - options.add_experimental_option('excludeSwitches', ['enable-logging']) + # options.add_experimental_option('excludeSwitches', ['enable-logging']) super(Booking, self).__init__(options=options, service=ChromeService(ChromeDriverManager().install())) # self.implicitly_wait(10) + def accept_cookies(self): + self.find_element(By.ID, 'onetrust-accept-btn-handler').click() + def __exit__(self, exc_type, exc_val, exc_tb): if self.teardown: self.quit() @@ -54,36 +58,15 @@ def select_place_to_go(self, place_to_go): self.find_element(first_result_locator).click() def select_dates(self, check_in_date, check_out_date): + # accommodation_element = self.find_element(By.ID, "accommodation") + # accommodation_element.click() check_in_date_element = get_webdriver_wait(self, Timeout.MEDIUM).until( - EC.visibility_of_element_located((By.XPATH, f'//span[@data-date="{check_in_date}"]'))) + EC.visibility_of_element_located((By.XPATH, f'//*[@data-date="{check_in_date}"]'))) check_in_date_element.click() - check_out_element = self.find_element(By.XPATH, f'//span[@data-date="{check_out_date}"]') + check_out_element = self.find_element(By.XPATH, f'//*[@data-date="{check_out_date}"]') check_out_element.click() - def select_adults(self, count=1): - selection_element = self.find_element(By.XPATH, "//button[@data-testid='occupancy-config']") - selection_element.click() - - while True: - decrease_adults_element = self.find_element( - By.CSS_SELECTOR, 'button[aria-label="Decrease number of Adults"]') - decrease_adults_element.click() - # If the value of adults reaches 1, then we should get out - # of the while loop - adults_value_element = self.find_element(By.ID, 'group_adults') - adults_value = adults_value_element.get_attribute( - 'value' - ) # Should give back the adults count - - if int(adults_value) == 1: - break - - increase_button_element = self.find_element(By.CSS_SELECTOR, 'button[aria-label="Increase number of Adults"]') - - for _ in range(count - 1): - increase_button_element.click() - def click_search(self): search_button = self.find_element(By.CSS_SELECTOR, 'button[type="submit"]') search_button.click() @@ -95,9 +78,9 @@ def apply_filtrations(self): filtration.sort_price_lowest_first() def report_results(self): - hotel_boxes = self.find_element(By.ID, 'hotellist_inner') + hotel_boxes = self.find_elements(By.XPATH, "//div[@data-testid='property-card']") - report = Report(hotel_boxes) + report = Report(self, hotel_boxes) table = PrettyTable( field_names=["Hotel Name", "Hotel Price", "Hotel Score"] ) diff --git a/booking/filtration.py b/booking/filtration.py index 86e6763..bef7e4f 100644 --- a/booking/filtration.py +++ b/booking/filtration.py @@ -4,22 +4,32 @@ from selenium.webdriver.common.by import By from selenium.webdriver.remote.webdriver import WebDriver +from selenium.webdriver.support import expected_conditions as EC + +from booking.helpers import scroll_into_view_with_js, get_webdriver_wait +from config import Timeout + class Filtration: def __init__(self, driver: WebDriver): self.driver = driver def apply_star_rating(self, *star_values): - star_filtration_box = self.driver.find_element(By.ID, 'filter_class') + star_filtration_box = self.driver.find_element(By.XPATH, "//div[contains(@id, 'filter_group_class')]") star_child_elements = star_filtration_box.find_elements(By.CSS_SELECTOR, '*') + # todo optimize this loop maybe for star_value in star_values: + for star_element in star_child_elements: if str(star_element.get_attribute('innerHTML')).strip() == f'{star_value} stars': + scroll_into_view_with_js(self.driver, star_element) star_element.click() def sort_price_lowest_first(self): - element = self.driver.find_element(By.CSS_SELECTOR, - 'li[data-id="price"]' - ) - element.click() + sort_by_element = self.driver.find_element(By.XPATH, "//button[@data-testid='sorters-dropdown-trigger']") + sort_by_element.click() + lowest_price_locator = (By.XPATH, "//span[contains(text(), 'Price') and contains(text(), 'lowest')]") + lowest_price_element = get_webdriver_wait(self.driver, Timeout.MEDIUM).until( + EC.element_to_be_clickable(lowest_price_locator)) + lowest_price_element.click() diff --git a/booking/helpers.py b/booking/helpers.py index 5902015..2a6ec39 100644 --- a/booking/helpers.py +++ b/booking/helpers.py @@ -5,3 +5,15 @@ def get_webdriver_wait(driver, timeout: Timeout) -> WebDriverWait: return WebDriverWait(driver, timeout.value) + + +def scroll_into_view_with_js(driver, element): + driver.execute_script("arguments[0].scrollIntoView();", element) + + +def highlight_element(driver, element): + original_style = element.get_attribute("style") + driver.execute_script("arguments[0].setAttribute(arguments[1], arguments[2])", + element, + "style", + "border: 2px solid red;") diff --git a/booking/report.py b/booking/report.py index d1edde7..149d606 100644 --- a/booking/report.py +++ b/booking/report.py @@ -1,34 +1,25 @@ # This file is going to include method that will parse # The specific data that we need from each one of the deal boxes. +from selenium.common import NoSuchElementException from selenium.webdriver.common.by import By from selenium.webdriver.remote.webelement import WebElement +from booking.helpers import highlight_element -class Report: - def __init__(self, boxes_section_element: WebElement): - self.boxes_section_element = boxes_section_element - self.deal_boxes = self.pull_deal_boxes() - def pull_deal_boxes(self): - return self.boxes_section_element.find_elements(By.CLASS_NAME, - 'sr_property_block' - ) +class Report: + def __init__(self, driver, deal_boxes: list[WebElement]): + self.deal_boxes = deal_boxes + self.driver = driver def pull_deal_box_attributes(self): collection = [] for deal_box in self.deal_boxes: - # Pulling the hotel name - hotel_name = deal_box.find_element(By.CLASS_NAME, - 'sr-hotel__name' - ).get_attribute('innerHTML').strip() - hotel_price = deal_box.find_element(By.CLASS_NAME, - 'bui-price-display__value' - ).get_attribute('innerHTML').strip() - hotel_score = deal_box.get_attribute( - 'data-score' - ).strip() - - collection.append( - [hotel_name, hotel_price, hotel_score] - ) + hotel_name = deal_box.find_element(By.XPATH, ".//*[@data-testid='title']").text + hotel_price = deal_box.find_element(By.XPATH, ".//*[@data-testid='price-and-discounted-price']").text + try: + hotel_score = deal_box.find_element(By.XPATH, ".//*[@data-testid='review-score']/div").text + except NoSuchElementException: + hotel_score = 'new' + collection.append([hotel_name, hotel_price, hotel_score]) return collection diff --git a/run.py b/run.py index e1fcde3..c1d452a 100644 --- a/run.py +++ b/run.py @@ -3,9 +3,9 @@ with Booking() as bot: bot.land_first_page() # bot.change_currency(currency='USD') + bot.accept_cookies() bot.select_place_to_go("warsaw") bot.select_dates(check_in_date="2023-02-20", check_out_date="2023-02-23") - bot.select_adults(3) bot.click_search() bot.apply_filtrations() bot.refresh() # A workaround to let our bot to grab the data properly From bc5d37518dbcbd583ef71c2afc991bb4c931290b Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 13:39:17 +0100 Subject: [PATCH 15/22] simplified and moved reporting/extracting results --- booking/booking.py | 53 ++++++++++++++++++++++++++++++---------------- booking/report.py | 25 ---------------------- run.py | 38 ++++++++++++++++++++++++--------- 3 files changed, 63 insertions(+), 53 deletions(-) delete mode 100644 booking/report.py diff --git a/booking/booking.py b/booking/booking.py index a6e141c..cb8e18b 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,13 +1,12 @@ from selenium import webdriver +from selenium.common import NoSuchElementException from selenium.webdriver.common.by import By from selenium.webdriver.support.relative_locator import locate_with from selenium.webdriver.support import expected_conditions as EC from booking.filtration import Filtration import booking.constants as const -from booking.helpers import get_webdriver_wait -from booking.report import Report -from prettytable import PrettyTable +from booking.helpers import get_webdriver_wait, scroll_into_view_with_js from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager @@ -71,18 +70,36 @@ def click_search(self): search_button = self.find_element(By.CSS_SELECTOR, 'button[type="submit"]') search_button.click() - def apply_filtrations(self): - filtration = Filtration(driver=self) - filtration.apply_star_rating(4, 5) - - filtration.sort_price_lowest_first() - - def report_results(self): - hotel_boxes = self.find_elements(By.XPATH, "//div[@data-testid='property-card']") - - report = Report(self, hotel_boxes) - table = PrettyTable( - field_names=["Hotel Name", "Hotel Price", "Hotel Score"] - ) - table.add_rows(report.pull_deal_box_attributes()) - print(table) + def apply_star_rating(self, *star_values): + star_filtration_box = self.find_element(By.XPATH, "//div[contains(@id, 'filter_group_class')]") + star_child_elements = star_filtration_box.find_elements(By.CSS_SELECTOR, '*') + + # todo optimize this loop maybe + for star_value in star_values: + + for star_element in star_child_elements: + if str(star_element.get_attribute('innerHTML')).strip() == f'{star_value} stars': + scroll_into_view_with_js(self, star_element) + star_element.click() + + def sort_price_lowest_first(self): + sort_by_element = self.find_element(By.XPATH, "//button[@data-testid='sorters-dropdown-trigger']") + sort_by_element.click() + lowest_price_locator = (By.XPATH, "//span[contains(text(), 'Price') and contains(text(), 'lowest')]") + lowest_price_element = get_webdriver_wait(self, Timeout.MEDIUM).until( + EC.element_to_be_clickable(lowest_price_locator)) + lowest_price_element.click() + + def extract_results(self) -> list[list[str]]: + deal_boxes = self.find_elements(By.XPATH, "//div[@data-testid='property-card']") + + results = [] + for deal_box in deal_boxes: + hotel_name = deal_box.find_element(By.XPATH, ".//*[@data-testid='title']").text + hotel_price = deal_box.find_element(By.XPATH, ".//*[@data-testid='price-and-discounted-price']").text + try: + hotel_score = deal_box.find_element(By.XPATH, ".//*[@data-testid='review-score']/div").text + except NoSuchElementException: + hotel_score = 'new' + results.append([hotel_name, hotel_price, hotel_score]) + return results diff --git a/booking/report.py b/booking/report.py deleted file mode 100644 index 149d606..0000000 --- a/booking/report.py +++ /dev/null @@ -1,25 +0,0 @@ -# This file is going to include method that will parse -# The specific data that we need from each one of the deal boxes. -from selenium.common import NoSuchElementException -from selenium.webdriver.common.by import By -from selenium.webdriver.remote.webelement import WebElement - -from booking.helpers import highlight_element - - -class Report: - def __init__(self, driver, deal_boxes: list[WebElement]): - self.deal_boxes = deal_boxes - self.driver = driver - - def pull_deal_box_attributes(self): - collection = [] - for deal_box in self.deal_boxes: - hotel_name = deal_box.find_element(By.XPATH, ".//*[@data-testid='title']").text - hotel_price = deal_box.find_element(By.XPATH, ".//*[@data-testid='price-and-discounted-price']").text - try: - hotel_score = deal_box.find_element(By.XPATH, ".//*[@data-testid='review-score']/div").text - except NoSuchElementException: - hotel_score = 'new' - collection.append([hotel_name, hotel_price, hotel_score]) - return collection diff --git a/run.py b/run.py index c1d452a..bbbd12f 100644 --- a/run.py +++ b/run.py @@ -1,12 +1,30 @@ +from prettytable import PrettyTable + from booking.booking import Booking -with Booking() as bot: - bot.land_first_page() - # bot.change_currency(currency='USD') - bot.accept_cookies() - bot.select_place_to_go("warsaw") - bot.select_dates(check_in_date="2023-02-20", check_out_date="2023-02-23") - bot.click_search() - bot.apply_filtrations() - bot.refresh() # A workaround to let our bot to grab the data properly - bot.report_results() \ No newline at end of file + +def run(): + with Booking() as bot: + bot.land_first_page() + # bot.change_currency(currency='USD') + bot.accept_cookies() + bot.select_place_to_go("warsaw") + bot.select_dates(check_in_date="2023-02-20", check_out_date="2023-02-23") + bot.click_search() + bot.apply_star_rating(4, 5) + bot.sort_price_lowest_first() + bot.refresh() # A workaround to let our bot to grab the data properly + + results = bot.extract_results() + + print_results(results) + + +def print_results(results): + table = PrettyTable(field_names=["Hotel Name", "Hotel Price", "Hotel Score"]) + table.add_rows(results) + print(table) + + +if __name__ == '__main__': + run() From 74282f116f1dee62079166d07406aa6095c6f9a3 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 19:40:38 +0100 Subject: [PATCH 16/22] chekpoint after major changes throughout --- README.md | 8 +++++++ booking/booking.py | 50 ++++++++++++++----------------------------- booking/config.py | 31 ++++++++++++++++++++++----- booking/constants.py | 1 - booking/filtration.py | 35 ------------------------------ booking/helpers.py | 19 ---------------- booking/js_utils.py | 8 +++++++ run.py | 10 ++++----- 8 files changed, 63 insertions(+), 99 deletions(-) delete mode 100644 booking/constants.py delete mode 100644 booking/filtration.py delete mode 100644 booking/helpers.py create mode 100644 booking/js_utils.py diff --git a/README.md b/README.md index 81d7dc8..0aa12c6 100644 --- a/README.md +++ b/README.md @@ -4,3 +4,11 @@ Jim from JimShapedCoding made this as an example for his Selenium tutorials (featured on FreeCodeCamp's YT channel). The bot no longer works, so I decided to fork and update it. This fork only features the final iteration of the project. + +## ISSUES AND LIMITATIONS + +1. I skipped selecting currency and no. of people altogether. + +Booking.com seems to have some terrible, unstable locators - not sure if it +was better two years ago, but now some elements seem impossible to navigate + diff --git a/booking/booking.py b/booking/booking.py index cb8e18b..15fcd8d 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,45 +1,32 @@ from selenium import webdriver from selenium.common import NoSuchElementException -from selenium.webdriver.common.by import By from selenium.webdriver.support.relative_locator import locate_with from selenium.webdriver.support import expected_conditions as EC - -from booking.filtration import Filtration -import booking.constants as const -from booking.helpers import get_webdriver_wait, scroll_into_view_with_js - +from selenium.webdriver.common.by import By +from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager -from config import Timeout +from booking import config +from booking.config import MEDIUM_TIMEOUT +from booking.js_utils import scroll_into_view_with_js, highlight_element class Booking(webdriver.Chrome): - # todo specify input or manual config - def __init__(self, teardown=False): - self.teardown = teardown + def __init__(self): options = webdriver.ChromeOptions() - # options.add_experimental_option('excludeSwitches', ['enable-logging']) + [options.add_argument(option) for option in config.CHROME_OPTIONS] super(Booking, self).__init__(options=options, service=ChromeService(ChromeDriverManager().install())) - # self.implicitly_wait(10) - - def accept_cookies(self): - self.find_element(By.ID, 'onetrust-accept-btn-handler').click() def __exit__(self, exc_type, exc_val, exc_tb): - if self.teardown: + if config.TEARDOWN: self.quit() def land_first_page(self): - self.get(const.BASE_URL) - - def change_currency(self, currency=None): - currency_element = self.find_element(By.CSS_SELECTOR, 'button[data-tooltip-text="Choose your currency"]') - currency_element.click() + self.get(config.BASE_URL) - selected_currency_element = self.find_element( - By.CSS_SELECTOR, f'a[data-modal-header-async-url-param*="selected_currency={currency}"]') - selected_currency_element.click() + def accept_cookies(self): + self.find_element(By.ID, 'onetrust-accept-btn-handler').click() def select_place_to_go(self, place_to_go): search_field = self.find_element(By.NAME, 'ss') @@ -47,19 +34,16 @@ def select_place_to_go(self, place_to_go): search_field.clear() results_list_locator = (By.XPATH, "//ul") - popular_destinations_locator = (By.XPATH, "//span[contains(text(), 'Popular destinations nearby')]") + popular_destinations_locator = (By.XPATH, "//div[contains(text(), 'Popular destinations nearby')]") search_field.send_keys(place_to_go) - # search_field.click() - get_webdriver_wait(self, Timeout.MEDIUM).until(EC.visibility_of_element_located(results_list_locator)) - get_webdriver_wait(self, Timeout.MEDIUM).until(EC.invisibility_of_element_located(popular_destinations_locator)) + WebDriverWait(self, MEDIUM_TIMEOUT).until(EC.visibility_of_element_located(results_list_locator)) + WebDriverWait(self, MEDIUM_TIMEOUT).until(EC.invisibility_of_element_located(popular_destinations_locator)) first_result_locator = locate_with(By.XPATH, "//li[1]").below({By.NAME: 'ss'}) self.find_element(first_result_locator).click() def select_dates(self, check_in_date, check_out_date): - # accommodation_element = self.find_element(By.ID, "accommodation") - # accommodation_element.click() - check_in_date_element = get_webdriver_wait(self, Timeout.MEDIUM).until( + check_in_date_element = WebDriverWait(self, MEDIUM_TIMEOUT).until( EC.visibility_of_element_located((By.XPATH, f'//*[@data-date="{check_in_date}"]'))) check_in_date_element.click() @@ -74,9 +58,7 @@ def apply_star_rating(self, *star_values): star_filtration_box = self.find_element(By.XPATH, "//div[contains(@id, 'filter_group_class')]") star_child_elements = star_filtration_box.find_elements(By.CSS_SELECTOR, '*') - # todo optimize this loop maybe for star_value in star_values: - for star_element in star_child_elements: if str(star_element.get_attribute('innerHTML')).strip() == f'{star_value} stars': scroll_into_view_with_js(self, star_element) @@ -86,7 +68,7 @@ def sort_price_lowest_first(self): sort_by_element = self.find_element(By.XPATH, "//button[@data-testid='sorters-dropdown-trigger']") sort_by_element.click() lowest_price_locator = (By.XPATH, "//span[contains(text(), 'Price') and contains(text(), 'lowest')]") - lowest_price_element = get_webdriver_wait(self, Timeout.MEDIUM).until( + lowest_price_element = WebDriverWait(self, MEDIUM_TIMEOUT).until( EC.element_to_be_clickable(lowest_price_locator)) lowest_price_element.click() diff --git a/booking/config.py b/booking/config.py index 40b4908..8c3fe65 100644 --- a/booking/config.py +++ b/booking/config.py @@ -1,7 +1,28 @@ -from enum import Enum +TEARDOWN = False + +BASE_URL = "https://booking.com" + +# use capital letter abbreviations, i.e. 'USD', 'EUR' etc. +CURRENCY = "USD" +PLACE_TO_GO = "warsaw" + +# must be in 'YYYY-MM-DD' format +CHECK_IN_DATE = "2023-02-20" +CHECK_OUT_DATE = "2023-02-24" + +# list of ints between 2 and 5 +PREFERRED_STAR_RATINGS = [4, 5] + +# to use with explicit/conditional waits, defined in seconds +SHORT_TIMEOUT = 0.5 +MEDIUM_TIMEOUT = 5. +LONG_TIMEOUT = 10. + +CHROME_OPTIONS = [ + # '--start-fullscreen' +] + + + -class Timeout(Enum): - SHORT = 0.5 - MEDIUM = 5. - LONG = 5. diff --git a/booking/constants.py b/booking/constants.py deleted file mode 100644 index 5ea7013..0000000 --- a/booking/constants.py +++ /dev/null @@ -1 +0,0 @@ -BASE_URL = "https://www.booking.com" \ No newline at end of file diff --git a/booking/filtration.py b/booking/filtration.py deleted file mode 100644 index bef7e4f..0000000 --- a/booking/filtration.py +++ /dev/null @@ -1,35 +0,0 @@ -# This file will include a class with instance methods. -# That will be responsible to interact with our website -# After we have some results, to apply filtrations. -from selenium.webdriver.common.by import By -from selenium.webdriver.remote.webdriver import WebDriver - -from selenium.webdriver.support import expected_conditions as EC - -from booking.helpers import scroll_into_view_with_js, get_webdriver_wait -from config import Timeout - - -class Filtration: - def __init__(self, driver: WebDriver): - self.driver = driver - - def apply_star_rating(self, *star_values): - star_filtration_box = self.driver.find_element(By.XPATH, "//div[contains(@id, 'filter_group_class')]") - star_child_elements = star_filtration_box.find_elements(By.CSS_SELECTOR, '*') - - # todo optimize this loop maybe - for star_value in star_values: - - for star_element in star_child_elements: - if str(star_element.get_attribute('innerHTML')).strip() == f'{star_value} stars': - scroll_into_view_with_js(self.driver, star_element) - star_element.click() - - def sort_price_lowest_first(self): - sort_by_element = self.driver.find_element(By.XPATH, "//button[@data-testid='sorters-dropdown-trigger']") - sort_by_element.click() - lowest_price_locator = (By.XPATH, "//span[contains(text(), 'Price') and contains(text(), 'lowest')]") - lowest_price_element = get_webdriver_wait(self.driver, Timeout.MEDIUM).until( - EC.element_to_be_clickable(lowest_price_locator)) - lowest_price_element.click() diff --git a/booking/helpers.py b/booking/helpers.py deleted file mode 100644 index 2a6ec39..0000000 --- a/booking/helpers.py +++ /dev/null @@ -1,19 +0,0 @@ -from selenium.webdriver.support.wait import WebDriverWait - -from booking.config import Timeout - - -def get_webdriver_wait(driver, timeout: Timeout) -> WebDriverWait: - return WebDriverWait(driver, timeout.value) - - -def scroll_into_view_with_js(driver, element): - driver.execute_script("arguments[0].scrollIntoView();", element) - - -def highlight_element(driver, element): - original_style = element.get_attribute("style") - driver.execute_script("arguments[0].setAttribute(arguments[1], arguments[2])", - element, - "style", - "border: 2px solid red;") diff --git a/booking/js_utils.py b/booking/js_utils.py new file mode 100644 index 0000000..5c3d531 --- /dev/null +++ b/booking/js_utils.py @@ -0,0 +1,8 @@ +def scroll_into_view_with_js(driver, element): + driver.execute_script("arguments[0].scrollIntoView();", element) + + +# to be used for testing or debugging +def highlight_element(driver, element): + driver.execute_script("arguments[0].setAttribute(arguments[1], arguments[2])", element, + "style", "border: 2px solid red;") diff --git a/run.py b/run.py index bbbd12f..9be5fb8 100644 --- a/run.py +++ b/run.py @@ -1,19 +1,19 @@ from prettytable import PrettyTable +from booking import config from booking.booking import Booking def run(): with Booking() as bot: bot.land_first_page() - # bot.change_currency(currency='USD') bot.accept_cookies() - bot.select_place_to_go("warsaw") - bot.select_dates(check_in_date="2023-02-20", check_out_date="2023-02-23") + bot.select_place_to_go(config.PLACE_TO_GO) + bot.select_dates(check_in_date=config.CHECK_IN_DATE, check_out_date=config.CHECK_OUT_DATE) bot.click_search() - bot.apply_star_rating(4, 5) + bot.apply_star_rating(*config.PREFERRED_STAR_RATINGS) bot.sort_price_lowest_first() - bot.refresh() # A workaround to let our bot to grab the data properly + bot.refresh() results = bot.extract_results() From d43c0ba3721c4a252a7806342ec7c6d428877221 Mon Sep 17 00:00:00 2001 From: Kosma <56829298+kosma-hyzoe@users.noreply.github.com> Date: Fri, 10 Feb 2023 20:44:47 +0100 Subject: [PATCH 17/22] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 81d7dc8..b25f21b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Updated booking.com bot -Jim from JimShapedCoding made this as an example for his Selenium tutorials -(featured on FreeCodeCamp's YT channel). The bot no longer works, -so I decided to fork and update it. This fork only features the final iteration -of the project. +Jim from [JimShapedCoding](https://www.youtube.com/channel/UCU8d7rcShA7MGuDyYH1aWGg) +made this bot as an example for his Selenium tutorials. The bot no longer works, +so I decided to fork and update it. I'm by no means an expert, +but fairly happy with the code - hopefully someone will find it useful. From 61197cd4e7a10e42ec7e65b3f3f8e32e3ccc0976 Mon Sep 17 00:00:00 2001 From: kosma-hyzoe Date: Fri, 10 Feb 2023 21:30:17 +0100 Subject: [PATCH 18/22] requirements update + final touches --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++------ booking/booking.py | 23 +++++++++++++------- booking/config.py | 3 ++- run.py | 10 ++++----- 4 files changed, 70 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 0aa12c6..5d27430 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,56 @@ # Updated booking.com bot -Jim from JimShapedCoding made this as an example for his Selenium tutorials -(featured on FreeCodeCamp's YT channel). The bot no longer works, -so I decided to fork and update it. This fork only features the final iteration -of the project. +Jim from [JimShapedCoding](https://www.youtube.com/channel/UCU8d7rcShA7MGuDyYH1aWGg) +made this bot as an example for his Selenium tutorials. The bot no longer works, +so I decided to fork and update it. I'm by no means an expert, +but fairly happy with the code - hopefully someone will find it useful. -## ISSUES AND LIMITATIONS +* [Updated booking.com bot](#updated-bookingcom-bot) +* [Issues and limitations](#issues-and-limitations) +* [Major changes](#major-changes) +* [Improvements](#improvements) +* [See also](#see-also) + -1. I skipped selecting currency and no. of people altogether. +## Issues and limitations +I skipped selecting currency and selecting the number of people altogether. Booking.com seems to have some terrible, unstable locators - not sure if it was better two years ago, but now some elements seem impossible to navigate +without sacrificing readability. I might give it another try with +CSS selectors once I have more experience with them. +The bot works around 7-8/10 times - the "*Where are you going?*" and +"*check-in - check-out*" dropdowns still misbehave sometimes. I suppose +Selenium actions could fix that. + +## Major changes + +1. Uses Selenium 4! +1. No inputs. A descriptive Python config file with some extra settings instead + (`booking/config.py`) +1. Explicit, conditional, configurable waits +1. Most Jim's locators no longer worked, so I used different ones - mostly + xpath if By.ID and By.NAME failed +1. I used [webdriver_manager](https://github.com/SergeyPirogov/webdriver_manager). + No need to download chrome drivers manually! + + +## Improvements + +1. A method for accepting cookies +1. Simplified and moved methods from `Filtration.py` and `Raport.py` +1. Conditional waits in `select_place_to_go()` prevent the driver + from accidentally choosing from "*Popular destinations nearby*" +1. The bot can now handle problematic deals marked as "*New to booking.com*" + (with not enough ratings) +1. Star rating checkboxes are "scrolled to" with JavaScript to avoid errors +1. Proper `requirements.txt` +1. A relative locator for the first location/place to go result +1. An extra `highligh_element()` function in `booking/js_utils.py`. I recommend + using it when testing or debugging. + + +## See also + +* Jim's tutorial was also feetured on [FreeCodeCamp's YT channel](https://www.youtube.com/watch?v=j7VZsCCnptM&t=4603s) diff --git a/booking/booking.py b/booking/booking.py index 15fcd8d..28622bc 100644 --- a/booking/booking.py +++ b/booking/booking.py @@ -1,5 +1,5 @@ from selenium import webdriver -from selenium.common import NoSuchElementException +from selenium.common import NoSuchElementException, TimeoutException from selenium.webdriver.support.relative_locator import locate_with from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By @@ -8,8 +8,8 @@ from webdriver_manager.chrome import ChromeDriverManager from booking import config -from booking.config import MEDIUM_TIMEOUT -from booking.js_utils import scroll_into_view_with_js, highlight_element +from booking.config import MEDIUM_TIMEOUT, SHORT_TIMEOUT +from booking.js_utils import scroll_into_view_with_js class Booking(webdriver.Chrome): @@ -25,9 +25,6 @@ def __exit__(self, exc_type, exc_val, exc_tb): def land_first_page(self): self.get(config.BASE_URL) - def accept_cookies(self): - self.find_element(By.ID, 'onetrust-accept-btn-handler').click() - def select_place_to_go(self, place_to_go): search_field = self.find_element(By.NAME, 'ss') search_field.click() @@ -41,6 +38,17 @@ def select_place_to_go(self, place_to_go): first_result_locator = locate_with(By.XPATH, "//li[1]").below({By.NAME: 'ss'}) self.find_element(first_result_locator).click() + # try: + # results_list_locator = (By.XPATH, "//ul") + # WebDriverWait(self, MEDIUM_TIMEOUT).until(EC.visibility_of_element_located(results_list_locator)) + # popular_destinations_locator = (By.XPATH, "//div[contains(text(), 'Popular destinations nearby')]") + # WebDriverWait(self, SHORT_TIMEOUT).until(EC.invisibility_of_element_located(popular_destinations_locator)) + # first_result_element = self.find_element(locate_with(By.XPATH, "//li[1]").below({By.NAME: 'ss'})) + # first_result_element.click() + # except TimeoutException: + # return + + def select_dates(self, check_in_date, check_out_date): check_in_date_element = WebDriverWait(self, MEDIUM_TIMEOUT).until( @@ -67,9 +75,8 @@ def apply_star_rating(self, *star_values): def sort_price_lowest_first(self): sort_by_element = self.find_element(By.XPATH, "//button[@data-testid='sorters-dropdown-trigger']") sort_by_element.click() - lowest_price_locator = (By.XPATH, "//span[contains(text(), 'Price') and contains(text(), 'lowest')]") lowest_price_element = WebDriverWait(self, MEDIUM_TIMEOUT).until( - EC.element_to_be_clickable(lowest_price_locator)) + EC.element_to_be_clickable((By.XPATH, "//span[contains(text(), 'Price') and contains(text(), 'lowest')]"))) lowest_price_element.click() def extract_results(self) -> list[list[str]]: diff --git a/booking/config.py b/booking/config.py index 8c3fe65..7f20d0d 100644 --- a/booking/config.py +++ b/booking/config.py @@ -18,8 +18,9 @@ MEDIUM_TIMEOUT = 5. LONG_TIMEOUT = 10. +# uncomment the line below to run in fullscreen and/or add other options CHROME_OPTIONS = [ - # '--start-fullscreen' + '--start-fullscreen' ] diff --git a/run.py b/run.py index 9be5fb8..9374266 100644 --- a/run.py +++ b/run.py @@ -4,12 +4,12 @@ from booking.booking import Booking -def run(): +def main(): with Booking() as bot: bot.land_first_page() - bot.accept_cookies() bot.select_place_to_go(config.PLACE_TO_GO) - bot.select_dates(check_in_date=config.CHECK_IN_DATE, check_out_date=config.CHECK_OUT_DATE) + bot.select_dates(check_in_date=config.CHECK_IN_DATE, + check_out_date=config.CHECK_OUT_DATE) bot.click_search() bot.apply_star_rating(*config.PREFERRED_STAR_RATINGS) bot.sort_price_lowest_first() @@ -21,10 +21,10 @@ def run(): def print_results(results): - table = PrettyTable(field_names=["Hotel Name", "Hotel Price", "Hotel Score"]) + table = PrettyTable(["Hotel Name", "Hotel Price", "Hotel Score"]) table.add_rows(results) print(table) if __name__ == '__main__': - run() + main() From 716cdc0ad168baf6fa20ca9cd9de924abcd8a68b Mon Sep 17 00:00:00 2001 From: Kosma <56829298+kosma-hyzoe@users.noreply.github.com> Date: Fri, 10 Feb 2023 21:37:28 +0100 Subject: [PATCH 19/22] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 5d27430..4ecc7de 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ made this bot as an example for his Selenium tutorials. The bot no longer works, so I decided to fork and update it. I'm by no means an expert, but fairly happy with the code - hopefully someone will find it useful. -* [Updated booking.com bot](#updated-bookingcom-bot) * [Issues and limitations](#issues-and-limitations) * [Major changes](#major-changes) * [Improvements](#improvements) From e69a126a52cfd6ad18204f7e6036b0188afe7736 Mon Sep 17 00:00:00 2001 From: Kosma <56829298+kosma-hyzoe@users.noreply.github.com> Date: Fri, 10 Feb 2023 21:41:27 +0100 Subject: [PATCH 20/22] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 4ecc7de..58de5df 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,6 @@ Selenium actions could fix that. ## Improvements -1. A method for accepting cookies 1. Simplified and moved methods from `Filtration.py` and `Raport.py` 1. Conditional waits in `select_place_to_go()` prevent the driver from accidentally choosing from "*Popular destinations nearby*" From e76566ab55803a701d5a6e63310e09daceba88a2 Mon Sep 17 00:00:00 2001 From: Kosma <56829298+kosma-hyzoe@users.noreply.github.com> Date: Fri, 10 Feb 2023 21:57:11 +0100 Subject: [PATCH 21/22] Update README.md --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 58de5df..fb6e555 100644 --- a/README.md +++ b/README.md @@ -31,9 +31,6 @@ Selenium actions could fix that. 1. Explicit, conditional, configurable waits 1. Most Jim's locators no longer worked, so I used different ones - mostly xpath if By.ID and By.NAME failed -1. I used [webdriver_manager](https://github.com/SergeyPirogov/webdriver_manager). - No need to download chrome drivers manually! - ## Improvements @@ -47,7 +44,8 @@ Selenium actions could fix that. 1. A relative locator for the first location/place to go result 1. An extra `highligh_element()` function in `booking/js_utils.py`. I recommend using it when testing or debugging. - +1. I used [webdriver_manager](https://github.com/SergeyPirogov/webdriver_manager). + No need to download chrome drivers manually! ## See also From bc6644595fbb8abcee0571e55eb291273a3ccbf0 Mon Sep 17 00:00:00 2001 From: Kosma <56829298+kosma-hyzoe@users.noreply.github.com> Date: Fri, 10 Feb 2023 22:09:54 +0100 Subject: [PATCH 22/22] Update README.md --- README.md | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fb6e555..ba92c2e 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,30 @@ # Updated booking.com bot -Jim from [JimShapedCoding](https://www.youtube.com/channel/UCU8d7rcShA7MGuDyYH1aWGg) -made this bot as an example for his Selenium tutorials. The bot no longer works, -so I decided to fork and update it. I'm by no means an expert, -but fairly happy with the code - hopefully someone will find it useful. +A booking.com webscraping bot using Selenium 4 and Python. + +* [How to run](#how-to-run) * [Issues and limitations](#issues-and-limitations) * [Major changes](#major-changes) * [Improvements](#improvements) * [See also](#see-also) +## Why + +Jim from [JimShapedCoding](https://www.youtube.com/channel/UCU8d7rcShA7MGuDyYH1aWGg) +made this bot as an example for his Selenium tutorials. The bot no longer works, +so I decided to fork and update it. I'm by no means an expert, +but fairly happy with the code - hopefully someone will find it useful. + +## How to run + +1. Make sure Chrome is installed +1. Install requirements from `requirements.txt` +1. Manipulate `booking/config.py` to change location, check in/out dates, + star rating filters and other settings +1. execute `python3 run.py` + ## Issues and limitations I skipped selecting currency and selecting the number of people altogether. @@ -31,6 +45,9 @@ Selenium actions could fix that. 1. Explicit, conditional, configurable waits 1. Most Jim's locators no longer worked, so I used different ones - mostly xpath if By.ID and By.NAME failed +1. I used [webdriver_manager](https://github.com/SergeyPirogov/webdriver_manager). + No need to download chrome drivers manually! + ## Improvements @@ -44,8 +61,7 @@ Selenium actions could fix that. 1. A relative locator for the first location/place to go result 1. An extra `highligh_element()` function in `booking/js_utils.py`. I recommend using it when testing or debugging. -1. I used [webdriver_manager](https://github.com/SergeyPirogov/webdriver_manager). - No need to download chrome drivers manually! + ## See also