diff --git a/main.py b/main.py index 0232efa..13bea62 100644 --- a/main.py +++ b/main.py @@ -3,7 +3,7 @@ from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.common.by import By -from selenium.common.exceptions import NoSuchElementException, ElementClickInterceptedException, NoSuchElementException +from selenium.common.exceptions import TimeoutException, NoSuchElementException, ElementClickInterceptedException, NoSuchElementException from selenium.webdriver.common.action_chains import ActionChains import time import re @@ -27,10 +27,10 @@ def login_linkedin(self): self.driver.get("https://www.linkedin.com/login") # introduce email and password and hit enter - login_email = self.driver.find_element_by_name('session_key') + login_email = self.driver.find_element(By.NAME, 'session_key') login_email.clear() login_email.send_keys(self.email) - login_pass = self.driver.find_element_by_name('session_password') + login_pass = self.driver.find_element(By.NAME, 'session_password') login_pass.clear() login_pass.send_keys(self.password) login_pass.send_keys(Keys.RETURN) @@ -39,49 +39,46 @@ def job_search(self): """This function goes to the 'Jobs' section a looks for all the jobs that matches the keywords and location""" # go to Jobs - jobs_link = self.driver.find_element_by_link_text('Jobs') + jobs_link = self.driver.find_element(By.LINK_TEXT, 'Jobs') jobs_link.click() # search based on keywords and location and hit enter - search_keywords = self.driver.find_element_by_css_selector(".jobs-search-box__text-input[aria-label='Search jobs']") + search_keywords = WebDriverWait(self.driver, 20).until(EC.presence_of_element_located((By.CSS_SELECTOR, ".jobs-search-box__text-input[aria-label='Search by title, skill, or company']"))) search_keywords.clear() search_keywords.send_keys(self.keywords) - search_location = self.driver.find_element_by_css_selector(".jobs-search-box__text-input[aria-label='Search location']") + search_location = self.driver.find_element(By.CSS_SELECTOR, ".jobs-search-box__text-input[aria-label='City, state, or zip code']") search_location.clear() search_location.send_keys(self.location) - search_location.send_keys(Keys.RETURN) + # Added this to get it to search for me + time.sleep(2) + search_keywords.send_keys(Keys.RETURN) def filter(self): """This function filters all the job results by 'Easy Apply'""" - # select all filters, click on Easy Apply and apply the filter - all_filters_button = self.driver.find_element_by_xpath("//button[@data-control-name='all_filters']") + # select Easy Apply filter button + all_filters_button = WebDriverWait(self.driver, 20).until(EC.presence_of_element_located((By.XPATH, "//button[contains(@class, 'artdeco-pill artdeco-pill--slate artdeco-pill--2 artdeco-pill--choice ember-view search-reusables__filter-pill-button')]"))) all_filters_button.click() time.sleep(1) - easy_apply_button = self.driver.find_element_by_xpath("//label[@for='f_LF-f_AL']") - easy_apply_button.click() - time.sleep(1) - apply_filter_button = self.driver.find_element_by_xpath("//button[@data-control-name='all_filters_apply']") - apply_filter_button.click() def find_offers(self): """This function finds all the offers through all the pages result of the search and filter""" # find the total amount of results (if the results are above 24-more than one page-, we will scroll trhough all available pages) - total_results = self.driver.find_element_by_class_name("display-flex.t-12.t-black--light.t-normal") + total_results = self.driver.find_element(By.CSS_SELECTOR, ".display-flex.t-normal.t-12.t-black--light.jobs-search-results-list__text") total_results_int = int(total_results.text.split(' ',1)[0].replace(",","")) print(total_results_int) time.sleep(2) # get results for the first page current_page = self.driver.current_url - results = self.driver.find_elements_by_class_name("occludable-update.artdeco-list__item--offset-4.artdeco-list__item.p0.ember-view") + results = self.driver.find_elements(By.CSS_SELECTOR, ".job-card-container.relative") # for each job add, submits application if no questions asked for result in results: hover = ActionChains(self.driver).move_to_element(result) hover.perform() - titles = result.find_elements_by_class_name('job-card-search__title.artdeco-entity-lockup__title.ember-view') + titles = result.find_elements(By.CSS_SELECTOR, '.artdeco-entity-lockup__title.ember-view') for title in titles: self.submit_apply(title) @@ -90,10 +87,10 @@ def find_offers(self): time.sleep(2) # find the last page and construct url of each page based on the total amount of pages - find_pages = self.driver.find_elements_by_class_name("artdeco-pagination__indicator.artdeco-pagination__indicator--number") + find_pages = self.driver.find_elements(By.CLASS_NAME, "artdeco-pagination__indicator.artdeco-pagination__indicator--number.ember-view") total_pages = find_pages[len(find_pages)-1].text total_pages_int = int(re.sub(r"[^\d.]", "", total_pages)) - get_last_page = self.driver.find_element_by_xpath("//button[@aria-label='Page "+str(total_pages_int)+"']") + get_last_page = self.driver.find_element(By.XPATH, "//button[@aria-label='Page "+str(total_pages_int)+"']") get_last_page.send_keys(Keys.RETURN) time.sleep(2) last_page = self.driver.current_url @@ -103,11 +100,11 @@ def find_offers(self): for page_number in range(25,total_jobs+25,25): self.driver.get(current_page+'&start='+str(page_number)) time.sleep(2) - results_ext = self.driver.find_elements_by_class_name("occludable-update.artdeco-list__item--offset-4.artdeco-list__item.p0.ember-view") + results_ext = self.driver.find_elements(By.CSS_SELECTOR, ".job-card-container.relative") for result_ext in results_ext: hover_ext = ActionChains(self.driver).move_to_element(result_ext) hover_ext.perform() - titles_ext = result_ext.find_elements_by_class_name('job-card-search__title.artdeco-entity-lockup__title.ember-view') + titles_ext = result_ext.find_elements(By.CSS_SELECTOR, '.artdeco-entity-lockup__title.ember-view') for title_ext in titles_ext: self.submit_apply(title_ext) else: @@ -122,7 +119,7 @@ def submit_apply(self,job_add): # click on the easy apply button, skip if already applied to the position try: - in_apply = self.driver.find_element_by_xpath("//button[@data-control-name='jobdetails_topcard_inapply']") + in_apply = self.driver.find_element(By.CLASS_NAME, "jobs-apply-button") in_apply.click() except NoSuchElementException: print('You already applied to this job, go to next...') @@ -131,21 +128,24 @@ def submit_apply(self,job_add): # try to submit if submit application is available... try: - submit = self.driver.find_element_by_xpath("//button[@data-control-name='submit_unify']") + submit = self.driver.find_element(By.XPATH, "//button[@aria-label='Submit application']") submit.send_keys(Keys.RETURN) + done_button = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//button[.//span[text()='Done']]"))) + done_button.click() # ... if not available, discard application and go to next except NoSuchElementException: print('Not direct application, going to next...') try: - discard = self.driver.find_element_by_xpath("//button[@data-test-modal-close-btn]") + wait = WebDriverWait(self.driver, 5) + discard = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@data-test-modal-close-btn]"))) discard.send_keys(Keys.RETURN) time.sleep(1) - discard_confirm = self.driver.find_element_by_xpath("//button[@data-test-dialog-primary-btn]") + discard_confirm = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@data-test-dialog-secondary-btn]"))) discard_confirm.send_keys(Keys.RETURN) time.sleep(1) - except NoSuchElementException: - pass + except TimeoutException: + print('Could not find the close button, skipping...') time.sleep(1) @@ -176,4 +176,4 @@ def apply(self): data = json.load(config_file) bot = EasyApplyLinkedin(data) - bot.apply() \ No newline at end of file + bot.apply()