如何使用 Python 抓取 Glassdoor:职位、薪资与评论

最后更新于 April 16, 2026

如果你在 2022 年还能顺利运行 Glassdoor 爬虫,而现在却只返回一堆 403,那你并不孤单。各大论坛里都充斥着同一个问题:“有人知道为什么这个爬虫现在不能用了么?”

简单来说:Glassdoor 已经改版得面目全非。Recruit Holdings 于 2025 年 7 月将 Glassdoor 并入 Indeed,裁掉了 ,同时还加强了反爬机制,以至于原生 Selenium 和基于 requests 的爬虫,在 HTML 还没加载出第一个字节之前就会被拦下。截至 2026 年 2 月,Glassdoor 的登录流程已经完全改为 Indeed Login——所以任何硬编码 Glassdoor 专属登录表单的教程,从架构上就已经失效了。与此同时,这个平台仍然汇集着覆盖 。这些数据对 HR 基准分析、竞争情报和销售线索挖掘都极具价值——前提是你得先拿得到。本指南是那些变化发生之后还能真正工作的版本,且一次性覆盖 Glassdoor 的三类核心数据(职位、评论和薪资)。我会带你用 Python 跑通 2025 年可用的方案,讲清楚到底是什么在阻挡你、又该如何突破,同时也给想省掉工程实现的人提供一个无代码捷径。

为什么要在 2025 年用 Python 抓取 Glassdoor?

Glassdoor 不只是一个招聘网站。它是互联网上最丰富的雇主情报数据集之一——约有 都在使用它,每月独立访客约 5,500 万。藏在这些页面背后的数据,会直接影响多个团队的业务决策。

下面是不同团队实际如何使用 Glassdoor 数据的:

使用场景所需数据类型受益团队
薪资对标薪资分布、样本量HR、总薪酬、运营
竞品招聘追踪职位列表、发布频率销售、战略、VC/企业发展
雇主品牌监测评论文本、评分趋势、CEO 认可度HR、市场、公关
线索开发(成长型公司)职位列表 + 公司信息销售团队、SDR
市场/学术研究三类数据全部分析师、顾问、研究人员

glassdoor_stats_00937e478e.png

当 BLS 在 2025 年 10 月政府停摆期间无法发布就业数据时,Glassdoor 自家的经济研究团队直接基于其数据集发布了一份。这足以说明机构分析师如今有多重视这类数据。

Python 依然是首选语言,因为它的生态无可替代——Playwright 负责浏览器自动化,parsel/lxml 负责解析,curl_cffi 用来绕过 TLS 指纹识别,再加上庞大的社区不断分享可用方案。问题不在 Python,而在于 Glassdoor 现在变得非常难爬。

如果你想用无代码方式提取 Glassdoor 数据,Thunderbit 可以帮助你在不搭建和维护自定义 Python 技术栈的情况下,抓取职位、评论和薪资页面。

你到底能从 Glassdoor 抓到哪些数据?

大多数教程只讲职位列表。但根据我追踪到的论坛帖子、GitHub issue 和 Reddit 提问,用户需求最高的其实是那两类几乎没人教的数据:评论薪资。下面是三类数据中可提取字段的完整拆解。

职位列表

这是最容易获取的数据类型。你可以抓到:职位名称、公司名称、所在地、薪资预估、公司评分、发布时间、Easy Apply 标记以及职位链接。职位列表在未登录状态下也能部分访问,不过 Glassdoor 在翻了几页之后可能会弹出登录提示。

公司评论

这是雇主品牌分析最有价值的部分。可提取字段包括:总体评分、子评分(工作与生活平衡、文化与价值观、多元与包容、职业机会、薪酬与福利、高层管理)、优点文本、缺点文本、评论者职位、评论日期以及雇佣状态。完整评论文本需要登录才能查看——你能看到摘要,但完整的优缺点内容要认证后才能获取。

薪资数据

这是需求最高、也最让人头疼的数据类型。你可以提取:职位名称、基础薪资区间、总薪酬区间、薪资报告数量和所在地。但薪资页面完全需要登录,而且 Glassdoor 有时还会叠加“贡献后解锁”的流程,要求你先提交自己的薪资才能查看他人数据。市面上几乎没有教程能给出真正可用的代码——我们这里会补上。

哪些内容需要登录,哪些不需要

