JobSpy

speedyapplyMIT活跃维护

一个 Python 招聘信息抓取库,能从 LinkedIn、Indeed、Glassdoor 等八个招聘网站批量采集职位数据,省去逐个平台爬取的麻烦。

Jobs scraper library for LinkedIn, Indeed, Glassdoor, ZipRecruiter, Bayt, Naukri, BDJobs & HelloWork

v1.4.1发布于 2026-10-23 05:11 UTC4,685star +24 / 7 天933fork同步于 2026-11-10 11:37 UTC

怎么上手

由 README 提炼,执行前请确认
pip install -U python-jobspy
硬件要求按 README 说明

README 未说明,纯 Python 库,无 GPU 或显存要求

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

一个 Python 库,并发抓取多个招聘网站的职位并汇总成 DataFrame

一个 Python 招聘信息抓取库,能从 LinkedIn、Indeed、Glassdoor 等八个招聘网站批量采集职位数据,省去逐个平台爬取的麻烦。

核心能力

  • 并发抓取 LinkedIn、Indeed、Glassdoor 等八个招聘网站
  • 把抓取结果聚合为 pandas DataFrame,可直接导出 CSV
  • 支持代理轮换,绕过网站封禁
  • 提供 jobspy 命令行工具,参数与 scrape_jobs 一致
  • 附带 mcp-jobspy MCP 服务器,供 AI 客户端调用

容易踩的坑

