

# 重複する求人掲載を削除し、再掲載の可能性がある求人をグループ化する方法

取得元と求人IDを使って、同じ掲載求人を識別します。削除するのは、重複して取り込んだレコードだけです。後の時点で取得した観測記録は残します。IDが異なる求人は、採用企業、業務内容、勤務地、公開されている企業の求人管理番号を使って、同じ求人の候補かどうかを確認します。グループ化の判断ごとに、根拠と理由を残します。

## 基本となる区別と定義

掲載求人とは、特定の求人サイトに掲載された1件の求人情報です。採用枠とは、その掲載のもとになった、企業が実際に募集しているポジションです。観測記録とは、収集時点で取得した1回分のデータです。完全なコピーとは、まったく同じ観測記録をファイルに2回取り込んだものです。

取得元キーは、`linkedin:L101`のように、取得元のプラットフォームと掲載求人のIDを組み合わせたものです。求人IDは必ず文字列として保持してください。企業の求人管理番号とは、企業が付けた求人コードで、求人詳細や企業ページに公開されている場合に利用できます。企業はこれらの公開コードを再利用することがあります。レビュー用グループには、同じ求人の候補を、記録した理由とともにまとめます。グループは一致の候補を表すものであり、確認済みの採用枠を表すものではありません。

Actorは、ホスティング環境で動作するデータ収集プログラムです。

