求人ボードアグリゲーターの構築方法:ソース、重複排除、新鮮さ

優れた求人アグリゲーターは、求人情報のソースをどこにするか、どうやって重複を排除するか、そしてどうやって新鮮さを保つかという3つのエンジニアリング問題です。それを解決すれば、スクレイピングは簡単です — ここにその設計図があります。

求人ボードアグリゲーターは求人情報の検索エンジンです:多くのソースから求人情報を引き出し、一つの場所で表示します。難しい部分はHTTPリクエストではなく、適切なソースを選び、5つのサイトに掲載されている同じ求人を重複排除し、先月埋まった役職を表示しないようにすべてを新鮮に保つことです。この3つを正しく行えば、防御可能な製品になりますが、間違えると信頼を損なう古く重複したフィードになります。このガイドはエンジニアリングの設計図です:ソースミックス、正規の重複排除、新鮮さ、求人SERPの垂直統合、そして尊重すべき法的ライン。これは情報提供であり、法的アドバイスではありません。

ソースティア1:公開ATSエンドポイント

インターネット上で最もクリーンな求人データは求人ボードにはなく、応募者追跡システムによって提供される企業のキャリアページにあります。これらの多くは構造化されたJSONを公開しています。Greenhouse、Lever、Ashby、BambooHR、iCIMS、Paylocity、Workdayはすべて、直接リクエストできる求人情報を提供しています。HTML解析は不要です。これら7つからデータを取得するオープンソースのアグリゲーターは、それらを並行して取得し、各プラットフォームのレート制限に合わせてワーカー数を調整します — Workdayは約50並行、Greenhouse/Lever/iCIMSは30、BambooHRは10、最も厳しい制限は5です。このティアは、雇用主から直接の正規タイトル、勤務地、給与帯、応募URLを提供するため、真剣なアグリゲーターのバックボーンとなります。

import httpx

# Greenhouse exposes a public board JSON endpoint per company slug
def fetch_greenhouse(slug: str) -> list[dict]:
    url = f"https://boards-api.greenhouse.io/v1/boards/{slug}/jobs?content=true"
    r = httpx.get(url, timeout=20)
    r.raise_for_status()
    jobs = r.json().get("jobs", [])
    return [{
        "source": "greenhouse",
        "external_id": str(j["id"]),
        "title": j["title"],
        "company": slug,
        "location": (j.get("location") or {}).get("name"),
        "apply_url": j["absolute_url"],
        "updated_at": j.get("updated_at"),
    } for j in jobs]

rows = fetch_greenhouse("examplecompany")
print(len(rows), "roles")

ポーリングする企業を見つけるためには、手作業ではなくウェブインデックスから大規模にATSスラッグを収集します — Common CrawlのURLアーカイブをスキャンしてATSドメインパターンを探すことで、数万の企業識別子を見つけることができます。このディスカバリークロール自体がスクレイピングジョブです;インデックスは寛容で、スピードがIPの評判よりも重要であるため、データセンタープロキシプールを通じてルートします。

ソースティア2:求人SERPの垂直統合

ATSフィードは、アグリゲーターサイト、地域ボード、またはGoogleの独自の求人体験にのみ投稿されたものをすべて逃します。個別に数十のボードをスクレイピングせずにそのロングテールをカバーする効率的な方法は、求人SERPの垂直統合です — 役職と勤務地に対してGoogleが表示する求人を返す構造化クエリで、すでに正規化されています。私たちのSERP APIは、ニュース、画像、ショッピングと並んで求人の垂直統合を公開しているので、キーワードと地理で役職のリストをJSONとして取得し、同じパイプラインに折り込むことができます:

import httpx

def fetch_jobs_serp(query: str, location: str) -> list[dict]:
    r = httpx.get(
        "https://api.quantumproxies.io/serp",
        params={"engine": "google_jobs", "q": query,
                "location": location, "api_key": "QP_API_KEY"},
        timeout=30,
    )
    jobs = r.json().get("jobs", [])
    return [{
        "source": "jobs_serp",
        "external_id": j.get("job_id"),
        "title": j.get("title"),
        "company": j.get("company_name"),
        "location": j.get("location"),
        "apply_url": (j.get("apply_options") or [{}])[0].get("link"),
    } for j in jobs]

serp_rows = fetch_jobs_serp("react developer", "Austin, TX")

ターゲット都市で同じクエリをスケジュールに従って実行すると、単一のATSフィードが提供することのない地理的カバレッジが得られます。SERPの結果は場所によって変わるため、各クエリを地理的にターゲットにします — 2026年におけるSERPスクレイピングの仕組みに関する私たちの投稿では、地理とページネーションのメカニズムをカバーしています。

SERP APIから求人の垂直統合を取得する

ATS API、求人SERP、直接ボードからソースされた求人データを正規化、重複排除、更新するパイプライン図
3つのソースティアが1つのスキーマを供給します;重複排除と新鮮さのウィンドウが生のリストを信頼できるアグリゲーターに変えます。

正規キーによる重複排除

同じ求人が企業サイト、2つのアグリゲーター、Googleの求人カードに定期的に表示されます。それを4回表示するのは、壊れているように見える最速の方法です。標準的な修正は正規キーです:求人タイトル、企業、勤務地、そして利用可能な場合はソースの外部求人IDの組み合わせを正規化してハッシュします。ハッシュする前に、小文字化、句読点の削除、空白の折りたたみを行い、「Sr. Software Engineer」と「senior software engineer」が一緒に折りたたまれるようにします。

