Hacker-job API 参考手册数据结构和接口调用完全指南【免费下载链接】hacker-jobPlay with hackernews who is hiring项目地址: https://gitcode.com/gh_mirrors/ha/hacker-jobHacker-job 是一个强大的招聘数据分析平台专门用于解析和结构化 Hacker News Who is Hiring? 帖子的数据。本指南将详细介绍 Hacker-job 的数据结构和 API 接口帮助开发者和数据分析师充分利用这个宝贵的技术招聘数据集。无论你是想构建自己的招聘分析工具还是需要访问结构化的工作数据这个完整的 API 参考手册都将为你提供所需的一切信息。 项目概述与数据架构Hacker-job 项目通过 AI 解析 Hacker News 的招聘帖子将自由文本转换为结构化数据。整个系统采用无数据库架构数据直接存储在 JSON 文件中前端应用通过简单的 HTTP 请求访问这些数据。核心数据文件结构项目的数据存储在data/目录中采用以下组织方式data/ ├── jobs/ # 按月存储的工作数据 │ ├── 2026-07.json # 2026年7月的招聘数据 │ ├── 2026-06.json # 2026年6月的招聘数据 │ └── index.json # 所有可用月份的清单 ├── trends.json # 薪资和关键词趋势数据 └── hackers.json # GitHub 赞助者信息每个月的 JSON 文件采用 JSONL 格式每行一个 JSON 对象这种格式既便于流式处理又保持了良好的可读性。 API 接口调用指南基础数据访问接口Hacker-job 的前端通过简单的 HTTP GET 请求访问数据所有接口都返回 JSON 格式的响应。1. 获取月份清单 API// 前端实现 export function getManifest(): PromiseJobsManifest { return fetch(base data/jobs/index.json).then((r) r.json()) }接口地址/data/jobs/index.json响应示例{ months: [2026-07, 2026-06, 2026-05], count: 87183, generated: 2026-07-03T12:34:56Z }字段说明months可用的月份列表按从新到旧排序count工作职位总数generated数据生成时间戳2. 获取单月工作数据 API// 前端实现 export async function getMonth(month: string): PromiseJob[] { const text await fetch(base data/jobs/ month .json).then((r) r.text()) return text.split(\n).filter((l) l.trim()).map((l) JSON.parse(l) as Job) }接口地址/data/jobs/{month}.json例如/data/jobs/2026-07.json数据格式JSONL每行一个完整的 JSON 对象3. 获取趋势数据 API// 前端实现 export function getTrends(): PromiseTrends { return fetch(base data/trends.json).then((r) r.json()) }接口地址/data/trends.json4. 获取赞助者数据 API// 前端实现 export function getHackers(): PromiseHacker[] { return fetch(base data/hackers.json).then((r) r.json()) }接口地址/data/hackers.json 完整数据结构详解Job 工作职位数据结构工作职位数据结构是 Hacker-job 的核心包含了从 Hacker News 帖子中提取的所有关键信息interface Job { id: number; // Hacker News 帖子 ID author: string | null; // 发帖者用户名 ts: number; // 创建时间戳Unix 秒 company: string; // 公司名称 roles: string[]; // 职位角色数组 location: string | null; // 工作地点 remote_type: string | null; // 远程类型onsite/remote/hybrid remote_regions: string[]; // 远程工作允许的地区 salary_min: number | null; // 最低薪资 salary_max: number | null; // 最高薪资 salary_currency: string | null; // 薪资货币USD/EUR/GBP 等 tech_stack: string[]; // 技术栈标签数组 job_type: string | null; // 工作类型full-time/contract/intern visa: number | null; // 签证支持1支持0不支持null未提及 text: string; // 原始帖子文本 }数据示例分析让我们看一个实际的数据示例{ id: 48775598, author: maddie-confido, ts: 1783089373, company: Confido, roles: [Forward Deployed Engineer], location: NYC, Chelsea, remote_type: onsite, remote_regions: [], salary_min: 200000, salary_max: 270000, salary_currency: USD, tech_stack: [Rails, Python, TypeScript, Postgres, AWS], job_type: full-time, visa: null, text: Confido | Forward Deployed Engineer | $200-270k BASE | ON-SITE (NYC, Chelsea) | Full-time\n\nWe manage over $3B for CPG brands like OLIPOP, Tropicana, and Dr. Squatch... }关键字段解析id: 48775598 - 对应的 Hacker News 帖子 IDts: 1783089373 - 对应 2026年7月的发布时间salary_min/salary_max: 200000-270000 USD - 年薪范围tech_stack: 包含 Rails、Python 等技术标签remote_type: onsite - 现场办公职位Trends 趋势数据结构趋势数据提供了宏观的市场洞察interface Trends { meta: { jobs: number; // 总工作职位数 months: number; // 月份总数 from: string; // 起始月份YYYY-MM to: string; // 结束月份YYYY-MM }; volume: TrendPoint[]; // 每月职位数量趋势 salary: TrendPoint[]; // 平均薪资趋势 keywords: KeywordSeries[]; // 关键词趋势系列 } interface TrendPoint { x: string; // 月份YYYY-MM y: number; // 数值 } interface KeywordSeries { key: string; // 关键词标识 label: string; // 显示标签 default: boolean; // 是否默认显示 data: TrendPoint[]; // 趋势数据点 }Hacker 赞助者数据结构赞助者信息来自 GitHub 赞助者interface Hacker { login: string; // GitHub 用户名 name?: string; // 显示名称 avatar?: string; // 头像 URL url?: string; // GitHub 主页 bio?: string; // 个人简介 location?: string; // 地理位置 blog?: string; // 个人博客 twitter?: string; // Twitter 账号 readme?: string; // 详细的个人介绍 } 实战使用指南1. 快速获取最新招聘数据要获取最新的招聘数据你可以按照以下步骤// 获取可用月份列表 const manifest await fetch(/data/jobs/index.json).then(r r.json()); const latestMonth manifest.months[0]; // 最新的月份 // 获取该月所有工作职位 const jobsText await fetch(/data/jobs/${latestMonth}.json).then(r r.text()); const jobs jobsText.split(\n) .filter(line line.trim()) .map(line JSON.parse(line)); console.log(获取到 ${jobs.length} 个工作职位);2. 构建自定义筛选器利用 Hacker-job 的结构化数据你可以轻松构建各种筛选功能// 筛选远程工作 const remoteJobs jobs.filter(job job.remote_type remote); // 筛选特定技术栈 const pythonJobs jobs.filter(job job.tech_stack.some(tech tech.toLowerCase().includes(python) ) ); // 筛选高薪职位 const highSalaryJobs jobs.filter(job job.salary_min job.salary_min 150000 ); // 筛选支持签证的职位 const visaJobs jobs.filter(job job.visa 1);3. 分析技术趋势通过分析tech_stack字段你可以了解技术栈的流行趋势// 统计技术栈出现频率 const techFrequency {}; jobs.forEach(job { job.tech_stack.forEach(tech { techFrequency[tech] (techFrequency[tech] || 0) 1; }); }); // 获取最热门的技术栈 const popularTechs Object.entries(techFrequency) .sort((a, b) b[1] - a[1]) .slice(0, 10); console.log(最热门技术栈, popularTechs);4. 薪资数据分析薪资数据提供了重要的市场参考// 计算平均薪资 const jobsWithSalary jobs.filter(job job.salary_min job.salary_max); const avgMinSalary jobsWithSalary.reduce((sum, job) sum job.salary_min, 0) / jobsWithSalary.length; const avgMaxSalary jobsWithSalary.reduce((sum, job) sum job.salary_max, 0) / jobsWithSalary.length; // 按货币分类统计 const salaryByCurrency {}; jobsWithSalary.forEach(job { const currency job.salary_currency || USD; if (!salaryByCurrency[currency]) { salaryByCurrency[currency] []; } salaryByCurrency[currency].push({ min: job.salary_min, max: job.salary_max, company: job.company }); }); 高级查询技巧组合筛选查询// 高级组合筛选远程、高薪、特定技术栈 const advancedFilter (jobs, options) { return jobs.filter(job { // 远程类型筛选 if (options.remoteType job.remote_type ! options.remoteType) return false; // 薪资范围筛选 if (options.minSalary (!job.salary_max || job.salary_max options.minSalary)) return false; // 技术栈筛选 if (options.techStack options.techStack.length 0) { const hasTech options.techStack.some(tech job.tech_stack.some(jobTech jobTech.toLowerCase().includes(tech.toLowerCase()) ) ); if (!hasTech) return false; } // 工作类型筛选 if (options.jobType job.job_type ! options.jobType) return false; // 地点筛选 if (options.location (!job.location || !job.location.toLowerCase().includes(options.location.toLowerCase()))) { return false; } return true; }); }; // 使用示例 const filteredJobs advancedFilter(jobs, { remoteType: remote, minSalary: 100000, techStack: [react, typescript], jobType: full-time, location: europe });时间序列分析// 按月分析职位数量变化 const jobsByMonth {}; jobs.forEach(job { const date new Date(job.ts * 1000); const monthKey ${date.getFullYear()}-${String(date.getMonth() 1).padStart(2, 0)}; if (!jobsByMonth[monthKey]) { jobsByMonth[monthKey] []; } jobsByMonth[monthKey].push(job); }); // 计算每月统计 const monthlyStats Object.entries(jobsByMonth).map(([month, monthJobs]) { const salaries monthJobs.filter(j j.salary_min j.salary_max); const avgSalary salaries.length 0 ? salaries.reduce((sum, j) sum (j.salary_min j.salary_max) / 2, 0) / salaries.length : null; return { month, count: monthJobs.length, remoteCount: monthJobs.filter(j j.remote_type remote).length, avgSalary, topTechs: getTopTechnologies(monthJobs) }; });️ 数据质量与验证数据验证规则Hacker-job 的数据经过 AI 解析但可能存在解析错误。以下是重要的数据验证点薪资数据验证检查salary_min和salary_max的逻辑关系远程类型验证确保remote_type为有效值onsite/remote/hybrid时间戳验证确认ts在合理的时间范围内必填字段验证company和roles不应为空数据修正机制项目提供了数据问题报告机制function createIssueUrl(job) { const title Data issue: ${job.company} (job #${job.id}); const body The extracted info for this job looks wrong (fields are AI-extracted).\n\n Job: ${job.company}\nHacker News: https://news.ycombinator.com/item?id${job.id}\n\n What is incorrect:\n- ; return https://github.com/hacker-job/hacker-job/issues/new?title${encodeURIComponent(title)}body${encodeURIComponent(body)}; } 趋势数据应用使用趋势数据 API// 获取趋势数据 const trends await fetch(/data/trends.json).then(r r.json()); // 分析职位数量趋势 console.log(数据范围${trends.meta.from} 到 ${trends.meta.to}); console.log(总职位数${trends.meta.jobs}); console.log(覆盖月份${trends.meta.months}); // 绘制趋势图表 const volumeData trends.volume; // 职位数量趋势 const salaryData trends.salary; // 平均薪资趋势 const keywordSeries trends.keywords; // 关键词趋势系列 // 找出最热门的关键词 const popularKeywords trends.keywords .filter(series series.default) .map(series ({ keyword: series.label, latestValue: series.data[series.data.length - 1]?.y || 0 })) .sort((a, b) b.latestValue - a.latestValue);自定义趋势分析// 自定义技术趋势分析 function analyzeTechTrends(jobs, techKeywords) { const monthlyTechCount {}; jobs.forEach(job { const date new Date(job.ts * 1000); const month ${date.getFullYear()}-${String(date.getMonth() 1).padStart(2, 0)}; if (!monthlyTechCount[month]) { monthlyTechCount[month] {}; techKeywords.forEach(keyword { monthlyTechCount[month][keyword] 0; }); } // 检查每个关键词 techKeywords.forEach(keyword { if (job.tech_stack.some(tech tech.toLowerCase().includes(keyword.toLowerCase()) )) { monthlyTechCount[month][keyword]; } }); }); // 转换为趋势数据格式 return techKeywords.map(keyword ({ key: keyword, label: keyword.charAt(0).toUpperCase() keyword.slice(1), data: Object.entries(monthlyTechCount) .map(([month, counts]) ({ x: month, y: counts[keyword] })) .sort((a, b) a.x.localeCompare(b.x)) })); } 集成与扩展构建自定义 API 服务你可以基于 Hacker-job 的数据构建自己的 API 服务// 简单的 Express.js API 服务示例 const express require(express); const app express(); // 获取所有工作职位 app.get(/api/jobs, async (req, res) { const { month, company, tech, remote, minSalary } req.query; // 这里可以添加缓存逻辑 const jobs await getAllJobs(); // 从 Hacker-job 数据源获取 let filteredJobs jobs; // 应用筛选条件 if (month) { filteredJobs filteredJobs.filter(job new Date(job.ts * 1000).toISOString().startsWith(month) ); } if (company) { filteredJobs filteredJobs.filter(job job.company.toLowerCase().includes(company.toLowerCase()) ); } // 更多筛选逻辑... res.json({ count: filteredJobs.length, jobs: filteredJobs.slice(0, 100) // 分页 }); }); // 获取统计数据 app.get(/api/stats, async (req, res) { const jobs await getAllJobs(); const stats { totalJobs: jobs.length, remoteJobs: jobs.filter(j j.remote_type remote).length, hybridJobs: jobs.filter(j j.remote_type hybrid).length, onsiteJobs: jobs.filter(j j.remote_type onsite).length, avgSalary: calculateAverageSalary(jobs), topCompanies: getTopCompanies(jobs), popularTechs: getPopularTechnologies(jobs) }; res.json(stats); });数据导出与备份// 导出数据到 CSV function exportJobsToCSV(jobs) { const headers [ ID, Company, Roles, Location, Remote Type, Salary Min, Salary Max, Currency, Tech Stack, Job Type, Visa Support, Date, HN Link ]; const rows jobs.map(job [ job.id, ${job.company}, ${job.roles.join(, )}, job.location ? ${job.location} : , job.remote_type || , job.salary_min || , job.salary_max || , job.salary_currency || , ${job.tech_stack.join(, )}, job.job_type || , job.visa 1 ? Yes : job.visa 0 ? No : Not Mentioned, new Date(job.ts * 1000).toISOString().split(T)[0], https://news.ycombinator.com/item?id${job.id} ]); return [headers, ...rows].map(row row.join(,)).join(\n); } 最佳实践建议1. 数据缓存策略由于数据文件相对稳定建议实现适当的缓存机制// 简单的浏览器缓存实现 class JobDataCache { constructor() { this.cache new Map(); this.cacheDuration 24 * 60 * 60 * 1000; // 24小时 } async getMonthData(month) { const cacheKey month-${month}; const cached this.cache.get(cacheKey); if (cached Date.now() - cached.timestamp this.cacheDuration) { return cached.data; } // 从服务器获取数据 const data await fetch(/data/jobs/${month}.json).then(r r.text()); const jobs data.split(\n).filter(l l.trim()).map(l JSON.parse(l)); // 更新缓存 this.cache.set(cacheKey, { timestamp: Date.now(), data: jobs }); return jobs; } }2. 错误处理与重试async function fetchWithRetry(url, retries 3) { for (let i 0; i retries; i) { try { const response await fetch(url); if (!response.ok) throw new Error(HTTP ${response.status}); return await response.text(); } catch (error) { if (i retries - 1) throw error; await new Promise(resolve setTimeout(resolve, 1000 * Math.pow(2, i))); // 指数退避 } } }3. 性能优化技巧// 使用 Web Worker 处理大量数据 function processJobsInWorker(jobs) { return new Promise((resolve, reject) { const worker new Worker(job-processor.js); worker.postMessage({ jobs }); worker.onmessage (event) { resolve(event.data); worker.terminate(); }; worker.onerror (error) { reject(error); worker.terminate(); }; }); } // 分批加载数据 async function loadJobsInBatches(months, batchSize 3) { const results []; for (let i 0; i months.length; i batchSize) { const batch months.slice(i, i batchSize); const batchPromises batch.map(month getMonth(month)); const batchResults await Promise.all(batchPromises); results.push(...batchResults.flat()); // 可选更新进度或触发渲染 updateProgress(i batch.length, months.length); } return results; } 总结与下一步Hacker-job 提供了丰富而结构化的技术招聘数据通过简单的 HTTP 接口即可访问。无论是构建招聘分析工具、研究技术趋势还是开发个性化的职位搜索平台这个数据集都是宝贵的资源。关键要点 数据存储在data/目录的 JSON 文件中 采用 JSONL 格式便于流式处理️ 完整的数据结构定义在scripts/types.ts 通过简单的 HTTP GET 请求访问数据 支持多种筛选和查询方式 包含趋势分析和统计数据下一步建议从获取月份清单开始/data/jobs/index.json根据需要加载特定月份的数据利用结构化字段进行精确筛选结合趋势数据进行市场分析构建自定义的可视化或分析工具通过本指南你应该能够充分利用 Hacker-job 的数据接口构建强大的招聘数据分析应用。记得查看项目中的frontend/src/data.ts文件获取最新的接口实现并根据实际需求调整数据加载和处理策略。Happy coding! 【免费下载链接】hacker-jobPlay with hackernews who is hiring项目地址: https://gitcode.com/gh_mirrors/ha/hacker-job创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考