求人カードがデータインターフェースを通じて、チェックマークの付いた保管トレーにつながっている。
返された行を確認してから保存します。

APIは、自分のプログラムから別のサービスへリクエストを送るための仕組みです。Apifyは、処理を開始して結果を保存するホストサービスです。Actorは、ホスト上で動作するデータ収集プログラムです。この方法では、Naukri Jobs Actorを使います。公開されているNaukriのページを読み取ります。Naukriへのログインは使いません。Taskは、保存済みの入力設定です。現在公開されているTaskは、入力の出発点にすぎません。求人を分析するものではありません。Actorは取得元の行を保存します。実行記録を確認し、自分のプロジェクトで使う行を選ぶのは利用者です。

この方法ではApifyのAPIを使います。Naukriの公式APIではありません。非公開の企業データベースでもありません。インドの求人を網羅した索引でもありません。ここで紹介するのは、この1つの公開Actorです。

1. 検索条件を1つ決め、行数の上限を設定する

開始前に、どのような行を取得したいかを書き出します。職種を1つ選びます。インドの都市を1つ選ぶか、都市を空欄にします。取得元の求人について、どのくらい最近の掲載までを対象にするかを決めます。保存する行数には小さな上限を設定します。

現在の公開Actorは、次の入力を受け付けます。

  • `searchTerm`には、1行につき1つのキーワードを指定します。
  • `location`は省略可能です。
  • `maxItems`は行数の上限です。実行全体で保存する行数を制限します。キーワードの行ごとに別々の上限を設けるものではありません。省略すると、数値による上限がなくなります。この方法では、必ず数値を送信してください。
  • `postedWithinDays`は文字列です。"0"、"1"、"3"、"7"、"15"、"30"のいずれかを使います。文字列の"0"は、掲載日を問わないことを意味します。
  • `experienceYears`は省略可能です。0から30までの整数を指定します。この検索設定でのFresherは、これまでの職務経験がほとんどない、またはまったくない人を意味します。値に0を指定すると、Fresher向けの検索設定を要求します。この方法では、求人への適性は判断しません。
  • `sortBy`は`relevance`または`date`です。
  • `excludeCompanies`は省略可能です。以下の例では使いません。
  • `searchDepth`は`standard`、`extended`、`deep`のいずれかです。`standard`は、最初に行う範囲を絞った確認向けの設定です。他の2つよりも、読み取る取得元ページが少なくなります。この方法では、より深い検索は必要ありません。

現在の公開Taskには、`searchTerm`が"software engineer"、`postedWithinDays`が"7"、`sortBy`が"date"、`searchDepth`が"standard"として保存されています。`searchTerm`に対象の職種を入力します。都市を1つに絞る場合は、`location`に都市を入力します。`maxItems`は自分で設定します。

検証で確認した実行では、異なる値を使いました。キーワードは"software engineer"の1つです。場所は送信していません。`postedWithinDays`は"0"、`maxItems`は2、`sortBy`は"relevance"、`searchDepth`は"standard"でした。この実行では、重複のないNaukriの求人IDが2件保存されました。各行には、求人タイトル、企業名、勤務地がありました。各行には、Naukriへの直接リンクである`jobUrl`がありました。各`sourceUrl`は、要求した検索条件と一致していました。`source`はnaukri.comでした。各行には`observedAt`がありました。両方の行に`descriptionText`が含まれていました。両方の行に`experienceRaw`が含まれていました。経験年数の数値範囲は0-5と0-2でした。これらの0は、取得元で確認した最低経験年数です。空のフィールドではありません。この検証用の実行では、`experienceYears`を送信していません。Fresher向けのフィルターが適用されたことを証明するものではありません。

確認した実行は正常に終了しました。そのOUTPUTレコードでは、次の値を確認できました。`rowsSaved`は2でした。`requestedMaxItems`は2でした。`pagesRequested`は1でした。実行記録では、候補を20件確認したと報告されていました。`failedRequests`は0でした。`blockedRequests`は0でした。`stopReason`は`max_items_reached`でした。実行は上限に達しました。保存された2行が、条件に一致する求人のすべてというわけではありません。この検証用の実行では、`searchesQueued`、`searchesFinished`、`sourceStates`は示されていませんでした。この3つの値は不明です。不明と0は同じではありません。

