2026年如何抓取Glassdoor公司评论和薪资数据:BFF GraphQL、DataDome与代理实战

面向HR分析与劳动力市场数据工程师的实战指南:覆盖Glassdoor公开评论与薪资数据的抓取方法、DataDome/Cloudflare反爬绕过策略、BFF GraphQL端点调用、以及ProxyHat住宅代理轮换配置。

How to Scrape Glassdoor Company Reviews and Salaries in 2026 (BFF GraphQL, DataDome, and Proxies)

法律与合规声明:本文仅讨论对公开可访问数据的合规采集。在抓取Glassdoor数据之前,你必须仔细阅读并遵守Glassdoor的服务条款(Terms of Service)。在美国,未经授权访问受保护计算机系统可能违反《计算机欺诈和滥用法》(CFAA, 18 U.S.C. § 1030);在欧盟,处理与可识别自然人相关的数据时须遵守《通用数据保护条例》(GDPR)。本文不鼓励、不教授任何绕过登录墙或抓取需认证才能访问的数据的方法。

对于HR分析团队和劳动力市场数据工程师来说,Glassdoor是雇主品牌情报和薪资基准数据的宝库。平台拥有超过1亿条公司评论和薪资数据点,覆盖全球190多个国家。然而,Glassdoor部署了多层反爬系统——DataDome和Cloudflare Bot Management——使得传统的requests库几乎无法工作。本文将深入讲解2026年如何抓取Glassdoor公司评论和薪资数据,包括BFF GraphQL端点的调用方法、TLS指纹伪装、以及住宅代理轮换策略。

如何抓取Glassdoor公司评论和薪资数据:技术概览

Glassdoor的数据访问分为两个层级:公开可访问登录墙后。理解这一边界是合规抓取的前提。

公开可访问的数据

以下内容在未登录状态下可见,URL格式为 /Reviews/<company>-Reviews-E<id>.htm

  • 公司概览页(行业、规模、总部位置)
  • 截断的评论列表(每页约10条,显示pros/cons摘要)
  • 总体评分(ratingOverall)和分项评分
  • 职位标题和评论日期

登录墙后的数据(本文不涉及)

以下数据需要登录账户才能访问,属于Glassdoor的付费/注册内容:

  • 完整的薪资明细(base + bonus + stock拆分)
  • 完整评论正文(超过截断长度的部分)
  • 面试问题详情
  • 个人评论者的详细资料
关键原则:如果你的目标数据需要登录才能看到,你应该使用Glassdoor官方API或数据授权计划,而不是绕过认证。本文的方法仅适用于公开页面和公开API端点返回的数据。

Glassdoor的反爬技术栈:DataDome + Cloudflare

Glassdoor使用了至少两层反爬系统,这也是为什么简单的 requests.get() 会返回403或CAPTCHA页面的原因。

第一层:Cloudflare Bot Management

Cloudflare在边缘节点执行JS挑战和TLS指纹检测。它会检查客户端的JA3/JA4 TLS指纹——这是TLS握手期间ClientHello包的特征哈希。Python标准 requests 库使用的urllib3底层TLS栈产生的指纹与真实浏览器截然不同,因此在TLS握手阶段就会被拦截,响应延迟通常在50ms以内就被拒绝。了解更多关于Cloudflare Bot Management的技术细节,参见Cloudflare官方文档

第二层:DataDome

通过Cloudflare后,请求进入DataDome的检测层。DataDome使用机器学习模型对以下信号进行评分:

  • IP信誉评分(数据中心IP几乎自动标记为高风险)
  • 浏览器指纹一致性(User-Agent与TLS指纹是否匹配)
  • 请求模式(速率、路径序列、鼠标行为信号)
  • Cookie一致性(datadome cookie是否与IP绑定)

DataDome的IP评分系统对数据中心IP段有极高的识别率。这就是为什么住宅代理是抓取Glassdoor的必要条件——住宅IP来自真实ISP分配段,在DataDome的IP信誉库中风险评分较低。详见DataDome官方说明

解决方案:curl_cffi + 住宅代理

