Skip to content

Commit

Permalink
Merge pull request nicolomantini#19 from krapes/next-review-button-na…
Browse files Browse the repository at this point in the history
…vigation

Next review button navigation
  • Loading branch information
nicolomantini authored Jun 18, 2020
2 parents a156257 + 03dfab0 commit d923cd1
Show file tree
Hide file tree
Showing 5 changed files with 240 additions and 83 deletions.
144 changes: 144 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@

# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python

### Python ###
# 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/

# pytype static type analyzer
.pytype/

# End of https://www.toptal.com/developers/gitignore/api/python

# User files
output.csv
venv8/
quickstart.py
Binary file removed __pycache__/loginGUI.cpython-38.pyc
Binary file not shown.
99 changes: 67 additions & 32 deletions easyapplybot.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,32 @@
import tkinter.messagebox as tm
from urllib.request import urlopen
import loginGUI
from selenium import webdriver
from webdriver_manager.chrome import ChromeDriverManager

driver = webdriver.Chrome(ChromeDriverManager().install())

# pyinstaller --onefile --windowed --icon=app.ico easyapplybot.py

class EasyApplyBot:

MAX_APPLICATIONS = 500

def __init__(self,username,password, language, position, location, resumeloctn, appliedJobIDs, filename):
def __init__(self,username,password, language, position, location, resumeloctn, appliedJobIDs=[], filename='output.csv'):

print("\nWelcome to Easy Apply Bot\n")
dirpath = os.getcwd()
print("current directory is : " + dirpath)
# get path chromedriver exec for relevant OS
chromepath = dirpath + '/assets/chromedriver_%s' % (platform.system()).lower()
#foldername = os.path.basename(dirpath)
#print("Directory name is : " + foldername)


self.position = position
self.location = "&location=" + location
self.resumeloctn = resumeloctn
self.language = language
self.appliedJobIDs = appliedJobIDs
self.filename = filename
self.options = self.browser_options()
#self.browser = webdriver.Chrome()
#self.browser = webdriver.Chrome(executable_path = "C:/chromedriver_win32/chromedriver.exe")
self.browser = webdriver.Chrome(chrome_options=self.options, executable_path = chromepath)
self.browser = driver
self.wait = WebDriverWait(self.browser, 30)
self.start_linkedin(username,password)

Expand Down Expand Up @@ -93,9 +95,6 @@ def fill_data(self):
self.browser.set_window_position(2000, 2000)
os.system("reset")

self.position = position
self.location = "&location=" + location
self.resumeloctn = resumeloctn
print(self.resumeloctn)

def start_apply(self):
Expand Down Expand Up @@ -160,26 +159,28 @@ def applications_loop(self):
if button is not False:
string_easy = "* has Easy Apply Button"
button.click()
self.send_resume()
time.sleep (3)
result = self.send_resume()
count_application += 1
else:
string_easy = "* Doesn't have Easy Apply Button"
result = False

position_number = str(count_job + jobs_per_page)
print(f"\nPosition {position_number}:\n {self.browser.title} \n {string_easy} \n")

# append applied job ID to csv file
timestamp = datetime.datetime.now()
toWrite = [timestamp, jobID]
toWrite = [timestamp, jobID, str(self.browser.title).split(' | ')[0], str(self.browser.title).split(' | ')[1], button, result]
with open(self.filename,'a') as f:
writer = csv.writer(f)
writer.writerow(toWrite)

# sleep every 20 applications
if count_application % 20 == 0:
if count_application != 0 and count_application % 20 == 0:
sleepTime = random.randint(500, 900)
print('\n\n****************************************\n\n')
print('Time for a nap - see you in: ' + int(sleepTime/60) + 'min..')
print(f'\n\n********count_application: {count_application}************\n\n')
print(f"Time for a nap - see you in:{int(sleepTime/60)} min")
print('\n\n****************************************\n\n')
time.sleep (sleepTime)

Expand Down Expand Up @@ -253,30 +254,64 @@ def click_button(self, xpath):
time.sleep(1)

def send_resume(self):
def is_present(button_locator):
return len(self.browser.find_elements(button_locator[0],
button_locator[1])) > 0

try:
self.browser.find_element_by_xpath('//*[@id="file-browse-input"]').send_keys(self.resumeloctn)
submit_button = None
time.sleep(3)
while not submit_button:
if language == "en":
submit_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[aria-label='Submit application']")))
elif language == "es":
submit_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[aria-label='Enviar solicitud']")))
#submit_button = self.browser.find_element_by_xpath("//*[contains(text(), 'Enviar solicitud')]")
elif language == "pt":
submit_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[aria-label='Enviar candidatura']")))
#submit_button = self.browser.find_element_by_xpath("//*[contains(text(), 'Enviar candidatura')]")
submit_button.click()
#print(f"Navigating... ")
next_locater = (By.CSS_SELECTOR, "button[aria-label='Continue to next step']")
review_locater = (By.CSS_SELECTOR, "button[aria-label='Review your application']")
submit_locater = (By.CSS_SELECTOR, "button[aria-label='Submit application']")
submit_application_locator = (By.CSS_SELECTOR, "button[aria-label='Submit application']")
error_locator = (By.CSS_SELECTOR, "p[data-test-form-element-error-message='true']")

submitted = False
while True:
button = None
for i, button_locator in enumerate([next_locater, review_locater, submit_locater, submit_application_locator]):
#print(i)
if is_present(button_locator):
#print("button found")
button = self.wait.until(EC.element_to_be_clickable(button_locator))

if is_present(error_locator):
for element in self.browser.find_elements(error_locator[0],
error_locator[1]):
text = element.text
if "Please enter a valid answer" in text:
#print("Error Found")
#print(element.get_attribute('class'))
button = None
break
if button:
button.click()
time.sleep(random.uniform(1.5, 2.5))
if i in (2, 3):
submitted = True
break
if button == None:
print("Could not complete submission")
break
elif submitted:
print("Application Submitted")
break

time.sleep(random.uniform(1.5, 2.5))

#After submiting the application, a dialog shows up, we need to close this dialog
close_button = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[aria-label='Dismiss']")))
close_button_locator = (By.CSS_SELECTOR, "button[aria-label='Dismiss']")
if is_present(close_button_locator):
close_button = self.wait.until(EC.element_to_be_clickable(close_button_locator))
close_button.click()

close_button.click()

except :
except Exception as e:
print(e)
print("cannot apply to this job")
raise(e)

return submitted

def load_page(self, sleep=1):
scroll_page = 0
Expand Down
51 changes: 0 additions & 51 deletions quickstart.py

This file was deleted.

29 changes: 29 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
beautifulsoup4==4.9.1
bs4==0.0.1
certifi==2020.4.5.2
chardet==3.0.4
colorama==0.4.3
configparser==5.0.0
crayons==0.3.1
idna==2.9
lxml==4.5.1
MouseInfo==0.1.3
numpy==1.18.5
pandas==1.0.4
Pillow==7.1.2
PyAutoGUI==0.9.50
PyGetWindow==0.0.8
PyMsgBox==1.0.8
pyperclip==1.8.0
PyRect==0.1.4
PyScreeze==0.1.26
python-dateutil==2.8.1
python3-xlib==0.15
PyTweening==1.0.3
pytz==2020.1
requests==2.23.0
selenium==3.141.0
six==1.15.0
soupsieve==2.0.1
urllib3==1.25.9
webdriver-manager==3.1.0

0 comments on commit d923cd1

Please sign in to comment.