保存される行には、以下のフィールドが含まれる場合があります。

  • `schemaVersion`
  • `jobId`
  • `title`
  • `companyName`
  • `location`
  • `experienceRaw`
  • `experienceMinYears`
  • `experienceMaxYears`
  • `skills`
  • `descriptionText`
  • `postedAt`
  • `postedAtRaw`
  • `jobUrl`
  • `searchTerm`
  • `searchLocation`
  • `sourceUrl`
  • `pageNumber`
  • `rankOnPage`
  • `status`
  • `statusReason`
  • `source`
  • `observedAt`

任意の値は、存在しない場合があります。値がなければ不明です。0ではありません。`descriptionText`が存在していても、雇用主の文章の一部が欠けている場合があります。応募要件が欠けている場合もあります。`postedAt`と経験年数のフィールドは、取得元に表示されていた情報です。雇用主による採用を保証するものではありません。`observedAt`は、Actorがその行を確認した時刻です。Naukriがその求人を最初に公開した時刻ではありません。この2つの時刻は区別してください。

`searchTerm`、`searchLocation`、`observedAt`は、取得元の未加工の行に含まれるフィールドです。後から独自のquery列を追加した場合、その列を作成したのは自分です。queryという名前の取得元フィールドではありません。

2. 実行を開始し、実行IDを保存する

自分のApifyアカウントを使います。トークンは、プロジェクトですでに機密情報の保管に使っている安全な場所に保管します。トークンとは、その呼び出しが自分のアカウントによるものだと証明する機密情報です。実際のトークンを記事に載せないでください。ブラウザー側のコードに含めないでください。ログに残さないでください。URLの画像にも写り込ませないでください。この公開情報の読み取りに、Naukriへのログインは不要です。

開始の呼び出しは、実行に関する情報を返します。求人の行は返しません。実行とは、Actorを1回起動して行う処理です。入力はJSONで送信します。JSONは、構造化データを表すテキスト形式です。`Content-Type: application/json`ヘッダーを含めます。トークンは`Authorization`ヘッダーで送信します。`Bearer`、半角スペース1つ、トークンの順に記述します。以下の本文は、説明用に独自に作成した入力です。実際の実行結果ではありません。

http
POST https://api.apify.com/v2/acts/chronometrica~naukri-jobs-scraper/runs
Authorization: Bearer <APIFY_TOKEN>
Content-Type: application/json

{
  "searchTerm": "data analyst",
  "location": "Bengaluru",
  "maxItems": 2,
  "postedWithinDays": "7",
  "sortBy": "date",
  "searchDepth": "standard"
}
2. 実行を開始し、実行IDを保存する

この本文は、職種としてdata analystを要求します。場所はBengaluruに限定します。保存する行数は最大2行です。過去7日間に掲載された求人を要求します。日付順に並べます。検索の深さはstandardです。このエンドポイントは、実行開始のドキュメントに記載されています。同じAPIで、タイムアウトと料金の上限も設定できます。料金の上限を設定すると、自分で決めた費用の限度額に達した時点で実行を停止します。最初の実行では、両方の制限を設定してください。この記事では価格を示していません。

レスポンスから`data.id`を保存します。これが実行IDです。サービスに保存された入力とオプションも保存します。実行ID、データセットID、ストアIDは、それぞれ異なる値です。区別して保管してください。

3. 終了状態になるまで待つ

同じ機密の認証ヘッダーを使って、実行情報を読み取ります。読み取りの間には短い待ち時間を設けます。ステータスが終了状態になったら、読み取りを停止します。終了状態とは、実行が終わったことを示す状態です。終了時の値は、`SUCCEEDED`、`FAILED`、`TIMED-OUT`、`ABORTED`です。

http
GET https://api.apify.com/v2/actor-runs/<RUN_ID>
Authorization: Bearer <APIFY_TOKEN>
3. 終了状態になるまで待つ