求人サイトによって、求人掲載の扱いは異なります。LinkedInは、募集終了、掲載期限切れ、または内容変更後の求人を再掲載した場合、新しい掲載として扱います。詳しくは[LinkedInの再掲載ガイド](https://www.linkedin.com/help/linkedin/answer/a522249/repost-a-closed-or-expired-job)に記載されています。この方針だけでは、必ずIDが変わることや、企業に採用の意思があることは証明できません。Indeedは、[Indeedの求人掲載の適正性に関するルール](https://www.indeed.com/help/employers/articles/job-posting-integrity-and-duplicates?co=US&hl=en)で説明しているように、重複掲載と募集を再開した求人を区別しています。これらのルールだけでは、収集データ内で繰り返し現れる掲載求人が不適切だとは証明できません。Googleの構造化データのルールは、企業の求人識別子を定義し、複数の勤務地を認めています。詳しくは[Googleの求人情報のドキュメント](https://developers.google.com/search/docs/appearance/structured-data/job-posting#identifier)に記載されています。`jobId`が求人サイトの掲載をどのように識別するかは、Googleのルールではなく、確認済みのActorの仕様によって定まります。

求人の分析担当者は、エクスポートデータを統合する際にこれらのルールを使います。[LinkedInの求人再掲載に関する質問](https://www.reddit.com/r/csMajors/comments/1s2pq11/linkedin_reposted_jobs/)に見られるように、LinkedInで何度も再掲載される求人が実際の募集なのかを疑問に思う読者もいます。外部の調査では、[Indeedのデータセット分析](https://www.reddit.com/r/recruitinghell/comments/1r3a8jj/i_tracked_63336_us_job_postings_last_week_feb_26/)のように、元の掲載件数、重複を除いた掲載件数、繰り返し掲載された件数を追跡しています。これらの集計値は、検証されていない外部の参考情報です。[Apifyのデータセットガイド](https://docs.apify.com/storage/dataset)で説明しているように、公開データを収集するスクレイパーは、元のレコードをデータセットに保存します。分析担当者はこれらのツールを使って行を収集し、完全な重複を削除し、再掲載の可能性がある求人をグループ化します。

## Taskとデータ収集の設定

Taskは、Actorの実行開始時の設定を保存したものです。Taskはデータを収集します。再掲載の分類や実行のスケジュール設定は行いません。[LinkedInの求人をCSVまたはExcelに出力するTask](https://apify.com/chronometrica/linkedin-jobs-scraper/examples/export-linkedin-jobs-to-csv-or-excel)は、LinkedInのActorを使います。初期設定では25件の求人を取得するよう要求します。[Indeedの求人再掲載の根拠を集めるデータセットTask](https://apify.com/chronometrica/indeed-jobs-scraper/examples/indeed-repost-watch)は、IndeedのActorを使います。プリセットでは15ページから500件の求人を取得するよう要求します。どちらのツールも、ユーザーのログインCookieを使わずに公開データを収集します。

テスト用のサンプルを収集する前に設定を調整します。検索クエリを設定します。`maxItems`を3に設定します。`maxPagesPerSearch`を1に設定します。`saveOnlyUniqueItems`はtrueのままにします。LinkedInでは、説明文の全文を収集するために`fetchJobDetails`をtrueに設定します。返されたレコードの`descriptionStatus`を確認します。抜粋だけでは、全文の一致を確認できません。

`saveOnlyUniqueItems`の設定は、1回の実行内で重複する掲載求人を除外します。複数回の実行にまたがる掲載求人のグループ化や、採用枠の照合は行いません。`maxItems`の設定は、保存する行数の共通上限として機能します。実行後にOUTPUTの概要を確認します。`jobsSaved`、`duplicatesSkipped`、`candidateItemsSeen`、`stopReason`を確認してください。候補項目数から保存済み求人数を引いても、その差が必ず重複件数になるわけではありません。

スクレイパーは、収集日時を`scrapedAt`として返します。取得元の日付の原文、または掲載からの相対的な経過期間を示す表記を`postedAtRaw`として返します。`postedAt`は、取得元の正確な日付を解析するか、「6日前」のような相対表記から日時を算出して返します。取得元の精度を保ち、両方のフィールドを残してください。算出した値は、正確な公開日時ではありません。レビュー担当者は、正常に取得して保持した観測記録から`firstSeen`と`lastSeen`の値を算出します。組み込み機能で復元された履歴から算出するものではありません。収集に空白期間があっても、募集の終了、再開、採用決定を証明することはできません。

## 分析担当者向けの10の手順

1. 元のエクスポートデータを保持します。データを編集する前に、取得元のエクスポートデータを変更せずに保存します。作業用コピーには、取り込み元のファイル名と行番号を追加し、元のレコードを探せるようにします。
2. 掲載求人キーを作成します。`linkedin:L101`のように、取得元の名前と文字列の求人IDを組み合わせます。IDは文字列として保持します。求人サイトをまたいでIDだけを使って結合したり、IDが空欄のレコードを統合したりしないでください。
3. コピーされた観測記録を見つけます。取得元キー、`scrapedAt`、保持している取得元のすべての値を比較して、完全なコピーを見つけます。一致の確認には、取り込み元のファイル名、行番号、レビューのメモを含めません。取得元の値が異なる場合は、レビューのために別々の行として残す必要があります。
4. 後の時点の観測記録を保持します。同じ掲載求人を後の時点で取得した観測記録は残します。実行の間に変わった業務内容、職種名、勤務地、状態の各フィールドを保持します。収集時刻が異なるデータのフィールドを、1つの行にまとめないでください。
5. IDがないレコードを分けます。求人IDのないレコードは、別のレビュー用テーブルに移します。確認済みの取得元URLが、同じ掲載かどうかを判断する手がかりになる場合があります。職種名、企業名、市、空欄のフィールドだけでは、自動的に一致と判定できません。
6. レビューするペアを作成します。確認済みの同じ採用企業の掲載求人だけを比較します。職種名、勤務地、業務内容の全文、収集時刻を比較します。比較用の列では前後の空白を取り除きますが、取得元の原文は保持します。チーム名、職位、勤務地、企業の求人管理番号を残します。雇用に関する定型文が共通していても、同じ掲載求人だとは証明できません。自動的に一致を判定できるとは主張しないでください。
7. 掲載求人の詳細を比較します。業務内容の全文、企業の求人管理番号、チーム、職位、勤務形態、勤務地を比較します。採用企業、職種名、市が同じでも、別々の採用枠である場合があります。企業の求人管理番号や業務内容が異なる場合は、別々の採用枠であることを裏付けます。企業の求人管理番号の一致はレビューに役立ちますが、企業がコードを再利用することもあります。
8. レビューの判断を記録します。ペアに`same_listing`、`possible_same_vacancy`、`separate_listing`、`unresolved`のいずれかのラベルを付けます。根拠、理由、レビュー担当者名、レビュー日を記録します。`possible_repost`に分類するには、後の掲載を示すIDがあり、業務内容が一致している必要があります。異なる求人サイトに同時に掲載されている求人は、取得元をまたぐ一致候補です。自動的に再掲載と判断できるものではありません。
9. グループ内の求人を直接比較します。追加する求人はすべて、そのグループの主な基準となる求人と比較します。掲載求人Aが掲載求人Bと一致し、掲載求人Bが掲載求人Cと一致しても、掲載求人Aが掲載求人Cと自動的に一致するわけではありません。矛盾がある掲載求人は分けて保持します。
10. レコードを分けて数えます。元の行数、重複を除いた観測記録数、取得元別の掲載求人キー数、候補をまとめたレビュー用グループ数を、それぞれ別に数えます。経過期間に一定の範囲を設けても、類似度スコアを使っても、一致を証明することはできません。

## 架空の具体例：Northshore Tools

この例のすべての行は、採用企業がNorthshore Tools、職種名がデータエンジニア、勤務地がトロントの求人です。IDはすべて架空のラベルです。企業の求人管理番号はレビュー担当者のメモであり、Actorが標準で返すフィールドではありません。この表は、取り込み元のファイルにある6件の元の観測記録を示しています。

| 取り込み行 | 取得元キー | 収集日時 | 保持した業務内容の全文 |
| --- | --- | --- | --- |
| R1 | `linkedin:L101` | 2026-10-01T09:00:00Z | レポーティングチーム、バッチ処理型ウェアハウス関連業務 |
| R2 | `linkedin:L101` | 2026-10-01T09:00:00Z | レポーティングチーム、バッチ処理型ウェアハウス関連業務 |
| R3 | `linkedin:L101` | 2026-10-03T09:00:00Z | レポーティングチーム、バッチ処理型ウェアハウス関連業務 |
| R4 | `linkedin:L202` | 2026-10-05T09:00:00Z | レポーティングチーム、バッチ処理型ウェアハウス関連業務 |
| R5 | `linkedin:L303` | 2026-10-05T09:00:00Z | プロダクトチーム、低遅延のデータ処理関連業務 |
| R6 | `indeed:I90` | 2026-10-05T09:00:00Z | レポーティングチーム、バッチ処理型ウェアハウス関連業務 |

行R2に保持されている取得元のすべての値は、完全な収集日時`2026-10-01T09:00:00Z`も含めて、行R1と一致しています。取り込み行のラベルは、取得元の値の一致を確認する対象に含めません。行R2は完全なコピーです。

行R3は取得元キー`linkedin:L101`が共通しており、後の時点である`2026-10-03T09:00:00Z`に収集した同じ取得元の値を含んでいます。これは、同じ掲載求人を後の時点で取得した記録です。行R3は行R1とともに保持します。

### グループ化と集計の手順

- 取り込んだ元の行数：6行。
- 完全なコピーの削除：取得元のすべての値が行R1と一致するため、行R2を削除します。
- 重複を除いた観測記録数：6 - 1 = 5件の有効な観測記録（R1、R3、R4、R5、R6）。
- 取得元別の掲載求人キー数：重複を除いて4個の掲載求人キー（`linkedin:L101`、`linkedin:L202`、`linkedin:L303`、`indeed:I90`）。
- 候補をまとめたレビュー用グループ数：2グループを作成します。G1には、業務内容の一致とレビュー担当者がメモした求人管理番号NT-20を根拠に、`linkedin:L101`、`linkedin:L202`、`indeed:I90`を含めます。G2には、プロダクトチームの業務内容とレビュー担当者がメモした求人管理番号NT-21を根拠に、`linkedin:L303`を含めます。

### 暫定的なレビュー用グループの判断表

| レビュー用グループ | 掲載求人キー | レビューによる分類 | レビュー担当者の根拠と理由 |
| --- | --- | --- | --- |
| G1 | `linkedin:L101`, `linkedin:L202`, `indeed:I90` | `possible_same_vacancy` | 業務内容の全文が一致しています。レビュー担当者は、説明文にある企業の求人管理番号NT-20を記録しました。L202はL101の再掲載である可能性があります。I90は取得元をまたぐ一致候補です。 |
| G2 | `linkedin:L303` | `separate_listing` | 業務内容に、プロダクトチームと低遅延システムが明記されています。レビュー担当者は、企業の求人管理番号NT-21を記録しました。G1とは別の掲載求人です。 |

確認済みの採用枠数は、依然として不明です。候補をまとめたレビュー用グループだけでは、実際に同じ採用枠なのか、現在も募集しているのかを確認できません。暫定的なレビュー用グループが2つあっても、採用枠が2つあると証明されたわけではありません。

## 実務上の対応と分析の限界

全文がない掲載求人は、人が確認するまで未解決として残します。同じIDで[業務内容が変更されている](/ja/articles/%E6%B1%82%E4%BA%BA%E7%A5%A8-%E6%9D%A1%E4%BB%B6%E5%A4%89%E6%9B%B4-%E7%A2%BA%E8%AA%8D%E6%96%B9%E6%B3%95/)場合は、新しい観測記録をログに保持します。職種名が一致していても、チームや求人管理番号が異なる場合は、掲載求人を分けて保持します。勤務地が異なる場合は、別々の採用枠なのか、複数の勤務地を含む1件の掲載なのかを確認します。

検証中に取得元URLへアクセスできなかった場合は、確認に失敗したと記録します。募集が終了した、または採用が決まったとは推測しないでください。企業の求人管理番号に矛盾がある場合は、その矛盾がメモで分かるように残します。掲載からの相対的な経過期間を示すテキストは、元の形式のまま保持します。小規模なテスト実行で重複件数が0でも、より大規模な実行に重複がないとは証明できません。

## 最後に行うこと

[Apify LinkedIn Jobs Scraper Actor](https://apify.com/chronometrica/linkedin-jobs-scraper)と[LinkedInの求人をCSVまたはExcelに出力するTask](https://apify.com/chronometrica/linkedin-jobs-scraper/examples/export-linkedin-jobs-to-csv-or-excel)を使って、小規模なテストを実行します。検索クエリを設定し、`maxItems`を3、`maxPagesPerSearch`を1、`saveOnlyUniqueItems`をtrue、`fetchJobDetails`をtrueに設定します。

必要に応じて、[Apify Indeed Jobs Scraper Actor](https://apify.com/chronometrica/indeed-jobs-scraper)と[Indeedの求人再掲載の根拠を集めるデータセットTask](https://apify.com/chronometrica/indeed-jobs-scraper/examples/indeed-repost-watch)を使って、Indeedのデータを追加します。取得元の名前、文字列のID、URL、説明文の取得状態、収集時刻を保持します。レビュー表の形式を使って、再掲載の可能性がある求人をグループ化します。

## 出典と参考資料

- [LinkedInヘルプ：募集終了または掲載期限切れの求人を再掲載する](https://www.linkedin.com/help/linkedin/answer/a522249/repost-a-closed-or-expired-job)
- [Indeed採用企業向けヘルプ：求人掲載の適正性と重複](https://www.indeed.com/help/employers/articles/job-posting-integrity-and-duplicates?co=US&hl=en)
- [Google検索セントラル：求人情報の構造化データにおける識別子のガイドライン](https://developers.google.com/search/docs/appearance/structured-data/job-posting#identifier)
- [Apifyドキュメント：データセットの保存ガイド](https://docs.apify.com/storage/dataset)
- [Apify Actor：LinkedIn Jobs Scraper](https://apify.com/chronometrica/linkedin-jobs-scraper)
- [Apify Actor：Indeed Jobs Scraper](https://apify.com/chronometrica/indeed-jobs-scraper)
- [Apify Taskの例：LinkedInの求人をCSVまたはExcelに出力する](https://apify.com/chronometrica/linkedin-jobs-scraper/examples/export-linkedin-jobs-to-csv-or-excel)
- [Apify Taskの例：Indeedの求人再掲載の根拠を集めるデータセット](https://apify.com/chronometrica/indeed-jobs-scraper/examples/indeed-repost-watch)
- [RedditのcsMajorsでの議論：LinkedInで再掲載される求人](https://www.reddit.com/r/csMajors/comments/1s2pq11/linkedin_reposted_jobs/)
- [RedditのRecruiting Hellでの議論：求人掲載の追跡](https://www.reddit.com/r/recruitinghell/comments/1r3a8jj/i_tracked_63336_us_job_postings_last_week_feb_26/)