diff --git a/README.md b/README.md index e5e0206..310aa7b 100644 --- a/README.md +++ b/README.md @@ -26,8 +26,10 @@ locations: - # Location you want to search for - # A second location you want to search in -cover_letter_loctn: -- # '/home/PATH_TO_FILE' +uploads: + Resume: # PATH TO Resume + Cover Letter: # PATH TO cover letter + Photo: # PATH TO photo output_filename: - # PATH TO OUTPUT FILE (default output.csv) @@ -37,6 +39,11 @@ blacklist: ``` __NOTE: AFTER EDITING SAVE FILE, DO NOT COMMIT FILE__ +### Uploads + +There is no limit to the number of files you can list in the uploads section. +The program takes the titles from the input boxes and tries to match them with +list in the config file. ## Execute diff --git a/config.yaml b/config.yaml index 655292f..af5b02b 100644 --- a/config.yaml +++ b/config.yaml @@ -2,19 +2,23 @@ username: password: positions: -- # positions you want to search for +- Data Scientist - # Another position you want to search for - # A third position you want to search for locations: -- # Location you want to search for +- Remote - # A second location you want to search in -cover_letter_loctn: -- # '/home/PATH_TO_FILE' +# --------- Optional Parameters ------- +# uploads: +# Resume: # PATH TO Resume +# Cover Letter: # PATH TO cover letter +# Photo: # PATH TO photo -output_filename: -- # PATH TO OUTPUT FILE (default output.csv) -blacklist: -- # Company names you want to ignore \ No newline at end of file +# output_filename: +# - # PATH TO OUTPUT FILE (default output.csv) + +# blacklist: +# - # Company names you want to ignore \ No newline at end of file diff --git a/easyapplybot.py b/easyapplybot.py index d1a35ac..7c90ee8 100644 --- a/easyapplybot.py +++ b/easyapplybot.py @@ -26,11 +26,10 @@ class EasyApplyBot: MAX_SEARCH_TIME = 10*60 - def __init__(self, username, password, - cover_letter_loctn=None, + uploads={}, filename='output.csv', blacklist=[]): @@ -38,8 +37,9 @@ def __init__(self, dirpath = os.getcwd() print("current directory is : " + dirpath) - self.cover_letter_loctn = cover_letter_loctn - self.appliedJobIDs = self.get_appliedIDs(filename) if self.get_appliedIDs(filename) != None else [] + self.uploads = uploads + past_ids = self.get_appliedIDs(filename) + self.appliedJobIDs = past_ids if past_ids != None else [] self.filename = filename self.options = self.browser_options() self.browser = driver @@ -49,13 +49,12 @@ def __init__(self, def get_appliedIDs(self, filename): - print(filename) try: df = pd.read_csv(filename, header=None, names=['timestamp', 'jobID', 'job', 'company', 'attempted', 'result'], lineterminator='\n', - encoding = 'utf-8') + encoding='utf-8') df['timestamp'] = pd.to_datetime(df['timestamp'], format="%Y-%m-%d %H:%M:%S.%f") df = df[df['timestamp'] > (datetime.now() - timedelta(days=2))] @@ -91,13 +90,10 @@ def start_linkedin(self,username,password): except TimeoutException: print("TimeoutException! Username/password field or login button not found") - def fill_data(self): self.browser.set_window_size(0, 0) self.browser.set_window_position(2000, 2000) - print(self.cover_letter_loctn) - def start_apply(self, positions, locations): start = time.time() self.fill_data() @@ -164,7 +160,6 @@ def applications_loop(self, position, location): jobIDs = [x for x in IDs if x not in self.appliedJobIDs] after = len(jobIDs) - if len(jobIDs) == 0 and len(IDs) > 24: jobs_per_page = jobs_per_page + 25 count_job = 0 @@ -236,8 +231,7 @@ def re_extract(text, pattern): def get_job_page(self, jobID): - #root = 'www.linkedin.com' - #if root not in job: + job = 'https://www.linkedin.com/jobs/view/'+ str(jobID) self.browser.get(job) self.job_page = self.load_page(sleep=0.5) @@ -264,7 +258,6 @@ def is_present(button_locator): try: time.sleep(random.uniform(1.5, 2.5)) - #print(f"Navigating... ") next_locater = (By.CSS_SELECTOR, "button[aria-label='Continue to next step']") review_locater = (By.CSS_SELECTOR, @@ -275,17 +268,28 @@ def is_present(button_locator): "button[aria-label='Submit application']") error_locator = (By.CSS_SELECTOR, "p[data-test-form-element-error-message='true']") - cover_letter = (By.CSS_SELECTOR, "input[name='file']") + upload_locator = (By.CSS_SELECTOR, "input[name='file']") + + submitted = False while True: # Upload Cover Letter if possible - if is_present(cover_letter): - input_button = self.browser.find_elements(cover_letter[0], - cover_letter[1]) + if is_present(upload_locator): - input_button[0].send_keys(self.cover_letter_loctn) + input_buttons = self.browser.find_elements(upload_locator[0], + upload_locator[1]) + for input_button in input_buttons: + parent = input_button.find_element(By.XPATH, "..") + sibling = parent.find_element(By.XPATH, "preceding-sibling::*") + grandparent = sibling.find_element(By.XPATH, "..") + for key in self.uploads.keys(): + if key in sibling.text or key in grandparent.text: + input_button.send_keys(self.uploads[key]) + + + #input_button[0].send_keys(self.cover_letter_loctn) time.sleep(random.uniform(4.5, 6.5)) # Click Next or submitt button if possible @@ -367,6 +371,9 @@ def next_jobs_page(self, position, location, jobs_per_page): def finish_apply(self): self.browser.close() + + + if __name__ == '__main__': with open("config.yaml", 'r') as stream: @@ -382,13 +389,17 @@ def finish_apply(self): print(parameters) - cover_letter_loctn = parameters.get('cover_letter_loctn', [None])[0] - output_filename = parameters.get('output_filename', ['output.csv'])[0] + + output_filename = [f for f in parameters.get('output_filename', ['output.csv']) if f != None] + output_filename = output_filename[0] if len(output_filename) > 0 else 'output.csv' blacklist = parameters.get('blacklist', []) + uploads = parameters.get('uploads', {}) + for key in uploads.keys(): + assert uploads[key] != None bot = EasyApplyBot(parameters['username'], parameters['password'], - cover_letter_loctn=cover_letter_loctn, + uploads=uploads, filename=output_filename, blacklist=blacklist )