`status`、`buildId`、`startedAt`、`finishedAt`、`defaultDatasetId`、`defaultKeyValueStoreId`を保存します。`SUCCEEDED`は、Actorの処理が完了したことを意味します。取得元を完全に網羅したことを意味するものではありません。`FAILED`、`TIMED-OUT`、`ABORTED`の場合は、行を信頼して使う前に確認が必要です。この読み取りは、実行ステータスのドキュメントに記載されています。

4. OUTPUTの実行記録を読み取る

OUTPUTは、Actorの処理概要を示すレコードです。キーバリューストアから読み取ります。キーバリューストアは、その実行に関する名前付きのレコードを保存する場所です。ストアIDには`defaultKeyValueStoreId`を使います。

http
GET https://api.apify.com/v2/key-value-stores/<STORE_ID>/records/OUTPUT
Authorization: Bearer <APIFY_TOKEN>
4. OUTPUTの実行記録を読み取る

次のフィールドを読み取ります。

  • `rowsSaved`は、保存された行数です。
  • `requestedMaxItems`は、指定した上限です。
  • `searchesQueued`は、処理待ちに登録された検索の数です。
  • `searchesFinished`は、処理が完了した検索の数です。
  • `pagesRequested`は、要求した取得元ページの数です。取得元ページとは、Naukriの検索結果ページ1ページを指します。保存済みデータセットの読み取り1回を指すものではありません。
  • `blockedRequests`は、取得元へのリクエストがブロックされた回数です。
  • `failedRequests`は、取得元へのリクエストが失敗した回数です。
  • `sourceStates`は、取得元に対する処理がどのような状態で終了したかを示します。
  • `stopReason`は、Actorが停止した理由を示します。

`rowsSaved`が`requestedMaxItems`と等しく、`stopReason`が`max_items_reached`なら、上限によって実行が停止しています。その行は、引き続き役立つ可能性があります。ただし、検索結果の全体ではありません。件数として報告された0は、Actorが該当なしと報告したことを意味します。フィールドがなければ不明です。不明な値を0に変えないでください。

空のファイルになった理由を説明する前に、`sourceStates`を確認します。結果が空の場合、取得元が行を返さなかった可能性があります。一方で、取得元に到達できなかった可能性もあります。この2つの結果は異なります。`failedRequests`または`blockedRequests`が0より大きい場合、その都市に求人がないとは判断しないでください。`sourceStates`がなければ、取得元の処理結果は不明です。どちらの場合も、実行記録を保管してください。

5. 実行終了後にデータセットを読み取る

データセットは、保存された行の集合です。実行が終了状態になってから読み取ります。行の追加が続いているデータセットは、内容が確定していません。先に実行を終了させます。終了前に行が必要な場合は、その用途に合わせて意図的に設計してください。途中で読み取ったページを、最終的なデータ全体として扱わないでください。

http
GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json&offset=0&limit=2
Authorization: Bearer <APIFY_TOKEN>
5. 実行終了後にデータセットを読み取る

レスポンスはJSON配列です。`offset`は、保存済みの行を先頭から何行読み飛ばすかを示します。`limit`は、今回の読み取りで返す行数です。この読み取りの単位は、データセットのページです。Naukriの取得元ページではありません。この呼び出しは、データセット項目のドキュメントに記載されています。配列内の行数を数えます。その件数を`offset`に加えます。再び読み取ります。読み取りごとに、簡単な記録を残します。その記録には、offset、limit、返された件数、読み取りエラーがあればその内容を含めます。

実行が終了した2行のデータセットでは、最初の読み取りで2行が返される場合があります。次の読み取りではoffsetを2、limitを2にします。空の配列が返される場合があります。空の配列で返された件数は0です。この0は、このページにはもう行がなかったことを意味します。前に取得した行が消えたという意味ではありません。読み取った行数は2です。`rowsSaved`が2なら、件数は一致します。

データセットの読み取りが失敗した場合は、その失敗を実行記録に残します。失敗した読み取りを空の配列に置き換えないでください。読み取れなかった行については、引き続き不明です。

