├── .gitignore ├── README.md ├── db.py ├── requirements.txt ├── search.py ├── server.py ├── static ├── css │ ├── main.css │ └── results.css ├── email.html └── index.html └── templates └── results.html /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # macos 107 | .DS_Store 108 | 109 | # extras 110 | sec.txt 111 | emailer.py 112 | .idea/ 113 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reddit-Job-Search 2 | Junter is a simple to use, free and open source web application to find job opportunities on Reddit. 3 | 4 | I built this when I was looking for a job as a python developer, Reddit is a great place to find jobs, but there are a lot of subreddits to check for 5 | and it's hard to search all these subs, and filter jobs by keywords. 6 | 7 | Junter uses Reddit API through PRAW and Flask on the backend, standard Jinja 2 is used for rendering (moving to VueJS/React/Angular) would make it faster 8 | 9 | So I decided to build Junter to make this easier for everyone, enjoy :D 10 | 11 | ## Screenshots: 12 | ![Homepage](https://i.imgur.com/1Q567qQ.png) 13 | ![Results page](https://i.imgur.com/p9UYjao.png) 14 | 15 | 16 | ## Setting Up: 17 | 18 | First, create a Reddit app and get the client ID and secret key, clone this project (or fork to add other features). 19 | 20 | Install the requirements : `pip install -r requirements.txt` , main dependencies are Flask and PRAW . 21 | 22 | ## Authentication: 23 | 24 | Grab the client ID and secret key, and put it in the search.py file, you will find two strings : client and secret (should be on the 20th and 21st line), paste your keys there 25 | 26 | ## Launching: 27 | 28 | You should be ready, just fire up the terminal and run `python server.py` and the web app should work . 29 | 30 | ## Some Suggestion: 31 | 32 | - Add craiglist scraping 33 | 34 | - Search jobs on Twitter 35 | 36 | - Search jobs on facebook groups 37 | 38 | - Make it automated and store jobs in a database to make search faster, scraper should run every hour to get up-to date results 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /db.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2022.12.7 2 | chardet==3.0.4 3 | click==6.7 4 | Flask==1.0.2 5 | idna==2.7 6 | itsdangerous==0.24 7 | Jinja2==2.10 8 | MarkupSafe==1.0 9 | praw==5.4.0 10 | prawcore==0.14.0 11 | requests==2.19.1 12 | update-checker==0.16 13 | urllib3==1.23 14 | Werkzeug==0.14.1 15 | -------------------------------------------------------------------------------- /search.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import praw 3 | import timeago 4 | 5 | 6 | class Job(): 7 | def __init__(self,post): 8 | self.title = post.title 9 | self.date = compute_ago(post.created_utc) 10 | self.url = post.url 11 | 12 | 13 | def compute_ago(created): 14 | date = datetime.datetime.fromtimestamp(int(float(str(created)))) 15 | now = datetime.datetime.now() 16 | print (timeago.format(date, now)) 17 | return timeago.format(date, now) 18 | 19 | 20 | client = "" # Client ID 21 | secret = "" # Secret key 22 | 23 | # Function to connect to Reddit API 24 | def connect(cl_id, cl_secret): 25 | return praw.Reddit(client_id = cl_id, client_secret = cl_secret, user_agent = "Job Finder by Hugeburger") 26 | 27 | def search_reddit(query): 28 | red = connect(client, secret) 29 | # Generating the string for keywords 30 | keys = query.split(",") 31 | subs = ["jobbit", "forhire"] 32 | 33 | # Generate string for keys 34 | str_keys = "(title : \"" +keys[0]+ "\"" 35 | for k in keys[1:]: 36 | str_keys = str_keys + " OR title:\"" + k + "\"" 37 | str_keys = str_keys + ")" 38 | 39 | # Generate string for subs 40 | str_subs = "(subreddit:hiring" 41 | for sub in subs: 42 | str_subs = str_subs + " OR subreddit:" + sub 43 | str_subs = str_subs + ") " 44 | 45 | # Creating the full search query using Reddit search syntax 46 | full_str = "(title:\"hiring\" OR flair:Hiring) AND " + str_keys + " AND " + str_subs 47 | 48 | # making the call 49 | all = red.subreddit("all") 50 | """ 51 | for post in all.search(query = full_str, sort = "new"): 52 | print post.title 53 | post_date = datetime.datetime.utcfromtimestamp(post.created_utc) 54 | print str(post_date.month) + ", " + str(post_date.day) + " in " + str(post_date.year) 55 | """ 56 | return all.search(query = full_str, sort = "new") 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /server.py: -------------------------------------------------------------------------------- 1 | from flask import * 2 | from search import * 3 | from flask_login.login_manager import * 4 | 5 | 6 | app = Flask(__name__) 7 | 8 | 9 | @app.route("/") 10 | def home(): 11 | return app.send_static_file("index.html") 12 | 13 | 14 | @app.route('/') 15 | def static_proxy(path): 16 | # send_static_file will guess the correct MIME type 17 | return app.send_static_file(path) 18 | 19 | 20 | @app.route("/search") 21 | def search(): 22 | data = request.args 23 | print data 24 | keyword = data["q"] 25 | print keyword 26 | jobs = [] 27 | results = search_reddit(query = keyword) 28 | jobs = [r for r in results] 29 | 30 | return render_template("results.html", post_list = jobs) 31 | 32 | 33 | @app.route("/email-form") 34 | def email_form(): 35 | return app.send_static_file("email.html") 36 | 37 | 38 | app.run(debug=True) -------------------------------------------------------------------------------- /static/css/main.css: -------------------------------------------------------------------------------- 1 | 2 | @import url('https://fonts.googleapis.com/css?family=Ubuntu'); 3 | 4 | 5 | .main{ 6 | 7 | position: fixed; 8 | top: 0; 9 | left: 0; 10 | bottom: 0; 11 | right: 0; 12 | overflow: auto; 13 | 14 | 15 | 16 | background: #8A2387; /* fallback for old browsers */ 17 | background: -webkit-linear-gradient(to right, #F27121, #E94057, #8A2387); /* Chrome 10-25, Safari 5.1-6 */ 18 | background: linear-gradient(to right, #F27121, #E94057, #8A2387); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ 19 | background-size : cover; 20 | color: white; 21 | 22 | text-align: center; 23 | font-family: 'Ubuntu' , sans-serif; 24 | 25 | 26 | } 27 | 28 | .main h1{ 29 | font-size: 58px; 30 | padding-top: 20vh; 31 | } 32 | 33 | .main input{ 34 | width: 60%; 35 | max-width: 500px; 36 | margin-top: 20px; 37 | margin-bottom: 20px; 38 | height: 35px; 39 | padding: 5px; 40 | border: none; 41 | border-radius: 8px; 42 | 43 | } 44 | 45 | .btn{ 46 | border: none; 47 | border-radius: 3px; 48 | padding: 10px; 49 | } 50 | 51 | .btn.main-btn{ 52 | background-color: transparent; 53 | 54 | width : 128px; 55 | height: 50px; 56 | font-size: 20px; 57 | color: white; 58 | background-color: orange; 59 | 60 | } 61 | 62 | .btn.main-btn:hover{ 63 | 64 | background-color: #F27121; 65 | 66 | } 67 | 68 | -------------------------------------------------------------------------------- /static/css/results.css: -------------------------------------------------------------------------------- 1 | .card{ 2 | margin: 25px; 3 | } 4 | .card time{ 5 | color: #E94057; 6 | } 7 | 8 | .title{ 9 | font-size: 30px; 10 | text-align: center; 11 | font-weight: bold; 12 | margin: 15px; 13 | } 14 | 15 | .sub{ 16 | font-size: 20px; 17 | text-align: center; 18 | font-weight: bold; 19 | margin: 15px; 20 | 21 | } -------------------------------------------------------------------------------- /static/email.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 56 |
57 | 58 |