curl_cffi 是一个基于libcurl的Python库,能够模拟Chrome的TLS握手特征。它通过底层调用curl-impersonate项目,使JA3/JA4指纹与真实Chrome浏览器一致,从而绕过Cloudflare的TLS指纹检测。这是抓取Glassdoor的基础工具。

BFF GraphQL端点:结构化数据的来源

Glassdoor的前端使用BFF(Backend for Frontend)架构,通过GraphQL端点获取结构化数据。虽然这些端点未公开文档化,但可以通过浏览器开发者工具在网络请求中观察到。

关键端点

  • /graph — 主GraphQL端点,接受POST请求,返回JSON格式的评论数据
  • /bff/ — BFF代理层端点,部分请求路径不同但返回类似结构

必需请求头

调用GraphQL端点时,以下请求头是必需的:

  • gd-csrf-token — CSRF令牌,可从页面HTML中的 <meta> 标签或内联JS中提取
  • Content-Type: application/json
  • 有效的 datadome cookie(通过初始页面访问获取)
  • 与浏览器一致的 User-Agent

典型的GraphQL查询结构如下(仅示例公开评论字段):

query GetReviews($employerId: ID!, $cursor: String) {
  employerReviews(employerId: $employerId, after: $cursor) {
    edges {
      node {
        ratingOverall
        pros
        cons
        jobTitle {
          name
        }
        reviewDateTime
      }
    }
    pageInfo {
      endCursor
      hasNextPage
    }
  }
}

实战:Python抓取Glassdoor评论的完整示例

以下示例展示如何通过ProxyHat住宅代理和BFF GraphQL端点抓取公开评论数据。代码使用curl_cffi进行TLS伪装,通过ProxyHat网关轮换IP。

步骤1:安装依赖

pip install curl_cffi

步骤2:通过ProxyHat住宅代理调用GraphQL端点

import json
import time
import re
from curl_cffi import requests

# ProxyHat 住宅代理配置
PROXY_URL = "http://user-country-US-session-glassdoor1:YOUR_PASSWORD@gate.proxyhat.com:8080"

HEADERS = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                 "AppleWebKit/537.36 (KHTML, like Gecko) "
                 "Chrome/131.0.0.0 Safari/537.36",
    "Content-Type": "application/json",
    "Accept": "application/json",
}

EMPLOYER_ID = "4138"  # 示例:某公司ID

# Step 1: 访问公开评论页,获取 csrf token 和 datadome cookie
review_url = f"https://www.glassdoor.com/Reviews/example-company-Reviews-E{EMPLOYER_ID}.htm"

session = requests.Session(impersonate="chrome")
resp = session.get(review_url, proxies={"https": PROXY_URL, "http": PROXY_URL}, timeout=30)

if resp.status_code != 200:
    print(f"初始页面请求失败: {resp.status_code}")
    exit(1)

# 从HTML中提取 gd-csrf-token
csrf_match = re.search(r'gd-csrf-token["\']?\s*[:=]\s*["\']?([a-f0-9-]+)', resp.text)
if not csrf_match:
    # 备选:从meta标签提取
    csrf_match = re.search(r'name="csrf"\s+content="([^"]+)"', resp.text)

csrf_token = csrf_match.group(1) if csrf_match else ""
print(f"CSRF Token: {csrf_token[:8]}...")

# Step 2: 调用 BFF GraphQL 端点获取结构化评论
graphql_url = "https://www.glassdoor.com/graph"
query = """
query GetReviews($employerId: ID!, $cursor: String) {
  employerReviews(employerId: $employerId, after: $cursor, first: 10) {
    edges {
      node {
        ratingOverall
        pros
        cons
        jobTitle { name }
        reviewDateTime
      }
    }
    pageInfo { endCursor hasNextPage }
  }
}
"""

payload = {
    "query": query,
    "variables": {"employerId": EMPLOYER_ID, "cursor": None},
    "operationName": "GetReviews",
}

headers = {**HEADERS, "gd-csrf-token": csrf_token}

resp = session.post(
    graphql_url,
    json=payload,
    headers=headers,
    proxies={"https": PROXY_URL, "http": PROXY_URL},
    timeout=30,
)