6. 絞り込む前に識別情報を保存する

未加工の行を保存します。`jobId`は文字列として保存します。先頭のゼロを残してください。IDを数値として保存するプログラムでは、そのゼロが失われる場合があります。求人IDの先頭に、取得元を示す接頭辞`naukri:`を付けます。この接頭辞が名前空間です。名前空間は、どのサイトがそのIDを作成したかを示します。別の求人サイトが、異なる求人に同じ数字を使うことがあります。その場合も、名前空間は別のものとして扱います。

`jobUrl`、`sourceUrl`、未加工の`searchTerm`、未加工の`searchLocation`、`observedAt`、実行IDも保存します。識別情報が存在することを確認します。想定したデータ型であることも確認します。`jobId`がなければ、その行は未解決のままにします。未解決とは、その行を取得元の求人にまだひも付けられない状態です。その行に取得元キーを作成しないでください。

観測記録とは、取得元の行を1回確認したことを保存した記録です。後の実行で同じ求人掲載を取得した場合、それは既知の取得元求人に対する新たな観測記録です。自動的に新しい募集中の求人になるわけではありません。重複行は、確認できる状態にしておきます。同じ識別情報を持つ2行の間に矛盾があれば、その矛盾も確認できる状態にしておきます。そのうえで、プロジェクトのルールを適用します。使える行を選ぶ際も、確認に通らなかった行は実行記録に残します。

給与の解釈は、この方法の範囲外です。求人への適性に関する判断ルールも、この方法の範囲外です。この方法では応募を送信しません。後で定期実行を設定するかどうかは、別に決めることです。この記事の手順では、繰り返しの収集を開始しません。

7. 実行終了後の記録を照合する

この節の事例は、独自に作成した架空のものです。実際のテスト出力ではありません。製品の実行結果でもありません。ID、URL、時刻は、説明のために作成したものです。この事例のURLにリクエストを送信しないでください。これらの行を実際のNaukriの行として扱わないでください。

架空の実行R1では、実行開始の節にある説明用の本文を使います。事例のリクエストは、`https://api.apify.com/v2/acts/chronometrica~naukri-jobs-scraper/runs`へのPOSTで、`Content-Type: application/json`を付けます。事例の開始レスポンスには、`data.id`として"R1"が含まれます。求人の行は含まれません。

その後、R1はステータス`SUCCEEDED`で終了します。事例のデータセットIDはD1です。事例のストアIDはK1です。この事例で保存した実行情報は、次のようになります。

json
{
  "data": {
    "id": "R1",
    "status": "SUCCEEDED",
    "buildId": "fiction-build",
    "startedAt": "2026-04-01T09:00:00Z",
    "finishedAt": "2026-04-01T09:05:00Z",
    "defaultDatasetId": "D1",
    "defaultKeyValueStoreId": "K1"
  }
}
7. 実行終了後の記録を照合する

事例のOUTPUTレコードは次のとおりです。

json
{
  "rowsSaved": 2,
  "requestedMaxItems": 2,
  "failedRequests": 0,
  "stopReason": "max_items_reached"
}
7. 実行終了後の記録を照合する

この事例では、そのOUTPUTレコードに`blockedRequests`、`searchesQueued`、`searchesFinished`、`pagesRequested`、`sourceStates`が示されていません。これらは不明のままにします。0と記入しないでください。`failedRequests`が0なのは、この事例でリクエストの失敗はなかったと示しているためです。この0は、確認できている値です。

最初のデータセットの読み取りでは、offsetを0、limitを2にします。次の配列が返されます。

json
[
  {
    "jobId": "000101",
    "jobUrl": "https://www.naukri.com/example-fiction/000101",
    "sourceUrl": "https://www.naukri.com/example-fiction/search",
    "searchTerm": "data analyst",
    "searchLocation": "Bengaluru",
    "source": "naukri.com",
    "observedAt": "2026-04-01T09:04:00Z"
  },
  {
    "jobId": "000102",
    "jobUrl": "https://www.naukri.com/example-fiction/000102",
    "sourceUrl": "https://www.naukri.com/example-fiction/search",
    "searchTerm": "data analyst",
    "searchLocation": "Bengaluru",
    "source": "naukri.com",
    "observedAt": "2026-04-01T09:04:00Z"
  }
]
7. 実行終了後の記録を照合する

