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

开发智能面经爬取系统:技术选型、架构设计与踩坑记录

从零开发一个智能面经爬取系统:技术选型、架构设计与踩坑记录

项目背景

在求职过程中,面经是求职者了解公司面试流程、准备面试的重要参考资料。牛客网等平台汇集了大量真实的面试经验分享,但手动浏览和整理这些内容效率较低。因此,我决定开发一个自动化的面经爬取系统,能够:

  1. 自动爬取牛客网等平台的面经内容
  2. 使用 AI 智能判断是否为真实面经
  3. 自动分类(公司、岗位、编程语言)
  4. 生成 AI 总结
  5. 提供友好的前端界面展示

技术选型

后端技术栈

技术 用途 选择理由
Python 3.11+ 主语言 生态丰富,爬虫和 AI 库完善
FastAPI Web 框架 高性能异步框架,自动生成 API 文档
SQLAlchemy ORM 灵活的数据库操作
SQLite 数据库 轻量级,本地开发友好
Playwright 爬虫引擎 支持动态页面,绕过反爬虫
PaddleOCR 图片识别 国产 OCR,中文识别效果好
DeepSeek API AI 服务 性价比高,中文理解能力强

前端技术栈

技术 用途 选择理由
Vue 3 前端框架 组合式 API,开发体验好
Element Plus UI 组件库 组件丰富,文档完善
Vite 构建工具 快速热更新,开发效率高

系统架构

┌─────────────────────────────────────────────────────────────┐
│                        前端展示层                            │
│                    (Vue 3 + Element Plus)                   │
└─────────────────────────────────────────────────────────────┘│▼
┌─────────────────────────────────────────────────────────────┐
│                        API 服务层                            │
│                      (FastAPI + Uvicorn)                    │
└─────────────────────────────────────────────────────────────┘│┌───────────────────┼───────────────────┐▼                   ▼                   ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│    爬虫引擎层    │ │    NLP 处理层    │ │   数据存储层     │
│  (Playwright)   │ │ (DeepSeek API)  │ │   (SQLite)      │
└─────────────────┘ └─────────────────┘ └─────────────────┘

核心功能实现

1. 动态页面爬取

牛客网采用动态渲染,直接请求 API 会返回 HTML 而非 JSON。使用 Playwright 模拟真实浏览器行为:

from playwright.sync_api import sync_playwrightdef fetch_detail_with_browser(post_id: str):with sync_playwright() as p:browser = p.chromium.launch(headless=True)page = browser.new_page()page.goto(f"https://www.nowcoder.com/discuss/{post_id}")# 提取内容result = page.evaluate('''() => {const title = document.querySelector('h1')?.textContent || '';const content = document.querySelector('.post-content')?.textContent || '';return { title, content };}''')browser.close()return result

踩坑记录

  • Windows 下异步 Playwright 会报 NotImplementedError,需要设置事件循环策略或使用同步版本
  • 页面内容选择器需要排除评论区,否则会爬取大量无关内容

2. AI 智能判断与分类

使用 DeepSeek API 判断内容是否为真实面经,并提取分类信息:

async def judge_interview(self, title: str, content: str) -> InterviewJudgeResult:prompt = f"""请判断以下内容是否为真实的面试经验分享(面经),并进行分类。标题:{title}
内容:{content[:2000]}请严格按照以下JSON格式回复:
{{"is_interview": true或false,"company": "面试公司名称","position": "面试岗位","language": "编程语言","tags": ["标签1", "标签2"]
}}重要规则:
1. 只有在内容中明确提到时才填写,否则填null
2. 不要强行分类,无法确定时返回null
"""response = await self._call_api(prompt)return self._parse_response(response)

踩坑记录

  • AI 容易"强行分类",需要在 prompt 中明确要求不确定时返回 null
  • 内容长度需要限制,否则 API 调用会超时或超出 token 限制

3. 爬取任务状态管理

使用类变量维护任务状态,确保跨请求共享:

class CrawlService:_tasks: Dict[str, Dict] = {}  # 类变量,所有实例共享async def start_crawl(self, request: CrawlRequest) -> str:task_id = str(uuid.uuid4())CrawlService._tasks[task_id] = {"status": "running","total_count": 0,"success_count": 0,}asyncio.create_task(self._run_crawl(task_id, request))return task_id

踩坑记录

  • 最初使用实例变量 self.tasks,导致不同请求无法共享状态
  • 服务重启后 running 状态的任务需要清理,否则前端显示异常

4. 前端自动刷新

使用 keep-aliveonActivated 钩子实现页面切换时自动刷新:

<template><router-view v-slot="{ Component }"><keep-alive><component :is="Component" /></keep-alive></router-view>
</template><script setup>
import { onActivated } from 'vue'onActivated(() => {fetchInterviews()  // 页面激活时刷新数据
})
</script>

踩坑总结

1. Docker 部署问题

最初计划使用 Docker 部署,但遇到多个问题:

  • Windows 下 Docker 安装路径配置复杂
  • WSL2 虚拟磁盘占用大量 C 盘空间
  • 网络配置问题导致镜像拉取失败

解决方案:改用本地模式,使用 SQLite 替代 MySQL/Redis,简化部署流程。

2. 反爬虫机制

