尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python后端爬虫专题23:三条职位能得出什么、不能得出什么——技能、薪资与城市趋势
Python后端爬虫专题23三条职位能得出什么、不能得出什么——技能、薪资与城市趋势上一篇练习完整答案创建成功返回 202任务落库并恰好入队一次seed 不在 allowlist 返回 422不写任务也不入队队列不可用时任务已落库随后标为 dispatch_failed接口返回 503跨租户读取返回 404不能看到任务职位查询也为空。副作用比响应 JSON 更重要。分页完整测试已经落在tests/test_api.py::test_jobs_endpoint_applies_page_and_size_to_real_query它用真实内存 SQLite 写三条不同 source_urlASGITransport 请求/api/jobs?page2size2断言 total3 且只返回“职位 2”。生产租户应由已验证 JWT 的 tenant claim 映射或由受信网关删除外部同名头后注入签名内部头不能接受用户任意声明。先写统计口径后写Counter城市计数每条有效职位按 city 计一次技能计数同一职位的技能已在 Pydantic 阶段去重、大小写用 casefold 合并但展示保留第一次标签年薪估计只有 salary_min 和 salary_max 都存在时取月薪区间中点乘发薪月数平均值只在这些样本之间计算。这不是市场薪资真相。招聘薪资可能是税前、范围夸大、经验要求不同缺失薪资通常也不是随机缺失。接口必须返回salary_sample_size让使用者知道平均值来自几条而不是把无薪资职位当 0 拉低结果。手算三条样本第一条杭州Python/FastAPI10k—20k、13 薪年薪中点 195000第二条上海Python/PostgreSQL20k—30k、12 薪年薪中点 300000第三条杭州、Scrapy、薪资缺失。城市是杭州 2、上海 1Python 2其余技能各 1平均年薪(195000300000)/2247500样本数 2。cd project.\.venv\Scripts\python.exe-m pytest tests\test_analytics.py::test_summary_counts_cities_skills_and_annual_salary_without_inventing_missing_values-q测试期望值是手算常量不调用实现计算 expected因此把salary_months错写为 12 会失败。它还确保缺薪资样本没有作为 0 进入平均。summarize_jobs 的输入为什么是Protocol它不需要整个 JobRecord只要求 city、skills、salary_min/max/months。JobForAnalyticsProtocol 让 JobItem、SQLAlchemy JobRecord 或别的只读对象都能使用只要具有这些属性。这叫结构化类型边界不要求业务类继承一个“为了统计”的基类。输出JobSummary是冻结 dataclass字段含义明确API 再把 top_skills tuple 转为对象列表。统计函数不读取数据库、不感知 FastAPI、不打印结果所以可在命令行、任务和接口复用。排名相同怎么稳定技能先按次数降序再按展示标签 casefold 排序保证同一输入多次运行顺序一致。Counter 的插入顺序不应被当作业务排名规则。城市同样按次数和名称排序。稳定输出对缓存、前端 diff 和测试都重要。top_n小于 1 直接 ValueErrorAPI 用 Query 限制 1—50在边界层返回 422。空输入返回 total0、salary_sample_size0、averageNone不发生除零也不编造 0 元平均薪资。调接口而不看内部函数Compose 运行并完成一次任务后$headers {X-Tenant-IDcourse-demo}Invoke-RestMethod-Urihttp://127.0.0.1:8010/api/analytics?top_n5-Headers$headers|ConvertTo-Json-Depth 5租户隔离已有 API 行为测试tenant-b 的城市与 SecretSkill 不会进入 tenant-a 结果。注意当前实现先读取最多 100000 行再在 Python 聚合适合课程和中小数据。百万级数据应将城市/薪资聚合下推 SQL技能 JSON 可规范化到关联表或使用 PostgreSQL JSONB/GIN并用离线物化视图控制查询成本。为AI/RAG准备的不是统计接口趋势接口给人和仪表盘RAG 更需要逐条、带来源的文档。项目的scripts.export_jobs从某租户读取 JobRecord转换回不含内部 id/tenant 的 JobItem原子写 JSONL每行带schema_versionjobradar.job.v1。它不会把另一个租户或抓取 Cookie 带出去。$env:JOBRADAR_DATABASE_URL postgresqlpsycopg://jobradar:jobradar-dev-password127.0.0.1:5435/jobradar.\.venv\Scripts\python.exe-m scripts.export_jobs--tenant course-demo--outputdata\jobs-for-rag.jsonl下游切分文本时应保留 source_url、published_at 和 schema_version用于引用和兼容性不能把模型生成的总结重新当原始职位事实。本篇完整统计模块按“口径—遍历累积—稳定排序—缺失处理”阅读。短小不代表随意它把容易引发误导的薪资规则集中到一个可测试位置。把经过校验的职位事实汇总为可解释统计。fromcollectionsimportCounterfromdataclassesimportdataclassfromtypingimportProtocol,SequenceclassJobForAnalytics(Protocol):city:strskills:Sequence[str]salary_min:int|Nonesalary_max:int|Nonesalary_months:intdataclass(frozenTrue)classJobSummary:total_jobs:intcity_counts:dict[str,int]top_skills:list[tuple[str,int]]salary_sample_size:intaverage_annual_salary:int|Nonedefsummarize_jobs(jobs:Sequence[JobForAnalytics],*,top_n:int10)-JobSummary:统计城市、技能和有明确区间样本的平均年薪中位估计。iftop_n1:raiseValueError(top_n must be positive)citiesCounter(job.cityforjobinjobs)skill_labels:dict[str,str]{}skill_counts:Counter[str]Counter()annual_salaries:list[int][]forjobinjobs:forskillinjob.skills:keyskill.casefold()skill_labels.setdefault(key,skill)skill_counts[key]1ifjob.salary_minisnotNoneandjob.salary_maxisnotNone:midpoint(job.salary_minjob.salary_max)/2annual_salaries.append(round(midpoint*job.salary_months))ranked_skillssorted(skill_counts.items(),keylambdaitem:(-item[1],skill_labels[item[0]].casefold()))[:top_n]average(round(sum(annual_salaries)/len(annual_salaries))ifannual_salarieselseNone)returnJobSummary(total_jobslen(jobs),city_countsdict(sorted(cities.items(),keylambdaitem:(-item[1],item[0]))),top_skills[(skill_labels[key],count)forkey,countinranked_skills],salary_sample_sizelen(annual_salaries),average_annual_salaryaverage,)本篇课后练习给四条手工样本其中一条无薪资、一条技能大小写不同先手算结果再写测试调用 summarize_jobs 对照。说明“平均薪资 247500”至少要同时展示哪些上下文避免被误解为市场结论。运行一次 JSONL 导出并逐行json.loads完整验证 schema_version、source_url 与职位数。下一篇会让每个任务在日志和指标中可追踪同时确保凭据不会泄露。
RELATED

