OppaAI commited on
Commit
f77befc
·
verified ·
1 Parent(s): 99376ba

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -38
app.py CHANGED
@@ -1,79 +1,93 @@
1
  import gradio as gr
2
  from bs4 import BeautifulSoup
3
  from urllib.parse import urlencode
4
- from fastmcp import FastMCP
5
  import requests
 
6
 
7
- # Initialize FastMCP agent
8
  mcp = FastMCP("Canada Job Bank Scraper Agent")
9
 
10
-
11
  @mcp.tool(name="search_jobs")
12
- def search_jobs_tool(query: str, location: str, limit: int = 10, salary: str = None, job_type: str = None):
13
  """
14
- Scrape job listings from the Canada Job Bank website based on a search query and location.
15
 
16
  Args:
17
  query (str): Job title or keyword to search for.
18
- location (str): Location to filter jobs.
19
  limit (int): Maximum number of job results to return (default: 10).
20
- salary (str, optional): Ignored in this scraper but included for future API compatibility.
21
- job_type (str, optional): Ignored in this scraper but included for future API compatibility.
22
 
23
  Returns:
24
  dict: A dictionary with a list of job listings or an error message.
25
  """
26
- base_url = "https://www.jobbank.gc.ca/jobsearch/jobsearch?"
27
  params = {
28
- "searchstring": query,
29
- "locationstring": location,
30
  "sort": "M", # Sort by most recent
31
  }
32
- url = base_url + urlencode(params)
33
 
34
  headers = {
35
  "User-Agent": (
36
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
37
  "AppleWebKit/537.36 (KHTML, like Gecko) "
38
- "Chrome/114.0.0.0 Safari/537.36"
39
  ),
40
  "Accept-Language": "en-US,en;q=0.9",
 
41
  }
42
 
43
  try:
44
- response = requests.get(url, headers=headers, timeout=15)
45
  response.raise_for_status()
46
 
47
  soup = BeautifulSoup(response.text, "html.parser")
48
  cards = soup.find_all("article", class_="resultJobItem")
49
 
 
 
 
50
  jobs = []
51
- for card in cards:
52
- title_elem = card.find("a", attrs={"data-testid": "result-job-title"})
53
- company_elem = card.find("div", attrs={"data-testid": "result-employer"})
54
- location_elem = card.find("div", attrs={"data-testid": "result-location"})
55
- link = title_elem.get("href") if title_elem else None
 
 
56
  if link and not link.startswith("http"):
57
  link = "https://www.jobbank.gc.ca" + link
58
 
59
  job = {
60
  "title": title_elem.get_text(strip=True) if title_elem else "No Title",
61
- "company": company_elem.get_text(strip=True) if company_elem else "Unknown Company",
62
- "location": location_elem.get_text(strip=True) if location_elem else "Unknown Location",
63
  "url": link or "#"
64
  }
65
  jobs.append(job)
66
 
67
- return {"jobs": jobs[:limit]} # Apply limit after scraping all
68
 
 
 
 
 
 
 
69
  except Exception as e:
70
- return {"error": str(e)}
71
-
72
 
73
  def search_jobs_ui(query, location, limit=10, salary=None, job_type=None):
74
  """
75
  Interface function for displaying job search results in markdown format.
76
  """
 
 
 
 
77
  result = search_jobs_tool(query, location, limit, salary, job_type)
78
 
79
  if "error" in result:
@@ -81,29 +95,34 @@ def search_jobs_ui(query, location, limit=10, salary=None, job_type=None):
81
 
82
  jobs = result.get("jobs", [])
83
  if not jobs:
84
- return "No jobs found for your search."
85
 
86
- output = ""
87
- for job in jobs:
88
- output += f"**{job['title']}** at *{job['company']}*\n📍 {job['location']}\n[Apply Here]({job['url']})\n\n"
 
 
 
 
 
89
 
90
  return output
91
 
92
-
93
  # Gradio Interface
94
  app = gr.Interface(
95
  fn=search_jobs_ui,
96
  inputs=[
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, ignored)"),
101
- gr.Textbox(label="Job Type (optional, ignored)")
102
  ],
103
- outputs="markdown",
104
- title="Canada Job Bank Job Search MCP Server",
105
- description="Search jobs by scraping Canada Job Bank using requests and BeautifulSoup."
 
106
  )
107
 
108
  if __name__ == "__main__":
109
- app.launch(mcp_server=True)
 
1
  import gradio as gr
2
  from bs4 import BeautifulSoup
3
  from urllib.parse import urlencode
 
4
  import requests
5
+ from fastmcp import FastMCP
6
 
7
+ # Initialize FastMCP server
8
  mcp = FastMCP("Canada Job Bank Scraper Agent")
9
 
 
10
  @mcp.tool(name="search_jobs")
