Skip to content
This repository was archived by the owner on Mar 1, 2020. It is now read-only.

Commit 17c5bc8

Browse files
authored
Merge pull request #1 from fabricio-aguiar/refac
Refactoring the code to become more readable
2 parents f7bf043 + dee1ff4 commit 17c5bc8

2 files changed

Lines changed: 214 additions & 160 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ This bot launches the chromedriver, waits the user login into LinkedIn account.
1818

1919
#### Installing ChromeDriver:
2020
* `sudo apt-get install chromium-chromedriver`
21-
* `export PATH=$PATH:/usr/lib/chromium-browser/`
21+
* `echo 'export PATH=$PATH:/usr/lib/chromium-browser/' >> ~/.bashrc`

easyapplybot.py

Lines changed: 213 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -5,173 +5,227 @@
55
from bs4 import BeautifulSoup
66
import pyautogui
77
from tkinter import filedialog, Tk
8+
import os
89

9-
APPLICATIONS = 30
1010

11+
class EasyApplyBot:
1112

12-
def getJobLinks(page):
13-
links = []
14-
for link in page.find_all('a'):
15-
url = link.get('href')
13+
MAX_APPLICATIONS = 30
1614

17-
if url:
18-
if '/jobs/view' in url:
19-
links.append(url)
20-
return links
15+
def __init__(self, language):
16+
self.language = language
17+
self.options = self.browser_options()
18+
self.browser = webdriver.Chrome(chrome_options=self.options)
19+
self.start_linkedin()
2120

21+
def browser_options(self):
22+
options = Options()
23+
options.add_argument("--start-maximized")
24+
options.add_argument("--ignore-certificate-errors")
25+
options.add_argument("user-agent=Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393")
26+
return options
2227

23-
def getEasy(page):
24-
achou = page.find("button", class_="jobs-s-apply__button js-apply-button")
25-
return str(achou)
28+
def start_linkedin(self):
29+
self.browser.get("https://linkedin.com/uas/login")
2630

31+
def wait_for_login(self):
32+
if language == "en":
33+
title = "Sign In to LinkedIn"
34+
elif language == "pt":
35+
title = "Entrar no LinkedIn"
2736