这张表能帮你避免辛辛苦苦跑完后才发现页面是空的:

数据类型无需登录可访问?说明
职位标题与基础信息大多可以翻到几页后可能出现弹窗
完整职位描述部分可见通常在查看 2–3 次后会被限制
公司评论(完整文本)否,需要登录可见摘要,完整文本受限
薪资数据否,需要登录可能还要先“贡献后解锁”

为什么你以前的 Glassdoor 爬虫大概率已经失效

glassdoor_defense_82a26cd8bd.png

我先直说:如果你还在照搬 2021–2023 年的教程代码,那它现在基本不会工作。GitHub 上 star 数最高的老牌 Glassdoor Selenium 爬虫仓库(,约 1.4k stars)有 12 个以上未解决 issue——包括“Glassdoor 新 UI 设计”、“Cloudflare 反爬防护”和“NoSuchElementException”。这个仓库实际上已经停更。,绕过难度为 8/10。

下面是变化点,以及老代码为什么会挂:

防护层变化内容对旧爬虫的影响
Cloudflare Bot Management自 2024 年起 JA3/JA4 指纹识别更严格基础 requests/Selenium 脚本会立刻返回 403
动态 CSS 类名每次构建都会随机化类名教程里的旧 CSS 选择器会悄悄失效
频率限制 + 会话追踪单 IP 和单会话限制更严格爬几页就可能被封
CAPTCHA 挑战(很可能是 Cloudflare Turnstile)更频繁,尤其在翻页时无头浏览器更容易触发挑战
扩大的登录墙更多页面类型需要认证薪资和评论页会返回空数据
Indeed Login 迁移(2026 年 2 月)Glassdoor 登录表单彻底替换所有指向旧登录 DOM 的代码都已失效

里有一句明确警告:“Glassdoor 以高封禁率著称,所以如果你运行 Python 代码时拿到 None,大概率是被拦了。” 而 则说得更直接:“用 requestshttpx 做简单 HTTP 请求会立刻被封。”

我接下来要介绍的对策——Patchright(一个隐身版 Playwright 分支)、data-test 属性选择器、轮换住宅代理,以及已认证的持久会话——正是为了应对这些防护层而设计的。

先选对方案:Glassdoor API 还是 Python 爬取?

很多论坛帖子都会问:“我是不是直接用 Glassdoor API 就行?”——答案是:不行。

。开发者门户虽然形式上还在,但会。它从来没有公开的评论接口——MatthewChatham 的爬虫最初就是“因为 Glassdoor 没有评论 API”才做出来的。并且在 Indeed 的 Publisher API 下,也没有评论或薪资数据的迁移路径。

下面是实话实说的对比:

因素Glassdoor Partner API v1Python 爬取Thunderbit(无代码)
访问方式不再接收新申请开放(需自行实现)Chrome 扩展
职位列表有限制/已退场可获取,但需处理可获取
公司评论从未公开提供可以(需要登录)可以(通过浏览器模式)
薪资数据从未公开提供可以(需要登录)可以
速率限制未公开由你自己控制基于额度
搭建成本无法注册新应用数小时到数天约 2 分钟
维护成本不适用高(HTML 变动就会坏)低(AI 会重新推荐字段)

如果你需要评论或薪资数据——而读到这里的大多数人正是如此——那 Python 爬取或者无代码工具就是你现实中能选的唯二方案。

开始之前

  • 难度:中级(需要你熟悉 Python 和终端)
  • 所需时间:完整搭建约 30–60 分钟;之后每种数据类型约 10 分钟
  • 你需要准备
    • Python 3.10+(推荐 3.11 或 3.12)
    • 已安装 Chrome 浏览器
    • 一个 Glassdoor 账号(免费,抓薪资和评论时需要)
    • 轮换住宅代理(如果要抓超过少量页面)
    • 可选:,如果你想走无代码路线

2025 年用 Python 抓取 Glassdoor 的工具与库

工具环境已经发生了很大变化。下面这些才是真正能应对 Glassdoor 当前防护机制的方案。

为什么 Patchright 是抓取 Glassdoor 的最佳选择

是 Playwright 的隐身分支,它修补了 Runtime.Enable 的 CDP 泄漏——这正是原生 Playwright 在受 Cloudflare 保护的网站上失效的具体技术原因。它和 Playwright 的 API 完全一致,所以如果你会 Playwright,你也会 Patchright。当前版本 1.58.2(2026 年 3 月)仍在积极维护中。

