1. 项目背景与核心价值
乐团派对这个项目名称乍看有些抽象,但结合副标题"牛客tracker & 每日一题"就能发现其技术社区属性。这是一个典型的编程学习辅助工具,主要面向牛客网(Nowcoder)平台的算法练习者。我去年在准备算法面试时,就曾苦于缺乏系统的刷题进度管理工具,这个项目正好切中了程序员群体的刚需痛点。
它的核心功能拆解来看包含两大模块:一是牛客刷题进度追踪(tracker),二是每日一题的推送与管理。前者解决"刷了多少题"的量化问题,后者解决"每天刷什么"的选择困难。这种工具类项目看似简单,但要做好数据抓取、用户行为分析和推送策略的平衡,需要处理不少技术细节。
2. 技术架构解析
2.1 数据采集层
牛客网没有公开API,所以必须通过爬虫获取数据。这里有几个关键点:
- 登录态保持:需要模拟登录获取cookies,建议使用requests.Session()维持会话
- 反爬绕过:牛客有基本的频率限制,需要:
- 设置随机间隔(2-5秒)
- 使用代理IP池(注意合规使用)
- 伪造常规浏览器的headers
- 数据解析:题目列表页用BeautifulSoup解析HTML,个人提交记录建议直接抓取接口数据
python复制# 示例:获取已通过题目列表
def get_accepted_problems(user_id):
session = requests.Session()
# 登录过程省略...
submissions_url = f"https://ac.nowcoder.com/acm/contest/profile/{user_id}/practice-coding"
resp = session.get(submissions_url, headers=random_headers())
soup = BeautifulSoup(resp.text, 'html.parser')
# 解析AC题目逻辑...
2.2 数据存储设计
推荐使用SQLite + JSON的组合方案:
- SQLite表结构设计:
sql复制CREATE TABLE users ( id INTEGER PRIMARY KEY, nowcoder_id TEXT UNIQUE, last_sync TIMESTAMP ); CREATE TABLE problems ( id INTEGER PRIMARY KEY, platform TEXT DEFAULT 'nowcoder', problem_id TEXT, title TEXT, difficulty TEXT, tags TEXT -- JSON数组存储 ); CREATE TABLE user_problems ( user_id INTEGER, problem_id INTEGER, status TEXT, -- 'AC'/'Trying' submit_time TIMESTAMP, FOREIGN KEY(user_id) REFERENCES users(id), FOREIGN KEY(problem_id) REFERENCES problems(id) );
2.3 每日一题算法
这是项目的核心算法模块,需要考虑多个维度:
- 难度阶梯:根据用户历史正确率动态调整
- 知识点覆盖:使用LeetCode官方tag体系
- 新鲜度:优先推荐近期未接触的题型
python复制def recommend_daily_problem(user_id):
# 获取用户历史数据
solved = get_solved_problems(user_id)
trying = get_trying_problems(user_id)
# 计算知识点覆盖率
knowledge_gaps = calculate_knowledge_gaps(solved)
# 结合难度曲线(基于历史正确率)
target_difficulty = calculate_optimal_difficulty(user_id)
# 检索候选题目
candidates = get_candidates(
exclude=solved + trying,
difficulty=target_difficulty,
tags=knowledge_gaps[:3] # 取最需要加强的3个知识点
)
# 加入随机性避免模式固定
return weighted_random_choice(candidates)
3. 关键实现细节
3.1 进度可视化
采用类似GitHub贡献日历的热力图展示刷题轨迹,技术要点:
- 使用D3.js或ECharts实现日历热力图
- 颜色深浅表示当日刷题数量
- 点击日期显示具体题目列表
javascript复制// ECharts配置示例
option = {
calendar: {
range: ['2023-01-01', '2023-12-31'],
itemStyle: {
borderWidth: 2
}
},
visualMap: {
min: 0,
max: 10,
calculable: true,
inRange: {
color: ['#ebedf0', '#9be9a8', '#40c463', '#30a14e', '#216e39']
}
},
series: [{
type: 'heatmap',
coordinateSystem: 'calendar',
data: heatmapData // [{date: '2023-01-01', value: 5},...]
}]
};
3.2 消息推送系统
支持多种通知方式:
- 邮件推送:使用SMTP协议
- Telegram Bot:通过官方API
- 微信提醒:需接入企业微信或ServerChan
重要提示:消息内容需要包含题目基本信息+直达链接,并确保在用户当地时间早上8-9点发送
4. 部署方案
4.1 服务端部署
推荐使用Docker Compose编排:
yaml复制version: '3'
services:
app:
build: .
ports:
- "5000:5000"
depends_on:
- redis
- db
environment:
- FLASK_ENV=production
redis:
image: redis:alpine
volumes:
- redis_data:/data
db:
image: postgres:13
volumes:
- db_data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=yourpassword
volumes:
redis_data:
db_data:
4.2 定时任务管理
使用Celery + Redis实现异步任务:
python复制@app.task
def sync_user_data(user_id):
try:
data = fetch_nowcoder_data(user_id)
process_and_store(data)
except Exception as e:
log_error(e)
self.retry(exc=e, countdown=60)
配置定时任务:
python复制from celery.schedules import crontab
app.conf.beat_schedule = {
'daily-morning-sync': {
'task': 'tasks.sync_all_users',
'schedule': crontab(hour=7, minute=30),
},
'daily-problem-push': {
'task': 'tasks.push_daily_problem',
'schedule': crontab(hour=8, minute=0),
}
}
5. 避坑指南
5.1 反爬对抗经验
-
请求指纹:牛客会检测Headers完整性,必须包含:
- Accept-Language
- Referer(设置为牛客域名)
- X-Requested-With(XMLHttpRequest)
-
行为检测:避免立即连续请求,建议:
- 每个操作后随机延迟1-3秒
- 模拟鼠标移动轨迹(如果使用Puppeteer)
-
验证码处理:遇到验证码时:
- 立即暂停1小时
- 记录日志并通知管理员
- 绝对不要尝试自动识别(法律风险)
5.2 数据一致性保障
- 增量同步:记录最后同步时间戳,只获取新数据
- 异常处理:网络中断时:
- 实现断点续传
- 使用消息队列保证任务不丢失
- 数据校验:对抓取的数据进行:
- 非空检查
- 字段类型验证
- 合理性校验(如提交时间不能晚于当前时间)
6. 扩展方向
6.1 智能推荐增强
- 题目相似度:使用TF-IDF或Word2Vec计算题目相似度
- 错题本功能:自动归类错误类型(边界条件、算法选择等)
- 个性化路径:根据目标公司调整题目优先级(如字节常考DP)
6.2 社交化功能
- 学习小组:多人刷题进度对比
- 题解协作:支持Markdown格式的题解共享
- 成就系统:设置里程碑奖励(如连续打卡30天)
这个项目最让我惊喜的是它的用户粘性——算法练习本身就是个需要长期坚持的过程,而恰当的工具辅助能让这个痛苦的过程变得可量化、可视化。在后续开发中,建议加入更多正向反馈机制,比如将刷题进度生成分享图片,或是设置阶段性成就奖励,这些都能显著提升用户留存率。