# OfferGet **Repository Path**: du1in9/OfferGet ## Basic Information - **Project Name**: OfferGet - **Description**: 多平台面经搜集工具:从牛客网、CSDN、掘金、思否等平台搜索面试经验,支持精准公司匹配、详情阅读与导出。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-06-09 - **Last Updated**: 2026-06-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # OfferGet 多平台面经搜集工具:从 **牛客网、CSDN、掘金、思否** 等平台聚合搜索面试经验,支持精准公司/岗位匹配、详情阅读与导出。 仓库地址:[https://gitee.com/du1in9/OfferGet](https://gitee.com/du1in9/OfferGet) ## 预览 | 桌面版(Windows) | Android 版 | |---|---| | ![桌面版截图](docs/screenshots/desktop.png) | ![手机版截图](docs/screenshots/mobile.jpg) | ## 功能概览 - 按 **公司**、**岗位** 搜索面经,字段均可留空 - **精准公司匹配**:排除「腾讯会议」等产品名误命中 - **多来源并行**:牛客网、CSDN、掘金、思否可同时勾选 - 结果按 **发布时间倒序** 排列 - 面经详情支持 Markdown / 图片渲染;桌面版可导出 TXT / MD / PDF - 不采集个人信息,无统计/广告 SDK(详见各端 `PRIVACY.txt`) --- ## 实现原理 ### 总体架构 OfferGet 采用 **「多来源适配器 + 统一筛选层 + 聚合搜索」** 的分层设计。桌面版(Python)与 Android 版(Kotlin)在业务逻辑上对齐,各自实现相同的来源适配与筛选规则。 ```mermaid flowchart TB subgraph UI["表现层"] D[desktop/main.py
CustomTkinter] M[mobile/MainScreen.kt
Jetpack Compose] end subgraph Hub["聚合层"] SH[search_hub / SearchRepository] end subgraph Sources["来源适配层"] NC[牛客网 nowcoder_api] CS[CSDN csdn_source] JU[掘金 juejin_source] SF[思否 segmentfault_source] end subgraph Core["公共能力"] REL[筛选规则 PostRelevance] HTTP[HTTP + SSL http_client] REN[正文解析 content_render] end D --> SH M --> SH SH --> NC & CS & JU & SF NC & CS & JU & SF --> REL NC & CS & JU & SF --> HTTP NC & CS & JU & SF --> REN ``` 用户输入公司名与岗位后,聚合层按固定顺序依次调用各来源适配器;每个适配器负责 **构造搜索词 → 请求平台 API → 解析响应 → 本地筛选 → 返回统一数据结构** `InterviewPost`。单来源失败不影响其他来源,错误信息汇总展示在状态栏。 --- ### 1. 数据来源与请求方式 | 来源 | 搜索接口 | 请求方式 | 详情获取 | |------|----------|----------|----------| | 牛客网 | `gw-c.nowcoder.com/api/sparta/pc/search` | POST JSON,带「面经」标签 (id=818) | 讨论帖 / 动态帖 API,或页面 HTML 兜底 | | CSDN | `so.csdn.net/api/v3/search` | GET,`t=blog` | 文章页 `#content_views` | | 掘金 | `api.juejin.cn/search_api/v1/search` | GET,cursor 分页 | 文章页 HTML | | 思否 | `segmentfault.com/search` | GET,解析 `__NEXT_DATA__` JSON | 文章页 `.article-content` | 各来源均模拟浏览器 User-Agent,并设置对应 `Referer` / `Origin`,降低被反爬拦截的概率。牛客、CSDN 等使用 **信创 CA(Xcc Trust)** 证书链;桌面版打包时通过 `certifi` 根证书 + PyInstaller 运行时钩子解决 HTTPS 校验问题(见 `desktop/http_client.py`、`desktop/pyi_rth_certifi.py`)。 --- ### 2. 搜索词构建策略 不同平台的索引习惯不同,搜索词策略也分开处理: **牛客 / CSDN(严格标题匹配来源)** ``` 公司 + 岗位 → 例:「腾讯 后端」 ``` **掘金 / 思否(外链来源,岗位表述多样)** ``` 公司 + 岗位 + 同义词 + 「面试」 → 例:「网易 前端 React Vue 面试」 ``` **思否专用(避免泛词污染)** 思否对 `Java`、`后台` 等泛词极其敏感,追加这些词会返回大量 PDF 资源帖。因此思否单独使用精简多查询策略: ``` 「公司 岗位 面试」「公司 岗位 面经」「公司 面经 岗位」 ``` 多组查询结果按 URL 去重后合并。 --- ### 3. 面经筛选规则(核心逻辑) 搜索结果从平台返回后,会在本地进行二次筛选,避免「关键词模糊命中」带来的噪声。规则实现在 `nowcoder_api.py`(桌面)/ `PostRelevance.kt`(Android)。 #### 3.1 牛客 / CSDN:严格标题匹配 ```python is_post_relevant = is_company_relevant AND is_position_relevant ``` - **公司匹配** `is_company_relevant`:只在 **标题** 中判断 - 先屏蔽误命中短语(如「腾讯会议」「字节码」) - 再匹配「腾讯-后端一面」「【腾讯】Java 凉经」等面试主体模式 - **岗位匹配** `is_position_relevant`:标题须 **完整包含** 岗位关键词(如「后端」) #### 3.2 掘金 / 思否 / CSDN:外链宽松匹配 ```python is_external_post_relevant = 面试信号 AND 公司匹配 AND 岗位同义词匹配 ``` - **面试信号**:标题或摘要须含 `面经|面试|校招|社招|凉经|…` 等 - **公司匹配**:在标题+摘要中查找,同样屏蔽产品名误命中 - **岗位同义词**:「后端」可匹配 `后台 / Java / Go / 服务端 / …`(见 `EXTERNAL_POSITION_ALIASES`) 这就是为什么同一组关键词下,牛客返回条数往往少于 CSDN/掘金——牛客筛得更严,只保留标题里明确写了公司+岗位的帖子。 #### 3.3 误匹配防护示例 | 标题 | 搜「腾讯+后端」 | 原因 | |------|----------------|------| | 腾讯-后端开发一面 | ✅ | 标题明确 | | 腾讯会议产品面经 | ❌ | 产品名误命中 | | 新东方测试开发面经 | ❌ | 非腾讯主体 | | 2023 Go 面经:…小红书… | ✅(外链) | 摘要含公司 + Go≈后端 | --- ### 4. 分页与结果合并 - **牛客**:最多 20 页(API 上限),每页约 20 条原始结果,按时间倒序翻页 - **CSDN / 掘金**:最多 20 页;CSDN 搜索前会先访问首页获取 Cookie - **思否**:每组查询最多 3 页,多查询合并去重 - 所有来源结果按 `created_at` **倒序** 合并展示;桌面版列表按来源分组统计条数 单来源异常(网络错误、反爬空结果等)被 `try/except` 隔离,写入 `errors` 字典,不阻断其他来源。 --- ### 5. 详情解析与展示 面经详情统一抽象为 **内容块** `DetailBlock`: | 块类型 | 说明 | |--------|------| | `paragraph` | 普通段落 | | `heading` | 标题(h1–h6) | | `quote` | 引用 | | `code` | 代码块 | | `image` | 图片(支持相对路径转绝对 URL) | 解析流程:各来源拉取 HTML → `content_render.py` / `ContentParser.kt` 去标签、识别结构 → UI 按块类型渲染。图片请求携带来源站点 Referer(`image_utils.py`),避免防盗链 403。 桌面版额外支持将 blocks 导出为 TXT / Markdown / PDF(`export_utils.py`)。 --- ### 6. 桌面版 UI 与线程模型 `main.py` 基于 CustomTkinter: 1. 主线程负责界面;搜索在 **后台线程** 执行,通过 `after()` 回调更新 UI 2. 布局:搜索栏 → 状态栏(单行)→ 左侧列表(40%)+ 右侧详情(60%) 3. 详情懒加载 + 邻近预取,减少重复请求 4. PyInstaller **onedir** 打包,内置 `certifi/cacert.pem` 保证 HTTPS 可用 --- ### 7. Android 版对应关系 | 桌面模块 | Android 模块 | |----------|--------------| | `search_hub.py` | `SearchRepository.kt` | | `nowcoder_api.py` | `NowcoderApi.kt` | | `csdn_source.py` | `CsdnSource.kt` | | `juejin_source.py` | `JuejinSource.kt` | | `segmentfault_source.py` | `SegmentFaultSource.kt` | | `PostRelevance` 规则 | `PostRelevance.kt` | | `content_render.py` | `ContentParser.kt` | | OkHttp | `HttpClient.kt` | UI 使用 Jetpack Compose + ViewModel,搜索逻辑与桌面版一致。 --- ### 8. 测试体系 桌面版在 `desktop/tests/` 提供完整测试: | 类型 | 数量 | 说明 | |------|------|------| | 单元测试 | 36 | 筛选规则、搜索词、HTML 解析、聚合逻辑、Mock | | 集成测试 | 180 | 33 组真实公司/岗位场景,验证四来源连通性与结果质量 | ```bash cd desktop pytest # 全部(约 6 分钟,需联网) pytest -m "not integration" # 仅单元测试 pytest -m integration -v # 33 组多样化集成场景 ``` 场景定义见 `desktop/tests/search_scenarios.py`,覆盖互联网大厂、多样岗位、外企、边界输入等。 --- ## 项目结构 ``` OfferGet/ ├── desktop/ # Python + CustomTkinter 桌面版 │ ├── main.py # UI 入口 │ ├── search_hub.py # 多来源聚合 │ ├── nowcoder_api.py # 牛客 API + 筛选规则 │ ├── csdn_source.py # CSDN 适配 │ ├── juejin_source.py # 掘金适配 │ ├── segmentfault_source.py │ ├── content_render.py # 正文解析 │ ├── http_client.py # HTTPS / SSL │ ├── tests/ # 单元 + 集成测试 │ └── scripts/ # 打包 / 测试脚本 ├── mobile/ # Kotlin + Compose Android 版 │ └── app/src/main/java/com/offerget/ │ ├── data/ # API 适配 + 筛选 │ └── ui/ # 界面 ├── docs/screenshots/ └── README.md ``` --- ## 快速开始 ### 桌面版 **环境**:Python 3.10+,Windows / macOS / Linux ```bash cd desktop pip install -r requirements.txt python main.py ``` **Windows 打包**: ```powershell cd desktop .\scripts\build_release.ps1 # 输出:dist\OfferGet\ 与 dist\OfferGet-portable-win64.zip ``` 更多说明:[desktop/README.md](desktop/README.md)、[desktop/USAGE.txt](desktop/USAGE.txt) ### Android 版 **环境**:Android Studio,或 JDK 17 + Android SDK 35 ```bash cd mobile # 复制 local.properties.example → local.properties,填写 sdk.dir gradlew.bat assembleRelease # Windows ``` 更多说明:[mobile/README.md](mobile/README.md) --- ## 隐私与免责 - 桌面版:[desktop/PRIVACY.txt](desktop/PRIVACY.txt) - Android 版:[mobile/app/src/main/assets/PRIVACY.txt](mobile/app/src/main/assets/PRIVACY.txt) - 数据来自各平台 **公开搜索接口**,仅供个人学习参考 - 请合理使用,避免高频请求;原文链接由系统浏览器打开,隐私策略由目标网站负责 ## 开源协议 本项目以学习交流为目的开源,欢迎 Issue 与 Pull Request。