牛客网有反爬虫机制,直接请求 API 会返回 HTML:

  • 使用 Playwright 模拟真实浏览器
  • 设置合理的 User-Agent
  • 添加请求延迟,避免被封

3. 内容提取问题

页面结构变化导致选择器失效:

  • 使用多个备选选择器
  • 添加回退机制,从整个页面提取内容
  • 排除评论区等无关内容

4. 环境变量加载

.env 文件加载时机问题:

  • settings.py 中直接加载 .env,确保配置在其他模块导入前生效
from dotenv import load_dotenv
from pathlib import PathBASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(BASE_DIR / ".env")

项目结构

interview_crawler/
├── api/                    # FastAPI 后端
│   ├── main.py            # 应用入口
│   ├── routers/           # 路由
│   └── services/          # 业务逻辑
├── crawler/               # 爬虫模块
│   └── spiders/           # 爬虫实现
├── nlp/                   # NLP 处理
│   ├── processor.py       # 分类处理
│   └── ai_service.py      # AI 服务
├── utils/                 # 工具模块
│   └── image_handler.py   # 图片处理和 OCR
├── db/                    # 数据库
│   └── models.py          # 数据模型
├── config/                # 配置
│   └── settings.py        # 配置项
├── frontend/              # Vue 前端
│   └── src/
│       ├── views/         # 页面组件
│       ├── api/           # API 调用
│       └── router/        # 路由配置
├── .env                   # 环境变量配置
├── requirements.txt       # Python 依赖
└── README.md              # 项目说明

启动方式

# 后端
cd interview_crawler
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
python run_server.py# 前端
cd frontend
npm install
npm run dev

访问地址:

  • 前端界面:http://localhost:3001
  • API 文档:http://localhost:8000/docs

总结

这个项目从零开始,完整经历了需求分析、技术选型、架构设计、功能实现、问题排查的全过程。主要收获:

  1. 技术选型要务实:Docker 虽好,但本地开发用 SQLite 更简单高效
  2. 爬虫要考虑反爬:使用 Playwright 模拟浏览器是有效方案
  3. AI 要善用 Prompt:好的 prompt 能显著提升分类准确性
  4. 状态管理要谨慎:异步任务的状态管理需要特别注意
  5. 用户体验很重要:自动刷新、实时状态显示等细节提升用户体验

项目代码已开源,欢迎交流讨论!


本文首发于博客园,记录开发过程中的技术选型和踩坑经验,希望对有类似需求的开发者有所帮助。

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

相关文章:

  • 麒麟V10系统下ffmpeg完整安装指南:从依赖包到环境变量配置
  • ANSYS Fluent浮点错误排查指南:从网格优化到求解器调参的5个实战技巧
  • Python 库使用全攻略(新手友好 + 实战导向)
  • x64dbg消息断点避坑指南:为什么你的WM_COMMAND断点总失效?
  • 1688 商品采集 API 避坑大全:常见错误及解决方案
  • CANoe实战技巧:用DBC文件实现车速信号从ESP到Display的完整通信链路
  • Axure RP 9汉化版 vs 英文原版:功能对比与使用体验分享
  • 4diac Forte运行时源码解析:从事件链调度到工业级应用优化
  • Excel数据转GIS神器:ArcGIS Pro批量处理SHP文件技巧大公开
  • LM2596动态调压新玩法:用单片机PWM实现0-9.9V无级调节(含滤波电路设计)
  • 用CryptoMiniSat处理CNF文件实战:从DIMACS格式解析到SAT问题求解
  • 220V通断检测电路设计避坑指南:从光耦选型到PCB布局实战
  • Android 12系统开发者的SELinux生存手册:以RK3588自启动服务为例
  • Halcon局部变形匹配避坑指南:检测橡胶件毛刺时如何避免误判?
  • 大模型本地推理环境配置全攻略:从CUDA安装到bitsandbytes报错解决
  • Cheat Engine修改器检测避坑指南:从原理到实战,FairGuard方案全解析
  • 传感器融合入门:激光雷达和相机坐标系转换的常见误区与避坑指南
  • 高阶行列式不再难:手把手教你用按行展开法则简化计算
  • Remix-IDE本地开发环境搭建全攻略:从安装到文档配置
  • Runway 推出可定制实时数字人,支持企业知识库;钉钉发布 DingTalk A1 医生版丨日报
  • VS2019配置CLR项目避坑指南:C++/WinForm界面开发常见报错解决方案
  • uniapp+webview+video.js播放m3u8直播全屏卡死?3步搞定通讯方案
  • 告别手机!3步搞定Google Authenticator密钥同步到Chrome插件(附截图技巧)
  • 移动端图片自适应:3种CSS技巧让不同尺寸图片完美填充固定容器(附代码)
  • Verilog调试必备:你不知道的$system和$typename隐藏用法
  • 国产FMQL10S400ZYNQ+SM25QH256MX FLASH开发踩坑实录:QE位异常与高低地址切换实战
  • Kubesphere镜像搜索卡顿?3分钟搞定国内镜像加速配置(附DaoCloud实战)
  • Obsidian新手必看:.obsidian文件夹全解析与插件迁移避坑指南
  • HTTPS握手过程全解析:用tcpdump抓包实战TLS1.2和1.3的差异
  • 3分钟看懂MRI报告单:振幅/频率/相位参数背后的临床诊断密码