需要 Python >= 3.10;LinkedIn、Indeed、BDJobs 使用 socks 代理时需额外执行 pip install "requests[socks]";部分招聘网站的搜索结果不含职位描述,需开启 fetch_description 逐条抓取,速度更慢且更易触发限流;Google Jobs 当前不可用

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v1.4.1
  1. v1.4.1最新正式版2026-10-23 · 2026-10-23 05:11 UTC
    • HelloWork returns jobs again after the site changed its result cards (#433)
    • ZipRecruiter returns jobs again after the site moved to a new search page layout; the old layout still works (#435, closes #434)
    • LinkedIn pay read from a job description keeps its cents, and a range with "k" on one side only, like "$90,000 - $120k", is read (#421)

    Thanks @tararuD and @sghanna for the fixes, and @daveroama for the ZipRecruiter report.

  2. v1.4.02026-10-19 · 2026-10-19 18:20 UTC
    查看更新说明
    • New jobspy command: search from the shell, one JSON line per job; -o saves the full records (#422)
    • New MCP server for AI agents: uvx --from "python-jobspy[mcp]" mcp-jobspy, listed in the MCP Registry as io.github.speedyapply/jobspy (#423, #424)
    • jobs.attrs["errors"] and jobs.attrs["warnings"] say which boards failed or changed the search (#419)
    • A blocked board says so: response status code 429 (blocked: use a proxy) (#423)
    • Invalid scrape_jobs arguments raise a one-line ValueError, unknown ones a TypeError (#415)
    • site_name, job_type and description_format take the exact lower-case value (#415)
    • A search with no jobs returns the usual columns with no rows (#418)
    • Logging uses the standard logging module, with loggers named jobspy.<board>; create_logger and set_logger_level are removed (#414)
    • Type hints ship with the package (py.typed) (#416)
    • JobPost fields are the output columns (#418, closes #265)
    • min_amount and max_amount are always floats, and an empty description is None (#416, #418)
    • Each board checks what it reads; a job that fails the check is skipped with a warning (#416)
    • A proxy passed in proxies is used even when the environment sets one (#415)
    • LinkedIn: pay written in the description fills in when the job page shows none, on US searches (#418)
    • HelloWork, Bayt and Naukri parsing fixes (#416, #418)
    • Removed the job types nights and summer and ten countries with no Indeed or Glassdoor site (#415)
    • For code that imports internals: JobResponse, desired_order, extract_salary and format_description are removed; scraper classes take no constructor arguments (#415, #416, #418)
  3. v1.3.02026-10-08 · 2026-10-08 07:21 UTC
    查看更新说明
    • New board: HelloWork (France), site_name="hellowork" (#411)
    • Unknown scrape_jobs arguments warn and suggest the right name; verbose defaults to 1 so warnings show (#402)
    • Google is out of the default boards; site_name="ziprecruiter" is accepted (#402)
    • Proxy lists no longer get challenged on switching proxy; socks5h:// and socks4:// proxies work; ca_cert is used on every board (#402)
    • is_remote no longer marks hybrid jobs remote on Indeed, Glassdoor, Bayt and BDJobs (#403, #405, #407, #408)
    • hours_old no longer returns older jobs on Indeed and Glassdoor, and takes far fewer requests on Bayt and Naukri (#403, #405, #407, #408)
    • A job page that fails is logged and the job is kept, on every board (#404, #405, #406, #407, #408)
    • date_posted is a UTC date on every board; description_format="plain" returns plain text on Indeed, Glassdoor and Bayt (#403, #405, #407)
    • Indeed: "from", "up to" and single-amount salaries keep their numbers; contract and temporary match jobs outside the US; company rating filled (#403)
    • LinkedIn: exact offset, paging stops at the last page, a throttled page is logged; is_remote and job_type warn, since the logged-out search ignores both (#404)
    • Glassdoor: descriptions fetched 25 jobs per request, 100 jobs a page with an exact offset, distance sent, company details filled (#405)
    • ZipRecruiter: nationwide jobs are returned; job_type="internship" works (#406)
    • Bayt: easy_apply uses the site's filter; part time and temporary supported; company size, industry and experience range filled (#407)
    • Naukri: part-time and contract jobs are no longer returned as full time; job_url_direct no longer needs fetch_description (#408)
    • BDJobs: jobs with no location and promoted jobs are returned; apply emails and employer links filled (#408)
    • user_agent is sent as given on every board (#409)
    • For code that imports internals: BaytScraper is now Bayt; CompensationInterval.get_interval, extract_job_type and get_enum_from_value are removed (#405, #407, #409)

    thanks @Kev744

  4. v1.2.02026-10-02 · 2026-10-02 05:27 UTC
    查看更新说明
    • Installs on Python 3.13 and 3.14; dependency pins removed (#390)
    • Glassdoor, ZipRecruiter, Bayt, Naukri and BDJobs return jobs again (#394, #399, #396, #398, #397)
    • New fetch_description flag for boards whose search results don't include the description; replaces linkedin_fetch_description. Glassdoor descriptions now need it (#394)
    • ZipRecruiter, Bayt, Naukri and BDJobs honour more filters (location, hours_old, job_type, is_remote, easy_apply, offset) and fill more fields
    • Indeed: filters combine instead of only one being sent; salaries outside the US (#392)
    • LinkedIn: salary read from the job page; unlisted countries and a missing seniority level no longer crash (#380, #391, #354)
    • One board failing no longer discards the others' jobs: boards log errors instead of raising (#400)
    • One 15 s request timeout and no automatic retries (#400)
    • is_remote on Indeed and LinkedIn no longer guesses from the description (#400)
    • Salaries keep cents (#380)
    • tls-client replaced by curl_cffi (#395)
    • Google returns no jobs: Google Jobs now requires JavaScript (#284)
    • Deprecated, to be removed in 2.0: linkedin_fetch_description, the jobspy.exception classes, Country.US_CANADA (#401)

    thanks @ZuoyunZheng, @lixianphys, @thesandrex, @Kaushik4141, @m3ridian-zero, @berkaygemici, @Recursing, @sankalp-b1401, @codechrl, @FullStackist

  5. v1.1.822026-09-30 · 2026-09-30 08:28 UTC
    查看更新说明

    Published to PyPI on 2025-07-28; this GitHub Release was added afterwards (#338). Version 1.1.81 was never published.

    • BDJobs (Bangladesh) job board (#280)
    • SOCKS5 proxy support (#266)
    • Glassdoor: rotate user agent / update headers for 403 responses (#274)

    thanks @itsShrizon, @Remote-Radical, @letrongtai902

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    JobSpy is a job scraping library that aggregates jobs from popular job boards with one tool:

    • Scrapes job postings from LinkedIn, Indeed, Glassdoor, ZipRecruiter, Bayt, Naukri, BDJobs & HelloWork concurrently
    • Aggregates the job postings in a dataframe
    • Proxy support to bypass blocking

    Installation

    pip install -U python-jobspy
    

    Python version >= 3.10 required

    Usage

    from jobspy import scrape_jobs
    
    jobs = scrape_jobs(
        site_name=["indeed", "linkedin", "zip_recruiter", "glassdoor"],  # also "bayt", "naukri", "bdjobs", "hellowork"
        search_term="software engineer",
        location="San Francisco, CA",
        results_wanted=20,
        hours_old=72,
        country_indeed="USA",
        # fetch_description=True,  # read each job's page on boards whose search results have no description (slower)
        # proxies=["user:pass@host:port", "localhost"],
    )
    print(f"Found {len(jobs)} jobs")
    print(jobs.head())
    jobs.to_csv("jobs.csv", index=False)
    

    Output

    SITE           TITLE                             COMPANY           LOCATION                JOB_TYPE  INTERVAL  MIN_AMOUNT  MAX_AMOUNT  JOB_URL                                            DESCRIPTION
    indeed         Software Engineer                 AMERICAN SYSTEMS  Arlington, VA, USA      None      yearly    150000      200000      https://www.indeed.com/viewjob?jk=5e409e577046...  THIS POSITION COMES WITH A 10K SIGNING BONUS!...
    indeed         Senior Software Engineer          TherapyNotes.com  Philadelphia, PA, USA   fulltime  yearly    110000      135000      https://www.indeed.com/viewjob?jk=da39574a40cb...  About Us TherapyNotes is the national leader i...
    linkedin       Software Engineer - Early Career  Lockheed Martin   Sunnyvale, CA, USA      fulltime  yearly    None        None        https://www.linkedin.com/jobs/view/3693012711      Description:By bringing together people that u...
    linkedin       Full-Stack Software Engineer      Rain              New York, NY, USA       fulltime  yearly    None        None        https://www.linkedin.com/jobs/view/3696158877      Rain’s mission is to create the fastest and ea...
    zip_recruiter  Software Engineer - New Grad      ZipRecruiter      Santa Monica, CA, USA   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, USA        fulltime  hourly    65          75          https://www.ziprecruiter.com/jobs/teksystems-0...  Top Skills' Details• 6 years of Java developme...
    
    

    Command line

    The package installs a jobspy command whose options mirror the scrape_jobs() parameters below; jobspy -h lists them, the output fields and exit codes.

    uv tool install python-jobspy
    
    jobspy "software engineer" --site indeed,linkedin --location Chicago -n 20
    jobspy nurse --site indeed,linkedin --hours-old 24 -o jobs.jsonl
    jobspy "data analyst" --remote --fields title,company,job_url
    

    MCP server

    mcp-jobspy is an MCP server that runs with uvx mcp-jobspy@latest. See mcp/README.md for setup, tools and proxies.

    Standard config works for most clients:

    {
      "mcpServers": {
        "jobspy": {
          "command": "uvx",
          "args": ["mcp-jobspy@latest"]
        }
      }
    }
    

    Parameters for scrape_jobs()

    Optional
    ├── site_name (list|str): 
    |    linkedin, zip_recruiter, indeed, glassdoor, google, bayt, bdjobs, naukri, hellowork
    |    (default is all)
    │
    ├── search_term (str)
    |
    ├── google_search_term (str)
    |     search term for google jobs. This is the only param for filtering google jobs.
    │
    ├── location (str)
    │
    ├── distance (int): 
    |    in miles, default 50
    │
    ├── job_type (str): 
    |    fulltime, parttime, contract, temporary, internship, perdiem, other, volunteer
    │
    ├── proxies (list|str): 
    |    in format ['user:pass@host:port', 'localhost'], or a single proxy as a string
    |    each job board scraper will round robin through the proxies
    |    socks proxies on LinkedIn, Indeed and BDJobs need pip install "requests[socks]"
    |
    ├── is_remote (bool)
    │
    ├── results_wanted (int): 
    |    number of job results to retrieve for each site specified in 'site_name'
    │
    ├── easy_apply (bool): 
    |    filters for jobs that are hosted on the job board site
    |
    ├── user_agent (str): 
    |    override the default user agent which may be outdated
    │
    ├── description_format (str): 
    |    markdown, html, plain (Format type of the job descriptions. Default is markdown.)
    │
    ├── offset (int): 
    |    skips that many results (e.g. 25 skips the first 25 and starts from the 26th)
    │
    ├── hours_old (int): 
    |    filters jobs by the number of hours since the job was posted 
    |    (Glassdoor filters by whole days, so it rounds up to the next day.)
    │
    ├── verbose (int) {0, 1, 2}: 
    |    Controls the verbosity of the runtime printouts 
    |    (0 prints only errors, 1 is errors+warnings, 2 is all logs. Default is 1.)
    │
    ├── fetch_description (bool): 
    |    for boards whose search results don't include the job description: fetches each job's
    |    page for the description and other details (e.g. job type). Without it these are empty
    |    for those boards. Adds one request per job, so use proxies for larger searches
    │
    ├── linkedin_fetch_description (bool): 
    |    deprecated, use fetch_description (still works; removed in 2.0)
    │
    ├── linkedin_company_ids (list[int]): 
    |    searches for linkedin jobs with specific company ids
    |
    ├── country_indeed (str): 
    |    filters the country on Indeed & Glassdoor (default is usa; see below for the names)
    |
    ├── enforce_annual_salary (bool): 
    |    converts wages to annual salary
    |
    ├── ca_cert (str)
    |    path to CA Certificate file for proxies
    

    Supported Countries for Job Searching

    LinkedIn

    LinkedIn searches globally & uses only the location parameter.

    ZipRecruiter

    ZipRecruiter searches for jobs in US/Canada & uses only the location parameter.

    Indeed / Glassdoor

    Indeed & Glassdoor support most countries, chosen with the country_indeed parameter (default usa). Additionally, use the location parameter to narrow down the location, e.g. city & state if necessary.

    You can specify the following countries when searching on Indeed (use the exact name, * indicates support for Glassdoor):

    Argentina*Australia*Austria*Bahrain
    Belgium*Brazil*Canada*Chile
    ChinaColombiaCosta RicaCzech Republic
    DenmarkEcuadorEgyptFinland
    France*Germany*GreeceHong Kong*
    HungaryIndia*IndonesiaIreland*
    IsraelItaly*JapanKuwait
    LuxembourgMalaysia*Mexico*Morocco
    Netherlands*New Zealand*NigeriaNorway
    OmanPakistanPanamaPeru
    PhilippinesPolandPortugalQatar
    RomaniaSaudi ArabiaSingapore*South Africa
    South KoreaSpain*SwedenSwitzerland*
    TaiwanThailandTurkeyUkraine
    United Arab EmiratesUK*USA*Uruguay
    VenezuelaVietnam*

    Bayt

    Bayt searches all countries unless location names one it covers, e.g. "Dubai, UAE" or "Saudi Arabia".

    BDJobs

    BDJobs searches Bangladesh. location takes a division or district, e.g. "Dhaka" or "Chattogram Division".

    Naukri

    Naukri searches India. location takes a city, e.g. "Pune".

    HelloWork

    HelloWork searches France. location takes a French city, postcode or region, e.g. "Lyon". Most jobs are posted in French, so French search terms find more.

    Notes

    • Indeed is the best scraper currently with no rate limiting.
    • Indeed's date_posted is the day a job was added to Indeed, the same date hours_old filters on.
    • Most job board endpoints are capped at around 1000 jobs on a given search.
    • LinkedIn rate limits bursts of requests with a 429, which clears within about a minute. Use proxies for larger searches.
    • Glassdoor rate limits above about 2 requests a second.

    Frequently Asked Questions

    Q: How do I tell a blocked board from one with no jobs?
    A: Failures and ignored options are logged as they happen, and the result's attrs carry the same messages so your code can read them:

    jobs.attrs["errors"]    # {"linkedin": "response status code 429 (blocked: use a proxy)"}
    jobs.attrs["warnings"]  # {"bdjobs": ["job_type internship isn't supported, ignoring it"]}
    

    Q: Why is Indeed giving unrelated roles?
    A: Indeed searches the description too.

    • use - to remove words
    • "" for exact match

    Example of a good Indeed query

    search_term='"engineering intern" software summer (java OR python OR c++) 2025 -tax -marketing'
    

    This searches the description/title and must include software, summer, 2025, one of the languages, engineering intern exactly, no tax, no marketing.


    Q: No results when using "google"?
    A: Google Jobs is currently unavailable.


    Q: Received a response code 429?
    A: The board blocked you for sending too many requests. All of the job board sites are aggressive with blocking. We recommend:

    • Wait some time between scrapes (site-dependent).
    • Try using the proxies param to change your IP address.

    JobPost Schema

    JobPost
    ├── id
    ├── site
    ├── job_url
    ├── job_url_direct
    ├── title
    ├── company
    ├── location
    ├── date_posted
    ├── job_type: fulltime, parttime, internship, contract, temporary, ...
    ├── salary_source: direct_data, description (parsed from posting)
    ├── interval: yearly, monthly, weekly, daily, hourly
    ├── min_amount
    ├── max_amount
    ├── currency
    ├── is_remote
    ├── job_level
    ├── job_function
    ├── listing_type
    ├── emails
    ├── description
    ├── company_industry
    ├── company_url
    ├── company_logo
    ├── company_url_direct
    ├── company_addresses
    ├── company_num_employees
    ├── company_revenue
    ├── company_description
    ├── skills
    ├── experience_range
    ├── company_rating
    ├── company_reviews_count
    ├── vacancy_count
    └── work_from_home_type
    

    A column is empty when the job board doesn't provide it.