Find Exclusive Jobs & Gigs from Reddit, Craiglist, Twitter, Facebook and more

59 |

Join our special list to be the first one to find these jobs, only {{places_left}}

60 |

What's in it for me ? By joining our list, you will be the first one to have access to Junter search engine, you can find great gigs quickly

61 | 62 |
63 |
64 |
65 | 66 |
67 | 68 |
69 |
70 | 71 |
72 | 73 |
74 | 75 | We hate spam and we promise to never spam you 76 |
77 |
78 | 79 | 80 | 81 |
82 |
83 | Send Me Opportunities 84 |
85 | 86 | 87 | 99 | 100 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Reddit Job Finder 8 | 9 | 10 |
11 |

Junter

12 |

Find job opportunities on Reddit 13 |

14 |
15 |
16 |
17 |
18 | 19 |
20 |
21 | 22 |
23 | 24 |
25 | 26 | -------------------------------------------------------------------------------- /templates/results.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Results 9 | 10 | 11 | 12 | 49 | 50 |
51 |

Results :

52 |

{{post_list|length}} Jobs Found

53 |
54 | {% for post in post_list %} 55 |
56 |
57 | 58 | 59 |

60 | {{post.title}} 61 |

62 |
63 |
64 | 65 | 66 |
67 | 68 |
69 | {%endfor%} 70 |
71 |
72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | --------------------------------------------------------------------------------