返された件数は2です。この事例の行には、`postedAt`が含まれていません。公開時刻は不明です。0ではありません。次の読み取りでは、offsetを2、limitを2にします。次の値が返されます。

json
[]
7. 実行終了後の記録を照合する

返された件数は0です。読み取った行数は2です。`rowsSaved`は2です。2と2なので、件数は一致します。確認済みの取得元IDは、`naukri:000101`と`naukri:000102`です。確認済みのIDは2件ということです。先頭の0は、文字列の中に残ります。両方の行に、事例の取得元URL、未加工の検索条件フィールド、`observedAt`を保持します。上限があるため、観測した求人掲載は2件です。Bengaluruの募集中の求人が2件しかないという意味ではありません。

同じ架空の事例の続きとして、実行R2があります。この事例では、R2は再び`000102`を返し、さらに`000103`を返します。R2について個別のOUTPUTレコードは示していません。R2の`stopReason`やリクエスト回数を推測で補わないでください。事例の2行は次のとおりです。

json
[
  {
    "jobId": "000102",
    "jobUrl": "https://www.naukri.com/example-fiction/000102",
    "sourceUrl": "https://www.naukri.com/example-fiction/search",
    "searchTerm": "data analyst",
    "searchLocation": "Bengaluru",
    "source": "naukri.com",
    "observedAt": "2026-04-08T09:04:00Z"
  },
  {
    "jobId": "000103",
    "jobUrl": "https://www.naukri.com/example-fiction/000103",
    "sourceUrl": "https://www.naukri.com/example-fiction/search",
    "searchTerm": "data analyst",
    "searchLocation": "Bengaluru",
    "source": "naukri.com",
    "observedAt": "2026-04-08T09:04:00Z"
  }
]
7. 実行終了後の記録を照合する

R2の両方の行を、新たな観測記録として保存します。新たに確認できた取得元IDは、`naukri:000103`だけです。`naukri:000102`はすでに確認済みです。

計算には、単純な件数を使います。

  • R1の観測記録数 = 2。
  • R2の観測記録数 = 2。
  • 観測記録の総数 = 2 + 2 = 4。
  • R1終了後の確認済みID数 = 2。
  • R2で新たに確認できたID数 = 1。
  • R2で再び取得した確認済みID数 = 1。
  • 両方の実行終了後の確認済みID数 = 2 + 1 = 3。
  • この4行にある未解決のID数 = 0。事例の各行に`jobId`があるためです。

観測記録が4件あっても、求人が4件あるという意味ではありません。確認済みの取得元IDが3件あっても、雇用主の求人が3件あるという意味ではありません。この事例では、雇用主をまたいだ別途の照合は行っていません。募集が3件あることを証明するものではありません。

7. 実行終了後の記録を照合する
確認項目件数
R1の観測記録2
R2の観測記録2
観測記録の合計4
確認済みの取得元ID3
R2で新たに確認できたID1
R2で再び取得した確認済みID1
4行にある未解決のID0
存在を証明できた雇用主の求人示されていない

追加の架空の確認例を1つ示します。これは、先ほどの4行には含まれません。ダウンロード時に同じページで`000102`が重複して返された場合は、その重複を記録します。2つ目の識別情報を作成しないでください。確認済みIDは`naukri:000102`のままです。別の架空の行にタイトルはあっても`jobId`がない場合は、未解決の件数に1を加えます。`naukri:unknown`のようなキーを作成しないでください。

未加工の行、実行ID、この実行記録をプロジェクトに渡します。これらの行が、このActorを通じてNaukriの公開ページから取得されたことを短く注記します。`observedAt`の時刻も加えます。これで、プロジェクトはそのサンプルを使えます。上限を設けたサンプルが、市場全体を表すわけではありません。

出典と参考資料

著者 Chronometrica · 更新日

ページの先頭へ ↑