OppaAI commited on
Commit
091e154
·
verified ·
1 Parent(s): 3654ea6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +56 -50
app.py CHANGED
@@ -2,93 +2,99 @@ import os
2
  import gradio as gr
3
  import requests
4
  from fastmcp import FastMCP
 
5
  from datetime import datetime
 
6
 
7
  # Initialize FastMCP agent
8
- mcp = FastMCP("RapidAPI Job Search Agent")
9
-
10
- RAPIDAPI_URL = "https://indeed-indeed.p.rapidapi.com/apisearch?v=2&format=json"
11
- RAPIDAPI_HOST = "indeed-indeed.p.rapidapi.com"
12
-
13
- # Load RapidAPI key from HF secret (environment variable)
14
- RAPIDAPI_KEY = os.getenv("RAPIDAPI_KEY") # Make sure this matches your secret name exactly
15
 
16
  @mcp.tool(name="search_jobs")
17
  def search_jobs_tool(query: str, location: str, limit: int = 10, salary: str = None, job_type: str = None):
18
  """
19
- Search jobs from a RapidAPI job search API based on query, location, and optional filters.
20
 
21
  Args:
22
  query (str): Job title or keywords to search for.
23
  location (str): Location (city, region) to search jobs in.
24
- limit (int): Number of job results to return (default is 10).
25
- salary (str, optional): Minimum salary filter.
26
- job_type (str, optional): Job type filter (e.g., fulltime, parttime).
27
 
28
  Returns:
29
- dict: JSON response from the API containing job listings or error details.
30
  """
 
 
 
31
  params = {
32
  "q": query,
33
  "l": location,
34
- "limit": limit,
35
- "sort": "date", # Automatically sort by most recent date
36
- # Add optional filters only if provided
37
- **({"salary": salary} if salary else {}),
38
- **({"job_type": job_type} if job_type else {}),
39
  }
40
- # Clean params to remove None values
41
- params = {k: v for k, v in params.items() if v}
42
 
43
  headers = {
44
- "X-RapidAPI-Host": RAPIDAPI_HOST,
45
- "X-RapidAPI-Key": RAPIDAPI_KEY,
46
- "Accept": "application/json"
47
  }
48
 
49
  try:
50
- response = requests.get(RAPIDAPI_URL, params=params, headers=headers)
51
  response.raise_for_status()
52
- return response.json()
53
- except requests.RequestException as e:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  return {"error": str(e)}
55
 
56
  def search_jobs_ui(query, location, limit=10, salary=None, job_type=None):
57
  """
58
- Gradio UI handler for searching jobs using FastMCP + RapidAPI job API.
59
 
60
  Args:
61
  query (str): Job title or keyword.
62
  location (str): Job location.
63
  limit (int, optional): Number of jobs to retrieve (default 10).
64
- salary (str, optional): Salary filter.
65
- job_type (str, optional): Job type filter.
66
 
67
  Returns:
68
  str: Markdown-formatted list of jobs or error message.
69
  """
70
- try:
71
- result = search_jobs_tool(query, location, limit, salary, job_type)
72
-
73
- if "error" in result:
74
- return f"❌ Error: {result['error']}"
75
 
76
- jobs = result.get("jobs", []) # adapt based on actual API response
 
77
 
78
- if not jobs:
79
- return "No jobs found for your search."
 
80
 
81
- output = ""
82
- for job in jobs:
83
- title = job.get("title", "No Title")
84
- company = job.get("company", "Unknown Company")
85
- job_location = job.get("location", "Unknown Location")
86
- job_url = job.get("url", "#")
87
- output += f"**{title}** at *{company}*\n📍 {job_location}\n[Apply Here]({job_url})\n\n"
88
 
89
- return output
90
- except Exception as e:
91
- return f"❌ An error occurred: {str(e)}"
92
 
93
  # Gradio Interface
94
  app = gr.Interface(
@@ -97,12 +103,12 @@ app = gr.Interface(
97
  gr.Textbox(label="Job Title / Keyword"),
98
  gr.Textbox(label="Location"),
99
  gr.Number(value=10, label="Number of Results (limit)", precision=0),
100
- gr.Textbox(label="Salary (optional)"),
101
- gr.Textbox(label="Job Type (optional: fulltime, parttime, contract)")
102
  ],
103
  outputs="markdown",
104
- title="Job Search with RapidAPI + FastMCP",
105
- description="Search jobs using a RapidAPI job search API. Results are sorted by most recent date automatically."
106
  )