import re, hashlib

def canonical_key(job: dict) -> str:
    def norm(s):
        return re.sub(r"[^a-z0-9]+", " ", (s or "").lower()).strip()
    basis = "|".join([
        norm(job.get("title")),
        norm(job.get("company")),
        norm(job.get("location")),
        (job.get("external_id") or ""),
    ])
    return hashlib.sha1(basis.encode()).hexdigest()

def dedupe(rows: list[dict]) -> list[dict]:
    seen, out = set(), []
    for job in rows:
        k = canonical_key(job)
        if k not in seen:
            seen.add(k)
            out.append(job)
    return out

新鮮さは後回しではなく、機能です

求人ボードの信頼性はその新鮮さにあります。2つのメカニズムがそれを正直に保ちます:各ソースをスケジュールに従って再取得します(高ボリュームのATSフィードは毎時、ロングテールは毎日)、そして再確認していないものをローリングウィンドウでプルーンします — 30日は合理的なデフォルトであり、動きの速い市場では短くします。ソースごとの最終確認タイムスタンプと日次カウントを追跡し、ソースが静かに壊れたときに気づくことができるようにします;自分のボリュームを監視するアグリゲーターは、プラットフォームの数が急落した瞬間にアラートを開くことができます。プロダクションソフトウェアとしてスクレイパーを実行するに関する私たちのメモでは、スケジューリング、ドリフト検出、アラートについてカバーしています。

尊重すべき法的ライン

求人データはスクレイピング法が現実になる場所です。なぜなら、投稿には個人データ(リクルーターの名前、連絡先情報)が含まれることがあり、詳細な説明は著作権で保護されている可能性があるからです。フランスのデータ保護当局は、LinkedInの連絡先データを適切な同意なしにスクレイピングしたとして、KASPR社に240,000ユーロの罰金を科しました;GDPRの罰則は2,000万ユーロまたは世界売上高の4%に達する可能性があり、著作権侵害の損害賠償は米国のケースで1作品あたり150,000ドルに達したことがあります。より安全な姿勢は構造的です:認可されたATSフィードと求人SERPを好み、ログインウォールで保護されたボードを強引に突破するのを避け、事実上のフィールド(タイトル、会社、場所)を保存し、完全な著作権で保護された説明を再公開せず、必要のない個人データを削除します。2026年におけるウェブスクレイピングの合法性LinkedInコンプライアンスのサガに関する私たちの概要はさらに深く掘り下げています。

7つのATSプラットフォーム、30日間の新鮮さウィンドウ、LinkedInスクレイピングでのKASPR 240,000ユーロの罰金を示す統計パネル
構造化されたATSフィードと正規の重複排除は、コスト、カバレッジ、法的リスクにおいて強引なHTMLスクレイピングを上回ります。

プロキシの役割

ATSティアはほとんど住宅IPを必要としませんが、ディスカバリークロール、SERPの垂直統合、および直接ボードスクレイピングは必要です — Googleや大手ボードはIPでレート制限を行い、結果を地理的に変動させます。それらを回転住宅プロキシを通じてルートし、リクエストごとに回転し、地理的にターゲットを絞ることで、各都市クエリが一致する場所から来るようにします。ティアを分けておきます:寛容なインデックスには安価なデータセンター、敏感なターゲットには住宅を使用します。

よくある質問

求人ボードアグリゲーターとは何ですか?

それは、企業のキャリアページ、ATSプラットフォーム、他のボード、求人SERPなど、さまざまなソースから求人情報を収集し、それを1つのスキーマに正規化し、重複を削除し、求職者に1つの検索可能な場所で提示する求人情報の検索エンジンです。エンジニアリング作業は、ソーシング、重複排除、新鮮さにあり、単なるスクレイピングではありません。

複数のソースからの求人情報をどのように重複排除しますか?

求人タイトル、会社、勤務地、ソースの外部求人IDを正規化してハッシュすることで正規キーを構築します。最初に小文字化、句読点の削除、空白の折りたたみを行い、異なるスペルが一緒に折りたたまれるようにします。最初の出現を保持し、一致するものを削除します。これにより、雇用主サイト、アグリゲーター、Google求人に表示される同じ役職をキャッチします。

求人ボードのスクレイピングは合法ですか?

それはソース、データ、あなたの法域に依存します。公開された事実上のリスティングフィールドは、個人データや完全な著作権で保護された説明よりもリスクが低く、ログインウォールを回避することはCFAAスタイルのリスクを高めます。認可されたATSフィードと求人SERPはよりクリーンなルートです。企業は個人データをスクレイピングしたことで重い罰金を科されています — 商業利用には法的アドバイスを受けてください。

集約された求人データはどれくらい新鮮であるべきですか?

ATSフィードのような高ボリュームのソースは毎時、ロングテールは毎日再取得し、ローリングウィンドウ内で再確認していないリスティングをプルーンします — 30日は一般的なデフォルトです。求人ごとに最終確認タイムスタンプを追跡し、ソースごとのボリュームを監視して、ユーザーが古い役職を見る前に壊れたソースをキャッチします。

集約を3つの問題として扱いましょう — ソース、重複排除、新鮮さ — そうすればスクレイピングは補助的な詳細になります。クリーンなATSフィードと求人SERPの垂直統合を先導し、正規キーで重複排除し、積極的にプルーンし、敏感なクロールを回転住宅IPで行います。それが人々が信頼する製品と、死んだリンクの墓場との違いです。

Scraper APIであなたのアグリゲーターを強化しましょう