# Campus Jd Scraper

> 从国内互联网/科技公司校招官网抓取**真实的**岗位清单与 JD 原文，不依赖任何第三方汇总站。适用于「帮我扒一下 XX 公司的岗位」「之前给的岗位数据是假的，重新抓」「某家公司今年在招哪些产品岗、base 在哪、JD 怎么写的」。核心手法是先渲染页面找出该站的数据接口，再用 curl/python 批量拉取，比逐页点击快 10 倍。

- Skill: `paloma333/campus-jd-scraper` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paloma333/campus-jd-scraper`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paloma333/campus-jd-scraper/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Paloma333 (https://skillmd.com/u/paloma333)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/paloma333/campus-jd-scraper

---


# 校招官网 JD 抓取

## 什么时候用

- 用户要「真实的」岗位列表 / JD 原文，不接受第三方汇总站或概括性描述
- 用户抱怨之前给的岗位数据是假的
- 需要跨多家公司批量获取「岗位名 + 工作城市 + JD 逐条原文 + 任职要求」

## 为什么不能直接 WebFetch

国内校招官网（腾讯 / 字节 / 美团 / 百度 / 快手 / 京东 / 蚂蚁 / 小红书…）**全部是 JS 单页应用**。
`curl` 或 WebFetch 只能拿到一个空壳 `<div id="app">`，看不到任何岗位。
**必须先用真浏览器渲染，再想办法拿数据。**

## 标准流程

### Step 1 — 渲染页面（加载 agent-browser skill）

```bash
agent-browser open "https://<公司校招官网>/campus/position"
agent-browser wait --load load        # 不要用 networkidle，SPA 永不 idle，会挂住
agent-browser get url                 # 确认真的跳转成功，出现过 about:blank 就是没加载上
```

### Step 2 — 找接口（关键一步）

```bash
agent-browser eval "JSON.stringify(performance.getEntriesByType('resource').map(r=>r.name).filter(n=>/api|position|job|search|query/i.test(n)&&!/\.(js|css|png|jpg|svg|woff|webp|gif|ttf)/i.test(n)).slice(-25))"
```

`performance.getEntriesByType('resource')` 能列出页面加载过的**所有**请求，
按 `/api|position|job|search/` 过滤，通常一眼就能看到岗位列表接口和详情接口。

> 如果列表是懒加载的，先在页面上点一下「搜索」或滚动一屏，再执行这条命令。

### Step 3 — 试探参数 → 批量拉取

先看请求方式，再猜 body：

```bash
curl -s -X POST "<接口>" -H 'Content-Type: application/json' \
  -H 'Referer: <官网>' -H 'User-Agent: Mozilla/5.0 ...' -d '{"pageIndex":1,"pageSize":100}' | head -c 500