28-
def loadPage(browser):
29-
tpage = 0
30-
while tpage < 4000:
31-
browser.execute_script("window.scrollTo(0,"+str(tpage)+" );")
32-
tpage += 200
3337
time.sleep(1)
34-
return BeautifulSoup(browser.page_source, "lxml")
35-
36-
37-
def avoidLock():
38-
x, y = pyautogui.position()
39-
pyautogui.moveTo(x+200, None, duration=1.0)
40-
pyautogui.moveTo(x, None, duration=0.5)
41-
pyautogui.keyDown('ctrl')
42-
pyautogui.press('esc')
43-
pyautogui.keyUp('ctrl')
44-
time.sleep(0.5)
45-
pyautogui.press('esc')
46-
47-
48-
def containEasy(browser, cargo, local, pp):
49-
browser.get(
50-
"https://www.linkedin.com/jobs/search/?f_LF=f_AL&keywords=" +
51-
cargo + local + "&start="+str(pp))
52-
avoidLock()
53-
loadPage(browser)
54-
return (browser, pp)
55-
56-
57-
def ViewBot(browser):
58-
count = 0
59-
count5 = 0
60-
pp = 0
61-
browser.set_window_size(0, 0)
62-
aux = input('Enter the desired job title : ')
63-
cargo = aux.replace(" ", "%20")
64-
print("Location codes: \n[1] GLOBAL\n[2] Country\n[3] State\n[4] City")
65-
aux = input('Enter the location code: ')
66-
if aux == "1":
67-
aux2 = "Worldwide"
68-
if aux == "2":
69-
aux2 = input('Enter the country name: ')
70-
if aux == "3":
71-
aux2 = input('Enter the state name: ')
72-
if aux == "4":
73-
aux2 = input('Enter the city name: ')
74-
local = "&location=" + aux2.replace(" ", "%20") + "&sortBy=DD"
75-
print("\nPlease select your curriculum\n")
76-
time.sleep(1)
77-
root = Tk()
78-
resumeloctn = filedialog.askopenfilename(parent=root, initialdir="/",
79-
title='Please select your curriculum')
80-
81-
root.destroy()
82-
83-
browser, pp = containEasy(browser, cargo, local, pp)
84-
85-
while count < APPLICATIONS:
86-
# sleep to make sure everything loads, add random to make us look human.
87-
time.sleep(random.uniform(3.5, 6.9))
88-
89-
page = BeautifulSoup(browser.page_source, 'lxml')
90-
91-
jobs = getJobLinks(page)
92-
if jobs:
93-
for job in jobs:
94-
root = 'http://www.linkedin.com'
95-
roots = 'https://www.linkedin.com'
96-
if root not in job or roots not in job:
97-
job = 'https://www.linkedin.com'+job
98-
browser.get(job)
99-
time.sleep(random.uniform(3.5, 4.5))
100-
print("\n" + str(count5 + pp + 1))
101-
pagina = BeautifulSoup(browser.page_source, "lxml")
102-
103-
tej = getEasy(pagina)
104-
ember = pagina.find("div",
105-
class_="jobs-s-apply--top-card jobs-s-apply--fadein inline-flex mr2 jobs-s-apply ember-view")
106-
count5 += 1
107-
108-
if len(tej) > 4:
109-
tej = "* Easy Apply Button"
110-
embert = str(ember)
111-
list_of_words = embert.split()
112-
next_word = [word for word in list_of_words if "ember" in word and "id" in word]
113-
pember = next_word[0][:-1]
114-
xpath = '//*[@'+pember+']/button'
115-
time.sleep(1.5)
116-
triggerDropDown = browser.find_element_by_xpath(xpath)
117-
time.sleep(0.5)
118-
triggerDropDown.click()
119-
time.sleep(1.5)
120-
browser.find_element_by_xpath('//*[@id="file-browse-input"]').send_keys(resumeloctn)
121-
time.sleep(random.uniform(10, 14))
122-
try:
123-
browser.find_element_by_xpath("//*[contains(text(), 'Submit application')]").click()
124-
except:
125-
browser.find_element_by_xpath("//*[contains(text(), 'Enviar candidatura')]").click()
126-
time.sleep(random.uniform(2, 3.5))
127-
count += 1
128-
129-
else:
130-
tej = "* NOT Easy Apply Button"
131-
132-
try:
133-
print("-> "+browser.title+"\n"+tej+" - Checked! \n")
134-
except:
135-
pass
136-
137-
if count5 == len(jobs):
138-
pp = pp + 25
139-
count5 = 0
140-
print("Going to next jobs page !")
141-
avoidLock()
142-
browser, pp = containEasy(browser, cargo, local, pp)
143-
else:
144-
print("I'm Lost Exiting")
145-
break
146-
147-
148-
def Main():
149-
print("\n\n ===== Please Login to your LinkedIn account =====\n\n")
150-
time.sleep(3)
151-
152-
options = Options()
153-
options.add_argument("--start-maximized")
154-
options.add_argument("--ignore-certificate-errors")
155-
options.add_argument("user-agent=Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393")
156-
browser = webdriver.Chrome(chrome_options=options)
157-
browser.get("https://linkedin.com/uas/login")
158-
time.sleep(8)
159-
160-
if "Sign In" in browser.title:
161-
message = "Sign In to LinkedIn"
162-
elif "Entrar" in browser.title:
163-
message = "Entrar no LinkedIn"
164-
165-
while True:
166-
if browser.title != message:
167-
print("\nStarting LinkedIn bot\n")
168-
break
169-
else:
170-
time.sleep(2)
171-
print("\nPlease Login to your LinkedIn account\n")
172-
173-
ViewBot(browser)
174-
browser.close()
38+
39+
while True:
40+
if self.browser.title != title:
41+
print("\nStarting LinkedIn bot\n")
42+
break
43+
else:
44+
time.sleep(1)
45+
print("\nPlease Login to your LinkedIn account\n")
46+
47+
def fill_data(self):
48+
self.browser.set_window_size(0, 0)
49+
self.browser.set_window_position(2000, 2000)
50+
os.system("reset")
51+
52+
position = input('Enter the desired job title : ')
53+
self.position = position.replace(" ", "%20")
54+
55+
print("Location codes: \n[1] GLOBAL\n[2] Country\n[3] State\n[4] City")
56+
location_code = input('Enter the location code: ')
57+
if location_code == "1":
58+
location = "Worldwide"
59+
elif location_code == "2":
60+
location = input('Enter the country name: ')
61+
elif location_code == "3":
62+
location = input('Enter the state name: ')
63+
elif location_code == "4":
64+
location = input('Enter the city name: ')
65+
self.location = "&location=" + location.replace(" ", "%20") + "&sortBy=DD"
66+
67+
print("\nPlease select your curriculum\n")
68+
time.sleep(1)
69+
root = Tk()
70+
self.resumeloctn = filedialog.askopenfilename(parent=root, initialdir="/",
71+
title='Please select your curriculum')
72+
73+
root.destroy()
74+
75+
def start_apply(self):
76+
self.wait_for_login()
77+
self.fill_data()
78+
self.applications_loop()
79+
80+
def applications_loop(self):
81+
count_application = 0
82+
count_job = 0
83+
jobs_per_page = 0
84+
85+
os.system("reset")
86+
87+
self.browser.set_window_position(0, 0)
88+
self.browser.maximize_window()
89+
self.browser, _ = self.next_jobs_page(jobs_per_page)
90+
91+
while count_application < self.MAX_APPLICATIONS:
92+
# sleep to make sure everything loads, add random to make us look human.
93+
time.sleep(random.uniform(3.5, 6.9))
94+
95+
page = BeautifulSoup(self.browser.page_source, 'lxml')
96+
97+
jobs = self.get_job_links(page)
98+
99+
if not jobs:
100+
print("Jobs not found")
101+
break
102+
103+
for job in jobs:
104+
count_job += 1
105+
job_page = self.get_job_page(job)
106+
107+
if self.got_easy_apply(job_page):
108+
string_easy = "* has Easy Apply Button"
109+
xpath = self.easy_apply_xpath()
110+
self.click_button(xpath)
111+
self.send_resume()
112+
count_application += 1
113+
114+
else:
115+
string_easy = "* Doesn't have Easy Apply Button"
116+
117+
position_number = str(count_job + jobs_per_page)
118+
print(f"\nPosition {position_number}:\n {self.browser.title} \n {string_easy} \n")
119+
120+
if count_job == len(jobs):
121+
jobs_per_page = jobs_per_page + 25
122+
count_job = 0
123+
print("Going to next jobs page !")
124+
self.avoid_lock()
125+
self.browser, jobs_per_page = self.next_jobs_page(jobs_per_page)
126+
127+
self.finish_apply()
128+
129+
def get_job_links(self, page):
130+
links = []
131+
for link in page.find_all('a'):
132+
url = link.get('href')
133+
134+
if url:
135+
if '/jobs/view' in url:
136+
links.append(url)
137+
return set(links)
138+
139+
def get_job_page(self, job):
140+
root = 'www.linkedin.com'
141+
if root not in job:
142+
job = 'https://www.linkedin.com'+job
143+
self.browser.get(job)
144+
self.job_page = self.load_page(sleep=0.5)
145+
return self.job_page
146+
147+
def got_easy_apply(self, page):
148+
button = page.find("button", class_="jobs-s-apply__button js-apply-button")
149+
return len(str(button)) > 4
150+
151+
def get_easy_apply_button(self):
152+
button_class = "jobs-s-apply--top-card jobs-s-apply--fadein inline-flex mr2 jobs-s-apply ember-view"
153+
button = self.job_page.find("div", class_=button_class)
154+
return button
155+
156+
def easy_apply_xpath(self):
157+
button = self.get_easy_apply_button()
158+
button_inner_html = str(button)
159+
list_of_words = button_inner_html.split()
160+
next_word = [word for word in list_of_words if "ember" in word and "id" in word]
161+
ember = next_word[0][:-1]
162+
xpath = '//*[@'+ember+']/button'
163+
return xpath
164+
165+
def click_button(self, xpath):
166+
triggerDropDown = self.browser.find_element_by_xpath(xpath)
167+
time.sleep(0.5)
168+
triggerDropDown.click()
169+
time.sleep(1.5)
170+
171+
def send_resume(self):
172+
self.browser.find_element_by_xpath('//*[@id="file-browse-input"]').send_keys(self.resumeloctn)
173+
submit_button = None
174+
time.sleep(1)
175+
while not submit_button:
176+
if language == "en":
177+
submit_button = self.browser.find_element_by_xpath("//*[contains(text(), 'Submit application')]")
178+
elif language == "pt":
179+
submit_button = self.browser.find_element_by_xpath("//*[contains(text(), 'Enviar candidatura')]")
180+
submit_button.click()
181+
time.sleep(random.uniform(1.5, 2.5))
182+
183+
def load_page(self, sleep=1):
184+
scroll_page = 0
185+
while scroll_page < 4000:
186+
self.browser.execute_script("window.scrollTo(0,"+str(scroll_page)+" );")
187+
scroll_page += 200
188+
time.sleep(sleep)
189+
190+
if sleep != 1:
191+
self.browser.execute_script("window.scrollTo(0,0);")
192+
time.sleep(sleep * 3)
193+
194+
page = BeautifulSoup(self.browser.page_source, "lxml")
195+
return page
196+
197+
def avoid_lock(self):
198+
x, _ = pyautogui.position()
199+
pyautogui.moveTo(x+200, None, duration=1.0)
200+
pyautogui.moveTo(x, None, duration=0.5)
201+
pyautogui.keyDown('ctrl')
202+
pyautogui.press('esc')
203+
pyautogui.keyUp('ctrl')
204+
time.sleep(0.5)
205+
pyautogui.press('esc')
206+
207+
def next_jobs_page(self, jobs_per_page):
208+
self.browser.get(
209+
"https://www.linkedin.com/jobs/search/?f_LF=f_AL&keywords=" +
210+
self.position + self.location + "&start="+str(jobs_per_page))
211+
self.avoid_lock()
212+
self.load_page()
213+
return (self.browser, jobs_per_page)
214+
215+
def finish_apply(self):
216+
self.browser.close()
217+
175218

176219
if __name__ == '__main__':
177-
Main()
220+
221+
print("\nEasy Apply Bot\n")
222+
languages = ["pt", "en"]
223+
language = None
224+
225+
while language not in languages:
226+
language = input("LinkedIn language [pt/en]: ")
227+
if language not in languages:
228+
print("Please, enter a valid language!\n")
229+
230+
bot = EasyApplyBot(language)
231+
bot.start_apply()

0 commit comments

Comments
 (0)