和其他方案相比:

  • 原生 Playwright: 因 Runtime.Enable 泄漏,会在 Glassdoor 登录页被检测到
  • Selenium + undetected-chromedriver: undetected-chromedriver 上一次发布还是 2024 年 2 月,基本算是旧方案了。 显示它“在我们测试的每个域名上都失败了”
  • requests + BeautifulSoup: 不能渲染 JavaScript,会被 Cloudflare 的 TLS 指纹识别立刻拦下
  • 如果页面在初始 HTML 里直接带 __NEXT_DATA__,它是快速路径的绝佳选择(比浏览器快 10–20 倍),但它处理不了登录或中间挑战页

辅助库

  • parsel(1.11.0)或 lxml(6.0.4):快速 HTML/XPath 解析
  • csvpandas:导出数据
  • asyncio:异步抓取,加速分页

代理:只用住宅代理

Glassdoor 的 Cloudflare 层会积极挑战数据中心 ASN。。入门价格大约是 (促销价)或 的 $3.00/GB。用于生产抓取时,按流量预算 $3–8/GB 比较稳妥。

无论代理质量如何,页面请求之间都必须加入随机延迟(至少 3–8 秒,长任务建议 5–15 秒)。

第 1 步:配置 Python 环境

创建项目目录并安装推荐依赖:

1mkdir glassdoor-scraper && cd glassdoor-scraper
2python3.11 -m venv .venv
3source .venv/bin/activate
4pip install --upgrade pip
5# 核心依赖
6pip install patchright==1.58.2 parsel==1.11.0
7# 安装浏览器二进制
8patchright install chromium
9# 可选:用于提取 __NEXT_DATA__ 的快速路径
10pip install "curl_cffi==0.15.0"

你应该会看到 Patchright 下载 Chromium 二进制文件。如果 patchright install chromium 失败,检查磁盘空间是否足够(约 300MB),以及你的 Python 版本是否为 3.10+。

第 2 步:启动 Patchright 并访问 Glassdoor

下面是能对抗 Glassdoor Cloudflare 层的基础启动方式:

1from patchright.sync_api import sync_playwright
2import random, time
3with sync_playwright() as p:
4    browser = p.chromium.launch(
5        headless=False,          # 无头模式仍然更容易被识别
6        channel="chrome",        # 使用真实 Chrome,而不是自带 Chromium
7    )
8    context = browser.new_context(
9        viewport={"width": 1440, "height": 900},
10        locale="en-US",
11        timezone_id="America/New_York",
12        user_agent=(
13            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
14            "AppleWebKit/537.36 (KHTML, like Gecko) "
15            "Chrome/134.0.0.0 Safari/537.36"
16        ),
17    )
18    page = context.new_page()
19    page.goto(
20        "https://www.glassdoor.com/Job/new-york-data-engineer-jobs-"
21        "SRCH_IL.0,8_IC1132348_KO9,22.htm"
22    )
23    # 关闭登录遮罩层——内容其实仍在 DOM 里
24    page.add_style_tag(content="""
25        #HardsellOverlay, .LoginModal { display: none !important; }
26        body { overflow: auto !important; position: initial !important; }
27    """)
28    page.wait_for_selector("[data-test='jobListing']")
29    print("页面已加载——职位卡片可见。")

