Compare commits

..

19 Commits

Author SHA1 Message Date
Berkay Gemici
fda080a373 fix(linkedin): add fallback for date parsing on new job listings (#343)
LinkedIn uses two CSS classes for job posting dates:
- `job-search-card__listdate` for older posts
- `job-search-card__listdate--new` for recent posts (< 24h)

The scraper only checked the first class, causing `date_posted` to be
None for all fresh listings. This adds a fallback to also check for
the `--new` variant.
2026-02-18 13:39:52 -06:00
Sean
6e7ab6ff74 Fix: re Issue #295 (@krishianjan): added (seemingly missing) user_agent keyword argument to BDJobs 2026-01-09 23:28:27 -06:00
kj55-dev
7160d0faed fix: relax numpy version constraint to >=1.26.0 (#337) 2026-01-09 23:27:54 -06:00
Cullen Watson
6e014cf732 chore: codeowners 2025-08-23 22:42:45 +02:00
Kaushik H S
6e8576f8a8 fix(naukri): prevent str.find error by normalizing input and parsing before Markdown (#300) 2025-08-23 15:38:26 -05:00
Alexander Smirnov
51888004b7 Update __init__.py (#296)
pagination fix: start update with job_cards instead of job_list
2025-08-23 15:38:02 -05:00
Lixian Wang
b6d5cd8d79 fix:correct LinkedIn logger naming (#291)
* fix:correct LinkedIn logger naming

* add:linkedin description plain format
2025-08-23 15:37:49 -05:00
ZuoyunZheng
84ed670df3 chore: bump markdownify from 0.13.1 to 1.1.0 (#290) 2025-08-23 15:37:34 -05:00
Cullen Watson
4b16ac7967 chore:readme 2025-07-28 17:19:56 +02:00
itsShrizon
ae2b1ea42c Bdjobs Fixed (#280) 2025-07-28 10:05:10 -05:00
Cullen Watson
53b3b41385 fix: glassdoor ua 2025-07-28 16:55:51 +02:00
Lê Trọng Tài
9aae02453d issue#270: glassdoor 403 response by rotating user-agent and updating headers (#274) 2025-07-28 09:55:05 -05:00
Piotr Geca
94d413bad1 support for socks5 proxies (#266)
Co-authored-by: Piotr Geca <piotr.geca@npl.co.uk>
2025-04-10 15:53:28 -05:00
Cullen Watson
61205bcc77 chore: version 2025-03-27 21:59:47 -05:00
Nikhil Sasi
f1602eca70 Fix date parsing error: prevent negative days by using timedelta (#264)
subtracting extracted "days" from label with current day causes negative days
datetime class rejects negative day association
Use timedelta for proper date limitation

Co-authored-by: NIKHIL S <nikhil_s@nikhilMac.local>
2025-03-27 21:58:42 -05:00
Cullen Watson
d4d52d05f5 chore:version 2025-03-21 17:35:23 -05:00
Liju Thomas
0946cb3373 feat: add naukri.com support (#259) 2025-03-21 17:23:07 -05:00
prudvisorra-aifa
051981689f Update util.py (#256) 2025-03-17 11:51:19 -05:00
Cullen Watson
903b7e6f1b fix(linkedin):is remote 2025-03-06 13:38:28 -06:00
35 changed files with 1009 additions and 2563 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @cullenwatson

View File

@@ -1,45 +0,0 @@
name: JobSpy Scraper Dynamic Workflow
on:
workflow_dispatch:
inputs:
user_email:
description: 'Email of user'
required: true
run_id:
description: 'Run ID from Power Automate'
required: true
permissions:
contents: read
id-token: write
jobs:
scrape_jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: Set Up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Sanitize Email
id: vars
run: |
raw_email="${{ github.event.inputs.user_email }}"
safe_email=$(echo "$raw_email" | sed 's/@/_at_/g; s/\./_/g')
echo "safe_email=$safe_email" >> $GITHUB_OUTPUT
- name: Run Job Scraper
run: |
python job_scraper_dynamic.py "${{ github.event.inputs.user_email }}" "${{ github.event.inputs.run_id }}"
- name: Upload Output Artifact
uses: actions/upload-artifact@v4
with:
name: jobspy_output_${{ steps.vars.outputs.safe_email }}_${{ github.event.inputs.run_id }}
path: outputs/jobspy_output_${{ steps.vars.outputs.safe_email }}_${{ github.event.inputs.run_id }}.csv

View File

@@ -1,48 +0,0 @@
name: JobSpy Scraper Workflow
on:
workflow_dispatch: # Allows manual trigger from GitHub or Power Automate
# Remove or comment out the schedule to prevent auto-runs
# schedule:
# - cron: '0 */6 * * *' # Runs every 6 hours (DISABLED)
permissions:
actions: read
contents: read
id-token: write
jobs:
scrape_jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run JobSpy Scraper
run: python job_scraper_exact_match.py
- name: Debug - Check if jobspy_output.csv exists
run: |
if [ ! -f jobspy_output.csv ]; then
echo "❌ ERROR: jobspy_output.csv not found!"
exit 1
else
echo "✅ jobspy_output.csv found, proceeding to upload..."
fi
- name: Upload JobSpy Output as Artifact
uses: actions/upload-artifact@v4 # Explicitly using latest version
with:
name: jobspy-results
path: jobspy_output.csv

View File

@@ -4,7 +4,7 @@
## Features
- Scrapes job postings from **LinkedIn**, **Indeed**, **Glassdoor**, **Google**, **ZipRecruiter**, & **Bayt** concurrently
- Scrapes job postings from **LinkedIn**, **Indeed**, **Glassdoor**, **Google**, **ZipRecruiter**, & other job boards concurrently
- Aggregates the job postings in a dataframe
- Proxies support to bypass blocking
@@ -25,7 +25,7 @@ import csv
from jobspy import scrape_jobs
jobs = scrape_jobs(
site_name=["indeed", "linkedin", "zip_recruiter", "glassdoor", "google", "bayt"],
site_name=["indeed", "linkedin", "zip_recruiter", "google"], # "glassdoor", "bayt", "naukri", "bdjobs"
search_term="software engineer",
google_search_term="software engineer jobs near San Francisco, CA since yesterday",
location="San Francisco, CA",
@@ -51,6 +51,7 @@ linkedin Software Engineer - Early Career Lockheed Martin Sunnyvale
linkedin Full-Stack Software Engineer Rain New York NY fulltime yearly None None https://www.linkedin.com/jobs/view/3696158877 Rains mission is to create the fastest and ea...
zip_recruiter Software Engineer - New Grad ZipRecruiter Santa Monica CA fulltime yearly 130000 150000 https://www.ziprecruiter.com/jobs/ziprecruiter... We offer a hybrid work environment. Most US-ba...
zip_recruiter Software Developer TEKsystems Phoenix AZ fulltime hourly 65 75 https://www.ziprecruiter.com/jobs/teksystems-0... Top Skills' Details• 6 years of Java developme...
```
### Parameters for `scrape_jobs()`
@@ -58,7 +59,7 @@ zip_recruiter Software Developer TEKsystems Phoenix
```plaintext
Optional
├── site_name (list|str):
| linkedin, zip_recruiter, indeed, glassdoor, google, bayt
| linkedin, zip_recruiter, indeed, glassdoor, google, bayt, bdjobs
| (default is all)
├── search_term (str)
@@ -85,6 +86,9 @@ Optional
├── easy_apply (bool):
| filters for jobs that are hosted on the job board site (LinkedIn easy apply filter no longer works)
|
├── user_agent (str):
| override the default user agent which may be outdated
├── description_format (str):
| markdown, html (Format type of the job descriptions. Default is markdown.)
@@ -220,6 +224,7 @@ JobPost
│ ├── country
│ ├── city
│ ├── state
├── is_remote
├── description
├── job_type: fulltime, parttime, internship, contract
├── job_function
@@ -229,8 +234,7 @@ JobPost
│ ├── currency
│ └── salary_source: direct_data, description (parsed from posting)
├── date_posted
── emails
└── is_remote
── emails
Linkedin specific
└── job_level
@@ -245,4 +249,12 @@ Indeed specific
├── company_revenue_label
├── company_description
└── company_logo
Naukri specific
├── skills
├── experience_range
├── company_rating
├── company_reviews_count
├── vacancy_count
└── work_from_home_type
```

View File

@@ -1,8 +0,0 @@
{
"search_terms": ["IT Support", "Help Desk"],
"results_wanted": 50,
"max_days_old": 7,
"target_state": "NY",
"user_email": "Branden@autoemployme.onmicrosoft.com"
}

View File

@@ -1 +0,0 @@
{"search_terms":["Mortgage"," Bank"],"results_wanted":"50\n","max_days_old":"1\n","target_state":"NY","user_email":"Branden@autoemployme.onmicrosoft.com"}

View File

@@ -1,116 +0,0 @@
import csv
import datetime
from jobspy.google import Google
from jobspy.linkedin import LinkedIn
from jobspy.indeed import Indeed
from jobspy.ziprecruiter import ZipRecruiter
from jobspy.model import ScraperInput
# Define job sources
sources = {
"google": Google,
"linkedin": LinkedIn,
"indeed": Indeed,
"zip_recruiter": ZipRecruiter,
}
# Define search preferences
search_terms = ["Automation Engineer", "CRM Manager", "Implementation Specialist"]
results_wanted = 200 # Fetch more jobs
max_days_old = 2 # Fetch jobs posted in last 48 hours
target_state = "NY" # Only keep jobs from New York
def scrape_jobs(search_terms, results_wanted, max_days_old, target_state):
"""Scrape jobs from multiple sources and filter by state."""
all_jobs = []
today = datetime.date.today()
print("\n🔎 DEBUG: Fetching jobs for search terms:", search_terms)
for search_term in search_terms:
for source_name, source_class in sources.items():
print(f"\n🚀 Scraping {search_term} from {source_name}...")
scraper = source_class()
search_criteria = ScraperInput(
site_type=[source_name],
search_term=search_term,
results_wanted=results_wanted,
)
job_response = scraper.scrape(search_criteria)
for job in job_response.jobs:
# Normalize location fields
location_city = job.location.city.strip() if job.location.city else "Unknown"
location_state = job.location.state.strip().upper() if job.location.state else "Unknown"
location_country = str(job.location.country) if job.location.country else "Unknown"
# Debug: Show all jobs being fetched
print(f"📍 Fetched Job: {job.title} - {location_city}, {location_state}, {location_country}")
# Ensure the job is recent
if job.date_posted and (today - job.date_posted).days <= max_days_old:
if location_state == target_state or job.is_remote:
print(f"✅ MATCH (In NY or Remote): {job.title} - {location_city}, {location_state} (Posted {job.date_posted})")
all_jobs.append({
"Job ID": job.id,
"Job Title (Primary)": job.title,
"Company Name": job.company_name if job.company_name else "Unknown",
"Industry": job.company_industry if job.company_industry else "Not Provided",
"Experience Level": job.job_level if job.job_level else "Not Provided",
"Job Type": job.job_type[0].name if job.job_type else "Not Provided",
"Is Remote": job.is_remote,
"Currency": job.compensation.currency if job.compensation else "",
"Salary Min": job.compensation.min_amount if job.compensation else "",
"Salary Max": job.compensation.max_amount if job.compensation else "",
"Date Posted": job.date_posted.strftime("%Y-%m-%d") if job.date_posted else "Not Provided",
"Location City": location_city,
"Location State": location_state,
"Location Country": location_country,
"Job URL": job.job_url,
"Job Description": job.description[:500] if job.description else "No description available",
"Job Source": source_name
})
else:
print(f"❌ Ignored (Wrong State): {job.title} - {location_city}, {location_state} (Posted {job.date_posted})")
else:
print(f"⏳ Ignored (Too Old): {job.title} - {location_city}, {location_state} (Posted {job.date_posted})")
print(f"\n{len(all_jobs)} jobs retrieved in NY")
return all_jobs
def save_jobs_to_csv(jobs, filename="jobspy_output.csv"):
"""Save job data to a CSV file."""
if not jobs:
print("⚠️ No jobs found matching criteria.")
return
fieldnames = [
"Job ID", "Job Title (Primary)", "Company Name", "Industry",
"Experience Level", "Job Type", "Is Remote", "Currency",
"Salary Min", "Salary Max", "Date Posted", "Location City",
"Location State", "Location Country", "Job URL", "Job Description",
"Job Source"
]
with open(filename, mode="w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=fieldnames)
writer.writeheader()
writer.writerows(jobs)
print(f"✅ Jobs saved to {filename} ({len(jobs)} entries)")
# Run the scraper with multiple job searches
job_data = scrape_jobs(
search_terms=search_terms,
results_wanted=results_wanted,
max_days_old=max_days_old,
target_state=target_state
)
# Save results to CSV
save_jobs_to_csv(job_data)

View File

@@ -1,105 +0,0 @@
import csv, datetime, os, sys, json
from jobspy.google import Google
from jobspy.linkedin import LinkedIn
from jobspy.indeed import Indeed
from jobspy.model import ScraperInput
# Define sources
sources = {
"google": Google,
"linkedin": LinkedIn,
"indeed": Indeed,
}
def sanitize_email(email):
return email.replace("@", "_at_").replace(".", "_")
def load_config(email):
safe_email = sanitize_email(email)
config_path = os.path.join("configs", f"config_{safe_email}.json")
if not os.path.exists(config_path):
raise FileNotFoundError(f"❌ Config for {email} not found at {config_path}")
with open(config_path, "r", encoding="utf-8") as f:
return json.load(f), safe_email
def scrape_jobs(search_terms, results_wanted_str, max_days_old_str, target_state):
# Convert string values to integers
results_wanted = int(results_wanted_str.strip())
max_days_old = int(max_days_old_str.strip())
today = datetime.date.today()
all_jobs = []
for term in search_terms:
for source, Scraper in sources.items():
print(f"🔍 Scraping {term} from {source}")
scraper = Scraper()
try:
jobs = scraper.scrape(ScraperInput(
site_type=[source],
search_term=term,
results_wanted=results_wanted
)).jobs
except Exception as e:
print(f"⚠️ {source} error: {e}")
continue
for job in jobs:
if job.date_posted and (today - job.date_posted).days <= max_days_old:
if target_state == (job.location.state or "").upper() or job.is_remote:
if any(term.lower() in job.title.lower() for term in search_terms):
all_jobs.append({
"Job ID": job.id,
"Job Title (Primary)": job.title,
"Company Name": job.company_name or "Unknown",
"Industry": job.company_industry or "Not Provided",
"Experience Level": job.job_level or "Not Provided",
"Job Type": job.job_type[0].name if job.job_type else "Not Provided",
"Is Remote": job.is_remote,
"Currency": job.compensation.currency if job.compensation else "",
"Salary Min": job.compensation.min_amount if job.compensation else "",
"Salary Max": job.compensation.max_amount if job.compensation else "",
"Date Posted": job.date_posted.strftime("%Y-%m-%d"),
"Location City": job.location.city or "Unknown",
"Location State": (job.location.state or "Unknown").upper(),
"Location Country": job.location.country or "Unknown",
"Job URL": job.job_url,
"Job Description": job.description.replace(",", "") if job.description else "No description",
"Job Source": source
})
print(f"✅ Found {len(all_jobs)} jobs")
return all_jobs
def save_to_csv(jobs, path):
os.makedirs(os.path.dirname(path), exist_ok=True)
fieldnames = [
"Job ID", "Job Title (Primary)", "Company Name", "Industry",
"Experience Level", "Job Type", "Is Remote", "Currency",
"Salary Min", "Salary Max", "Date Posted", "Location City",
"Location State", "Location Country", "Job URL", "Job Description", "Job Source"
]
header = "|~|".join(fieldnames)
rows = [header] + ["|~|".join(str(job.get(col, "Not Provided")).replace(",", "").strip() for col in fieldnames) for job in jobs]
with open(path, "w", encoding="utf-8") as f:
f.write(",".join(rows))
print(f"💾 Saved output to: {path}")
if __name__ == "__main__":
try:
if len(sys.argv) != 3:
raise ValueError("❌ Usage: python job_scraper_dynamic.py <user_email> <run_id>")
user_email, run_id = sys.argv[1], sys.argv[2]
config, safe_email = load_config(user_email)
jobs = scrape_jobs(
config["search_terms"],
config["results_wanted"],
config["max_days_old"],
config["target_state"]
)
save_to_csv(jobs, f"outputs/jobspy_output_{safe_email}_{run_id}.csv")
except Exception as e:
print(f"❌ Fatal error: {e}")
sys.exit(1)

View File

@@ -1,146 +0,0 @@
import csv
import datetime
import os
from jobspy.google import Google
from jobspy.linkedin import LinkedIn
from jobspy.indeed import Indeed
from jobspy.model import ScraperInput
# Define job sources
sources = {
"google": Google,
"linkedin": LinkedIn,
"indeed": Indeed,
}
# Define search preferences
search_terms = ["Automation Engineer", "CRM Manager", "Implementation Specialist", "CRM", "Project Manager", "POS", "Microsoft Power", "IT Support"]
results_wanted = 100 # Fetch more jobs
max_days_old = 2 # Fetch jobs posted in last 48 hours
target_state = "NY" # Only keep jobs from New York
def scrape_jobs(search_terms, results_wanted, max_days_old, target_state):
"""Scrape jobs from multiple sources and filter by state."""
all_jobs = []
today = datetime.date.today()
print("\n🔎 DEBUG: Fetching jobs for search terms:", search_terms)
for search_term in search_terms:
for source_name, source_class in sources.items():
print(f"\n🚀 Scraping {search_term} from {source_name}...")
scraper = source_class()
search_criteria = ScraperInput(
site_type=[source_name],
search_term=search_term,
results_wanted=results_wanted,
)
job_response = scraper.scrape(search_criteria)
for job in job_response.jobs:
# Normalize location fields
location_city = job.location.city.strip() if job.location.city else "Unknown"
location_state = job.location.state.strip().upper() if job.location.state else "Unknown"
location_country = str(job.location.country) if job.location.country else "Unknown"
# Debug: Show all jobs being fetched
print(f"📍 Fetched Job: {job.title} - {location_city}, {location_state}, {location_country}")
# Exclude jobs that dont explicitly match the search terms
if not any(term.lower() in job.title.lower() for term in search_terms):
print(f"🚫 Excluding: {job.title} (Doesn't match {search_terms})")
continue # Skip this job
# Ensure the job is recent
if job.date_posted and (today - job.date_posted).days <= max_days_old:
# Only accept jobs if they're in NY or Remote
if location_state == target_state or job.is_remote:
print(f"✅ MATCH: {job.title} - {location_city}, {location_state} (Posted {job.date_posted})")
all_jobs.append({
"Job ID": job.id,
"Job Title (Primary)": job.title,
"Company Name": job.company_name if job.company_name else "Unknown",
"Industry": job.company_industry if job.company_industry else "Not Provided",
"Experience Level": job.job_level if job.job_level else "Not Provided",
"Job Type": job.job_type[0].name if job.job_type else "Not Provided",
"Is Remote": job.is_remote,
"Currency": job.compensation.currency if job.compensation else "",
"Salary Min": job.compensation.min_amount if job.compensation else "",
"Salary Max": job.compensation.max_amount if job.compensation else "",
"Date Posted": job.date_posted.strftime("%Y-%m-%d") if job.date_posted else "Not Provided",
"Location City": location_city,
"Location State": location_state,
"Location Country": location_country,
"Job URL": job.job_url,
"Job Description": job.description.replace(",", "") if job.description else "No description available",
"Job Source": source_name
})
else:
print(f"❌ Ignored (Wrong State): {job.title} - {location_city}, {location_state} (Posted {job.date_posted})")
else:
print(f"⏳ Ignored (Too Old): {job.title} - {location_city}, {location_state} (Posted {job.date_posted})")
print(f"\n{len(all_jobs)} jobs retrieved in NY")
return all_jobs
def save_jobs_to_csv(jobs, filename="jobspy_output.csv"):
"""Save job data to a CSV file with custom formatting:
- Fields within a record are separated by the custom delimiter |~|
- Records are separated by a comma
- All commas in field values are removed
- Blank fields are replaced with 'Not Provided'
"""
if not jobs:
print("⚠️ No jobs found matching criteria.")
return
# Remove old CSV file before writing
if os.path.exists(filename):
os.remove(filename)
fieldnames = [
"Job ID", "Job Title (Primary)", "Company Name", "Industry",
"Experience Level", "Job Type", "Is Remote", "Currency",
"Salary Min", "Salary Max", "Date Posted", "Location City",
"Location State", "Location Country", "Job URL", "Job Description",
"Job Source"
]
# Build header record using custom field delimiter
header_record = "|~|".join(fieldnames)
records = [header_record]
for job in jobs:
row = []
for field in fieldnames:
value = str(job.get(field, "")).strip()
if not value:
value = "Not Provided"
# Remove all commas from the value
value = value.replace(",", "")
row.append(value)
# Join fields with the custom delimiter
record = "|~|".join(row)
records.append(record)
# Join records with a comma as the record separator
output = ",".join(records)
with open(filename, "w", encoding="utf-8") as file:
file.write(output)
print(f"✅ Jobs saved to {filename} ({len(jobs)} entries)")
# Run the scraper with multiple job searches
job_data = scrape_jobs(
search_terms=search_terms,
results_wanted=results_wanted,
max_days_old=max_days_old,
target_state=target_state
)
# Save results to CSV with custom formatting
save_jobs_to_csv(job_data)

View File

@@ -6,10 +6,12 @@ from typing import Tuple
import pandas as pd
from jobspy.bayt import BaytScraper
from jobspy.bdjobs import BDJobs
from jobspy.glassdoor import Glassdoor
from jobspy.google import Google
from jobspy.indeed import Indeed
from jobspy.linkedin import LinkedIn
from jobspy.naukri import Naukri
from jobspy.model import JobType, Location, JobResponse, Country
from jobspy.model import SalarySource, ScraperInput, Site
from jobspy.util import (
@@ -24,6 +26,8 @@ from jobspy.util import (
from jobspy.ziprecruiter import ZipRecruiter
# Update the SCRAPER_MAPPING dictionary in the scrape_jobs function
def scrape_jobs(
site_name: str | list[str] | Site | list[Site] | None = None,
search_term: str | None = None,
@@ -44,6 +48,7 @@ def scrape_jobs(
hours_old: int = None,
enforce_annual_salary: bool = False,
verbose: int = 0,
user_agent: str = None,
**kwargs,
) -> pd.DataFrame:
"""
@@ -57,6 +62,8 @@ def scrape_jobs(
Site.GLASSDOOR: Glassdoor,
Site.GOOGLE: Google,
Site.BAYT: BaytScraper,
Site.NAUKRI: Naukri,
Site.BDJOBS: BDJobs, # Add BDJobs to the scraper mapping
}
set_logger_level(verbose)
job_type = get_enum_from_value(job_type) if job_type else None
@@ -96,10 +103,11 @@ def scrape_jobs(
def scrape_site(site: Site) -> Tuple[str, JobResponse]:
scraper_class = SCRAPER_MAPPING[site]
scraper = scraper_class(proxies=proxies, ca_cert=ca_cert)
scraper = scraper_class(proxies=proxies, ca_cert=ca_cert, user_agent=user_agent)
scraped_data: JobResponse = scraper.scrape(scraper_input)
cap_name = site.value.capitalize()
site_name = "ZipRecruiter" if cap_name == "Zip_recruiter" else cap_name
site_name = "LinkedIn" if cap_name == "Linkedin" else cap_name
create_logger(site_name).info(f"finished scraping")
return site.value, scraped_data
@@ -139,6 +147,7 @@ def scrape_jobs(
**job_data["location"]
).display_location()
# Handle compensation
compensation_obj = job_data.get("compensation")
if compensation_obj and isinstance(compensation_obj, dict):
job_data["interval"] = (
@@ -157,7 +166,6 @@ def scrape_jobs(
and job_data["max_amount"]
):
convert_to_annual(job_data)
else:
if country_enum == Country.USA:
(
@@ -176,6 +184,17 @@ def scrape_jobs(
if "min_amount" in job_data and job_data["min_amount"]
else None
)
#naukri-specific fields
job_data["skills"] = (
", ".join(job_data["skills"]) if job_data["skills"] else None
)
job_data["experience_range"] = job_data.get("experience_range")
job_data["company_rating"] = job_data.get("company_rating")
job_data["company_reviews_count"] = job_data.get("company_reviews_count")
job_data["vacancy_count"] = job_data.get("vacancy_count")
job_data["work_from_home_type"] = job_data.get("work_from_home_type")
job_df = pd.DataFrame([job_data])
jobs_dfs.append(job_df)
@@ -200,3 +219,9 @@ def scrape_jobs(
).reset_index(drop=True)
else:
return pd.DataFrame()
# Add BDJobs to __all__
__all__ = [
"BDJobs",
]

View File

@@ -25,7 +25,7 @@ class BaytScraper(Scraper):
band_delay = 3
def __init__(
self, proxies: list[str] | str | None = None, ca_cert: str | None = None
self, proxies: list[str] | str | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
super().__init__(Site.BAYT, proxies=proxies, ca_cert=ca_cert)
self.scraper_input = None

353
jobspy/bdjobs/__init__.py Normal file
View File

@@ -0,0 +1,353 @@
# __init__.py
from __future__ import annotations
import random
import time
from datetime import datetime
from typing import Optional, List, Dict, Any
from urllib.parse import urljoin
from bs4 import BeautifulSoup
from bs4.element import Tag
from jobspy.exception import BDJobsException
from jobspy.bdjobs.constant import headers, search_params
from jobspy.bdjobs.util import (
parse_location,
parse_date,
find_job_listings,
is_job_remote,
)
from jobspy.model import (
JobPost,
Location,
JobResponse,
Country,
Scraper,
ScraperInput,
Site,
DescriptionFormat,
)
from jobspy.util import (
extract_emails_from_text,
create_session,
create_logger,
remove_attributes,
markdown_converter,
)
log = create_logger("BDJobs")
class BDJobs(Scraper):
base_url = "https://jobs.bdjobs.com"
search_url = "https://jobs.bdjobs.com/jobsearch.asp"
delay = 2
band_delay = 3
def __init__(
self, proxies: list[str] | str | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
"""
Initializes BDJobsScraper with the BDJobs job search url
"""
super().__init__(Site.BDJOBS, proxies=proxies, ca_cert=ca_cert)
self.session = create_session(
proxies=self.proxies,
ca_cert=ca_cert,
is_tls=False,
has_retry=True,
delay=5,
clear_cookies=True,
)
self.session.headers.update(headers)
self.scraper_input = None
self.country = "bangladesh"
def scrape(self, scraper_input: ScraperInput) -> JobResponse:
"""
Scrapes BDJobs for jobs with scraper_input criteria
:param scraper_input:
:return: job_response
"""
self.scraper_input = scraper_input
job_list: list[JobPost] = []
seen_ids = set()
page = 1
request_count = 0
# Set up search parameters
params = search_params.copy()
params["txtsearch"] = scraper_input.search_term
continue_search = lambda: len(job_list) < scraper_input.results_wanted
while continue_search():
request_count += 1
log.info(f"search page: {request_count}")
try:
# Add page parameter if needed
if page > 1:
params["pg"] = page
response = self.session.get(
self.search_url,
params=params,
timeout=getattr(scraper_input, "request_timeout", 60),
)
if response.status_code != 200:
log.error(f"BDJobs response status code {response.status_code}")
break
soup = BeautifulSoup(response.text, "html.parser")
job_cards = find_job_listings(soup)
if not job_cards or len(job_cards) == 0:
log.info("No more job listings found")
break
log.info(f"Found {len(job_cards)} job cards on page {page}")
for job_card in job_cards:
try:
job_post = self._process_job(job_card)
if job_post and job_post.id not in seen_ids:
seen_ids.add(job_post.id)
job_list.append(job_post)
if not continue_search():
break
except Exception as e:
log.error(f"Error processing job card: {str(e)}")
page += 1
# Add delay between requests
time.sleep(random.uniform(self.delay, self.delay + self.band_delay))
except Exception as e:
log.error(f"Error during scraping: {str(e)}")
break
job_list = job_list[: scraper_input.results_wanted]
return JobResponse(jobs=job_list)
def _process_job(self, job_card: Tag) -> Optional[JobPost]:
"""
Processes a job card element into a JobPost object
:param job_card: Job card element
:return: JobPost object
"""
try:
# Extract job ID and URL
job_link = job_card.find("a", href=lambda h: h and "jobdetail" in h.lower())
if not job_link:
return None
job_url = job_link.get("href")
if not job_url.startswith("http"):
job_url = urljoin(self.base_url, job_url)
# Extract job ID from URL
job_id = (
job_url.split("jobid=")[-1].split("&")[0]
if "jobid=" in job_url
else f"bdjobs-{hash(job_url)}"
)
# Extract title
title = job_link.get_text(strip=True)
if not title:
title_elem = job_card.find(
["h2", "h3", "h4", "strong", "div"],
class_=lambda c: c and "job-title-text" in c,
)
title = title_elem.get_text(strip=True) if title_elem else "N/A"
# Extract company name - IMPROVED
company_elem = job_card.find(
["span", "div"],
class_=lambda c: c and "comp-name-text" in (c or "").lower(),
)
if company_elem:
company_name = company_elem.get_text(strip=True)
else:
# Try alternative selectors
company_elem = job_card.find(
["span", "div"],
class_=lambda c: c
and any(
term in (c or "").lower()
for term in ["company", "org", "comp-name"]
),
)
company_name = (
company_elem.get_text(strip=True) if company_elem else "N/A"
)
# Extract location
location_elem = job_card.find(
["span", "div"],
class_=lambda c: c and "locon-text-d" in (c or "").lower(),
)
if not location_elem:
location_elem = job_card.find(
["span", "div"],
class_=lambda c: c
and any(
term in (c or "").lower()
for term in ["location", "area", "locon"]
),
)
location_text = (
location_elem.get_text(strip=True)
if location_elem
else "Dhaka, Bangladesh"
)
# Create Location object
location = parse_location(location_text, self.country)
# Extract date posted
date_elem = job_card.find(
["span", "div"],
class_=lambda c: c
and any(
term in (c or "").lower()
for term in ["date", "deadline", "published"]
),
)
date_posted = None
if date_elem:
date_text = date_elem.get_text(strip=True)
date_posted = parse_date(date_text)
# Check if job is remote
is_remote = is_job_remote(title, location=location)
# Create job post object
job_post = JobPost(
id=job_id,
title=title,
company_name=company_name, # Use company_name instead of company
location=location,
date_posted=date_posted,
job_url=job_url,
is_remote=is_remote,
site=self.site,
)
# Always fetch description for BDJobs
job_details = self._get_job_details(job_url)
job_post.description = job_details.get("description", "")
job_post.job_type = job_details.get("job_type", "")
return job_post
except Exception as e:
log.error(f"Error in _process_job: {str(e)}")
return None
def _get_job_details(self, job_url: str) -> Dict[str, Any]:
"""
Gets detailed job information from the job page
:param job_url: Job page URL
:return: Dictionary with job details
"""
try:
response = self.session.get(job_url, timeout=60)
if response.status_code != 200:
return {}
soup = BeautifulSoup(response.text, "html.parser")
# Find job description - IMPROVED based on correct.py
description = ""
# Try to find the job content div first (as in correct.py)
job_content_div = soup.find("div", class_="jobcontent")
if job_content_div:
# Look for responsibilities section
responsibilities_heading = job_content_div.find(
"h4", id="job_resp"
) or job_content_div.find(
["h4", "h5"], string=lambda s: s and "responsibilities" in s.lower()
)
if responsibilities_heading:
responsibilities_elements = []
# Find all following elements until the next heading or hr
for sibling in responsibilities_heading.find_next_siblings():
if sibling.name in ["hr", "h4", "h5"]:
break
if sibling.name == "ul":
responsibilities_elements.extend(
li.get_text(separator=" ", strip=True)
for li in sibling.find_all("li")
)
elif sibling.name == "p":
responsibilities_elements.append(
sibling.get_text(separator=" ", strip=True)
)
description = (
"\n".join(responsibilities_elements)
if responsibilities_elements
else ""
)
# If no description found yet, try the original approach
if not description:
description_elem = soup.find(
["div", "section"],
class_=lambda c: c
and any(
term in (c or "").lower()
for term in ["job-description", "details", "requirements"]
),
)
if description_elem:
description_elem = remove_attributes(description_elem)
description = description_elem.prettify(formatter="html")
if (
hasattr(self.scraper_input, "description_format")
and self.scraper_input.description_format
== DescriptionFormat.MARKDOWN
):
description = markdown_converter(description)
# Extract job type
job_type_elem = soup.find(
["span", "div"],
string=lambda s: s
and any(
term in (s or "").lower()
for term in ["job type", "employment type"]
),
)
job_type = None
if job_type_elem:
job_type_text = job_type_elem.find_next(["span", "div"]).get_text(
strip=True
)
job_type = job_type_text if job_type_text else None
# Extract company industry
industry_elem = soup.find(
["span", "div"], string=lambda s: s and "industry" in (s or "").lower()
)
company_industry = None
if industry_elem:
industry_text = industry_elem.find_next(["span", "div"]).get_text(
strip=True
)
company_industry = industry_text if industry_text else None
return {
"description": description,
"job_type": job_type,
"company_industry": company_industry,
}
except Exception as e:
log.error(f"Error getting job details: {str(e)}")
return {}

32
jobspy/bdjobs/constant.py Normal file
View File

@@ -0,0 +1,32 @@
#constant.py
# Headers for BDJobs requests
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Connection": "keep-alive",
"Referer": "https://jobs.bdjobs.com/",
"Cache-Control": "max-age=0",
}
# Search parameters that work best for BDJobs
search_params = {
"hidJobSearch": "jobsearch",
}
# Selectors for job listings
job_selectors = [
"div.job-item", # Catches both normal and premium job cards, as well as other types
"div.sout-jobs-wrapper", # Catches job listings in the main search results page
"div.norm-jobs-wrapper", # Catches normal job listings
"div.featured-wrap", # Catches featured job listings
]
# Date formats used by BDJobs
date_formats = [
"%d %b %Y",
"%d-%b-%Y",
"%d %B %Y",
"%B %d, %Y",
"%d/%m/%Y",
]

100
jobspy/bdjobs/util.py Normal file
View File

@@ -0,0 +1,100 @@
#util.py
from bs4 import BeautifulSoup
from datetime import datetime
from typing import Optional, List, Dict, Any
from jobspy.model import Location, Country
def parse_location(location_text: str, country: str = "bangladesh") -> Location:
"""
Parses location text into a Location object
:param location_text: Location text from job listing
:param country: Default country
:return: Location object
"""
parts = location_text.split(",")
if len(parts) >= 2:
city = parts[0].strip()
state = parts[1].strip()
return Location(
city=city,
state=state,
country=Country.from_string(country)
)
else:
return Location(
city=location_text.strip(),
country=Country.from_string(country)
)
def parse_date(date_text: str) -> Optional[datetime]:
"""
Parses date text into a datetime object
:param date_text: Date text from job listing
:return: datetime object or None if parsing fails
"""
from .constant import date_formats
try:
# Clean up date text
if "Deadline:" in date_text:
date_text = date_text.replace("Deadline:", "").strip()
# Try different date formats
for fmt in date_formats:
try:
return datetime.strptime(date_text, fmt)
except ValueError:
continue
return None
except Exception:
return None
def find_job_listings(soup: BeautifulSoup) -> List[Any]:
"""
Finds job listing elements in the HTML
:param soup: BeautifulSoup object
:return: List of job card elements
"""
from .constant import job_selectors
# Try different selectors
for selector in job_selectors:
if "." in selector:
tag_name, class_name = selector.split(".", 1)
elements = soup.find_all(tag_name, class_=class_name)
if elements and len(elements) > 0:
return elements
# If no selectors match, look for job detail links
job_links = soup.find_all("a", href=lambda h: h and "jobdetail" in h.lower())
if job_links:
# Return parent elements of job links
return [link.parent for link in job_links]
return []
def is_job_remote(title: str, description: str = None, location: Location = None) -> bool:
"""
Determines if a job is remote based on title, description, and location
:param title: Job title
:param description: Job description
:param location: Job location
:return: True if job is remote, False otherwise
"""
remote_keywords = ["remote", "work from home", "wfh", "home based"]
# Combine all text fields
full_text = title.lower()
if description:
full_text += " " + description.lower()
if location:
full_text += " " + location.display_location().lower()
# Check for remote keywords
return any(keyword in full_text for keyword in remote_keywords)

View File

@@ -34,3 +34,12 @@ class GoogleJobsException(Exception):
class BaytException(Exception):
def __init__(self, message=None):
super().__init__(message or "An error occurred with Bayt")
class NaukriException(Exception):
def __init__(self,message=None):
super().__init__(message or "An error occurred with Naukri")
class BDJobsException(Exception):
def __init__(self, message=None):
super().__init__(message or "An error occurred with BDJobs")

View File

@@ -34,13 +34,13 @@ log = create_logger("Glassdoor")
class Glassdoor(Scraper):
def __init__(
self, proxies: list[str] | str | None = None, ca_cert: str | None = None
self, proxies: list[str] | str | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
"""
Initializes GlassdoorScraper with the Glassdoor job search url
"""
site = Site(Site.GLASSDOOR)
super().__init__(site, proxies=proxies, ca_cert=ca_cert)
super().__init__(site, proxies=proxies, ca_cert=ca_cert, user_agent=user_agent)
self.base_url = None
self.country = None
@@ -65,6 +65,8 @@ class Glassdoor(Scraper):
)
token = self._get_csrf_token()
headers["gd-csrf-token"] = token if token else fallback_token
if self.user_agent:
headers["user-agent"] = self.user_agent
self.session.headers.update(headers)
location_id, location_type = self._get_location(

View File

@@ -13,7 +13,7 @@ headers = {
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36",
}
query_template = """
query JobSearchResultsQuery(

View File

@@ -22,7 +22,7 @@ from jobspy.google.util import log, find_job_info_initial_page, find_job_info
class Google(Scraper):
def __init__(
self, proxies: list[str] | str | None = None, ca_cert: str | None = None
self, proxies: list[str] | str | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
"""
Initializes Google Scraper with the Goodle jobs search url

View File

@@ -28,7 +28,7 @@ log = create_logger("Indeed")
class Indeed(Scraper):
def __init__(
self, proxies: list[str] | str | None = None, ca_cert: str | None = None
self, proxies: list[str] | str | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
"""
Initializes IndeedScraper with the Indeed API url
@@ -205,8 +205,6 @@ class Indeed(Scraper):
description = job["description"]["html"]
if self.scraper_input.description_format == DescriptionFormat.MARKDOWN:
description = markdown_converter(description)
description = description.replace(",", "")
job_type = get_job_type(job["attributes"])
timestamp_seconds = job["datePublished"] / 1000

View File

@@ -20,7 +20,7 @@ def get_job_type(attributes: list) -> list[JobType]:
def get_compensation(compensation: dict) -> Compensation | None:
"""
Parses the job to get compensation
:param sssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrompensation:
:param compensation:
:return: compensation object
"""
if not compensation["baseSalary"] and not compensation["estimated"]:
@@ -58,11 +58,14 @@ def is_job_remote(job: dict, description: str) -> bool:
any(keyword in attr["label"].lower() for keyword in remote_keywords)
for attr in job["attributes"]
)
is_remote_in_description = any(
keyword in description.lower() for keyword in remote_keywords
)
is_remote_in_location = any(
keyword in job["location"]["formatted"]["long"].lower()
for keyword in remote_keywords
)
return is_remote_in_attributes or is_remote_in_location
return is_remote_in_attributes or is_remote_in_description or is_remote_in_location
def get_compensation_interval(interval: str) -> CompensationInterval:

View File

@@ -14,10 +14,11 @@ from bs4.element import Tag
from jobspy.exception import LinkedInException
from jobspy.linkedin.constant import headers
from jobspy.linkedin.util import (
is_job_remote,
job_type_code,
parse_job_type,
parse_job_level,
parse_company_industry,
parse_company_industry
)
from jobspy.model import (
JobPost,
@@ -34,6 +35,7 @@ from jobspy.util import (
extract_emails_from_text,
currency_parser,
markdown_converter,
plain_converter,
create_session,
remove_attributes,
create_logger,
@@ -49,7 +51,7 @@ class LinkedIn(Scraper):
jobs_per_page = 25
def __init__(
self, proxies: list[str] | str | None = None, ca_cert: str | None = None
self, proxies: list[str] | str | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
"""
Initializes LinkedInScraper with the LinkedIn job search url
@@ -163,7 +165,7 @@ class LinkedIn(Scraper):
if continue_search():
time.sleep(random.uniform(self.delay, self.delay + self.band_delay))
start += len(job_list)
start += len(job_cards)
job_list = job_list[: scraper_input.results_wanted]
return JobResponse(jobs=job_list)
@@ -173,7 +175,7 @@ class LinkedIn(Scraper):
) -> Optional[JobPost]:
salary_tag = job_card.find("span", class_="job-search-card__salary-info")
compensation = None
compensation = description = None
if salary_tag:
salary_text = salary_tag.get_text(separator=" ").strip()
salary_values = [currency_parser(value) for value in salary_text.split("-")]
@@ -207,6 +209,10 @@ class LinkedIn(Scraper):
if metadata_card
else None
)
if not datetime_tag and metadata_card:
datetime_tag = metadata_card.find(
"time", class_="job-search-card__listdate--new"
)
date_posted = None
if datetime_tag and "datetime" in datetime_tag.attrs:
datetime_str = datetime_tag["datetime"]
@@ -217,8 +223,8 @@ class LinkedIn(Scraper):
job_details = {}
if full_descr:
job_details = self._get_job_details(job_id)
description = description.replace(",", "")
description = job_details.get("description")
is_remote = is_job_remote(title, description, location)
return JobPost(
id=f"li-{job_id}",
@@ -226,6 +232,7 @@ class LinkedIn(Scraper):
company_name=company,
company_url=company_url,
location=location,
is_remote=is_remote,
date_posted=date_posted,
job_url=f"{self.base_url}/jobs/view/{job_id}",
compensation=compensation,
@@ -234,7 +241,7 @@ class LinkedIn(Scraper):
company_industry=job_details.get("company_industry"),
description=job_details.get("description"),
job_url_direct=job_details.get("job_url_direct"),
emails=extract_emails_from_text(job_details.get("description")),
emails=extract_emails_from_text(description),
company_logo=job_details.get("company_logo"),
job_function=job_details.get("job_function"),
)
@@ -265,7 +272,8 @@ class LinkedIn(Scraper):
description = div_content.prettify(formatter="html")
if self.scraper_input.description_format == DescriptionFormat.MARKDOWN:
description = markdown_converter(description)
elif self.scraper_input.description_format == DescriptionFormat.PLAIN:
description = plain_converter(description)
h3_tag = soup.find(
"h3", text=lambda text: text and "Job function" in text.strip()
)

View File

@@ -1,6 +1,6 @@
from bs4 import BeautifulSoup
from jobspy.model import JobType
from jobspy.model import JobType, Location
from jobspy.util import get_enum_from_job_type
@@ -83,3 +83,14 @@ def parse_company_industry(soup_industry: BeautifulSoup) -> str | None:
industry = industry_span.get_text(strip=True)
return industry
def is_job_remote(title: dict, description: str, location: Location) -> bool:
"""
Searches the title, location, and description to check if job is remote
"""
remote_keywords = ["remote", "work from home", "wfh"]
location = location.display_location()
full_string = f'{title} {description} {location}'.lower()
is_remote = any(keyword in full_string for keyword in remote_keywords)
return is_remote

View File

@@ -68,6 +68,7 @@ class Country(Enum):
AUSTRALIA = ("australia", "au", "com.au")
AUSTRIA = ("austria", "at", "at")
BAHRAIN = ("bahrain", "bh")
BANGLADESH = ("bangladesh", "bd") # Added Bangladesh
BELGIUM = ("belgium", "be", "fr:be")
BULGARIA = ("bulgaria", "bg")
BRAZIL = ("brazil", "br", "com.br")
@@ -233,7 +234,7 @@ class Compensation(BaseModel):
class DescriptionFormat(Enum):
MARKDOWN = "markdown"
HTML = "html"
PLAIN = "plain"
class JobPost(BaseModel):
id: str | None = None
@@ -254,13 +255,13 @@ class JobPost(BaseModel):
is_remote: bool | None = None
listing_type: str | None = None
# linkedin specific
# LinkedIn specific
job_level: str | None = None
# linkedin and indeed specific
# LinkedIn and Indeed specific
company_industry: str | None = None
# indeed specific
# Indeed specific
company_addresses: str | None = None
company_num_employees: str | None = None
company_revenue: str | None = None
@@ -268,9 +269,16 @@ class JobPost(BaseModel):
company_logo: str | None = None
banner_photo_url: str | None = None
# linkedin only atm
# LinkedIn only atm
job_function: str | None = None
# Naukri specific
skills: list[str] | None = None #from tagsAndSkills
experience_range: str | None = None #from experienceText
company_rating: float | None = None #from ambitionBoxData.AggregateRating
company_reviews_count: int | None = None #from ambitionBoxData.ReviewsCount
vacancy_count: int | None = None #from vacancy
work_from_home_type: str | None = None #from clusters.wfhType (e.g., "Hybrid", "Remote")
class JobResponse(BaseModel):
jobs: list[JobPost] = []
@@ -283,6 +291,8 @@ class Site(Enum):
GLASSDOOR = "glassdoor"
GOOGLE = "google"
BAYT = "bayt"
NAUKRI = "naukri"
BDJOBS = "bdjobs" # Add this line
class SalarySource(Enum):
@@ -306,17 +316,20 @@ class ScraperInput(BaseModel):
linkedin_company_ids: list[int] | None = None
description_format: DescriptionFormat | None = DescriptionFormat.MARKDOWN
request_timeout: int = 60
results_wanted: int = 15
hours_old: int | None = None
class Scraper(ABC):
def __init__(
self, site: Site, proxies: list[str] | None = None, ca_cert: str | None = None
self, site: Site, proxies: list[str] | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
self.site = site
self.proxies = proxies
self.ca_cert = ca_cert
self.user_agent = user_agent
@abstractmethod
def scrape(self, scraper_input: ScraperInput) -> JobResponse: ...

304
jobspy/naukri/__init__.py Normal file
View File

@@ -0,0 +1,304 @@
from __future__ import annotations
import math
import random
import time
from datetime import datetime, date, timedelta
from typing import Optional
import regex as re
import requests
from jobspy.exception import NaukriException
from jobspy.naukri.constant import headers as naukri_headers
from jobspy.naukri.util import (
is_job_remote,
parse_job_type,
parse_company_industry,
)
from jobspy.model import (
JobPost,
Location,
JobResponse,
Country,
Compensation,
DescriptionFormat,
Scraper,
ScraperInput,
Site,
)
from jobspy.util import (
extract_emails_from_text,
currency_parser,
markdown_converter,
create_session,
create_logger,
)
log = create_logger("Naukri")
class Naukri(Scraper):
base_url = "https://www.naukri.com/jobapi/v3/search"
delay = 3
band_delay = 4
jobs_per_page = 20
def __init__(
self, proxies: list[str] | str | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
"""
Initializes NaukriScraper with the Naukri API URL
"""
super().__init__(Site.NAUKRI, proxies=proxies, ca_cert=ca_cert)
self.session = create_session(
proxies=self.proxies,
ca_cert=ca_cert,
is_tls=False,
has_retry=True,
delay=5,
clear_cookies=True,
)
self.session.headers.update(naukri_headers)
self.scraper_input = None
self.country = "India" #naukri is india-focused by default
log.info("Naukri scraper initialized")
def scrape(self, scraper_input: ScraperInput) -> JobResponse:
"""
Scrapes Naukri API for jobs with scraper_input criteria
:param scraper_input:
:return: job_response
"""
self.scraper_input = scraper_input
job_list: list[JobPost] = []
seen_ids = set()
start = scraper_input.offset or 0
page = (start // self.jobs_per_page) + 1
request_count = 0
seconds_old = (
scraper_input.hours_old * 3600 if scraper_input.hours_old else None
)
continue_search = (
lambda: len(job_list) < scraper_input.results_wanted and page <= 50 # Arbitrary limit
)
while continue_search():
request_count += 1
log.info(
f"Scraping page {request_count} / {math.ceil(scraper_input.results_wanted / self.jobs_per_page)} "
f"for search term: {scraper_input.search_term}"
)
params = {
"noOfResults": self.jobs_per_page,
"urlType": "search_by_keyword",
"searchType": "adv",
"keyword": scraper_input.search_term,
"pageNo": page,
"k": scraper_input.search_term,
"seoKey": f"{scraper_input.search_term.lower().replace(' ', '-')}-jobs",
"src": "jobsearchDesk",
"latLong": "",
"location": scraper_input.location,
"remote": "true" if scraper_input.is_remote else None,
}
if seconds_old:
params["days"] = seconds_old // 86400 # Convert to days
params = {k: v for k, v in params.items() if v is not None}
try:
log.debug(f"Sending request to {self.base_url} with params: {params}")
response = self.session.get(self.base_url, params=params, timeout=10)
if response.status_code not in range(200, 400):
err = f"Naukri API response status code {response.status_code} - {response.text}"
log.error(err)
return JobResponse(jobs=job_list)
data = response.json()
job_details = data.get("jobDetails", [])
log.info(f"Received {len(job_details)} job entries from API")
if not job_details:
log.warning("No job details found in API response")
break
except Exception as e:
log.error(f"Naukri API request failed: {str(e)}")
return JobResponse(jobs=job_list)
for job in job_details:
job_id = job.get("jobId")
if not job_id or job_id in seen_ids:
continue
seen_ids.add(job_id)
log.debug(f"Processing job ID: {job_id}")
try:
fetch_desc = scraper_input.linkedin_fetch_description
job_post = self._process_job(job, job_id, fetch_desc)
if job_post:
job_list.append(job_post)
log.info(f"Added job: {job_post.title} (ID: {job_id})")
if not continue_search():
break
except Exception as e:
log.error(f"Error processing job ID {job_id}: {str(e)}")
raise NaukriException(str(e))
if continue_search():
time.sleep(random.uniform(self.delay, self.delay + self.band_delay))
page += 1
job_list = job_list[:scraper_input.results_wanted]
log.info(f"Scraping completed. Total jobs collected: {len(job_list)}")
return JobResponse(jobs=job_list)
def _process_job(
self, job: dict, job_id: str, full_descr: bool
) -> Optional[JobPost]:
"""
Processes a single job from API response into a JobPost object
"""
title = job.get("title", "N/A")
company = job.get("companyName", "N/A")
company_url = f"https://www.naukri.com/{job.get('staticUrl', '')}" if job.get("staticUrl") else None
location = self._get_location(job.get("placeholders", []))
compensation = self._get_compensation(job.get("placeholders", []))
date_posted = self._parse_date(job.get("footerPlaceholderLabel"), job.get("createdDate"))
job_url = f"https://www.naukri.com{job.get('jdURL', f'/job/{job_id}')}"
raw_description = job.get("jobDescription") if full_descr else None
job_type = parse_job_type(raw_description) if raw_description else None
company_industry = parse_company_industry(raw_description) if raw_description else None
description = raw_description
if description and self.scraper_input.description_format == DescriptionFormat.MARKDOWN:
description = markdown_converter(description)
is_remote = is_job_remote(title, description or "", location)
company_logo = job.get("logoPathV3") or job.get("logoPath")
# Naukri-specific fields
skills = job.get("tagsAndSkills", "").split(",") if job.get("tagsAndSkills") else None
experience_range = job.get("experienceText")
ambition_box = job.get("ambitionBoxData", {})
company_rating = float(ambition_box.get("AggregateRating")) if ambition_box.get("AggregateRating") else None
company_reviews_count = ambition_box.get("ReviewsCount")
vacancy_count = job.get("vacancy")
work_from_home_type = self._infer_work_from_home_type(job.get("placeholders", []), title, description or "")
job_post = JobPost(
id=f"nk-{job_id}",
title=title,
company_name=company,
company_url=company_url,
location=location,
is_remote=is_remote,
date_posted=date_posted,
job_url=job_url,
compensation=compensation,
job_type=job_type,
company_industry=company_industry,
description=description,
emails=extract_emails_from_text(description or ""),
company_logo=company_logo,
skills=skills,
experience_range=experience_range,
company_rating=company_rating,
company_reviews_count=company_reviews_count,
vacancy_count=vacancy_count,
work_from_home_type=work_from_home_type,
)
log.debug(f"Processed job: {title} at {company}")
return job_post
def _get_location(self, placeholders: list[dict]) -> Location:
"""
Extracts location data from placeholders
"""
location = Location(country=Country.INDIA)
for placeholder in placeholders:
if placeholder.get("type") == "location":
location_str = placeholder.get("label", "")
parts = location_str.split(", ")
city = parts[0] if parts else None
state = parts[1] if len(parts) > 1 else None
location = Location(city=city, state=state, country=Country.INDIA)
log.debug(f"Parsed location: {location.display_location()}")
break
return location
def _get_compensation(self, placeholders: list[dict]) -> Optional[Compensation]:
"""
Extracts compensation data from placeholders, handling Indian salary formats (Lakhs, Crores)
"""
for placeholder in placeholders:
if placeholder.get("type") == "salary":
salary_text = placeholder.get("label", "").strip()
if salary_text == "Not disclosed":
log.debug("Salary not disclosed")
return None
# Handle Indian salary formats (e.g., "12-16 Lacs P.A.", "1-5 Cr")
salary_match = re.match(r"(\d+(?:\.\d+)?)\s*-\s*(\d+(?:\.\d+)?)\s*(Lacs|Lakh|Cr)\s*(P\.A\.)?", salary_text, re.IGNORECASE)
if salary_match:
min_salary, max_salary, unit = salary_match.groups()[:3]
min_salary, max_salary = float(min_salary), float(max_salary)
currency = "INR"
# Convert to base units (INR)
if unit.lower() in ("lacs", "lakh"):
min_salary *= 100000 # 1 Lakh = 100,000 INR
max_salary *= 100000
elif unit.lower() == "cr":
min_salary *= 10000000 # 1 Crore = 10,000,000 INR
max_salary *= 10000000
log.debug(f"Parsed salary: {min_salary} - {max_salary} INR")
return Compensation(
min_amount=int(min_salary),
max_amount=int(max_salary),
currency=currency,
)
else:
log.debug(f"Could not parse salary: {salary_text}")
return None
return None
def _parse_date(self, label: str, created_date: int) -> Optional[date]:
"""
Parses date from footerPlaceholderLabel or createdDate, returning a date object
"""
today = datetime.now()
if not label:
if created_date:
return datetime.fromtimestamp(created_date / 1000).date() # Convert to date
return None
label = label.lower()
if "today" in label or "just now" in label or "few hours" in label:
log.debug("Date parsed as today")
return today.date()
elif "ago" in label:
match = re.search(r"(\d+)\s*day", label)
if match:
days = int(match.group(1))
parsed_date = (today - timedelta(days = days)).date()
log.debug(f"Date parsed: {days} days ago -> {parsed_date}")
return parsed_date
elif created_date:
parsed_date = datetime.fromtimestamp(created_date / 1000).date()
log.debug(f"Date parsed from timestamp: {parsed_date}")
return parsed_date
log.debug("No date parsed")
return None
def _infer_work_from_home_type(self, placeholders: list[dict], title: str, description: str) -> Optional[str]:
"""
Infers work-from-home type from job data (e.g., 'Hybrid', 'Remote', 'Work from office')
"""
location_str = next((p["label"] for p in placeholders if p["type"] == "location"), "").lower()
if "hybrid" in location_str or "hybrid" in title.lower() or "hybrid" in description.lower():
return "Hybrid"
elif "remote" in location_str or "remote" in title.lower() or "remote" in description.lower():
return "Remote"
elif "work from office" in description.lower() or not ("remote" in description.lower() or "hybrid" in description.lower()):
return "Work from office"
return None

11
jobspy/naukri/constant.py Normal file
View File

@@ -0,0 +1,11 @@
headers = {
"authority": "www.naukri.com",
"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.7",
"accept-language": "en-US,en;q=0.9",
"cache-control": "max-age=0",
"upgrade-insecure-requests": "1",
"appid": "109",
"systemid": "Naukri",
"Nkparam": "Ppy0YK9uSHqPtG3bEejYc04RTpUN2CjJOrqA68tzQt0SKJHXZKzz9M8cZtKLVkoOuQmfe4cTb1r2CwfHaxW5Tg==",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
}

38
jobspy/naukri/util.py Normal file
View File

@@ -0,0 +1,38 @@
from __future__ import annotations
from bs4 import BeautifulSoup
from jobspy.model import JobType, Location
from jobspy.util import get_enum_from_job_type
def parse_job_type(soup: BeautifulSoup |str) -> list[JobType] | None:
"""
Gets the job type from the job page
"""
if isinstance(soup, str):
soup = BeautifulSoup(soup, "html.parser")
job_type_tag = soup.find("span", class_="job-type")
if job_type_tag:
job_type_str = job_type_tag.get_text(strip=True).lower().replace("-", "")
return [get_enum_from_job_type(job_type_str)] if job_type_str else None
return None
def parse_company_industry(soup: BeautifulSoup | str) -> str | None:
"""
Gets the company industry from the job page
"""
if isinstance(soup, str):
soup = BeautifulSoup(soup, "html.parser")
industry_tag = soup.find("span", class_="industry")
return industry_tag.get_text(strip=True) if industry_tag else None
def is_job_remote(title: str, description: str, location: Location) -> bool:
"""
Searches the title, description, and location to check if the job is remote
"""
remote_keywords = ["remote", "work from home", "wfh"]
location_str = location.display_location()
full_string = f"{title} {description} {location_str}".lower()
return any(keyword in full_string for keyword in remote_keywords)

View File

@@ -47,11 +47,12 @@ class RotatingProxySession:
"""Utility method to format a proxy string into a dictionary."""
if proxy.startswith("http://") or proxy.startswith("https://"):
return {"http": proxy, "https": proxy}
if proxy.startswith("socks5://"):
return {"http": proxy, "https": proxy}
return {"http": f"http://{proxy}", "https": f"http://{proxy}"}
class RequestsRotating(RotatingProxySession, requests.Session):
def __init__(self, proxies=None, has_retry=False, delay=1, clear_cookies=False):
RotatingProxySession.__init__(self, proxies=proxies)
requests.Session.__init__(self)
@@ -86,7 +87,6 @@ class RequestsRotating(RotatingProxySession, requests.Session):
class TLSRotating(RotatingProxySession, tls_client.Session):
def __init__(self, proxies=None):
RotatingProxySession.__init__(self, proxies=proxies)
tls_client.Session.__init__(self, random_tls_extension_order=True)
@@ -157,6 +157,15 @@ def markdown_converter(description_html: str):
markdown = md(description_html)
return markdown.strip()
def plain_converter(decription_html:str):
from bs4 import BeautifulSoup
if decription_html is None:
return None
soup = BeautifulSoup(decription_html, "html.parser")
text = soup.get_text(separator=" ")
text = re.sub(r'\s+',' ',text)
return text.strip()
def extract_emails_from_text(text: str) -> list[str] | None:
if not text:
@@ -344,4 +353,11 @@ desired_order = [
"company_num_employees",
"company_revenue",
"company_description",
# naukri-specific fields
"skills",
"experience_range",
"company_rating",
"company_reviews_count",
"vacancy_count",
"work_from_home_type",
]

View File

@@ -38,7 +38,7 @@ class ZipRecruiter(Scraper):
api_url = "https://api.ziprecruiter.com"
def __init__(
self, proxies: list[str] | str | None = None, ca_cert: str | None = None
self, proxies: list[str] | str | None = None, ca_cert: str | None = None, user_agent: str | None = None
):
"""
Initializes ZipRecruiterScraper with the ZipRecruiter job search url
@@ -216,4 +216,4 @@ class ZipRecruiter(Scraper):
Sends a session event to the API with device properties.
"""
url = f"{self.api_url}/jobs-app/event"
self.session.post(url, data=get_cookie_data)
self.session.post(url, data=get_cookie_data)

View File

@@ -28,4 +28,4 @@ def get_job_type_enum(job_type_str: str) -> list[JobType] | None:
for job_type in JobType:
if job_type_str in job_type.value:
return [job_type]
return None
return None

File diff suppressed because it is too large Load Diff

View File

@@ -1,612 +0,0 @@
Job ID|~|Job Title (Primary)|~|Company Name|~|Industry|~|Experience Level|~|Job Type|~|Is Remote|~|Currency|~|Salary Min|~|Salary Max|~|Date Posted|~|Location City|~|Location State|~|Location Country|~|Job URL|~|Job Description|~|Job Source,in-1204f360ed401e85|~|IT Support Technician Hospitality|~|Edge Communications|~|Not Provided|~|Not Provided|~|Not Provided|~|True|~|USD|~|70000.0|~|80000.0|~|2025-04-15|~|Honolulu|~|HI|~|US|~|https://www.indeed.com/viewjob?jk=1204f360ed401e85|~|Description:
**IT Support Technician Hospitality**
**Reports to: IT Services**
**Location: Honolulu**
**Company Description**
Edge provides integrated managed voice and data technology systems and services for small/medium businesses and enterprises.
**Position Description**
As an IT Support Technician you will be part of a team of IT professionals who provide onsite \& remote support for all facets of the IT ecosystem. Our "white\-glove" 24/7 support program specializes in industries where attention to detail and timely response is mission critical. Our hospitality division caters to high\-end large\-scale boutique hotels restaurants and nightclubs whose staff and patrons expect industry\-leading support. This is a fast\-paced interactive hands\-on role where you must "dress to impress' and give 100% daily.
As part of a team that supports multiple properties in several states we are looking for people who are self\-starters and can work remotely as well. You must manage your workload each day and be able to prioritize each task based on each unique situation. Using cutting\-edge industry remote management monitoring and access tools you will be assisted by teams in other regions and may be asked to do the same for them.
**Primary Responsibilities**
* Desktop support for hardware and software troubleshooting
* Willingness to learn industry\-specific and proprietary management systems
* Setup deploy and maintain end\-user equipment
* Perform network administration functions user account permissions Active Directory changes
* Follow up with clients to ensure resolution is complete and satisfactory
* Maintain accurate thorough and timely information in ticketing system
* Research and resolve problems through all IT functions
* Collaborate with peers to form technical solutions
* Completion of day\-to\-day help desk support requests and assigned projects that require interaction with other divisions of our company
Requirements:
**Required Skills**
* Ability to provide on\-site \& remote desktop support to customers.
* Ability to use remote support tools like VNC LogMeIn RDP etc.
* Strong troubleshooting abilities
* Ability to use our remote management platform for workstation configuration status testing
* Familiarity supporting (not engineering) TCP/IP cables IP phones workstation connectivity printer connectivity POS devices and Active Directory administration
* Ability to be responsible dependable and committed to building a long\-term career at Edge Communications.
* Being a goal\-driven team player with solid organizational skills and a keen attention to detail.
* Independent self\-starting attitude with the willingness to share knowledge.
* Thorough knowledge of all Windows server and desktop operating systems
* Understanding of Hotel property management \& Point of Sale applications
* Thorough knowledge of PC server hardware and configuration including related peripherals.
* Thorough knowledge of Word Excel PowerPoint Outlook Active Directory and Exchange
* Strong customer service and problem\-solving skills including the ability to provide diligent prompt and courteous responses to users questions or PC issues.
* Ability to function effectively in a fast\-paced environment
* Willingness to travel occasionally
* Ability to multi\-task and maintain good communication is a must
**Desired Skills \& Experience**
* Five years related experience or equivalent.
* Two years of telecommunications experience
* Knowledge of mobile devices in an enterprise including iPads iPhones Android devices
* Understanding of PCI compliance and certificates
* Familiarity with Ruckus APs and Meraki APs administration
* Understanding of IP Networking and troubleshooting
* Familiarity with hotel applications such as: PMS\-Opera; POS\-Micros; Revenue Management\-Ideas; Building Management HotSOS Safelock InnComm and more; Sales Delphi/SalesForce
* A\+ Certification
* MCSE / MCDST / A\+ certification(s)
* ACSP certification(s)|~|indeed,in-908e40df617013b9|~|IT Support Internship (Summer) — Lalor Family Dental|~|Lalor Family Dental|~|Not Provided|~|Not Provided|~|INTERNSHIP|~|False|~|USD|~|16.0|~|18.0|~|2025-04-15|~|Johnson City|~|NY|~|US|~|https://www.indeed.com/viewjob?jk=908e40df617013b9|~|**Join the growing team at Lalor Family Dental** a second\-generation family\-owned healthcare practice with over 60 years of experience in delivering exceptional patient care. We are seeking motivated tech\-savvy individuals for our **IT Support Internship** designed for those eager to gain real\-world IT experience in a dynamic multi\-location healthcare environment.
This is a **paid summer internship** ideal for students pursuing a career in IT systems administration or healthcare technology. Whether you're exploring the field or looking to build your resume this hands\-on opportunity offers a unique blend of technical training mentorship and meaningful work.
**Why Intern at Lalor Family Dental?**
* Work in a **collaborative family\-owned healthcare practice**
* Gain **hands\-on experience** supporting real IT systems and end\-users
* Shadow seasoned IT professionals in a **fast\-paced healthcare environment**
* Participate in IT projects and infrastructure design
* Named a **Great Place to Work** and **\#18 in Fortunes Best Workplaces in Health Care**
* Fun company culture with **team events** and a strong focus on **work\-life balance**
**Key Responsibilities:**
* Assist with **IT support tickets** and troubleshooting of hardware/software issues
* Shadow and support setup of **workstations mobile devices printers and medical equipment**
* Learn and participate in **network and server maintenance**
* Support system audits updates and performance tracking
* Help deploy IT equipment and assist with **asset management across six locations**
* Contribute to a **capstone project** aimed at improving IT operations
**Qualifications:**
* High school diploma or GED required
* Currently pursuing a degree in Information Technology or related field (preferred)
* Strong interest in **IT technology and healthcare**
* Basic understanding of **computers networking and troubleshooting**
* Excellent communication and problem\-solving skills
* Ability to work independently and in a collaborative team setting
**Internship Benefits:**
* **Mentorship** from experienced IT Systems Support staff
* **Real\-world experience** in a healthcare IT environment
* Opportunity to develop technical communication and project management skills
* Supportive team culture with **regular check\-ins and career development**
* Internship completion letter and experience for **resume or school credit**
**Ready to Launch Your Career in IT?**
Apply today to join Lalor Family Dentals IT team and gain the hands\-on experience that will set you apart. Here your learning growth and future in tech truly matter.
**Lalor Family Dental is an equal\-opportunity employer** committed to creating an inclusive and diverse team environment.|~|indeed,in-4238c0f342b06c39|~|Help Desk Associate|~|Initiate Government Solutions|~|Not Provided|~|Not Provided|~|Not Provided|~|True|~|USD|~|44615.0|~|55920.0|~|2025-04-15|~|Washington|~|DC|~|US|~|https://www.indeed.com/viewjob?jk=4238c0f342b06c39|~|Description:
Founded in 2007 Initiate Government Solutions (IGS) a Woman Owned Small Business. We are a fully remote IT services provider that delivers innovative Enterprise IT and Health Services solutions across the federal sector. Our focus is on data analytics health informatics cloud migration and the modernization of federal information systems.
IGS uses ISO 9001:2015 20000\-1:2018 27001:2013 28001:2007 CMMI/SVC3 CMMI/DEV3 best practices and PMBOK® methods to provide clients with a strategy to build solid foundations to grow capabilities and revenue. Our range of IT services and delivery methodologies are tailored to our customers unique needs to achieve maximum value.
IGS is currently recruiting for a **Help Desk Associate** to support the Department of Veterans Affairs.
**This position is pending contract award applicants will be reviewed post\-award.**
**Assignment of Work and Travel:**
This is a remote access assignment. Candidates will work remotely daily and will remotely access VA systems and therein use approved VA provided communications systems. Travel is not required; however the candidate may be required to attend onsite client meetings as requested.
**Responsibilities and Duties (Included but not limited to):**
* Provide help desk support assistance to the established Enterprise Service Desk (ESD) for managed access
* Log help\-desk tickets into the appropriate existing workload management tracking system
* Respond to email and phone inquiries from the ESD Helpdesk or customer
* Provide user training and concierge services associated with access applications by creating workflow process documents and or using MS Word PowerPoint or ad hoc
* Assess what types of data are available in the VA and what data is being requested to ensure requestors are only requesting data that they need to perform duties
Requirements:
* Bachelors degree in computer science Engineering or other technical discipline. (Bachelors Degree \- Can be substituted for an Associates Degree and two (2\) additional years of relevant experience or four (4\) additional years of relevant experience and High School Diploma/GED. Associates degree \- Can be substituted for High School Diploma/GED and two (2\) additional years relevant experience.)
* 3 years relevant experience including significant experience in an help desk environment preferably with the Dept. of Veterans Affairs
* Must have experience in the analysis of IT business and information environment activities and events.
* Must have experience in finding trends errors and reviewing data with report writing skills.
* Must have reliable internet service that allows for effective telecommuting
* Must be able to obtain and maintain a VA Public Trust clearance
* Excellent verbal and written communication skills
* Must be eligible to work in the United States without sponsorship due to clearance requirement
**Preferred Qualifications and Core Competencies:**
* Active VA Public Trust
* Experience supporting Department of Veterans Affairs and/or other federal organizations
* Prior successful experience working in a remote environment
**Successful IGS employees embody the following Core Values:**
* **Integrity Honesty and Ethics:** We conduct our business with the highest level of ethics. Doing things like being accountable for mistakes accepting helpful criticism and following through on commitments to ourselves each other and our customers.
* **Empathy Emotional Intelligence**: How we interact with others including peers colleagues stakeholders and customers. We take collective responsibility to create an environment where colleagues and customers feel valued included and respected. We work within a diverse integrated and collaborative team to drive towards accomplishing the larger mission. We conscientiously and meticulously learn about our customers and end\-users business drivers and challenges to ensure solutions meet not only technical needs but also support their mission.
* **Strong Work Ethic (Reliability Dedication Productivity):** We are driven by a strong self\-motivated and results\-driven work ethic. We are reliable accountable proactive and tenacious and will do what it takes to get the job done.
* **Life\-Long Learner (Curious Perspective Goal Orientated):** We challenge ourselves to continually learn and improve ourselves. We strive to be an expert in our field continuously honing our craft and finding solutions where others see problems.
**Compensation:** There are a host of factors that can influence final salary including but not limited to geographic location Federal Government contract labor categories and contract wage rates relevant prior work experience specific skills and competencies education and certifications.
**Benefits:** Initiate Government Solutions offers competitive compensation and a robust benefits package including comprehensive medical dental and vision care matching 401K and profit sharing paid time off training time for personal development flexible spending accounts employer\-paid life insurance employer\-paid short and long term disability coverage an education assistance program with potential merit increases for obtaining a work\-related certification employee recognition and referral programs spot bonuses and other benefits that help provide financial protection for the employee and their family.
Initiate Government Solutions participates in the Electronic Employment Verification Program.|~|indeed,in-c09e1d318a6a0bdc|~|IT Help Desk Technician|~|Ramaz School|~|Not Provided|~|Not Provided|~|FULL_TIME|~|False|~|USD|~|24.0|~|27.0|~|2025-04-15|~|New York|~|NY|~|US|~|https://www.indeed.com/viewjob?jk=c09e1d318a6a0bdc|~|**About The Ramaz School:**
The Ramaz School is a prestigious Jewish day school renowned for its integration of rich Jewish traditions with superior academic achievement. Located in the vibrant heart of New York City Ramaz is dedicated to nurturing individual talents fostering social responsibility and encouraging community service. We are seeking a motivated and tech\-savvy Help Desk Technician to join our IT department. This role is crucial for providing top\-notch technical support to our dynamic community of educators and students.
**Position Summary:**
As a Help Desk Technician you will be the go\-to person for faculty staff and students experiencing IT\-related issues. This position plays a key role in ensuring the smooth functioning of our educational technologies and systems. You will be responsible for troubleshooting diagnosing and resolving technical problems thus ensuring minimal disruption to our educational activities. Furthermore you will assist with AV maintenance and provide support during school events guaranteeing all presentations and performances are executed flawlessly.
**Operational Hours:**
\- Monday to Friday 8 AM \- 5 PM
* Occasional evening and weekend support required for school events and critical IT needs.
**Key Responsibilities:**
* Act as the first point of contact for technical assistance via phone or in\-person.
* Troubleshoot and resolve computer software and hardware issues.
* Assist with AV system maintenance setup and troubleshooting for school events.
* Escalate unresolved issues to higher\-level IT support staff.
* Maintain detailed records of IT issues and resolutions.
* Stay updated on the latest system information changes and updates.
* Assist in the installation of new equipment and software across classrooms and administrative offices.
**Qualifications:**
* High School diploma or equivalent; a degree or enrollment in a degree program in Information Technology Computer Science or a related field is a plus.
* Knowledge of Windows/Mac OS computer systems mobile devices and AV technology.
* Ability to diagnose and troubleshoot basic technical problems effectively.
* Strong communication skills and a commitment to excellent customer service.
* Must be available to workfull\-timehours as specified including occasional evenings and weekends.
**Salary Range:**
\- $24 \- $27 per hour commensurate with experience and qualifications.
**Why Join** **The** **Ramaz School?**
* Competitive compensation within the specified salary range.
* Work in a leading educational environment that values technology and innovation.
* Opportunities for professional growth in educational technology and AV support.
* Bepartof a supportive community that promotes learning and development.|~|indeed,go-3O6aUUjO8LS9FWVJAAAAAA==|~|Help Desk / Customer Support Lead|~|Cormac|~|Not Provided|~|Not Provided|~|CONTRACT|~|True|~||~||~||~|2025-04-15|~|Leesburg|~|VA|~|Unknown|~|https://www.monster.com/job-openings/help-desk-customer-support-lead-leesburg-va--a6bfa827-0fe2-4c03-8965-704c6f205929?utm_campaign=google_jobs_apply&utm_source=google_jobs_apply&utm_medium=organic|~|Help Desk/Customer Support Lead
CORMAC is seeking a Help Desk/Customer Support Lead to support the Department of Health and Human Services (HHS) Office of Head Start (OHS) Aligned Monitoring System 2.0 Digital Services Platform (IT-AMS). IT-AMS is a data management system which supports an innovative comprehensive and integrated approach to recipient oversight allowing OHS to effectively gain understanding of recipient compliance identify and understand the differences in program performance among OHS programs and to ensure the effective use of federal funds. This is a Hybrid (Remote-First) role where the candidate must be local to the Washington Metropolitan area encompassing the District of Columbia Maryland and Virginia.
Essential Duties & Responsibilities?
Daily duties will vary according to project needs with job responsibilities including:?
• Provide helpdesk support to teams using OHS monitoring systems
• Track and analyze rising trending and high-volume Helpdesk issues to coordinate and support intuitive software enhancements and develop training for the use of those options.
• Generate and present regular reports on Help Desk performance user satisfaction and ticket resolution metrics to stakeholders.
• Participate in release and deployment planning to ensure Help Desk preparedness and seamless user transitions.
• Act as the primary liaison between end users and technical teams ensuring accurate communication of user needs and system limitations.
• Support change management and user adoption strategies for new features or updates to the system.
• Manage a Help Desk team
Required Skills & Experience?
• Bachelor s Degree or higher in Information Management Information Systems Computer Science or equivalent field.
• Must have understanding of multi-tiered help desk operations and experience supervising a Help Desk team
• Experience analyzing support patterns and sharing the feedback with the development team
• Experience collaborating with the project team members to address recurring support issues via new or revised product stories and design work
• Experience in technical support in product or project management
• Experience with ServiceNow ticketing system for help desk operations incident tracking and change management.
• Demonstrable experience with federal security standards (FISMA NIST SP 800-53 etc) as they relate to user access and incident handling
• Working knowledge of RESTful API troubleshooting
• Basic Database querying proficiency
• Proficiency using and interpreting SLA dashboards and support metrics
Preferred Skills & Experience?
• Knowledge of CLASS or other federally mandated reviewer scoring systems
• Understanding of FedRAMP-authorized cloud environments (AWS GovCloud Azure Government)
• Experience supporting users on data visualization platforms (e.g. Tableau or similar)
• Experience in a federal Agile DevSecOps environment with exposure to CI/CD pipelines and cross-system API integration troubleshooting.
Why CORMAC??
At CORMAC we leverage the power of data management and analytics to enable our customers to achieve their strategic goals. With over 20 years of experience in health information technology (HIT) human-centered design principles and Agile development methodologies CORMAC delivers complex digital solutions to solve some of the most challenging problems facing public healthcare programs today.?
As a US Federal Government contractor in the public healthcare sector our work is impactful and cutting-edge while being performed in a supportive collaborative and welcoming environment. We offer flexible work schedules with remote hybrid or fully in-person workplace options to empower our employees to decide the workplace most suitable for them. At CORMAC we have a highly diverse workforce and believe a work environment is a place where creativity collaboration enthusiasm and innovation happen regardless of location.?
Position Requires Employment Eligibility Verification /E-Verify Participation/EEO?
As an Equal Employment Opportunity employer CORMAC provides equal employment opportunity to all employees and applicants without regard to an individual's protected status including race/ethnicity color national origin ancestry religion creed age gender gender identity/expression sexual orientation marital status parental status including pregnancy childbirth or related conditions disability military service veteran status genetic information or any other protected status.?
About the Company:
Cormac|~|google,go-qzGAEQlq1-gsmD_KAAAAAA==|~|Help Desk Technician|~|LMI Consulting LLC|~|Not Provided|~|Not Provided|~|CONTRACT|~|True|~||~||~||~|2025-04-15|~|McLean|~|VA|~|Unknown|~|https://www.whatjobs.com/gfj/1934920528?utm_campaign=google_jobs_apply&utm_source=google_jobs_apply&utm_medium=organic|~|Help Desk Technician Job Locations US-Remote Job ID 2025-12517 # of Openings 2 Category Information Technology Overview
LMI is seeking a skilled ATIS Help Desk Technician to provide Tier 2 and Tier 3 technical support for the RFMSS (Range Facility Management Support System) and ATMC (Army Training Management Capability) applications within the Army Training Information System (ATIS). This role is ideal for individuals with strong problem-solving skills and a passion for delivering high-quality customer service while supporting mission-critical applications for the U.S. Army.
At LMI we're reimagining the path from insight to outcome at The New Speed of Possible. Combining a legacy of over 60 years of federal expertise with our innovation ecosystem we minimize time to value and accelerate mission success. We energize the brightest minds with emerging technologies to inspire creative solutions and push the boundaries of capability. LMI advances the pace of progress enabling our customers to thrive while adapting to evolving mission needs.
Responsibilities Provide Tier 2 and Tier 3 technical support for RFMSS and ATMC users via phone email and ticketing systems. Troubleshoot application network and system-related issues escalating unresolved problems as necessary. Assist users with login issues password resets and account management. Document reported issues and resolutions in the ticketing system to support knowledge management. Conduct user training sessions and develop instructional materials on RFMSS and ATMC features and best practices. Collaborate with developers system administrators and cybersecurity teams to resolve recurring issues and improve system functionality. Ensure compliance with security protocols policies and guidelines related to ATIS RFMSS and ATMC operations. Participate in system updates testing and implementation efforts to minimize service disruptions. Travel required once per quarter for a four-day PI Planning event. Qualifications Associate's or Bachelor's degree in Information Technology Computer Science or a related field (or equivalent experience). 1-3 years of experience in a help desk or technical support role preferably in a Tier 2 or Tier 3 capacity. Experience supporting RFMSS ATMC or similar military training and range management systems is highly desirable. Strong troubleshooting skills and ability to communicate technical concepts to non-technical users. Familiarity with ITSM ticketing systems remote troubleshooting tools and enterprise support environments. Ability to work independently prioritize tasks and manage multiple support requests efficiently. Security+ or other relevant IT certifications are preferred. Knowledge of Army training systems DoD networks and cybersecurity best practices is a plus.
Disclaimer:
The salary range displayed represents the typical salary range for this position and is not a guarantee of compensation. Individual salaries are determined by various factors including but not limited to location internal equity business considerations client contract requirements and candidate qualifications such as education experience skills and security clearances.
LMI is an Equal Opportunity Employer. LMI is committed to the fair treatment of all and to our policy of providing applicants and employees with equal employment opportunities. LMI recruits hires trains and promotes people without regard to race color religion sex sexual orientation gender identity national origin pregnancy disability age protected veteran status citizenship status genetic information or any other characteristic protected by applicable federal state or local law. If you are a person with a disability needing assistance with the application process please contact
Colorado Residents: In any materials you submit you may redact or remove age-identifying information such as age date of birth or dates of school attendance or graduation. You will not be penalized for redacting or removing this information.
Need help finding the right job? We can recommend jobs specifically for you! Click here to get started.|~|google,in-1204f360ed401e85|~|IT Support Technician Hospitality|~|Edge Communications|~|Not Provided|~|Not Provided|~|Not Provided|~|True|~|USD|~|70000.0|~|80000.0|~|2025-04-15|~|Honolulu|~|HI|~|US|~|https://www.indeed.com/viewjob?jk=1204f360ed401e85|~|Description:
**IT Support Technician Hospitality**
**Reports to: IT Services**
**Location: Honolulu**
**Company Description**
Edge provides integrated managed voice and data technology systems and services for small/medium businesses and enterprises.
**Position Description**
As an IT Support Technician you will be part of a team of IT professionals who provide onsite \& remote support for all facets of the IT ecosystem. Our "white\-glove" 24/7 support program specializes in industries where attention to detail and timely response is mission critical. Our hospitality division caters to high\-end large\-scale boutique hotels restaurants and nightclubs whose staff and patrons expect industry\-leading support. This is a fast\-paced interactive hands\-on role where you must "dress to impress' and give 100% daily.
As part of a team that supports multiple properties in several states we are looking for people who are self\-starters and can work remotely as well. You must manage your workload each day and be able to prioritize each task based on each unique situation. Using cutting\-edge industry remote management monitoring and access tools you will be assisted by teams in other regions and may be asked to do the same for them.
**Primary Responsibilities**
* Desktop support for hardware and software troubleshooting
* Willingness to learn industry\-specific and proprietary management systems
* Setup deploy and maintain end\-user equipment
* Perform network administration functions user account permissions Active Directory changes
* Follow up with clients to ensure resolution is complete and satisfactory
* Maintain accurate thorough and timely information in ticketing system
* Research and resolve problems through all IT functions
* Collaborate with peers to form technical solutions
* Completion of day\-to\-day help desk support requests and assigned projects that require interaction with other divisions of our company
Requirements:
**Required Skills**
* Ability to provide on\-site \& remote desktop support to customers.
* Ability to use remote support tools like VNC LogMeIn RDP etc.
* Strong troubleshooting abilities
* Ability to use our remote management platform for workstation configuration status testing
* Familiarity supporting (not engineering) TCP/IP cables IP phones workstation connectivity printer connectivity POS devices and Active Directory administration
* Ability to be responsible dependable and committed to building a long\-term career at Edge Communications.
* Being a goal\-driven team player with solid organizational skills and a keen attention to detail.
* Independent self\-starting attitude with the willingness to share knowledge.
* Thorough knowledge of all Windows server and desktop operating systems
* Understanding of Hotel property management \& Point of Sale applications
* Thorough knowledge of PC server hardware and configuration including related peripherals.
* Thorough knowledge of Word Excel PowerPoint Outlook Active Directory and Exchange
* Strong customer service and problem\-solving skills including the ability to provide diligent prompt and courteous responses to users questions or PC issues.
* Ability to function effectively in a fast\-paced environment
* Willingness to travel occasionally
* Ability to multi\-task and maintain good communication is a must
**Desired Skills \& Experience**
* Five years related experience or equivalent.
* Two years of telecommunications experience
* Knowledge of mobile devices in an enterprise including iPads iPhones Android devices
* Understanding of PCI compliance and certificates
* Familiarity with Ruckus APs and Meraki APs administration
* Understanding of IP Networking and troubleshooting
* Familiarity with hotel applications such as: PMS\-Opera; POS\-Micros; Revenue Management\-Ideas; Building Management HotSOS Safelock InnComm and more; Sales Delphi/SalesForce
* A\+ Certification
* MCSE / MCDST / A\+ certification(s)
* ACSP certification(s)|~|indeed,in-b70651ea69f7c429|~|Bi-lingual Help Desk|~|Intone Networks|~|Not Provided|~|Not Provided|~|CONTRACT|~|False|~|USD|~|53115.0|~|73952.0|~|2025-04-15|~|New York|~|NY|~|US|~|https://www.indeed.com/viewjob?jk=b70651ea69f7c429|~|Role: Bi\-lingual Help Desk Location: New York NY (Hybrid)|~|indeed,in-908e40df617013b9|~|IT Support Internship (Summer) — Lalor Family Dental|~|Lalor Family Dental|~|Not Provided|~|Not Provided|~|INTERNSHIP|~|False|~|USD|~|16.0|~|18.0|~|2025-04-15|~|Johnson City|~|NY|~|US|~|https://www.indeed.com/viewjob?jk=908e40df617013b9|~|**Join the growing team at Lalor Family Dental** a second\-generation family\-owned healthcare practice with over 60 years of experience in delivering exceptional patient care. We are seeking motivated tech\-savvy individuals for our **IT Support Internship** designed for those eager to gain real\-world IT experience in a dynamic multi\-location healthcare environment.
This is a **paid summer internship** ideal for students pursuing a career in IT systems administration or healthcare technology. Whether you're exploring the field or looking to build your resume this hands\-on opportunity offers a unique blend of technical training mentorship and meaningful work.
**Why Intern at Lalor Family Dental?**
* Work in a **collaborative family\-owned healthcare practice**
* Gain **hands\-on experience** supporting real IT systems and end\-users
* Shadow seasoned IT professionals in a **fast\-paced healthcare environment**
* Participate in IT projects and infrastructure design
* Named a **Great Place to Work** and **\#18 in Fortunes Best Workplaces in Health Care**
* Fun company culture with **team events** and a strong focus on **work\-life balance**
**Key Responsibilities:**
* Assist with **IT support tickets** and troubleshooting of hardware/software issues
* Shadow and support setup of **workstations mobile devices printers and medical equipment**
* Learn and participate in **network and server maintenance**
* Support system audits updates and performance tracking
* Help deploy IT equipment and assist with **asset management across six locations**
* Contribute to a **capstone project** aimed at improving IT operations
**Qualifications:**
* High school diploma or GED required
* Currently pursuing a degree in Information Technology or related field (preferred)
* Strong interest in **IT technology and healthcare**
* Basic understanding of **computers networking and troubleshooting**
* Excellent communication and problem\-solving skills
* Ability to work independently and in a collaborative team setting
**Internship Benefits:**
* **Mentorship** from experienced IT Systems Support staff
* **Real\-world experience** in a healthcare IT environment
* Opportunity to develop technical communication and project management skills
* Supportive team culture with **regular check\-ins and career development**
* Internship completion letter and experience for **resume or school credit**
**Ready to Launch Your Career in IT?**
Apply today to join Lalor Family Dentals IT team and gain the hands\-on experience that will set you apart. Here your learning growth and future in tech truly matter.
**Lalor Family Dental is an equal\-opportunity employer** committed to creating an inclusive and diverse team environment.|~|indeed,in-4238c0f342b06c39|~|Help Desk Associate|~|Initiate Government Solutions|~|Not Provided|~|Not Provided|~|Not Provided|~|True|~|USD|~|44615.0|~|55920.0|~|2025-04-15|~|Washington|~|DC|~|US|~|https://www.indeed.com/viewjob?jk=4238c0f342b06c39|~|Description:
Founded in 2007 Initiate Government Solutions (IGS) a Woman Owned Small Business. We are a fully remote IT services provider that delivers innovative Enterprise IT and Health Services solutions across the federal sector. Our focus is on data analytics health informatics cloud migration and the modernization of federal information systems.
IGS uses ISO 9001:2015 20000\-1:2018 27001:2013 28001:2007 CMMI/SVC3 CMMI/DEV3 best practices and PMBOK® methods to provide clients with a strategy to build solid foundations to grow capabilities and revenue. Our range of IT services and delivery methodologies are tailored to our customers unique needs to achieve maximum value.
IGS is currently recruiting for a **Help Desk Associate** to support the Department of Veterans Affairs.
**This position is pending contract award applicants will be reviewed post\-award.**
**Assignment of Work and Travel:**
This is a remote access assignment. Candidates will work remotely daily and will remotely access VA systems and therein use approved VA provided communications systems. Travel is not required; however the candidate may be required to attend onsite client meetings as requested.
**Responsibilities and Duties (Included but not limited to):**
* Provide help desk support assistance to the established Enterprise Service Desk (ESD) for managed access
* Log help\-desk tickets into the appropriate existing workload management tracking system
* Respond to email and phone inquiries from the ESD Helpdesk or customer
* Provide user training and concierge services associated with access applications by creating workflow process documents and or using MS Word PowerPoint or ad hoc
* Assess what types of data are available in the VA and what data is being requested to ensure requestors are only requesting data that they need to perform duties
Requirements:
* Bachelors degree in computer science Engineering or other technical discipline. (Bachelors Degree \- Can be substituted for an Associates Degree and two (2\) additional years of relevant experience or four (4\) additional years of relevant experience and High School Diploma/GED. Associates degree \- Can be substituted for High School Diploma/GED and two (2\) additional years relevant experience.)
* 3 years relevant experience including significant experience in an help desk environment preferably with the Dept. of Veterans Affairs
* Must have experience in the analysis of IT business and information environment activities and events.
* Must have experience in finding trends errors and reviewing data with report writing skills.
* Must have reliable internet service that allows for effective telecommuting
* Must be able to obtain and maintain a VA Public Trust clearance
* Excellent verbal and written communication skills
* Must be eligible to work in the United States without sponsorship due to clearance requirement
**Preferred Qualifications and Core Competencies:**
* Active VA Public Trust
* Experience supporting Department of Veterans Affairs and/or other federal organizations
* Prior successful experience working in a remote environment
**Successful IGS employees embody the following Core Values:**
* **Integrity Honesty and Ethics:** We conduct our business with the highest level of ethics. Doing things like being accountable for mistakes accepting helpful criticism and following through on commitments to ourselves each other and our customers.
* **Empathy Emotional Intelligence**: How we interact with others including peers colleagues stakeholders and customers. We take collective responsibility to create an environment where colleagues and customers feel valued included and respected. We work within a diverse integrated and collaborative team to drive towards accomplishing the larger mission. We conscientiously and meticulously learn about our customers and end\-users business drivers and challenges to ensure solutions meet not only technical needs but also support their mission.
* **Strong Work Ethic (Reliability Dedication Productivity):** We are driven by a strong self\-motivated and results\-driven work ethic. We are reliable accountable proactive and tenacious and will do what it takes to get the job done.
* **Life\-Long Learner (Curious Perspective Goal Orientated):** We challenge ourselves to continually learn and improve ourselves. We strive to be an expert in our field continuously honing our craft and finding solutions where others see problems.
**Compensation:** There are a host of factors that can influence final salary including but not limited to geographic location Federal Government contract labor categories and contract wage rates relevant prior work experience specific skills and competencies education and certifications.
**Benefits:** Initiate Government Solutions offers competitive compensation and a robust benefits package including comprehensive medical dental and vision care matching 401K and profit sharing paid time off training time for personal development flexible spending accounts employer\-paid life insurance employer\-paid short and long term disability coverage an education assistance program with potential merit increases for obtaining a work\-related certification employee recognition and referral programs spot bonuses and other benefits that help provide financial protection for the employee and their family.
Initiate Government Solutions participates in the Electronic Employment Verification Program.|~|indeed,in-9f7403b0512eed78|~|Senior Technical Analyst Yardi Help Desk - REMOTE|~|Welltower Inc|~|Not Provided|~|Not Provided|~|FULL_TIME|~|True|~|USD|~|73744.0|~|108594.0|~|2025-04-15|~|Unknown|~|TX|~|US|~|https://www.indeed.com/viewjob?jk=9f7403b0512eed78|~|**SUMMARY**
The Senior Technical Analyst Yardi Help Desk is an experienced and dynamic team player who will be on the front line of support for stakeholders using the Yardi Senior product suite. The ideal candidate possesses the ability to work cross\-functionally be detailed\-oriented to provide advanced technical support to stakeholders troubleshooting complex issues leading escalations and ensuring efficient resolution of technical problems. The Senior Technical Analyst Help Desk will be required to work within a high demand performance driven environment that focuses on implementing scalable solutions that are aligned with the companys overall business strategy.
**KEY RESPONSIBILITIES**
* Develops and leverages relationships with internal and external stakeholders to meet strategic business objectives
* Provide expert\-level technical support for escalated help desk issues
* Troubleshoot complex issues and offer solutions across different modules within the Yardi Senior product suite
* Owns and manages high\-priority or escalated incidents to resolution ensuring that issues are tracked communicated effectively to stakeholders and resolved in a timely manner
* Responds to inbound support requests related to the Yardi Senior product suite via help desk platform phone email or chat
* Troubleshoot and resolve technical issues related to the platform ensuring a high level of customer satisfaction
* Document prioritize and track all inquiries and issues in the help desk ticketing system (e.g. JIRA ServiceNow Zendesk)
* Stays up to date on new features and product updates within the Yardi Senior product suite to maintain a high level of technical knowledge and service excellence
* Strives to meet or exceed service level agreements (SLAs) for ticket resolution response time and customer satisfaction
* Collaborates with internal support teams to resolve challenges
* Understands and fosters cross\-program and cross\-functional dependencies to champion execution success and maximize value capture
* Develops regular and thorough status communications for senior leadership and stakeholders
* Anticipates and mitigates risks dependencies and impediments to facilitate resolutions
**OTHER DUTIES**
Please note this job description is not designed to provide a comprehensive listing of activities duties or responsibilities that are required of this role. Duties responsibilities and activities may change at any time with or without notice.
**TRAVEL**
Out\-of\-area and overnight travel should be expected as outlined in specific projects for which this role will engage.
**MINIMUM REQUIREMENTS**
**Skills / Specialized Knowledge:**
* Ability to manage portfolios of work
* Solid understanding of project management and agile practices with the ability to teach and coach others
* Keen ability to engage and work with different teams
* Strong interpersonal conflict management and communications skills
* Effective documentation and reporting skills
**Experience:**
* At least 5 years of experience in technical support help desk or IT roles with at least 2 years in a senior capacity
* Strong knowledge of the Yardi Senior product suite is highly preferred
* Experience with help desk platforms ticketing systems and customer relationship management tools (JIRA ServiceNow Zendesk)
* Proficient troubleshooting skills with a solid understanding of web\-based applications SaaS products and general IT systems
* Strong knowledge and expertise with property management software (Yardi) or experience in the senior housing industry is a plus
* Project Management and Technical Support experience
**Education:**
* Bachelors degree in computer science information technology or related field or equivalent work experience
* Relevant certifications (ITIL Help Desk Management) are a plus
* Agile Six Sigma or PMP certification strongly preferred
Applicants must be able to pass a pre\-employment drug screen.
**WHAT WE OFFER**
* Competitive Base Salary \+ Annual Bonus
* Generous Paid Time Off and Holidays
* Employer\-matching 401(k) Program \+ Profit Sharing Program
* Student Debt Program well contribute up to $10000 towards your student loans!
* Tuition Assistance Program
* Employee Stock Purchase Program purchase shares at a 15% discount
* Comprehensive and progressive Medical/Dental/Vision options
* And much more! https://welltower.com/newsroom/careers/
**ABOUT WELLTOWER**
Welltower® Inc. (NYSE: WELL) an S\&P 500 company is the world's preeminent residential wellness and healthcare infrastructure company. Our portfolio of 1500\+ Seniors and Wellness Housing communities is positioned at the intersection of housing healthcare and hospitality creating vibrant communities for mature renters and older adults in the United States United Kingdom and Canada. We also seek to support physicians in our Outpatient Medical buildings with the critical infrastructure needed to deliver quality care.
Our real estate portfolio is unmatched located in highly attractive micro\-markets with stunning built environments. Yet we are an unusual real estate organization as we view ourselves as a product company in a real estate wrapper driven by relationships and unconventional culture.
Through our disciplined approach to capital allocation powered by our data science platform and superior operating results driven by the Welltower Business System we aspire to deliver long\-term compounding of per share growth and returns for our existing investors our North Star.
\#LI\-REMOTE
Equal Opportunity Employer/Protected Veterans/Individuals with Disabilities
The contractor will not discharge or in any other manner discriminate against employees or applicants because they have inquired about discussed or disclosed their own pay or the pay of another employee or applicant. However employees who have access to the compensation information of other employees or applicants as a part of their essential job functions cannot disclose the pay of other employees or applicants to individuals who do not otherwise have access to compensation information unless the disclosure is (a) in response to a formal complaint or charge (b) in furtherance of an investigation proceeding hearing or action including an investigation conducted by the employer or (c) consistent with the contractors legal duty to furnish information. 41 CFR 60\-1\.35(c)|~|indeed
Can't render this file because it contains an unexpected character in line 16 and column 153.

194
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,12 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "python-jobspy"
version = "1.1.78"
version = "1.1.82"
description = "Job scraper for LinkedIn, Indeed, Glassdoor, ZipRecruiter & Bayt"
authors = ["Cullen Watson <cullen@cullenwatson.com>", "Zachary Hampton <zachary@zacharysproducts.com>"]
homepage = "https://github.com/cullenwatson/JobSpy"
readme = "README.md"
keywords = [ "jobs-scraper", "linkedin", "indeed", "glassdoor", "ziprecruiter", "bayt"]
keywords = [ "jobs-scraper", "linkedin", "indeed", "glassdoor", "ziprecruiter", "bayt", "naukri"]
[[tool.poetry.packages]]
include = "jobspy"
@@ -17,14 +17,14 @@ include = "jobspy"
line-length = 88
[tool.poetry.dependencies]
python = "^3.10 || ^3.12"
python = "^3.10"
requests = "^2.31.0"
beautifulsoup4 = "^4.12.2"
pandas = "^2.1.0"
NUMPY = "1.26.3"
numpy = ">=1.26.0"
pydantic = "^2.3.0"
tls-client = "^1.0.1"
markdownify = "^0.13.1"
markdownify = "^1.1.0"
regex = "^2024.4.28"
[tool.poetry.group.dev.dependencies]

View File

@@ -1,118 +0,0 @@
annotated-types==0.7.0
anyio==4.6.2.post1
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
arrow==1.3.0
asttokens==2.4.1
async-lru==2.0.4
attrs==24.2.0
babel==2.16.0
beautifulsoup4==4.12.3
black==24.10.0
bleach==6.1.0
certifi==2024.8.30
cffi==1.17.1
cfgv==3.4.0
charset-normalizer==3.4.0
click==8.1.7
comm==0.2.2
debugpy==1.8.7
decorator==5.1.1
defusedxml==0.7.1
distlib==0.3.9
executing==2.1.0
fastjsonschema==2.20.0
filelock==3.16.1
fqdn==1.5.1
h11==0.14.0
httpcore==1.0.6
httpx==0.27.2
identify==2.6.1
idna==3.10
ipykernel==6.29.5
ipython==8.28.0
ipywidgets==8.1.5
isoduration==20.11.0
jedi==0.19.1
Jinja2==3.1.4
json5==0.9.25
jsonpointer==3.0.0
jsonschema==4.23.0
jsonschema-specifications==2024.10.1
jupyter==1.1.1
jupyter-console==6.6.3
jupyter-events==0.10.0
jupyter-lsp==2.2.5
jupyter_client==8.6.3
jupyter_core==5.7.2
jupyter_server==2.14.2
jupyter_server_terminals==0.5.3
jupyterlab==4.2.5
jupyterlab_pygments==0.3.0
jupyterlab_server==2.27.3
jupyterlab_widgets==3.0.13
markdownify==0.13.1
MarkupSafe==3.0.2
matplotlib-inline==0.1.7
mistune==3.0.2
mypy-extensions==1.0.0
nbclient==0.10.0
nbconvert==7.16.4
nbformat==5.10.4
nest-asyncio==1.6.0
nodeenv==1.9.1
notebook==7.2.2
notebook_shim==0.2.4
numpy==1.26.3
overrides==7.7.0
packaging==24.1
pandas==2.2.3
pandocfilters==1.5.1
parso==0.8.4
pathspec==0.12.1
pexpect==4.9.0
platformdirs==4.3.6
pre_commit==4.0.1
prometheus_client==0.21.0
prompt_toolkit==3.0.48
psutil==6.1.0
ptyprocess==0.7.0
pure_eval==0.2.3
pycparser==2.22
pydantic==2.9.2
pydantic_core==2.23.4
Pygments==2.18.0
python-dateutil==2.9.0.post0
-e git+https://github.com/fakebranden/JobSpy@60819a8fcabbd3eaba7741b673023612dc3d3692#egg=python_jobspy
python-json-logger==2.0.7
pytz==2024.2
PyYAML==6.0.2
pyzmq==26.2.0
referencing==0.35.1
regex==2024.9.11
requests==2.32.3
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rpds-py==0.20.0
Send2Trash==1.8.3
setuptools==75.2.0
six==1.16.0
sniffio==1.3.1
soupsieve==2.6
stack-data==0.6.3
terminado==0.18.1
tinycss2==1.3.0
tls-client==1.0.1
tornado==6.4.1
traitlets==5.14.3
types-python-dateutil==2.9.0.20241003
typing_extensions==4.12.2
tzdata==2024.2
uri-template==1.3.0
urllib3==2.2.3
virtualenv==20.27.0
wcwidth==0.2.13
webcolors==24.8.0
webencodings==0.5.1
websocket-client==1.8.0
widgetsnbextension==4.0.13