
API 是你的程序向另一项服务发出的请求。Apify 是启动这项工作并存储结果的宿主。Actor 是一个托管的数据采集程序。Naukri Jobs Actor 是本方法中的 Actor。它读取公开的 Naukri 页面。它不使用 Naukri 登录。Task 是已保存的输入预设。当前公开 Task 只是一个起始输入。它不会替你分析职位。Actor 保存来源行。你检查回执,并为你的项目选择行。
这条路径使用 Apify 的 API。它不是官方 Naukri API。它不是私有的公司数据库。它不是印度职位的完整索引。它展示的是这一个公开 Actor。
1. 设定一次搜索和行数上限
在开始之前,先写下你想要的行。选择一个职位。选择一个印度城市,或者把城市留空。选择来源帖子应当有多新。选择一个较小的已保存行数上限。
当前公开 Actor 接受以下输入:
- `searchTerm` 每一行接受一个关键词。
- `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` 是较窄的初次检查。它读取的来源页面少于另外两项。本方法不需要更深的搜索。
当前公开 Task 存储的是 `searchTerm` "software engineer"、`postedWithinDays` "7"、`sortBy` "date" 和 `searchDepth` "standard"。把你的职位放进 `searchTerm`。如果你只要一个城市,把该城市放进 `location`。设定你自己的 `maxItems`。
一次已核对的对照使用了不同的值。它使用一个关键词 "software engineer"。它没有发送地点。它使用 `postedWithinDays` "0"、`maxItems` 2、`sortBy` "relevance" 和 `searchDepth` "standard"。这次运行保存了两个互不相同的 Naukri 职位 ID。每一行都有职位名称、公司名称和地点。每一行都有直接的 Naukri `jobUrl`。每个 `sourceUrl` 都与所请求的查询相符。`source` 是 naukri.com。每一行都有 `observedAt`。两行都包含 `descriptionText`。两行都包含 `experienceRaw`。两个数字范围是 0-5 和 0-2。这些零是观察到的最低年限。它们不是空字段。该对照没有发送 `experienceYears`。它不能证明已应用 fresher 筛选。
已核对的运行以成功结束。它的 OUTPUT 记录显示了这些已知值。`rowsSaved` 为 2。`requestedMaxItems` 为 2。`pagesRequested` 为 1。回执报告看到了 20 个候选。`failedRequests` 为 0。`blockedRequests` 为 0。`stopReason` 为 `max_items_reached`。这次运行达到了上限。已保存的两行并不是每一个匹配的职位。该对照没有给出 `searchesQueued`、`searchesFinished` 或 `sourceStates`。这三个值未知。未知不等于 0。
已保存的一行可以包含下面的字段。
- `schemaVersion`
- `jobId`
- `title`
- `companyName`
- `location`
- `experienceRaw`
- `experienceMinYears`
- `experienceMaxYears`
- `skills`
- `descriptionText`
- `postedAt`
- `postedAtRaw`
- `jobUrl`
- `searchTerm`
- `searchLocation`
- `sourceUrl`
- `pageNumber`
- `rankOnPage`
- `status`
- `statusReason`
- `source`
- `observedAt`
可选值可以不存在。不存在就是未知。它不是零。`descriptionText` 可以存在,却仍可能缺少雇主文本的一部分。它也可能缺少一项要求。`postedAt` 和经历字段是来源所显示的内容。它们不是雇主将会聘用的承诺。`observedAt` 是 Actor 看到该行的时间。它不是 Naukri 首次发布该帖的时间。把这两个时间分开保留。
`searchTerm`、`searchLocation` 和 `observedAt` 属于原始来源行。如果你后来添加自己的查询列,那一列是你创建的。它不是名为 query 的来源字段。
2. 启动运行并保留运行 ID
使用你自己的 Apify 账户。把令牌放在你的项目已经用来存放机密的安全位置。令牌是一种机密,用来证明该调用属于你的账户。不要把真实令牌写进文章。不要把它放进浏览器代码。不要把它放进日志。不要把它放进网址的图片里。这次公开读取不需要 Naukri 登录。
启动调用返回关于这次运行的信息。它不返回职位行。一次运行是 Actor 的一次启动。以 JSON 发送输入。JSON 是用于结构化数据的文本格式。包含标头 `Content-Type: application/json`。在 `Authorization` 标头中发送令牌。先写 `Bearer`,然后一个空格,再写令牌。下面的正文是原创的教学输入。它不是实时结果。
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"
}此正文请求的职位是 data analyst。它把地点限制为 Bengaluru。它最多保存 2 行。它要求最近 7 天内的帖子。它按日期排序。它使用 standard 深度。启动运行文档说明了这个端点。同一 API 让你可以设置超时和费用上限。费用上限会在你选择的成本限制处停止这次运行。第一次运行时设置这两项限制。本文不给出价格。
从响应中保存 `data.id`。那就是运行 ID。同时保存该服务所存储的输入和选项。运行 ID、数据集 ID 和存储 ID 是三个不同的值。把它们分开保留。
3. 等待结束状态
用同一个私有标头读取这次运行。两次读取之间等待一小段时间。当状态为终止状态时停止。终止状态是已结束的状态。结束值是 `SUCCEEDED`、`FAILED`、`TIMED-OUT` 和 `ABORTED`。
GET https://api.apify.com/v2/actor-runs/<RUN_ID>
Authorization: Bearer <APIFY_TOKEN>保存 `status`、`buildId`、`startedAt`、`finishedAt`、`defaultDatasetId` 和 `defaultKeyValueStoreId`。`SUCCEEDED` 表示 Actor 已结束。它不表示来源已被完整覆盖。`FAILED`、`TIMED-OUT` 和 `ABORTED` 在你信任这些行之前需要查看。运行状态文档说明了这次读取。
4. 读取 OUTPUT 回执
OUTPUT 是 Actor 的摘要记录。从键值存储中读取它。键值存储保存这次运行的命名记录。使用 `defaultKeyValueStoreId` 作为存储 ID。
GET https://api.apify.com/v2/key-value-stores/<STORE_ID>/records/OUTPUT
Authorization: Bearer <APIFY_TOKEN>读取这些字段:
- `rowsSaved` 是已存储的行数。
- `requestedMaxItems` 是你要求的上限。
- `searchesQueued` 是已排入队列的搜索工作。
- `searchesFinished` 是已完成的搜索工作。
- `pagesRequested` 是所请求的来源页面数。来源页面是一个 Naukri 结果页。它不是对已保存数据集的一次读取。
- `blockedRequests` 是被拦截的来源请求数。
- `failedRequests` 是失败的来源请求数。
- `sourceStates` 说明来源如何结束。
- `stopReason` 说明 Actor 为何停止。
如果 `rowsSaved` 等于 `requestedMaxItems`,并且 `stopReason` 是 `max_items_reached`,就是上限停止了这次运行。这些行仍然可以有帮助。它们不是完整搜索。计为 0 表示 Actor 报告没有。缺失的字段是未知。不要把未知改成 0。
在你解释空文件之前,先使用 `sourceStates`。空结果可以表示来源没有返回行。也可以表示没有连上来源。这两种结果不同。如果 `failedRequests` 或 `blockedRequests` 大于 0,不要把该城市当作没有职位。如果 `sourceStates` 缺失,来源结果就是未知。无论哪种情况都保留回执。
5. 在运行结束后读取数据集
数据集是已保存的行项目集合。在运行处于终止状态之后再读取它。仍在增长的数据集并不稳定。先结束运行。如果你需要在结束前取得行,应有意设计那种情况。不要把中间的一页当作最终集合。
GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json&offset=0&limit=2
Authorization: Bearer <APIFY_TOKEN>响应是一个 JSON 数组。`offset` 是要跳过的已保存行数。`limit` 是这次读取要返回的行数。这次读取是数据集的一页。它不是 Naukri 来源页面。数据集项目文档说明了这次调用。数数组中的行数。把该计数加到 `offset`。再次读取。为每次读取保留一份小回执。回执需要 offset、limit、返回计数,以及任何读取错误。
对于一个已结束、共有两行的数据集,第一次读取可以返回两行。下一次读取使用 offset 2 和 limit 2。它可以返回一个空数组。空数组的返回计数为 0。这个 0 表示这一页没有更多行。它不表示先前的行已经消失。已读取的行数是 2。如果 `rowsSaved` 为 2,这些计数相符。
如果一次数据集读取失败,把该失败保留在回执中。不要用空数组替换失败的读取。尚未读取的行仍然未知。
6. 在筛选之前先保存身份
存储原始行。把 `jobId` 存为文本。保留前导零。把 ID 存为数字的程序可能会丢掉这些零。在职位 ID 前加上来源前缀 `naukri:`。该前缀是命名空间。命名空间表明是哪个网站创建了该 ID。另一个职位网站可以用相同的数字表示不同的职位。它的命名空间保持分开。
同时存储 `jobUrl`、`sourceUrl`、原始 `searchTerm`、原始 `searchLocation`、`observedAt` 和运行 ID。检查身份是否存在。检查你所期望的类型。如果缺少 `jobId`,该行保持未解析。未解析表示你还不能把该行对应到一个来源职位。不要为它创建来源键。
一次观察是看见某个来源行之后所保存的一条记录。同一条招聘信息在稍后的一次运行中,是对一个已知来源职位的新观察。它不会自动成为一份新的在招职位。让重复行保持可见。让同一身份的两行之间的冲突保持可见。然后再应用你的项目规则。在你选出有用的行时,把失败的行保留在回执中。
薪资含义不在本方法之内。关于是否适合该职位的规则不在本方法之内。本方法不提交申请。若要安排以后的采集,那是一项单独的新选择。本文不启动重复采集。
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。故事中已保存的运行记录如下:
{
"data": {
"id": "R1",
"status": "SUCCEEDED",
"buildId": "fiction-build",
"startedAt": "2026-04-01T09:00:00Z",
"finishedAt": "2026-04-01T09:05:00Z",
"defaultDatasetId": "D1",
"defaultKeyValueStoreId": "K1"
}
}故事中的 OUTPUT 记录是:
{
"rowsSaved": 2,
"requestedMaxItems": 2,
"failedRequests": 0,
"stopReason": "max_items_reached"
}在这个故事中,`blockedRequests`、`searchesQueued`、`searchesFinished`、`pagesRequested` 和 `sourceStates` 没有出现在该 OUTPUT 记录上。让它们保持未知。不要为它们写入 0。`failedRequests` 为 0,因为故事说明没有失败的请求。这个 0 是已知的。
第一次数据集读取使用 offset 0 和 limit 2。它返回这个数组:
[
{
"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"
}
]返回计数为 2。这些故事行不包含 `postedAt`。发布时间未知。它不是 0。下一次读取使用 offset 2 和 limit 2。它返回:
[]返回计数为 0。已读取的行数是 2。`rowsSaved` 为 2。计数相符:2 等于 2。已知来源 ID 是 `naukri:000101` 和 `naukri:000102`。这是两个已知 ID。前导零保留在文本中。两行都保留故事中的来源 URL、原始查询字段和 `observedAt`。上限表示两条被观察到的招聘信息。它不表示 Bengaluru 只有两份在招职位。
同一虚构的后一部分是运行 R2。故事说 R2 再次返回 `000102`,并返回 `000103`。它没有给出单独的 OUTPUT 记录。不要为 R2 编造 `stopReason` 或请求计数。它的两行故事数据是:
[
{
"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"
}
]把运行 R2 的两行都存为新的观察。只有 `naukri:000103` 是新的已知来源 ID。`naukri:000102` 此前已知。
这里的算术使用简单计数:
- R1 观察数 = 2。
- R2 观察数 = 2。
- 全部观察数 = 2 + 2 = 4。
- R1 之后的已知 ID 数 = 2。
- R2 上的新已知 ID 数 = 1。
- R2 上重复的已知 ID 数 = 1。
- 两次运行之后的已知 ID 数 = 2 + 1 = 3。
- 这四行中的未解析 ID 数 = 0,因为每一行故事数据都有 `jobId`。
四次观察不是四份职位。三个已知来源 ID 不是三份雇主职位。这个故事没有雇主之间的单独对应。它不能证明有三个空缺。
| 检查 | 计数 |
|---|---|
| R1 观察数 | 2 |
| R2 观察数 | 2 |
| 全部观察数 | 4 |
| 已知来源 ID | 3 |
| R2 中的新已知 ID | 1 |
| R2 中重复的已知 ID | 1 |
| 四行中的未解析 ID | 0 |
| 已证明的雇主职位 | 未显示 |
另有一次虚构检查不属于那四行。如果一次下载在同一页上重复 `000102`,记录该重复。不要创建第二个身份。已知 ID 仍是 `naukri:000102`。如果另一行故事数据有职位名称但没有 `jobId`,给未解析数加 1。不要创建诸如 `naukri:unknown` 这样的键。
把原始行、运行 ID 和这份回执交给你的项目。加上一条简短说明:这些行是通过此 Actor 从公开的 Naukri 页面取得的。加上 `observedAt` 中的时间。然后,项目就可以使用这个样本。上限不是完整市场。
来源和延伸阅读
作者 Chronometrica · 更新日期
返回顶部 ↑