data = resp.json()
reviews = data.get("data", {}).get("employerReviews", {})

for edge in reviews.get("edges", []):
    node = edge["node"]
    print(f"评分: {node['ratingOverall']} | 职位: {node['jobTitle']['name']}")
    print(f"  优点: {node['pros'][:80]}...")
    print(f"  缺点: {node['cons'][:80]}...")
    print(f"  日期: {node['reviewDateTime']}")
    print("---")

page_info = reviews.get("pageInfo", {})
print(f"下一页游标: {page_info.get('endCursor')}")
print(f"是否有更多: {page_info.get('hasNextPage')}")

Node.js 示例:使用 SOCKS5 代理

const { SOCKSProxyAgent } = require('socks-proxy-agent');
const fetch = require('node-fetch');

const proxyUrl = 'socks5://user-country-US-session-glassdoor1:YOUR_PASSWORD@gate.proxyhat.com:1080';
const agent = new SOCKSProxyAgent(proxyUrl);

async function fetchReviews(employerId, cursor = null) {
  const query = `
    query GetReviews($employerId: ID!, $cursor: String) {
      employerReviews(employerId: $employerId, after: $cursor, first: 10) {
        edges { node { ratingOverall pros cons jobTitle { name } } }
        pageInfo { endCursor hasNextPage }
      }
    }`;

  const resp = await fetch('https://www.glassdoor.com/graph', {
    method: 'POST',
    agent,
    headers: {
      'Content-Type': 'application/json',
      'gd-csrf-token': process.env.GD_CSRF_TOKEN,
      'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
    },
    body: JSON.stringify({
      query,
      variables: { employerId, cursor },
      operationName: 'GetReviews',
    }),
  });

  return resp.json();
}

fetchReviews('4138').then(console.log);

分页、粘性会话与速率控制

游标分页

Glassdoor的GraphQL使用Relay风格的游标分页。每次响应中的 pageInfo.endCursor 是下一页的游标值,将其作为下一次请求的 after 参数传入即可。当 hasNextPagefalse 时,分页结束。

粘性会话(Sticky Session)

DataDome的 datadome cookie与IP地址绑定。如果在获取cookie后切换IP,DataDome会立即失效并要求重新验证。因此,在同一个公司的完整抓取流程中,必须使用粘性会话保持同一出口IP。

在ProxyHat中,通过在用户名中添加 -session- 标志实现粘性会话:

# 同一会话保持同一IP
proxy_a = "http://user-country-US-session-companyA123:PASSWORD@gate.proxyhat.com:8080"
proxy_b = "http://user-country-US-session-companyB456:PASSWORD@gate.proxyhat.com:8080"
# companyA123 和 companyB456 是不同的会话ID,分配不同IP

请求节奏与重试逻辑

建议的请求间隔为3-5秒每次请求,以模拟人类浏览行为。以下是一个带重试逻辑的分页循环示例:

import time
import random

def scrape_all_reviews(session, proxy_url, employer_id, csrf_token, max_pages=50):
    all_reviews = []
    cursor = None
    
    for page in range(max_pages):
        payload = {
            "query": query,
            "variables": {"employerId": employer_id, "cursor": cursor, "first": 10},
            "operationName": "GetReviews",
        }
        headers = {**HEADERS, "gd-csrf-token": csrf_token}
        
        for attempt in range(3):
            try:
                resp = session.post(
                    "https://www.glassdoor.com/graph",
                    json=payload,
                    headers=headers,
                    proxies={"https": proxy_url, "http": proxy_url},
                    timeout=30,
                )
                
                if resp.status_code == 200:
                    data = resp.json()
                    reviews = data["data"]["employerReviews"]
                    all_reviews.extend([e["node"] for e in reviews["edges"]])
                    
                    if not reviews["pageInfo"]["hasNextPage"]:
                        return all_reviews  # 分页结束
                    
                    cursor = reviews["pageInfo"]["endCursor"]
                    break  # 成功,跳出重试循环
                
                elif resp.status_code == 403:
                    print(f"第{page}页被拦截,尝试{attempt+1}/3")
                    time.sleep(10 * (attempt + 1))  # 指数退避
                    continue
                
            except Exception as e:
                print(f"请求异常: {e}")
                time.sleep(5 * (attempt + 1))
        else:
            print(f"第{page}页连续3次失败,终止")
            break
        
        # 节奏控制:3-5秒随机延迟
        time.sleep(random.uniform(3, 5))
    
    return all_reviews

