-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' of https://github.com/artursniegowski/FUN_PYTHO…
- Loading branch information
Showing
280 changed files
with
8,095 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
MY_EMAIL = "EXAMPLE.USER@gmail.com" | ||
GMAIL_APP_PASSWORD = "GMAIL_APP_PASSWORD" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Automated_Amazon_price_tracker | ||
|
||
This program will works like a bot, that in first step scrapes the data from a given Amazon website product, and looks for the price and title of this product. In the next step this program can be set to check the price once per a day, with the use of https://www.pythonanywhere.com/ , and if the price drops below the target value set by the user, it will send an email notification, to make sure we don't miss this bargain!</br> | ||
|
||
|
||
--- | ||
|
||
Necessary steps to make the program work:</br> | ||
|
||
1. Create a Gmail account that the program can use and generate an app_pssword for the account (https://help.prowly.com/how-to-create-use-gmail-app-passwords).</br> | ||
|
||
2. After creating the Gmail account, we have to change the name of .env.example to .env and define the environmental variables according to our account:</br> | ||
MY_EMAIL = "EXAMPLE.USER@gmail.com"</br> | ||
GMAIL_APP_PASSWORD = "GMAIL_APP_PASSWORD"</br> | ||
|
||
3. The user has to adjust the starting variables in the main.py:</br> | ||
*TARGET_PRICE* = 800.00 - target price needs to be adjusted, example 800.00 </br> | ||
*AMAZON_PRODUCT_TO_TRACK_URL* = "https://www.amazon.com/OnePlus-Smartphone-Unlocked-co-Developed-Hasselblad/dp/B07XM54RWB" - the Amazon url of the product we want to track, example onplus 10 url </br> | ||
*EMAIL_RECIVER* = "example@proton.me - the email where the program will send the notification. </br> | ||
*MY_HTTP_HEADER* = { } - define your header as dictionary, you can use http://myhttpheader.com/ to retrieve the data. This is needed to be able to read with request the Amazon url and to escape the captcha check. </br> | ||
|
||
|
||
|
||
Hint:</br> | ||
In order to find a good target price, we can use | ||
https://camelcamelcamel.com/</br> | ||
|
||
--- | ||
|
||
Example of received email:</br> | ||
![Screenshot](docs/img/01_example_email.png) | ||
|
||
--- | ||
|
||
|
||
**The program was developed using python 3.10.6, BeautifulSoup, requests, dotenv, smtplib** | ||
|
||
|
||
In order to run the program, you have to execute main.py. | ||
|
||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
# main program | ||
from web_scrapper import AmazonScrapper | ||
from notification_manager import NotificationManager | ||
|
||
# env variables ! | ||
from dotenv import load_dotenv | ||
load_dotenv() | ||
import os | ||
|
||
################################################################################ | ||
# SET THESE VARIABLES !! | ||
# target price | ||
TARGET_PRICE = 800.00 | ||
# COPY here your own url, amazon product url | ||
AMAZON_PRODUCT_TO_TRACK_URL = "https://www.amazon.com/OnePlus-Smartphone-Unlocked-co-Developed-Hasselblad/dp/B07XM54RWB" | ||
# email adress where we want to send the notification to, when the prices drops | ||
# below the given threshold | ||
# dont forget to adjust ! | ||
EMAIL_RECIVER = "example@proton.me"#"example@proton.me" | ||
# define your header | ||
# You'll need to pass along some headers in order for the request to return the actual website HTML | ||
# go to http://myhttpheader.com/ to check these values and update them accordingly. | ||
MY_HTTP_HEADER = { | ||
# "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36", | ||
"User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0", | ||
"Accept-Encoding":"gzip, deflate", | ||
"Accept-Language":"en-US,en;q=0.9,pl-PL;q=0.8,pl;q=0.7,de;q=0.6,la;q=0.5,ar;q=0.4", | ||
# "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", | ||
"Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", | ||
# "Connection" : "keep-alive", | ||
"DNT":"1", | ||
"Connection" : "close", | ||
"Upgrade-Insecure-Requests":"1", | ||
} | ||
# MY_HTTP_HEADER = { | ||
# "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:66.0) Gecko/20100101 Firefox/66.0", | ||
# "Accept-Encoding":"gzip, deflate", | ||
# "Accept-Language":"en-US,en;q=0.9,pl-PL;q=0.8,pl;q=0.7,de;q=0.6,la;q=0.5,ar;q=0.4", | ||
# "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", | ||
# "DNT":"1", | ||
# "Connection":"close", | ||
# "Upgrade-Insecure-Requests":"1" | ||
# } | ||
################################################################################ | ||
|
||
|
||
|
||
################################################################################ | ||
## sensitive data ### | ||
##################### | ||
# user defined email and app password - has to be GMAIL ! | ||
# env variables ! - dont change here ! | ||
MY_EMAIL = os.environ.get('MY_EMAIL') | ||
GMAIL_APP_PASSWORD = os.environ.get('GMAIL_APP_PASSWORD') | ||
################################################################################ | ||
|
||
|
||
|
||
|
||
# creating an email sender manager object for sending emails | ||
email_sender_manager = NotificationManager(email_app_password=GMAIL_APP_PASSWORD | ||
,email_from=MY_EMAIL) | ||
|
||
|
||
|
||
####################scraping the data from given amazon url##################### | ||
# creatign an object for scrapign data | ||
amazon_scraper = AmazonScrapper(AMAZON_PRODUCT_TO_TRACK_URL, headers = MY_HTTP_HEADER) | ||
product_price = amazon_scraper.return_the_price_of_product() | ||
product_title = amazon_scraper.return_the_title_of_product() | ||
# if price below our target price, send an email notification | ||
|
||
if product_price: # if product price found | ||
print(product_price) | ||
if product_price <= TARGET_PRICE: | ||
# creating message for the email | ||
message_content = f"{product_title}\nnow:${product_price}\n{AMAZON_PRODUCT_TO_TRACK_URL}" | ||
# sending an email | ||
email_sender_manager.send_gmail_mail(email_title="Amazon Price Alert!", | ||
message_to_send=message_content, | ||
email_to=EMAIL_RECIVER) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# This class is responsible for sending notifications. | ||
# class responsible for sending emails | ||
import smtplib | ||
|
||
class NotificationManager: | ||
""" | ||
managing the sending of emails with a gamil account | ||
This class is responsible for sending notifications with the deal flight details. | ||
""" | ||
def __init__(self, email_app_password: str, email_from: str) -> None: | ||
self.email_app_password = email_app_password | ||
self.email_from = email_from | ||
|
||
|
||
def send_gmail_mail(self, email_title: str, message_to_send: str, \ | ||
email_to: str) -> None: | ||
""" | ||
sends email from a gmail account | ||
""" | ||
# using encode utf-8 to escape the UnicodeEncodeError for specific characters | ||
email_message = lambda title, content: f"Subject:{title}\n\n{content}".encode('utf-8') | ||
|
||
with smtplib.SMTP("smtp.gmail.com", port=587) as connection: | ||
connection.starttls() # making the conection secure | ||
result = connection.login(user=self.email_from, password=self.email_app_password) | ||
connection.sendmail(from_addr=self.email_from, | ||
to_addrs=email_to, | ||
msg=email_message(title=email_title,content=message_to_send)) | ||
|
||
|
||
def send_emails(self, list_of_users : list[dict], email_title: str, \ | ||
message_to_send: str) -> None: | ||
""" | ||
send a email to a list of defined users | ||
""" | ||
for user in list_of_users: | ||
self.send_gmail_mail( | ||
email_title=email_title, | ||
message_to_send=message_to_send, | ||
email_to=user['email'] | ||
) |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Python 3.10.6 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# class for scraping data | ||
from bs4 import BeautifulSoup | ||
import requests | ||
import lxml | ||
|
||
class AmazonScrapper: | ||
def __init__(self, product_website_url: str, **kwargs) -> None: | ||
self.product_website_url = product_website_url | ||
self.response = requests.Response() | ||
self.soup = BeautifulSoup() | ||
self.headers = kwargs.get('headers', None) | ||
|
||
# init values | ||
self._get_raw_data_from_website() | ||
|
||
def _get_raw_data_from_website(self) -> None: | ||
""" | ||
returns raw data from the given website, | ||
the response will be populated with that data. | ||
the soup object will be poppulated with data | ||
The text() method of the Request interface reads the request body and | ||
returns it as a promise that resolves with a String. | ||
The response is always decoded using UTF-8. | ||
""" | ||
self.response = requests.get(url= self.product_website_url, | ||
headers=self.headers) | ||
self.response.raise_for_status() # check for any errors | ||
# # in case of encoding problems - if the encoding cant be resolved from the request | ||
# self.response.encoding = 'utf-8' | ||
|
||
# print(self.response.text) | ||
# getting bs4 / BeautifulSoup object | ||
# self.soup = BeautifulSoup(self.response.text, 'html.parser') | ||
self.soup = BeautifulSoup(self.response.text, "lxml") | ||
|
||
|
||
def return_the_price_of_product(self) -> float : | ||
""" | ||
return the price of the product as an float object, if found | ||
otherwise returns None | ||
""" | ||
price_element = self.soup.select_one(selector="div#corePrice_feature_div div.a-section span.a-price span.a-offscreen") | ||
|
||
if price_element: | ||
price = price_element.get_text().strip() | ||
try: | ||
price = float(price.split("$")[1]) | ||
# price = float(price[1:]) | ||
except SyntaxError: | ||
print(f"{price} : Issue with casting to float !!") | ||
else: | ||
return price | ||
|
||
return None | ||
|
||
|
||
def return_the_title_of_product(self) -> str: | ||
""" | ||
returns the title of the product, if it cant fidn it, | ||
it will return empty string | ||
""" | ||
|
||
title_element = self.soup.select_one(selector="div#titleSection h1#title span#productTitle") | ||
|
||
if title_element: | ||
title = title_element.get_text().strip() | ||
|
||
return title | ||
|
||
return "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
MY_LINKEDIN_EMAIL = "YOUR.EMAIL@gmail.com" | ||
MY_LINKEDIN_PASSWORD = "Your_password" |
Binary file added
BIN
+11.3 MB
Automating_job_applications_on_linkedin/ChromeDriver/chromedriver.exe
Binary file not shown.
2 changes: 2 additions & 0 deletions
2
Automating_job_applications_on_linkedin/ChromeDriver/notes.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
---------ChromeDriver 104.0.5112.79 (2022-08-03)--------- | ||
Supports Chrome version 104 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Automating_job_applications_on_linkedin | ||
|
||
This is an automated job application bot. The program will use selenium WebDriver | ||
to automate applying for jobs on LinkedIn with the "Easy Apply" function to send applications to all the jobs that meet your criteria (instead of just a single listing). | ||
In the first step, it will log in to your LinkedIn account, and then go to the specific search job URL defined in the program by the user. | ||
After accessing this URL, the bot will go through each of the application and asses if there is a function of Easy Apply. | ||
If it doesn't exist, the job application will be skipped, and the bot will move to the next job application. | ||
After finding the Easy Apply button, the bot will submit the first step of application by pressing it. | ||
In the next step, the bot will validate how the application form looks. | ||
If the application form will have the button next which indicates a multistep application, the bot will terminate the application, | ||
only application with "submit application" will be accepted. | ||
The bot will submit the application, and fill in the given phone number and send out the application. | ||
After that, the bot will move to the next job application. | ||
|
||
|
||
|
||
Necessary steps to make the program work:</br> | ||
1. Install Chrome browser https://www.google.com/intl/en_uk/chrome/ </br> | ||
2. Download chrome driver (don't forget to match the version of your chrome with the version of the chrome driver) https://chromedriver.storage.googleapis.com/index.html?path=104.0.5112.79/, and unzip the file for your OS. | ||
Mark the DIR to the chromedriver.exe file and adjust the *chrome_driver_path* in main.py. </br> | ||
3. Sign up to LinkedIn https://www.linkedin.com/ and configure your Profile. </br> | ||
Make sure you've signed up to LinkedIn and save your email and password somewhere for later use.</br> | ||
Upload your resume by going to Me -> Settings & Privacy -> Data Privacy -> Job Seeking Preferences -> Job Application Settings.</br> | ||
NOTE: Do not enable 2-factor authentication/phone number verification while we are using Selenium.</br> | ||
4. After creating the LinkedIn account, we have to change the name of .env.example to .env and define the environmental variables according to our account:</br> | ||
MY_LINKEDIN_EMAIL = "YOUR.EMAIL@gmail.com"</br> | ||
MY_LINKEDIN_PASSWORD = "Your_password"</br> | ||
5. The user has to adjust the starting variables in the main.py:</br> | ||
*PHONE_NUMBER* - phone number to be filled in the form during application.</br> | ||
*WEBSITE_URL_FOR_THE_DRIVER* - URL for job search.</br> | ||
|
||
|
||
--- | ||
|
||
**Example of view Easy Apply application:**</br> | ||
|
||
![Screenshot](docs/img/01_easy_apply.png)</br> | ||
|
||
|
||
**Example of view of submitting application:**</br> | ||
|
||
![Screenshot](docs/img/02_submiting_applicatio.png)</br> | ||
|
||
|
||
**Example of view of invalid form with a next button - multistep application is not supported:**</br> | ||
|
||
![Screenshot](docs/img/03_inalid_form_next.png)</br> | ||
|
||
--- | ||
|
||
|
||
**The program was developed using python 3.10.6, selenium** | ||
|
||
|
||
In order to run the program, you have to execute main.py. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+100 KB
Automating_job_applications_on_linkedin/docs/img/02_submiting_applicatio.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+62.9 KB
Automating_job_applications_on_linkedin/docs/img/03_inalid_form_next.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.