HRアナリティクスチームや労働市場データのエンジニアにとって、Glassdoorは企業レビュー、給与水準、面接体験など貴重な公開情報を提供するプラットフォームです。本記事では、Glassdoorの企業レビューと給与データのスクレイピングを2026年の最新アンチボット環境下で実装する方法を、実践的なコード例とともに解説します。
法的注意事項: 本記事は公開データへのアクセスのみを対象としています。Glassdoorの利用規約(Terms of Service)を遵守し、米国ではComputer Fraud and Abuse Act (CFAA)、EUではGDPRに準拠してください。ログインが必要なデータのスクレイピングは推奨しません。本記事の内容は教育目的であり、実際のスクレイピングを行う前に法的専門家に相談することを推奨します。
Glassdoorの企業レビューと給与データのスクレイピング2026 — 公開データとログイン壁の境界
Glassdoorのデータアクセシビリティは、ページタイプによって大きく異なります。スクレイピングを設計する前に、何が公開されていて何がログイン壁の後ろにあるかを正確に把握することが不可欠です。
ログインなしでアクセス可能なデータ
- 企業概要ページ:
/Overview/Working-at-<company>-EI_IE<id>.htm— 企業名、業界、規模、本社所在地など - レビュー一覧ページ:
/Reviews/<company>-Reviews-E<id>.htm— 各レビューの評価(ratingOverall)、長所(pros)、短所(cons)、職種(jobTitle)の先頭部分が公開状態で表示されます。ただし全文は途中で切り詰められています。 - 企業の基本スコア: 総合評価スコア、推奨率、CEO支持率などがHTML内に埋め込まれています。
ログイン壁の後ろにあるデータ
- 給与の詳細内訳: 職種別、地域別、経験年数別の給与分布はログインが必要です。
- レビュー全文: 切り詰められた部分以降の本文は、ログイン後にのみ表示されます。
- 面接体験の詳細: 面接の質問内容やプロセスの詳細はログイン壁の後ろにあります。
本記事では、ログインなしでアクセス可能な公開レビューデータの取得に焦点を当てます。給与の詳細内訳にはアクセスせず、公開範囲のデータのみを対象とします。
Glassdoorのアンチボットスタック — DataDome + Cloudflare Bot Management
Glassdoorは2026年時点で多層防御のアンチボットスタックを採用しています。主要なコンポーネントは以下の通りです。
DataDome — IPスコアリングと行動分析
DataDomeは、IPレピュテーション、リクエスト頻度、ブラウザフィンガープリントを組み合わせてbotトラフィックを検出します。データセンターIPからのリクエストは、DataDomeのIPスコアリングにより80%以上の確率でブロックされます。一方、レジデンシャルIPからのリクエストははるかに高い通過率を示します。
Cloudflare Bot Management — JSチャレンジとTLSフィンガープリンティング
Cloudflare Bot Managementは、JavaScriptチャレンジとTLSフィンガープリンティングを組み合わせて、非ブラウザクライアントを検出します。標準的なPythonのrequestsライブラリが送信するTLSハンドシェイクは、Chromeのそれとは異なる暗号スイートの順序と拡張機能を持つため、Cloudflareによって即座に識別されます。
なぜcurl_cffiが必要なのか
curl_cffiは、ChromeのTLSハンドシェイクを忠実に再現するPythonライブラリです。impersonate="chrome120"パラメータを指定することで、実際のChromeブラウザと同一のTLSフィンガープリントを送信し、CloudflareのTLSベースの検査を通過できます。標準のrequestsライブラリではこの偽装は不可能です。
| クライアント | TLSフィンガープリント | DataDome通過率 | Cloudflare JS通過 |
|---|---|---|---|
| Python requests | Python/OpenSSL | 低(約15%) | 不可 |
| curl_cffi (Chrome偽装) | Chrome 120 | 高(約90%) | 可能 |
| Playwright/Puppeteer | 実際のChrome | 最高(95%+) | 可能 |
GraphQL BFFエンドポイントの発見と活用
GlassdoorのフロントエンドはReactベースのSPAであり、データ取得にGraphQLを使用しています。ブラウザのDevToolsでネットワークタブを確認すると、以下のエンドポイントが見つかります。
/bff/graphql— BFF(Backend for Frontend)層のGraphQLエンドポイント/graph— 内部GraphQLエンドポイント(一部のクエリで使用)
これらのエンドポイントは、HTMLをパースする代わりに構造化JSONを直接返すため、スクレイピングの効率と信頼性が大幅に向上します。
必須ヘッダー — gd-csrf-token
GraphQLエンドポイントにPOSTリクエストを送信する際、gd-csrf-tokenヘッダーが必須です。このトークンは、初期ページアクセス時にHTML内またはCookieに埋め込まれています。最初のGETリクエストでページをロードし、レスポンスからCSRFトークンを抽出した後、それを用いてGraphQLエンドポイントにPOSTリクエストを送信するという2段階のアプローチが必要です。
なぜローテーション型レジデンシャルプロキシがDataDomeに勝るのか
DataDomeのIPスコアリングは、データセンターIP範囲を即座にフラグ付けします。レジデンシャルプロキシは実際のISPに割り当てられたIPアドレスを使用するため、DataDomeのIPレピュテーションチェックを通過します。ProxyHatのレジデンシャルプロキシネットワークは、世界中のISPベースのIPを提供し、Geoターゲティングもサポートしています。
Python実装例 — ProxyHat + curl_cffiでBFF GraphQLにPOST
以下は、ProxyHatのレジデンシャルプロキシを経由して、GlassdoorのBFF GraphQLエンドポイントにPOSTリクエストを送信し、公開レビューデータを取得する完全なPython例です。
from curl_cffi import requests
import json
import re
import time
# ProxyHat レジデンシャルプロキシ(US geo、スティッキーセッション)
proxy_url = "http://user-country-US-session-gd001:PASSWORD@gate.proxyhat.com:8080"
proxies = {"http": proxy_url, "https": proxy_url}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/120.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
}
# ステップ1: 初期ページにGETしてCSRFトークンを取得
review_url = "https://www.glassdoor.com/Reviews/Example-Co-Reviews-E12345.htm"
resp = requests.get(
review_url,
headers=headers,
proxies=proxies,
impersonate="chrome120",
timeout=30
)
# HTMLから gd-csrf-token を抽出
csrf_match = re.search(r'gd-csrf-token["\s:=]+([A-Za-z0-9_-]+)', resp.text)
csrf_token = csrf_match.group(1) if csrf_match else ""
print(f"CSRF Token extracted: {csrf_token[:20]}...")
# ステップ2: GraphQL BFF エンドポイントにPOST
graphql_query = {
"query": """
query EmployerReviews($employerId: ID!, $cursor: String) {
employerReviews(employerId: $employerId, after: $cursor) {
reviews {
id
ratingOverall
pros
cons
jobTitle { text }
}
pageInfo { hasNextPage endCursor }
}
}
""",
"variables": {
"employerId": "E12345",
"cursor": None
}
}
graphql_headers = {
**headers,
"Content-Type": "application/json",
"gd-csrf-token": csrf_token,
"Origin": "https://www.glassdoor.com",
"Referer": review_url,
}
time.sleep(3) # ペース調整
graphql_resp = requests.post(
"https://www.glassdoor.com/bff/graphql",
json=graphql_query,
headers=graphql_headers,
proxies=proxies,
impersonate="chrome120",
timeout=30
)
data = graphql_resp.json()
reviews = data["data"]["employerReviews"]["reviews"]
for r in reviews:
print(f"Rating: {r['ratingOverall']}")
print(f"Title: {r['jobTitle']['text']}")
print(f"Pros: {r['pros'][:120]}")
print(f"Cons: {r['cons'][:120]}")
print("---")
page_info = data["data"]["employerReviews"]["pageInfo"]
print(f"Has next page: {page_info['hasNextPage']}")
print(f"End cursor: {page_info['endCursor']}")
このコードは、まず公開レビューページにGETリクエストを送信してCSRFトークンを抽出し、次にそのトークンを使ってGraphQLエンドポイントにPOSTリクエストを送信します。impersonate="chrome120"により、Chrome 120のTLSフィンガープリントが再現されます。
curlコマンドラインでの確認
コードを書く前に、curlでエンドポイントの動作を確認することも可能です。
curl -x "http://user-country-US-session-gd001:PASSWORD@gate.proxyhat.com:8080" \
-H "Content-Type: application/json" \
-H "gd-csrf-token: YOUR_CSRF_TOKEN" \
-H "Origin: https://www.glassdoor.com" \
-H "Referer: https://www.glassdoor.com/Reviews/" \
-d '{"query":"{ employerReviews(employerId:\"E12345\") { reviews { ratingOverall pros cons } } }"}' \
"https://www.glassdoor.com/bff/graphql"
ページネーション、スティッキーセッション、ペース調整
Glassdoorのレビューデータを複数ページにわたって取得する場合、以下の3つの要素が成功の鍵を握ります。
ページネーションカーソル
GraphQLのレスポンスにはpageInfoオブジェクトが含まれ、hasNextPageとendCursorが返されます。次のリクエストでは、variables.cursorに前回のendCursorを渡すことで、次のページのレビューを取得できます。
cursor = None
all_reviews = []
while True:
graphql_query["variables"]["cursor"] = cursor
resp = requests.post(
"https://www.glassdoor.com/bff/graphql",
json=graphql_query,
headers=graphql_headers,
proxies=proxies,
impersonate="chrome120",
timeout=30
)
data = resp.json()
page_reviews = data["data"]["employerReviews"]["reviews"]
all_reviews.extend(page_reviews)
page_info = data["data"]["employerReviews"]["pageInfo"]
if not page_info["hasNextPage"]:
break
cursor = page_info["endCursor"]
time.sleep(5) # 5秒のペース調整
print(f"Total reviews scraped: {len(all_reviews)}")
スティッキーセッションでDataDome Cookieを維持
DataDomeはCookieベースのセッション追跡を行います。リクエストごとにIPが変わると、DataDomeのCookieが無効になり、再チャレンジが発生する可能性があります。ProxyHatのスティッキーセッション機能(-session-フラグ)を使用すると、同一セッションIDの間は同じ出口IPを維持できます。
# 同一セッションで同じIPを維持
proxy_url = "http://user-country-US-session-gd001:PASSWORD@gate.proxyhat.com:8080"
セッションIDは企業ごとに一意にすることを推奨します。例えばsession-glassdoor-E12345のように命名すれば、企業ごとに異なるIPが割り当てられ、同一企業内ではIPが安定します。50〜100並行セッション程度であれば、DataDomeのレート制限に引っかかることなく安定してデータを取得できます。
ペース調整とチャレンジリトライ
各リクエストの間に3〜5秒の遅延を入れることで、DataDomeの行動分析に引っかかるリスクを大幅に減らせます。また、HTTP 403または429ステータスコードを受け取った場合は、指数バックオフでリトライを実装します。
import random
def fetch_with_retry(url, max_retries=3, **kwargs):
for attempt in range(max_retries):
resp = requests.post(url, **kwargs)
if resp.status_code == 200:
return resp
elif resp.status_code in (403, 429):
wait = (2 ** attempt) + random.uniform(0, 1)
print(f"Blocked ({resp.status_code}), retrying in {wait:.1f}s...")
time.sleep(wait)
else:
resp.raise_for_status()
raise Exception(f"Max retries ({max_retries}) exceeded")
Node.js実装例
Node.js環境では、undiciのProxyAgentと組み合わせて実装できます。ただし、Node.js標準のTLSスタックはChromeと異なるため、本格的なスクレイピングにはnode-libcurlまたはPuppeteerの使用を推奨します。
import { ProxyAgent } from 'undici';
const proxyUrl = 'http://user-country-US-session-gd001:PASSWORD@gate.proxyhat.com:8080';
const agent = new ProxyAgent(proxyUrl);
const query = {
query: `query EmployerReviews($employerId: ID!, $cursor: String) {
employerReviews(employerId: $employerId, after: $cursor) {
reviews { id ratingOverall pros cons jobTitle { text } }
pageInfo { hasNextPage endCursor }
}
}`,
variables: { employerId: 'E12345', cursor: null }
};
const response = await fetch('https://www.glassdoor.com/bff/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'gd-csrf-token': csrfToken,
'Origin': 'https://www.glassdoor.com',
'Referer': 'https://www.glassdoor.com/Reviews/',
},
body: JSON.stringify(query),
dispatcher: agent,
});
const data = await response.json();
data.data.employerReviews.reviews.forEach(r => {
console.log(`Rating: ${r.ratingOverall}, Title: ${r.jobTitle.text}`);
});
プロキシタイプ比較 — Glassdoorスクレイピング向け
GlassdoorのDataDome + Cloudflare環境において、各プロキシタイプの性能を比較します。
| プロキシタイプ | DataDome通過率 | 平均レイテンシ | コスト | 推奨用途 |
|---|---|---|---|---|
| レジデンシャル | 高(約90%) | 200〜500ms | 中 | Glassdoorレビューの通常スクレイピング |
| モバイル | 最高(95%+) | 300〜800ms | 高 | 高難度ターゲット・大量取得 |
| データセンター | 低(約15%) | 50〜100ms | 低 | 開発・テスト環境のみ |
Glassdoorのスクレイピングには、ProxyHatのレジデンシャルプロキシが最適なバランスを提供します。コストパフォーマンスとDataDome通過率の両立が可能で、GeoターゲティングによりUS向けコンテンツに確実にアクセスできます。
倫理的スクレイピング — 集計データのみ、ログイン壁越えなし
Glassdoorからデータを取得する際は、以下の倫理的ガイドラインを遵守することを強く推奨します。
集計・非個人データのみを対象とする
個別のレビュー投稿者を特定できるデータ(氏名、メールアドレスなど)は収集しないでください。レビューの評価スコア、職種、長所・短所のテキストなど、企業分析に必要な集計データのみを抽出し、個人を特定できる情報は除外します。
ログイン壁の後ろのデータはスクレイピングしない
ログインが必要な給与の詳細内訳やレビュー全文は、本記事の対象範囲外です。ログイン壁を越えてデータを取得することは、CFAAやGlassdoorの利用規約に違反する可能性が高いです。
GDPRにおけるEU従業員データの取り扱い
EUの従業員によって投稿されたレビューは、GDPRの「個人データ」に該当する可能性があります。レビューのテキストから投稿者を特定できる情報を抽出・保存する場合、GDPRのデータ保護要件が適用されます。集計分析のみを行い、個人レベルのデータ保存を避けることで、リスクを軽減できます。詳細についてはGDPR.euを参照してください。
公式データライセンスを選択すべきタイミング
大規模なデータ収集や商用利用を計画している場合、Glassdoorの公式データライセンスまたはAPIの利用を検討してください。公式ライセンスは法的リスクを排除し、データの品質と更新頻度も保証されます。スクレイピングは小規模な研究やプロトタイピングに留め、本番環境では公式チャネルを優先すべきです。
Key Takeaways
- 公開データのみを対象:
/Reviews/ページの切り詰められたレビューと基本評価は公開されていますが、給与の詳細内訳とレビュー全文はログイン壁の後ろにあります。 - TLS偽装が必須: CloudflareのTLSフィンガープリンティングを通過するには、
curl_cffiのimpersonate="chrome120"を使用します。標準のrequestsでは不十分です。 - GraphQL BFFエンドポイントを活用:
/bff/graphqlにPOSTリクエストを送信し、gd-csrf-tokenヘッダーを含めることで、HTMLパース不要の構造化データを取得できます。 - レジデンシャルプロキシでDataDomeを回避: データセンターIPはDataDomeによって80%以上ブロックされます。レジデンシャルプロキシは約90%の通過率を達成します。
- スティッキーセッションでCookieを維持:
-session-フラグで同一IPを維持し、DataDomeのCookieベースセッションを安定させます。 - ペース調整は3〜5秒: リクエスト間隔を空け、指数バックオフでリトライを実装することで、ブロックリスクを最小化します。
- 倫理と法令遵守: 集計データのみを収集し、ログイン壁を越えず、GDPR/CFAAを遵守してください。大規模利用には公式データライセンスを検討しましょう。
Glassdoorのスクレイピング実装についてさらに詳しい情報が必要な場合は、ウェブスクレイピングのユースケースおよびProxyHatドキュメントを参照してください。SERPトラッキングのユースケースも、検索結果ページからのデータ抽出に役立つ参考情報を提供しています。