相关推荐

使用 xberg Go SDK 独立提取 HWPX 文档:基于 extract 的完整实践指南

使用 xberg Go SDK 独立提取 HWPX 文档:基于 extract 的完整实践指南

后端AI 应用NLP 【免费下载链接】xberg Polyglot document intelligence with a Rust core: extract text, metadata, images, tables, and structured data from 106 formats across 140 file extensions, plus code intelligence for 371 languages. Fifteen bindings, with …

📅 2026/10/6 7:54:59
基于 Jinja2 的自动化 README 生成模板:解析 python-docs-samples 的 README.tmpl.rst 渲染机制

基于 Jinja2 的自动化 README 生成模板:解析 python-docs-samples 的 README.tmpl.rst 渲染机制

示例工程 【免费下载链接】python-docs-samples Code samples used on cloud.google.com 项目地址: https://gitcode.com/GitHub_Trending/py/python-docs-samples 点击查看 免费下载 导读 本文聚焦 python-docs-samples 仓库中的文档生成基础设施——scripts/rea…

📅 2026/10/6 7:49:59
MIT 6.S081 util 实验篇(lab1):sleep (easy)

MIT 6.S081 util 实验篇(lab1):sleep (easy)

sleep (easy) 实验目标 本实验是 6.S081 的"热身关",目的不在难度,而是先把手感建立起来: 熟悉 xv6 实验环境——git 分支怎么切、内核怎么构建、怎么运行、怎么调试、怎么评分。写出第一个用户程序 sleep,体会"…

📅 2026/10/6 7:49:59
MORE NEWS

更多资讯

📰

OpenShell定制Windows开始菜单:从基础配置到高级玩法全攻略

1. 为什么我还在折腾开始菜单:OpenShell的定位与价值接触过的老Windows用户应该都记得,Win7时代那个素净、分类清晰的开始菜单,在Win8被一刀切之后,多少人光是找"关机"按钮就找了半个月。后来Win10回来了一个凑合能用的…

📰

王卓数据结构PPT使用指南:从课堂截图到考研复习全攻略

简介:青岛大学王卓教授的《数据结构与算法》课程PPT截图,是一份面向计算机专业学生、考研备考者及自学编程初学者的学习资料,用于梳理核心概念与课堂重点。内容覆盖绪论、数据结构两个层次、逻辑结构、数据类型与抽象数据类型、算法分析及线性…

📰

基于SpringBoot的动物园智能化管理系统开发实战

1. 项目概述与需求拆解 1.1 从一张手写门票说起 西安秦岭野生动物园,占地两千多亩,展出动物三百多种,旺季单日游客量能冲到好几万。放在十年前,这套流程全靠人扛:售票窗口排队、进门查票喊喇叭、饲养员用纸质台账记录…

📰

SAP FICO顾问面试实战:从固定资产折旧到S/4HANA迁移的核心考点与排查思路

1. 先交代岗位和整体时间线面试这行有个不太成文的规矩:拿到offer不等于会面试,但会面试的人往往更容易拿到offer。这句话听着像绕口令,但它是我的真实体会。我是从SAP ECC时代的FI初级顾问做起,做过几年总账和应收应付的配置&…

📰

SoapUI与RestAssured深度对比:接口测试工具选型与实战经验

在接口测试这个圈子里,SoapUI和RestAssured的“站队”之争从来没停过。做传统企业级项目的老手习惯打开SoapUI点点点,写自动化脚本的新生代则抱着RestAssured的代码不撒手。我两边都深度用过,今天不站队,只把两个工具从底层逻辑到…

📰

Flutter业务迁移OpenHarmony实战:Provider状态管理完整指南

之前接手了一个有点头疼的任务:把一套已经在Android端稳定运行的Flutter业务模块,移植到OpenHarmony设备上。业务模块本身不算复杂,但页面多、共享状态多——用户登录态、设备列表、消息未读数要在好几个页面之间同步。用setState写到第二个页…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