107
 
108
  if __name__ == "__main__":
 
2
  import gradio as gr
3
  import requests
4
  from fastmcp import FastMCP
5
+ from bs4 import BeautifulSoup
6
  from datetime import datetime
7
+ from urllib.parse import urlencode
8
 
9
  # Initialize FastMCP agent
10
+ mcp = FastMCP("Indeed Web Scraper Agent")
 
 
 
 
 
 
11
 
12
  @mcp.tool(name="search_jobs")
13
  def search_jobs_tool(query: str, location: str, limit: int = 10, salary: str = None, job_type: str = None):
14
  """
15
+ Scrape jobs from Indeed website based on query, location, and optional filters.
16
 
17
  Args:
18
  query (str): Job title or keywords to search for.
19
  location (str): Location (city, region) to search jobs in.
20
+ limit (int): Number of job results to return (default 10).
21
+ salary (str, optional): Not used in scraper, reserved for future.
22
+ job_type (str, optional): Not used in scraper, reserved for future.
23
 
24
  Returns:
25
+ dict: Contains a list of jobs with title, company, location, and url.
26
  """
27
+ base_url = "https://ca.indeed.com/jobs?"
28
+
29
+ # Build query parameters
30
  params = {
31
  "q": query,
32
  "l": location,
33
+ "sort": "date", # sort by most recent
 
 
 
 
34
  }
35
+ url = base_url + urlencode(params)
 
36
 
37
  headers = {
38
+ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)"
 
 
39
  }
40
 
41
  try:
42
+ response = requests.get(url, headers=headers)
43
  response.raise_for_status()
44
+ soup = BeautifulSoup(response.text, "html.parser")
45
+
46
+ jobs = []
47
+ cards = soup.find_all("a", class_="tapItem", limit=limit) # job cards
48
+
49
+ for card in cards:
50
+ title_elem = card.find("h2", class_="jobTitle")
51
+ company_elem = card.find("span", class_="companyName")
52
+ location_elem = card.find("div", class_="companyLocation")
53
+ link = card.get("href")
54
+
55
+ if not link.startswith("http"):
56
+ link = "https://ca.indeed.com" + link
57
+
58
+ job = {
59
+ "title": title_elem.get_text(strip=True) if title_elem else "No Title",
60
+ "company": company_elem.get_text(strip=True) if company_elem else "Unknown Company",
61
+ "location": location_elem.get_text(strip=True) if location_elem else "Unknown Location",
62
+ "url": link,
63
+ }
64
+ jobs.append(job)
65
+
66
+ return {"jobs": jobs}
67
+ except Exception as e:
68
  return {"error": str(e)}
69
 
70
  def search_jobs_ui(query, location, limit=10, salary=None, job_type=None):
71
  """
72
+ Gradio UI handler for scraping Indeed jobs using FastMCP.
73
 
74
  Args:
75
  query (str): Job title or keyword.
76
  location (str): Job location.
77
  limit (int, optional): Number of jobs to retrieve (default 10).
78
+ salary (str, optional): Not used.
79
+ job_type (str, optional): Not used.
80
 
81
  Returns:
82
  str: Markdown-formatted list of jobs or error message.
83
  """
84
+ result = search_jobs_tool(query, location, limit, salary, job_type)
 
 
 
 
85
 
86
+ if "error" in result:
87
+ return f"❌ Error: {result['error']}"
88
 
89
+ jobs = result.get("jobs", [])
90
+ if not jobs:
91
+ return "No jobs found for your search."
92
 
93
+ output = ""
94
+ for job in jobs:
95
+ output += f"**{job['title']}** at *{job['company']}*\n📍 {job['location']}\n[Apply Here]({job['url']})\n\n"
 
 
 
 
96
 
97
+ return output
 
 
98
 
99
  # Gradio Interface
100
  app = gr.Interface(
 
103
  gr.Textbox(label="Job Title / Keyword"),
104
  gr.Textbox(label="Location"),
105
  gr.Number(value=10, label="Number of Results (limit)", precision=0),
106
+ gr.Textbox(label="Salary (optional, ignored)"),
107
+ gr.Textbox(label="Job Type (optional, ignored)")
108
  ],
109
  outputs="markdown",
110
+ title="Indeed Job Search (Web Scraping) with FastMCP",
111
+ description="Search jobs by scraping Indeed.ca. Results sorted by most recent."
112
  )
113
 
114
  if __name__ == "__main__":