当前位置: 首页 > news >正文

Hacker-job API 参考手册:数据结构和接口调用完全指南

Hacker-job API 参考手册:数据结构和接口调用完全指南

【免费下载链接】hacker-jobPlay with hackernews' "who is hiring"项目地址: https://gitcode.com/gh_mirrors/ha/hacker-job

Hacker-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(): Promise<JobsManifest> { 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): Promise<Job[]> { 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(): Promise<Trends> { return fetch(base + 'data/trends.json').then((r) => r.json()) }

接口地址:/data/trends.json

4. 获取赞助者数据 API
// 前端实现 export function getHackers(): Promise<Hacker[]> { 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 帖子 ID
  • ts: 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 解析,但可能存在解析错误。以下是重要的数据验证点:

  1. 薪资数据验证:检查salary_minsalary_max的逻辑关系
  2. 远程类型验证:确保remote_type为有效值(onsite/remote/hybrid)
  3. 时间戳验证:确认ts在合理的时间范围内
  4. 必填字段验证companyroles不应为空

数据修正机制

项目提供了数据问题报告机制:

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 请求访问数据
  • 🔍 支持多种筛选和查询方式
  • 📈 包含趋势分析和统计数据

下一步建议:

  1. 从获取月份清单开始(/data/jobs/index.json
  2. 根据需要加载特定月份的数据
  3. 利用结构化字段进行精确筛选
  4. 结合趋势数据进行市场分析
  5. 构建自定义的可视化或分析工具

通过本指南,你应该能够充分利用 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),仅供参考

http://www.jsqmd.com/news/1136533/

相关文章:

  • 内容安全策略实施:Instatic规则与监控指南 [特殊字符]️
  • Notepad--深度解析:国产跨平台文本编辑器的10个专业级使用技巧
  • 5个实用技巧:用Python轻松处理PDF文档
  • Dreamer v3-torch监控与可视化:使用TensorBoard跟踪训练进度的最佳实践
  • 为什么U8g2能成为嵌入式显示开发的瑞士军刀?揭秘单色屏终极解决方案
  • LTX-2.3 Foley LoRA提示词教程:如何写出完美的音效生成指令
  • 连续三天项目对接攒下十几段会议录音:2026实用会议纪要生成指南
  • Instatic性能测试方法论:指标与基准设定
  • MobileFace MXNet框架集成指南:GluonCV与模型部署最佳实践
  • 终极指南:如何在5分钟内搞定VMware虚拟机与主机的文件共享
  • 终极自动化方案:使用mssql-scripter实现SQL Server DevOps持续集成
  • GoHBase与Prometheus监控:实现全面的性能指标收集
  • 终极AI助手:Chatbox如何让你轻松管理所有对话历史?
  • Kaggler部署指南:生产环境中的机器学习模型优化终极教程
  • Datoviz 150种颜色映射全解析:科学可视化的色彩选择终极手册
  • EnergyBar项目归档后的替代方案:5个最佳分支推荐
  • CosyVoice_For_Windows安全指南:保护你的语音数据和API接口
  • 终极指南:Goose桌面应用让AI助手可视化操作更简单高效
  • 终极Office激活指南:3分钟解锁Microsoft 365完整功能的免费解决方案
  • FocalNet多尺度聚合技术详解:如何实现高效的上下文建模
  • MongoKitten与Hummingbird集成:构建轻量级Swift API服务
  • R3F-Perf社区贡献指南:如何参与开源Three.js性能工具开发
  • Haar小波变换图像压缩实战:4步分解重构,PSNR 35dB+ 效果实测
  • 终极3DS游戏格式转换指南:如何快速将3DS文件转为CIA格式
  • IDA Pro逆向分析利器:findcrypt-yara插件配置与实战指南
  • TPAFE0808与STM32F732IE的多通道信号控制方案
  • coredumpy安全特性解析:如何保护API密钥和环境变量不泄露的终极指南
  • ReForum数据模型设计:MongoDB Schema与关系映射的完整方案
  • 如何高效配置socialhunter参数?提升社交媒体链接劫持检测效率的10个技巧
  • Chebyshev距离:网格化场景下的极值敏感型距离度量