```

- 返回 `{"message":"请求方式不正确！"}` → 换个方法（GET/POST）
- 返回岗位数组 → 成功，改用 python 写循环批量拉
- 分页参数常见写法：`pageIndex/pageSize`、`pageNum/pageSize`、`offset/limit`、`page/limit`
- **pageSize 常有上限**（腾讯 1000，超了报 400）
- **pageNum 不一定生效**（美团就是永远返回同一页）→ 发现「翻页拿到重复数据」时，说明这个站的列表只能靠 UI 筛，别硬刚分页

拿列表 → 逐个取 details → 落 JSON，最后再生成 markdown。

### Step 4 — 反爬站点改用 URL 参数

字节跳动的接口带 `_signature`（JS 现算的签名），
在页内 `fetch` 同样会被拒（返回 HTML 而不是 JSON）。

**绕过方式**：列表页支持把筛选条件写在 URL 里，直接导航再读 DOM：

```bash
agent-browser open "https://jobs.bytedance.com/campus/position?keywords=%E4%BA%A7%E5%93%81"
sleep 2
agent-browser eval "document.body.innerText.slice(0,3000)"          # 读 JD 正文
agent-browser eval "JSON.stringify([...document.querySelectorAll('a')].filter(a=>/\/position\/\d/.test(a.getAttribute('href')||'')).map(a=>a.innerText.replace(/\n+/g,' | ').slice(0,110)+' || '+a.getAttribute('href')))"   # 拿岗位名 + 详情链接
```

### Step 5 — 收尾

```bash
agent-browser close --all
```

**务必执行。** 漏掉会留僵尸 Chromium 进程吃内存。

## 已知接口速查（2026-09 实测）

| 公司 | 接口 | 参数 |
|---|---|---|
| 腾讯 | `POST https://join.qq.com/api/v1/position/searchPosition` | `{"pageIndex":1,"pageSize":1000,"keyword":"产品"}`，pageSize≤1000 |
| 腾讯（详情） | `GET https://join.qq.com/api/v1/jobDetails/getJobDetailsByPostId?postId=` | 字段 `desc` / `request`（**不是 require**）/ `graduateBonus` |
| 美团 | `POST https://zhaopin.meituan.com/api/official/job/getJobList` | 必带 `campusHiringType`（1=应届生 / 2=转正实习 / 6=日常实习）；**pageNum 无效** |
| 美团（枚举） | `GET https://zhaopin.meituan.com/api/official/job/search/enum?enumType=JF` | 岗位族；产品类 = `11002`；`enumType=CAMPUS_HIRING` 取招聘类型 |
| 字节 | 无签名不可用 → 走 `https://jobs.bytedance.com/campus/position?keywords=产品` | — |
| **得物** | 与字节**同一套 ATS 平台** → 走 `https://campus.dewu.com/578078/position/list?keywords=产品` | 列表页渲染出 JD 全文 |
| 米哈游 | `POST https://ats.openout.mihoyo.com/ats-portal/v1/job/list` | 参数名是 **`channel`**（不是 pageIndex）；**详情接口需登录**，改用列表页 DOM |

## 坑与教训

1. **`timeout` 命令 macOS 没有**（是 `gtimeout`），别在命令里写 `timeout 120 ...`，会 command not found。
2. **一条 bash 里串太多命令会被 SIGKILL（exit 137）**。把「打开页面」和「读取内容」拆成两条命令。
3. `agent-browser get url` 返回 `about:blank` = 页面根本没加载上，别继续往下做，重开。
4. JS 文本里中文的引号是 `""` `""`，直接塞进 shell 双引号字符串会出问题 → 用 python 写脚本文件，别用 `-c` 拼超长命令。
5. **不要把第三方汇总站（求职机构公众号、简历站）的 JD 当原文用**，它们会改写、会过期。要么官网原文，要么明确标注为二手来源。
6. 抓完必须核对：**工作城市**和**专业要求**是求职者最容易被误导的两栏，务必逐条抄原文。
7. **hash 路由的详情页在无头浏览器里经常渲染失败**（`document.documentElement.innerText.length === 0`，
   但 `outerHTML.length` 有值 = 页面只剩骨架）。表现：列表页正常、详情页空白、reload 也无效。
   → 别死磕，**回头读列表页的 DOM（很多站列表项里就带 JD 全文）**，或改用官方岗位页的转载补详情。
8. **React 站的筛选器用 JS `.click()` 常常无效**（要触发框架的合成事件）。
   可靠做法：`agent-browser find text "<筛选项>" click`，再用
   `document.body.innerText.match(/职位列表\((\d+)\)/)` **核对数量有没有变** —— 数量没变就是没点上。
9. **想拿 POST 请求体时，monkey-patch `fetch` + `XMLHttpRequest` 再触发一次操作是可行的**，
   但要先确认请求真的走 XHR/fetch（有些站用 axios 的 adapter 或先于 patch 发出，就会抓不到）。

## 输出建议

产物写成一份 markdown 报告，每家公司包含：

- 官方入口 URL + 毕业时间窗口 + 投递规则（能投几个志愿 / 顺序 / 是否可改）+ 笔试规则
- 岗位清单表：`岗位名 | 工作城市 | 是否卡专业`
- 每个核心岗：`岗位描述（逐条原文）` / `任职要求（逐条原文）` / `专业要求（原话）` / `来源 URL`
- 最后附「待扒清单」，**没抓到的写「未获取到」，绝不编**