11
+ def search_jobs_tool(query: str, location: str, limit: int = 10, salary: str = None, job_type: str = None) -> dict:
12
  """
13
+ Scrape job listings from the Canada Job Bank website.
14
 
15
  Args:
16
  query (str): Job title or keyword to search for.
17
+ location (str): Location to filter jobs (e.g., "Toronto, ON").
18
  limit (int): Maximum number of job results to return (default: 10).
19
+ salary (str, optional): Ignored (for API compatibility).
20
+ job_type (str, optional): Ignored (for API compatibility).
21
 
22
  Returns:
23
  dict: A dictionary with a list of job listings or an error message.
24
  """
25
+ base_url = "https://www.jobbank.gc.ca/jobsearch/jobsearch"
26
  params = {
27
+ "searchstring": query.strip(),
28
+ "locationstring": location.strip(),
29
  "sort": "M", # Sort by most recent
30
  }
31
+ url = base_url + "?" + urlencode(params, safe=",")
32
 
33
  headers = {
34
  "User-Agent": (
35
  "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
36
  "AppleWebKit/537.36 (KHTML, like Gecko) "
37
+ "Chrome/120.0.0.0 Safari/537.36"
38
  ),
39
  "Accept-Language": "en-US,en;q=0.9",
40
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9",
41
  }
42
 
43
  try:
44
+ response = requests.get(url, headers=headers, timeout=10)
45
  response.raise_for_status()
46
 
47
  soup = BeautifulSoup(response.text, "html.parser")
48
  cards = soup.find_all("article", class_="resultJobItem")
49
 
50
+ if not cards:
51
+ return {"error": "No job listings found or website structure changed."}
52
+
53
  jobs = []
54
+ for card in cards[:limit]:
55
+ title_elem = card.find("span", class_="noctitle")
56
+ company_elem = card.find("li", class_="business")
57
+ location_elem = card.find("li", class_="location")
58
+ link_elem = card.find("a", href=True)
59
+
60
+ link = link_elem.get("href") if link_elem else None
61
  if link and not link.startswith("http"):
62
  link = "https://www.jobbank.gc.ca" + link
63
 
64
  job = {
65
  "title": title_elem.get_text(strip=True) if title_elem else "No Title",
66
+ "company": company_elem.get_text(strip=True).replace("Employer", "").strip() if company_elem else "Unknown Company",
67
+ "location": location_elem.get_text(strip=True).replace("Location", "").strip() if location_elem else "Unknown Location",
68
  "url": link or "#"
69
  }
70
  jobs.append(job)
71
 
72
+ return {"jobs": jobs}
73
 
74
+ except requests.exceptions.HTTPError as http_err:
75
+ return {"error": f"HTTP error occurred: {http_err}"}
76
+ except requests.exceptions.Timeout:
77
+ return {"error": "Request timed out. Please try again later."}
78
+ except requests.exceptions.RequestException as req_err:
79
+ return {"error": f"Request error: {req_err}"}
80
  except Exception as e:
81
+ return {"error": f"Unexpected error: {str(e)}"}
 
82
 
83
  def search_jobs_ui(query, location, limit=10, salary=None, job_type=None):
84
  """
85
  Interface function for displaying job search results in markdown format.
86
  """
87
+ if not query or not location:
88
+ return "❌ Please provide both a job title/keyword and location."
89
+
90
+ limit = int(max(1, min(limit, 50))) # Enforce reasonable limit
91
  result = search_jobs_tool(query, location, limit, salary, job_type)
92
 
93
  if "error" in result:
 
95
 
96
  jobs = result.get("jobs", [])
97
  if not jobs:
98
+ return "No jobs found for your search. Try different keywords or a broader location."
99
 
100
+ output = "# Job Search Results\n\n"
101
+ for i, job in enumerate(jobs, 1):
102
+ output += (
103
+ f"### {i}. {job['title']}\n"
104
+ f"**Company**: {job['company']}\n"
105
+ f"**Location**: {job['location']}\n"
106
+ f"[Apply Here]({job['url']})\n\n"
107
+ )
108
 
109
  return output
110
 
 
111
  # Gradio Interface
112
  app = gr.Interface(
113
  fn=search_jobs_ui,
114
  inputs=[
115
+ gr.Textbox(label="Job Title / Keyword", placeholder="e.g., Software Engineer"),
116
+ gr.Textbox(label="Location", placeholder="e.g., Toronto, ON"),
117
+ gr.Slider(minimum=1, maximum=50, value=10, step=1, label="Number of Results"),
118
+ gr.Textbox(label="Salary (optional, ignored)", placeholder="Not used"),
119
+ gr.Textbox(label="Job Type (optional, ignored)", placeholder="Not used")
120
  ],
121
+ outputs=gr.Markdown(),
122
+ title="Canada Job Bank Job Search",
123
+ description="Search jobs by scraping Canada Job Bank using FastMCP and Gradio.",
124
+ theme="huggingface"
125
  )
126
 
127
  if __name__ == "__main__":
128
+ app.launch(mcp_server=True)