常见错误与边界情况

问题原因解决方案
403 ForbiddenDataDome IP评分过高或TLS指纹不匹配切换为住宅代理 + 确认curl_cffi的impersonate="chrome"生效
400 Bad Requestgd-csrf-token缺失或过期重新访问评论页获取新token
空响应/超时出口IP被临时封锁更换session ID获取新IP,等待60秒后重试
CAPTCHA页面请求频率过高触发人机验证降低请求速率至每5秒1次,使用粘性会话减少IP切换
GraphQL schema错误字段名变更或端点结构调整通过浏览器DevTools重新检查最新查询结构

ProxyHat配置与最佳实践

ProxyHat提供住宅、移动和数据中心代理。对于Glassdoor抓取,住宅代理是唯一推荐类型,因为DataDome对数据中心IP的拦截率极高。

连接参数

  • HTTP网关: gate.proxyhat.com:8080
  • SOCKS5网关: gate.proxyhat.com:1080
  • 国家定位: user-country-US:password@gate.proxyhat.com:8080
  • 城市定位: user-country-US-city-sanfrancisco:password@gate.proxyhat.com:8080
  • 粘性会话: user-session-myID123:password@gate.proxyhat.com:8080

查看完整代理位置列表请访问 ProxyHat代理位置,了解定价方案参见 ProxyHat定价。更多关于 网页抓取用例SERP追踪 的技术细节可参考我们的用例文档。完整API文档见 ProxyHat官方文档

并发与性能建议

  • 单会话建议保持≤5个并发请求
  • 多公司并行抓取时,为每个公司分配独立的session ID
  • 监控成功率指标,低于90%时降低并发或增加延迟
  • 目标延迟控制在200-500ms范围内(代理转发延迟)

伦理抓取与何时使用官方API

数据采集原则

  • 仅采集聚合、非个人数据:公司评分分布、薪资范围统计、评论趋势——而非单条评论中的个人身份信息
  • 不抓取登录墙后数据:完整薪资明细、面试详情等需要认证的内容应通过官方渠道获取
  • 遵守robots.txt:检查 https://www.glassdoor.com/robots.txt 并尊重其中的Disallow规则
  • GDPR考量:评论中可能包含欧盟用户的个人信息(如评论者职位+公司可间接识别个人)。对欧盟公司数据进行匿名化处理,移除可能关联到特定员工的信息

何时应该使用官方数据授权

以下场景中,Glassdoor的官方数据授权计划是更合适的选择:

  • 需要大规模、系统性地获取薪资明细数据
  • 需要将数据用于商业产品或公开报告
  • 需要历史数据快照或定期数据交付
  • 团队没有维护反爬绕过基础设施的工程能力

官方数据授权虽然成本更高,但提供法律保障、数据质量保证和稳定的交付SLA。对于HR分析团队来说,这通常是总拥有成本更低的选择。

关键要点

  • 仅抓取公开可访问的数据——公司概览、截断评论、总体评分。登录墙后的完整薪资数据不在本文方法范围内。
  • Glassdoor使用DataDome + Cloudflare双重反爬。curl_cffi的Chrome TLS伪装是绕过Cloudflare指纹检测的必要手段。
  • BFF GraphQL端点(/graph)返回结构化JSON数据,需要gd-csrf-token请求头和有效的datadome cookie。
  • 住宅代理是抓取Glassdoor的必要条件——数据中心IP在DataDome的IP评分系统中几乎自动被拦截。
  • 使用粘性会话(-session-标志)保持DataDome cookie有效,请求间隔控制在3-5秒。
  • 遵守CFAA和GDPR,仅采集聚合非个人数据,在需要大规模商业数据时优先考虑官方数据授权。

准备开始了吗?

通过AI过滤访问148多个国家的5000多万个住宅IP。

查看价格住宅代理
← 返回博客