这里有几点要注意。channel="chrome" 会让 Patchright 使用你已安装的 Chrome 二进制,而不是自带的 Chromium——这样浏览器指纹更像真实用户。add_style_tag 这一招会把 Glassdoor 的登录弹窗(#HardsellOverlay)隐藏起来,而无需点击任何按钮。:“所有内容其实都还在,只是被遮罩层盖住了”——无论弹窗显示与否,HTML 里都有这些数据。

你应该会看到一个 Chrome 窗口打开,跳转到 Glassdoor 职位搜索页,并显示职位卡片,而不会被登录弹窗挡住。

第 3 步:抓取 Glassdoor 职位列表

识别稳定选择器

Glassdoor 会在每次构建时随机化 CSS 类名——所以 2023 年教程里的 .jobCard_xyz123 选择器,现在会悄悄返回空值。你应该改用 data-test 属性,这些是 Glassdoor 内部 QA 常用的标记,跨版本通常都比较稳定。

下面是职位列表字段的选择器参考:

字段选择器
职位卡片容器[data-test="jobListing"]
职位名称[data-test="job-title"]
职位链接a[data-test="job-link"]
公司名称[data-test="employer-name"]
所在地[data-test="emp-location"]
薪资区间[data-test="detailSalary"]
公司评分[data-test="rating"]
发布时间[data-test="job-age"]
下一页[data-test="pagination-next"]

提取职位数据

1from parsel import Selector
2import csv, random, time
3def scrape_jobs(page, max_pages=5):
4    all_jobs = []
5    for page_num in range(1, max_pages + 1):
6        html = page.content()
7        sel = Selector(text=html)
8        cards = sel.css('[data-test="jobListing"]')
9        if not cards:
10            print(f"第 {page_num} 页:没有找到卡片——可能被拦截或选择器已变更。")
11            break
12        for card in cards:
13            job = {
14                "title": card.css('[data-test="job-title"]::text').get("").strip(),
15                "company": card.css('[data-test="employer-name"]::text').get("").strip(),
16                "location": card.css('[data-test="emp-location"]::text').get("").strip(),
17                "salary": card.css('[data-test="detailSalary"]::text').get("").strip(),
18                "rating": card.css('[data-test="rating"]::text').get("").strip(),
19                "link": card.css('a[data-test="job-link"]::attr(href)').get(""),
20                "posted": card.css('[data-test="job-age"]::text').get("").strip(),
21            }
22            if job["link"] and not job["link"].startswith("http"):
23                job["link"] = "https://www.glassdoor.com" + job["link"]
24            all_jobs.append(job)
25        print(f"第 {page_num} 页:抓取到 {len(cards)} 个职位")
26        # 翻页
27        next_btn = page.query_selector('[data-test="pagination-next"]')
28        if next_btn and page_num < max_pages:
29            next_btn.click()
30            time.sleep(random.uniform(3, 8))
31            page.wait_for_selector("[data-test='jobListing']")
32        else:
33            break
34    return all_jobs

保存为 CSV

1def save_to_csv(jobs, filename="glassdoor_jobs.csv"):
2    if not jobs:
3        print("没有可保存的职位数据。")
4        return
5    keys = jobs[0].keys()
6    with open(filename, "w", newline="", encoding="utf-8") as f:
7        writer = csv.DictWriter(f, fieldnames=keys)
8        writer.writeheader()
9        writer.writerows(jobs)
10    print(f"已将 {len(jobs)} 条职位记录保存到 {filename}")

关于分页上限要注意:无论总结果有多少,Glassdoor 搜索结果大致只开放到 30 页左右。如果你需要更完整的覆盖,最好通过筛选条件(地点、职位类型、薪资范围)缩小每次搜索,而不是试图突破分页上限。

我测试时,抓取 5 页职位列表(大约 75 个职位)在随机延迟下耗时约 45 秒。手动复制粘贴做同样的事,至少要 20 分钟。

第 4 步:抓取 Glassdoor 公司评论

这一部分是其他教程通常不会提供可用代码的。评论才是真正的雇主情报所在——情绪分析、文化信号、管理层风险点。

进入评论页

评论页 URL 形如:/Reviews/{Company}-Reviews-E{id}.htm。你可以在 Glassdoor 上搜索公司,然后查看 URL 来找到 employer ID。

1def navigate_to_reviews(page, company_reviews_url):
2    page.goto(company_reviews_url)
3    page.add_style_tag(content="""
4        #HardsellOverlay, .LoginModal { display: none !important; }
5        body { overflow: auto !important; position: initial !important; }
6    """)
7    page.wait_for_selector('[data-test="review"]', timeout=15000)

隐藏的 BFF 接口(最干净的路径)

这是我研究中最关键的发现:Glassdoor 的评论页面其实有一个可用的内部 JSON API,可以完全绕过 HTML 解析。 文档里记录了这个接口,它比直接扒 DOM 稳定得多。

1import json, re, requests
2def get_review_ids(page):
3    """从评论页 HTML 中提取 employerId 和 dynamicProfileId。"""
4    html = page.content()
5    sel = Selector(text=html)
6    script_text = sel.xpath(
7        "//script[contains(text(), 'profileId')]/text()"
8    ).get("")
9    employer_match = re.search(r'"employer"\s*:\s*(\{[^}]+\})', script_text)
10    if employer_match:
11        meta = json.loads(employer_match.group(1))
12        return meta.get("id"), meta.get("profileId")
13    return None, None
14def fetch_reviews_bff(page, employer_id, profile_id, max_pages=5):
15    """调用 Glassdoor 的内部 BFF 接口,获取结构化评论数据。"""
16    all_reviews = []
17    cookies = {c["name"]: c["value"] for c in page.context.cookies()}
18    for pg in range(1, max_pages + 1):
19        payload = {
20            "applyDefaultCriteria": True,
21            "employerId": employer_id,
22            "dynamicProfileId": profile_id,
23            "employmentStatuses": ["REGULAR", "PART_TIME"],
24            "language": "eng",
25            "onlyCurrentEmployees": False,
26            "page": pg,
27            "pageSize": 10,
28            "sort": "DATE",
29            "textSearch": "",
30        }
31        resp = requests.post(
32            "https://www.glassdoor.com/bff/employer-profile-mono/employer-reviews",
33            json=payload,
34            cookies=cookies,
35            headers={"Content-Type": "application/json"},
36        )
37        if resp.status_code != 200:
38            print(f"BFF 在第 {pg} 页返回 {resp.status_code}")
39            break
40        data = resp.json()
41        reviews = data.get("data", {}).get("employerReviews", {}).get("reviews", [])
42        total_pages = data.get("data", {}).get("employerReviews", {}).get("numberOfPages", 1)
43        for r in reviews:
44            all_reviews.append({
45                "title": r.get("summary", ""),
46                "rating": r.get("ratingOverall"),
47                "pros": r.get("pros", ""),
48                "cons": r.get("cons", ""),
49                "author_role": r.get("jobTitle", {}).get("text", ""),
50                "date": r.get("reviewDateTime", ""),
51                "recommend": r.get("isRecommend"),
52            })
53        print(f"评论第 {pg}/{total_pages} 页:获取到 {len(reviews)} 条评论")
54        if pg >= total_pages:
55            break
56        time.sleep(random.uniform(3, 6))
57    return all_reviews

BFF 接口会返回干净的 JSON,包含所有评论字段——不需要 HTML 解析,也不用担心 CSS 选择器变动。你需要的是经过认证的 Playwright 会话里的 cookie(第 6 步会讲),以及先从评论页 HTML 中提取 employerIddynamicProfileId

评论页的 HTML 备用选择器

如果 BFF 接口改变了,或者你更想直接解析 DOM,下面是稳定的 data-test 选择器:

字段选择器
评论容器[data-test="review"]
标题[data-test="review-title"]
总评分[data-test="overall-rating"]
优点[data-test="pros"]
缺点[data-test="cons"]
日期[data-test="review-date"]
作者职位[data-test="author-jobTitle"]

第 5 步:抓取 Glassdoor 薪资数据

薪资页面完全需要登录。你必须先拥有已认证会话(第 6 步),否则下面的代码不会返回真实数据。

进入薪资页

薪资页 URL 形如:/Salary/{Company}-Salaries-E{id}.htm,分页形式为 _P{n}.htm

1def scrape_salaries(page, salary_url, max_pages=3):
2    all_salaries = []
3    for pg in range(1, max_pages + 1):
4        url = salary_url if pg == 1 else salary_url.replace(".htm", f"_P{pg}.htm")
5        page.goto(url)
6        page.add_style_tag(content="""
7            #HardsellOverlay { display: none !important; }
8            body { overflow: auto !important; position: initial !important; }
9        """)
10        time.sleep(random.uniform(3, 7))
11        html = page.content()
12        sel = Selector(text=html)
13        items = sel.css('[data-test="salary-item"]')
14        if not items:
15            print(f"薪资第 {pg} 页:没有数据——可能被登录墙挡住或已被封。")
16            break
17        for item in items:
18            salary = {
19                "job_title": item.css('[class*="SalaryItem_jobTitle__"]::text').get("").strip(),
20                "salary_range": item.css('[class*="SalaryItem_salaryRange__"]::text').get("").strip(),
21                "count": item.css('[class*="SalaryItem_salaryCount__"]::text').get("").strip(),
22            }
23            all_salaries.append(salary)
24        print(f"薪资第 {pg} 页:抓取到 {len(items)} 条记录")
25    return all_salaries

注意这里的 [class*="SalaryItem_jobTitle__"] 前缀匹配写法。Glassdoor 的薪资页使用 CSS Module 哈希类名(例如 SalaryItem_jobTitle__XWGpT),其中哈希后缀会在每次部署时变化。前缀通常稳定,但哈希不会。千万不要硬编码完整类名。

第 6 步:绕过 Glassdoor 的登录墙

这是解锁薪资数据和完整评论文本的关键步骤。思路是:先在可见浏览器里手动登录一次,保存认证会话状态,之后所有抓取任务都复用它。

保存已认证会话

先运行这个脚本一次。它会打开一个 Chrome 窗口,跳转到 Glassdoor 登录页(现在会重定向到 Indeed Login),然后等待你手动登录:

1import asyncio
2from pathlib import Path
3from patchright.async_api import async_playwright
4STATE_FILE = Path("glassdoor_state.json")
5async def login_and_save():
6    async with async_playwright() as p:
7        browser = await p.chromium.launch(headless=False, channel="chrome")
8        context = await browser.new_context(
9            viewport={"width": 1366, "height": 800},
10            locale="en-US",
11        )
12        page = await context.new_page()
13        await page.goto("https://www.glassdoor.com/profile/login_input.htm")
14        print("请先在浏览器窗口中登录,然后在这里按回车...")
15        input()
16        await context.storage_state(path=str(STATE_FILE))
17        print(f"会话已保存到 {STATE_FILE}")
18        await browser.close()
19asyncio.run(login_and_save())

登录后按回车,Patchright 会把所有 cookie 和本地存储保存到 glassdoor_state.json。这个文件里包含你的 gdIdGSESSIONIDcf_clearance 和认证 token。

复用会话进行抓取

后续每次抓取都加载这个已保存状态——无需再次手动登录:

1async def scrape_with_auth(target_url):
2    async with async_playwright() as p:
3        browser = await p.chromium.launch(headless=True, channel="chrome")
4        context = await browser.new_context(
5            storage_state="glassdoor_state.json"
6        )
7        page = await context.new_page()
8        await page.goto(target_url)
9        await page.add_style_tag(
10            content="#HardsellOverlay{display:none!important}"
11        )
12        await page.wait_for_load_state("networkidle")
13        html = await page.content()
14        await browser.close()
15        return html

这个已保存会话通常只能维持 20–30 分钟的活跃使用时间,之后 Glassdoor 往往会重新发起挑战。对于更长的抓取任务,建议加一个检查:如果某个本该有数据的页面结果却是 0,先当作会话失效,重新跑登录脚本刷新 state 文件。

检测并关闭登录弹窗

对于部分受限但仍能显示数据的页面(比如有内容却被模态层遮住的职位列表),前面提到的 CSS 注入方式就能处理:

1page.add_style_tag(content="""
2    #HardsellOverlay, .LoginModal { display: none !important; }
3    body { overflow: auto !important; position: initial !important; }
4""")

只有当 HTML 本身已经包含底层数据时,这招才有效。对于完全由服务器端封锁的页面(薪资、深层评论页),第 6 步中的认证会话才是唯一可行路径。

让 Glassdoor 爬虫持续运行的实用技巧

Glassdoor 的前端会频繁更新。下面这些方法能帮助你的爬虫保持韧性。

优先使用 data-test 属性,而不是类名

Glassdoor 会随机化 CSS 类名,但通常会保留 data-test 属性。始终优先用 [data-test="jobListing"],而不是 .jobCard_abc123。当没有 data-test 时(如薪资字段类名),就使用前缀匹配:[class*="SalaryItem_jobTitle__"]

轮换代理并随机延迟

使用轮换住宅代理——数据中心 IP 往往会被立即挑战。页面加载之间加入 3–8 秒随机延迟(长任务建议 5–15 秒)。如果可能,尽量避免在美国工作时间段抓取,因为这时 Cloudflare 的行为检测通常最激进。

监控是否失效

在你的爬虫里加入一个简单检查:如果某个本该有数据的页面返回 0 条结果,把它视为选择器失效,而不是“确实没数据”,并及时通知自己。建议每周跑一次小规模测试抓取,尽早发现问题——Glassdoor 经常不预告就更新前端。

能用 __NEXT_DATA__ 就优先用

Glassdoor 是基于 Next.js + Apollo GraphQL 的应用。很多页面会在 <script id="__NEXT_DATA__"> 中直接带上完整的 GraphQL 缓存 JSON。解析这个比直接抓 DOM 稳定得多,而且

1import json
2def extract_next_data(html):
3    sel = Selector(text=html)
4    raw = sel.css("script#__NEXT_DATA__::text").get()
5    if raw:
6        return json.loads(raw)["props"]["pageProps"].get("apolloCache", {})
7    return None

这会返回结构化的 Apollo 缓存,其中包含所有职位、评论和薪资字段——不需要 CSS 选择器。因为这和 Glassdoor React 前端使用的是同一份数据,所以它是目前最稳妥的提取方式。

不写代码也能抓:用 Thunderbit 抓取 Glassdoor

不是所有读这篇文章的人都是开发者。HR 团队、招聘人员、销售运营分析师和市场研究员同样需要 Glassdoor 数据——他们不应该为了拿到数据而去折腾 Playwright 上下文和代理轮换。

是一款 AI 网页爬虫 Chrome 扩展,可以在不写任何代码的情况下提取相同的职位、评论和薪资数据。我在 Thunderbit 团队工作,所以我先说明这一点——但我把它放在这里,是因为它确实解决了 Glassdoor 爬取里最难的两个问题。

Thunderbit 在 Glassdoor 上是怎么工作的

整个流程只要两步:

  1. 在 Chrome 中打开任意 Glassdoor 页面(职位搜索页、公司评论页、薪资页)
  2. 在 Thunderbit 侧边栏点击 AI Suggest Fields——AI 会读取页面 DOM,并推荐列(职位名称、公司、评分、薪资区间、优点、缺点等)
  3. 点击 Scrape——数据会直接提取成表格,无需 CSS 选择器或浏览器自动化代码

Thunderbit 提供了一个,单次运行即可为每家公司提取 23+ 个字段。对于职位列表、评论或薪资页面,通用的 AI Suggest Fields 流程都适用任何 Glassdoor URL。

不写代码也能处理登录墙

这正是 Thunderbit 在 Glassdoor 场景下的结构性优势。浏览器模式直接运行在你自己的 Chrome 会话里——如果你已经在 Chrome 中登录了 Glassdoor,Thunderbit 会自动继承这些 cookies。阻挡服务端爬虫的薪资和评论登录墙,对它就不再构成问题。无需 cookie 管理、无需持久化上下文、无需会话代码。

用子页面抓取做数据丰富

你可以先从列表页开始(比如搜索结果里的 30 家公司),让 Thunderbit 枚举所有行,然后启用,逐个访问每家公司的评论页或薪资页,把完整描述、评论文本或薪资明细补充进表格。

导出到业务工具

和只能输出 CSV 或 JSON 的 Python 脚本不同,Thunderbit 可以直接导出到 Google Sheets、Airtable、Notion 或 Excel——所有套餐都免费支持。对于需要协作共享和分析数据的团队来说尤其方便。

Python 和 Thunderbit:该用哪个?

场景推荐方案
搭建可重复运行的数据管道Python + Patchright
一次性研究或小团队项目Thunderbit
需要对每个字段都做程序化控制Python
不会开发、但今天就需要 Glassdoor 数据Thunderbit
单次抓取 1,000+ 页面Python + 代理
抓取 30 家公司并做补充两者都可——Thunderbit 上手更快

Thunderbit 提供免费起步计划(每月 6 页), 可获得 3,000 credits。按每条输出 1 credit 计算,子页面抓取每条 2 credits,这足以支撑每月大约 33 次“30 家公司补充抓取”的任务。

抓取 Glassdoor 合法吗?

我尽量简短、只说事实。Glassdoor 的 明确禁止自动化抓取:“未经我们明确书面许可,你不得使用任何机器人、蜘蛛、爬虫……以任何目的访问服务。”

但法律环境远比单条 ToS 更复杂:

  • (加州北区,2024 年 1 月):法院认为,如果你从未登录,也就从未同意 ToS;对公开未登录数据的抓取并不违反该条款
  • hiQ Labs v. LinkedIn(第九巡回):CFAA 不适用于对公开可访问数据的自动收集——但虚假账号和登录后抓取属于另一回事
  • Van Buren v. United States(美国最高法院,2021):收窄了 CFAA 中“超越授权访问”的解释

实际结论是:不登录、只抓公开职位列表,法律风险相对更低;而如果你使用已登录会话抓取,就等于在注册时已经同意 ToS,而条款里明确禁止这种行为。这个判断对 Python 脚本和 Thunderbit 的浏览器模式都同样适用。

无论如何,都建议遵守以下伦理原则:

  • 速率限制明显低于人工浏览速度
  • 不抓取或转售含个人身份信息的评论者资料
  • 尊重 robots.txt 规则
  • 只提取你真正需要的字段

结论:你该用哪种方法?

本文覆盖了 Glassdoor 的三类数据——职位、评论和薪资——并提供了可用的 2025 方案,考虑了 Indeed Login 迁移、Cloudflare Bot Management,以及让旧教程全部失效的 CSS module 类名轮换问题。

下面是决策框架:

你的情况最佳方案
开发者,要搭建数据管道Python + Patchright(按上面的步骤操作)
一次性研究或周期性小批量抓取Thunderbit(无代码,基于浏览器)
只需要小规模基础职位列表先看看 Glassdoor API 是否还能用(大概率不行)
明确需要薪资或评论数据只能用 Python 爬取或 Thunderbit——API 从未覆盖这些数据
非开发者团队,需要共享数据Thunderbit → 导出到 Google Sheets

Glassdoor 的防护机制还会继续演进。选择器会失效,新的挑战会出现。建议收藏本指南——如果你还想深入了解网页爬取工具和技巧,可以看看我们关于 的文章。你也可以在 观看操作演示。

试试 Thunderbit 提取 Glassdoor 数据

常见问题

1. 不登录能抓取 Glassdoor 吗?

可以,至少对大多数职位列表数据和公司总评分可以。不能,用于完整薪资拆分或超出前几页的完整评论文本。#HardsellOverlay 只是一个纯 CSS 模态层——底层 HTML 仍然包含第一页数据——但更深层的内容会被 Glassdoor 的“先贡献后获取”机制放在服务器端限制起来。

2. 2025 年抓取 Glassdoor 最好用哪个 Python 库?

默认推荐 Patchright(隐身版 Playwright 分支)。它修补了原生 Playwright 的 Runtime.Enable CDP 泄漏,而 Cloudflare 会显式检查这个问题。对于初始 HTML 中就带有 __NEXT_DATA__ 的列表页,带 impersonate="chrome124"curl_cffi 速度会快 10–20 倍,但它无法处理需要登录的页面。

3. 怎么避免在抓取 Glassdoor 时被封?

使用 Patchright 或 rebrowser-playwright(不要用原生 Playwright 或 Selenium)。轮换住宅代理——数据中心 IP 会立即被挑战。页面之间加入 3–8 秒随机延迟。跨请求持久化 cookie(gdIdcf_clearanceGSESSIONID)。预计会话大约 20–30 分钟后就会再次触发挑战。

4. 有没有可以替代抓取的 Glassdoor API?

基本没有。旧版 Partner API 已,公开评论接口从未存在,而 Indeed 的 Publisher API 也没有迁移路径。对于评论和薪资数据,抓取或像 Thunderbit 这样的无代码工具是唯一现实选择。

5. Glassdoor 爬虫多久会坏一次?

很频繁。Glassdoor 会不预告地更新前端,而 CSS module 的哈希类名每次构建都会变。最稳定的提取策略是:(1) data-test 属性选择器,(2) __NEXT_DATA__ JSON 块,(3) 内部 BFF 评论接口。建议加入“结果为空”检测,并每周跑一次小规模测试抓取,尽早发现失效。

了解更多

Ke
Ke
CTO @ Thunderbit. Ke is the person everyone pings when data gets messy. He's spent his career turning tedious, repetitive work into quiet little automations that just run. If you've ever wished a spreadsheet could fill itself in, Ke has probably already built the thing that does it.
目录

试试 Thunderbit

只需 2 次点击即可抓取潜在客户和其他数据。由 AI 驱动。

获取 Thunderbit 免费试用
使用 AI 提取数据
轻松将数据导入 Google Sheets、Airtable 或 Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week