尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python+大数据构建新闻推荐系统:从冷启动到实时推荐的完整实战
去年我在做用户行为数据分析时一个现象让我印象很深热度排行榜永远霸占首页真正对每个用户有意义的深度内容沉在底部。说白了多数“新闻推荐系统”只是个带过滤条件的SQL查询距离“推荐”还很远。后来我花了两三个月用Python搭了一套真正意义上的新闻推荐系统——从爬虫采集、大数据存储、离线计算到实时行为反馈再到前端展示完整跑通了。这篇文章就把这套系统的设计思路、技术栈分工、推荐算法落地细节、性能调优和踩坑记录一次性讲清楚。想用Python做大数据类项目、准备大数据面试、或者正在做相关毕业设计的朋友这篇文章应该能省掉不少试错时间。1. 系统定位从“热榜”到“千人千面”要越过哪些坎1.1 新闻推荐和大数据技术为什么会绑在一起很多人一开始不理解一个新闻推荐系统而已为什么非要扯上大数据技术我在最初设计时也有这个疑问。后来数据涨起来才明白新闻推荐天然就是大数据场景——新闻条目量级是几十万到几百万条用户行为日志一天就是几十万次曝光和点击而且新闻的时效性极强昨天的权重和今天的权重完全不是一个概念。用单机MySQL存这些数据、再用内存跑协同过滤短时间内能跑数据量一上来就完蛋。我当时做过一个测试两万条新闻、二十万用户行为数据单机跑一次ItemCF计算要将近四十分钟。这个时间线上根本没法用。所以必须上分布式存储和分布式计算这是新闻推荐系统和大数据技术绑定的根本原因——不是炫技是算力瓶颈逼着你换方案。1.2 模块划分和数据流主干这套系统我拆成了五个模块数据采集层、存储层、离线计算层、实时计算层、应用服务层。数据流主干是这样的爬虫抓取新闻和用户行为日志后落盘离线走Spark做模型训练和推荐结果预计算实时走Kafka缓冲后再交给实时任务做行为特征更新最终结果统一写入Redis后端接口从Redis读取推荐列表返回给前端展示。这个结构在我跑起来之后发现有个好处所有模块可以独立部署、独立扩展比如实时链路出问题不会影响离线推荐爬虫挂了也不影响已经算好的推荐结果对外服务。模块解耦这件事前期多花点时间后期省心非常多。2. 数据获取与清洗推荐效果的上限在数据层就决定了2.1 爬虫设计的增量策略与反爬取舍新闻爬虫我建议直接上Scrapy不用自己造轮子。Scrapy的高并发下载、中间件机制、Pipeline管道都是给这种大规模采集场景设计的。我最初的爬虫是简单写了个requests循环去抓跑到两万多条之后就被对方服务端限流了。后来改成Scrapy加AutoThrottle auto限流机制会根据下载延迟自动调整请求速率配合合理的下载间隔DOWNLOAD_DELAY设置在2到3秒长时间运行基本不会触发严重封禁。增量抓取这块是个关键点。新闻网站每天更新大量内容如果每天都全量重爬浪费带宽不说还会对目标站点造成负担。我的方案是维护一张已抓取URL表新请求进来时先查表判断是否已抓取或者直接看新闻发布时间做增量判断——发布时间超过当前时间一天的不再处理。注意爬虫不是本文重点但它是整个系统的数据源头处理不好后面全白搭。对反爬严格的目标站点宁可采集慢一点也要稳定运行即时性损失一点没关系数据完整性更重要。2.2 文本去重你用SimHash还是MinHash新闻推荐里最折磨人的问题之一就是重复内容。同一个热点事件几十家媒体转发标题改几个字正文几乎一样。如果不去重用户刷到的推荐列表里全是同一个事件的“换皮文章”体验极差。文本去重我用的是SimHash。原理你可以这样理解把一篇新闻正文分词后转成一个64位的指纹两篇新闻的指纹汉明距离小于等于3就判定为近似重复。SimHash对“把标题改了几个字”这种程度的改写有很强的识别能力。具体做法是每篇新闻入库时计算SimHash值存到一张去重表里新文章进来时和库里已有指纹做比对相似度高的直接丢进重复池。这个计算过程不需要跑MapReduce单机就能处理因为它只对新增新闻做比对而新增新闻一天也就两三万篇。2.3 存储选型元数据、正文快照和行为日志分开存放存储这块我把数据分了三类新闻元数据标题、作者、发布时间、分类、url——存MySQL单表日增两万行毫无压力。新闻正文快照清洗后的纯文本内容——存HDFS按天分区用于离线Spark计算。用户行为日志曝光、点击、停留时长、点击时间——存Kafka先缓冲再落HDFS同时由实时任务消费更新Redis缓存。刚开始我图省事把所有数据都塞MySQL结果Spark离线计算时要从MySQL全量拉数据慢得离谱。后来改成正文快照和行为日志走HDFSMySQL只负责元数据整个离线计算链条瞬间轻快起来。这个经验想重点强调大数据项目里数据库和数据仓库的职责一定要分开。3. 大数据技术栈分工HDFS、Spark在这套系统里究竟干了什么3.1 哪些环节真正需要HDFS我见过不少项目数据量就几千条非要搭个Hadoop集群纯属给自己找麻烦。我的判断标准很简单单机内存处理不了的数据才需要分布式存储和分布式计算。在这套系统里三个地方真正需要HDFS新闻正文快照。一篇平均800字一个月攒下来就是几个GB加上备份副本单机磁盘压力不大但读取效率很差。用户行为日志。这个是数据量增长最快的一块。一天几万用户每人几十次行为一个月就是几千万条记录。Spark中间计算结果。协同过滤产生的中间矩阵、临时表动辄上GB丢到HDFS上既安全又方便后续任务读取。3.2 Spark离线计算任务的资源预估Spark在新闻推荐里的主战场是离线协同过滤和用户/新闻特征矩阵计算。我用的Spark on YARN提交任务时最关键的参数是executor数量和每个executor的内存。以我当时处理的数据规模为例80万新闻、300万行为日志、清洗后约500万条有效记录。spark-submit \ --master yarn \ --deploy-mode cluster \ --executor-memory 8g \ --num-executors 8 \ --executor-cores 4 \ --driver-memory 4g \ --class com.news.RecommendJob \ news-recommend.jar8个executor每个8G内存总共64G内存处理500万记录的量级跑一轮ALS训练大约20分钟。这个配置不是拍脑袋定的主要取决于Spark Shuffle的数据量。ALS算法本身要反复迭代每轮迭代都要把用户-物品矩阵在executor间传来传去内存不够就会疯狂溢写磁盘性能断崖下跌。实测下来的经验公式是executor内存 预估数据集大小 / (num-executors * 0.6)。留出40%的余量给Shuffle缓存和系统开销。3.3 集群部署策略的实用建议小规模集群10台以内不用搞太复杂的部署方案。我的建议是一个主节点跑NameNode和ResourceManager三个数据节点跑DataNode和NodeManager再单独一台机器跑HBase或Redis。每个节点16G内存、4核CPU就够用存储盘用普通机械盘加三副本冗余即可。热点数据当天发布的新闻、Top100热榜放Redis历史数据放HDFS。这样既保证了实时访问的速度也不会把整个集群的资源耗在高频查询上。提示如果你只是做课程设计或个人项目没有真正的多台服务器用Docker在本机起一个3节点的Hadoop集群也可以。重点是跑通整套流程而不是追求集群规模。4. 推荐算法落地冷启动、召回、排序与可解释性4.1 冷启动阶段热度衰减公式不是拍脑袋定的任何推荐系统都绕不开冷启动。新用户没有行为数据新新闻没有阅读记录协同过滤在此时完全失效。我的做法是用户侧冷启动用热榜推荐兜底新闻侧冷启动用内容相似度推荐。核心是一个经过验证的热度衰减公式def heat_score(views, base_score, publish_hours): # base_score是初始权重views是浏览量half_life是半衰期单位小时 half_life 12 decay pow(2, -publish_hours / half_life) return (views * base_score) * decay这个公式的思路是热度 浏览量 × 基础分 × 时间衰减。衰减部分用指数函数半衰期设12小时意思是12小时前发布的新闻热度只剩一半。这么做是因为新闻的时效性极强昨天的爆款今天可能没人想看。注意避坑不要用线性衰减。我最初用的是score views / publish_hours结果发布5小时的新闻和发布50小时的新闻数值差别巨大但实际用户对5小时前和10小时前的新闻接受度差别没那么大。指数衰减更符合新闻的衰变规律。4.2 基于内容的召回TF-IDF与Word2Vec的组合基于内容的推荐是新闻推荐里最实用的召回策略。核心逻辑是找到用户历史上点击过的新闻计算它们的内容特征向量再从新闻库里找出向量相似的未读新闻。新闻文本的向量化我用的是TF-IDF加Word2Vec组合。TF-IDF先把分词后的词转成权重向量Word2Vec再把词向量聚合成句向量。简单来说jieba分词 → 去除停用词 → TF-IDF权重计算 → 得到每篇新闻的词权重向量用户向量 用户点击过的所有新闻向量的加权平均候选新闻得分 用户向量与新闻向量的余弦相似度这个方案的好处是无需用户行为数据也能工作所以新新闻入库后只要文本内容够丰富马上就能被推荐出去。import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity documents [.join(jieba.cut(news[content])) for news in news_list] vectorizer TfidfVectorizer(max_features50000) tfidf_matrix vectorizer.fit_transform(documents) similarity cosine_similarity(tfidf_matrix[user_vector_index], tfidf_matrix).flatten()文本类数据用TF-IDF做向量新闻这种短文本效果其实不错但词表量大的时候要注意维度爆炸max_features要限制在几万以内。4.3 ItemCF在新闻场景下的取舍协同过滤部分我选了ItemCF而不是UserCF。原因很简单新闻用户数远大于新闻条目数UserCF要维护一个用户间相似度矩阵计算量爆炸而ItemCF维护的是新闻间相似度矩阵新闻数量远少于用户数量计算成本可控。ItemCF的核心思想用户A点击了新闻X用户B同时点击了新闻X和新闻Y那么X和Y相似可以把Y推荐给用户A。用Spark MLlib的ALS做矩阵分解是我选的技术路线。ALS把用户-新闻评分矩阵分解成用户隐向量和新闻隐向量两个低维矩阵然后用这两个向量的点积预测用户对未读新闻的评分。from pyspark.ml.recommendation import ALS als ALS( maxIter10, regParam0.01, userColuser_id, itemColnews_id, ratingColrating, coldStartStrategydrop ) model als.fit(train_data) user_recs model.recommendForAllUsers(20)这个代码看似简单但有几个关键细节coldStartStrategydrop一定要设置否则预测时遇到冷门用户或新闻会直接Nan。ratingCol的定义很关键。新闻场景没有显式评分我用的是行为加权曝光不计分点击记1分完整阅读停留超过30秒记2分。这个映射关系对模型效果影响巨大。隐向量维度rank参数我试过5、10、20最终在验证集上rank10效果最好。维度太高容易过拟合维度太低表达力不够。4.4 融合排序离线算分与实时加权的组合召回完了有几百条候选新闻不可能全推给用户还需要一个排序层。我的排序方案是分两层。离线层用ALS预测分数、内容相似度分数、热度分数各占一定权重线性加权算出一个基础排序分。在线层再用用户实时行为做微调——比如用户最近10分钟刷新了三次都没点击某一类新闻这类新闻的排序分要降权用户刚点击过某个热点事件的相关新闻同事件新闻的排序分要临时提升。融合排序的权重我调了很久最终这组参数效果比较稳定特征维度权重说明协同过滤得分0.35用户历史行为偏好信号内容相似度0.35冷门好文也能被发掘热度衰减得分0.20保证时效性不推过时内容分类匹配得分0.10用户常读类别的偏好每一步排序都在产出可解释的理由比如“因为你常看科技类新闻所以推荐了这篇”。这个字段后期很有用展示层可以直接展示给用户提升推荐系统的可信度。5. 实时链路设计让推荐“跟上”用户当下的行为5.1 用户行为埋点与日志格式设计推荐系统不能只依赖离线计算的静态推荐。用户早上看了体育新闻中午还推荐昨天的财经新闻就很不合理。实时链路的价值就是捕捉用户当下的兴趣变化。埋点数据我设计了这样的JSON格式{ user_id: 123456, news_id: 789012, news_category: sports, behavior: click, duration: 35, timestamp: 1719398400 }behavior字段有四类exposure曝光、click点击、read完整阅读、share分享。每个行为都打上时间戳和新闻分类这是实时推荐和后续分析的基础。5.2 Kafka缓冲为什么不让行为数据直接进计算引擎用户行为日志的写入压力非常大一天几百万条如果直接写入后端数据库或者直接触发实时计算系统大概率被压垮。我的做法是用户行为先由Flume采集后写入KafkaKafka相当于一个蓄水池。实时计算任务用Spark Streaming或Flink从Kafka拉数据消费计算的结果写回Redis同时也落一份原始日志到HDFS做次日全量重算。这样做的好处有两个。一是削峰填谷突发流量比如热点新闻引爆访问不会被直接打穿。二是数据可重放Kafka会持久化一段窗口的数据即使下游计算程序崩溃重启也能从上次的消费位置继续处理不丢数据。5.3 实时链路的两个关键用途实时链路在这套系统里干了两件具体的事第一件事更新当天热榜。以前热榜是每小时跑一次Spark任务算出来的现在改成实时累加点击数并套用热度衰减公式热榜刷新延迟控制在10秒以内。热点事件一爆几分钟内就会反映在热榜上。第二件事更新用户实时兴趣向量。用户每产生一次点击就把点击的新闻向量叠加到用户向量上同时让旧浏览行为的影响随时间衰减。这个向量存在Redis里用户刷新推荐列表时会和离线预计算的推荐结果做混合排序。6. 模型上线与展示层推荐结果如何优雅地“被看到”6.1 API设计把推荐结果封装成标准接口整个系统最终要通过一个接口把推荐结果提供给前端。接口我用Flask写设计很简洁app.route(/api/recommend, methods[GET]) def recommend(): user_id request.args.get(user_id, ) page int(request.args.get(page, 1)) page_size int(request.args.get(page_size, 20)) # 先从Redis取推荐列表 rec_list redis.lrange(frec:{user_id}:{page}, 0, page_size - 1) # 缓存没有则请求离线推荐并回填缓存 if not rec_list: rec_list get_offline_recs(user_id, page, page_size) redis.rpush(frec:{user_id}:{page}, *rec_list) return jsonify({code: 0, data: rec_list})接口层不直接访问HDFS或Spark只和Redis交互所以响应非常快P99基本稳定在50毫秒以内。如果推荐结果有变动离线任务或实时任务会主动把新的推荐列表推送到Redis并清理旧缓存。6.2 Redis缓存层减少90%后端压力的关键Redis在整套系统里承担了“推荐结果缓存热榜用户实时向量”三种角色。设计要点是给缓存设置合理的过期时间——推荐结果缓存TTL设为30分钟热点新闻缓存TTL设为10分钟用户实时向量不设过期但每次更新会衰减。比较重要的一点是缓存穿透问题。当一个新用户首次请求推荐时缓存里肯定没有数据如果每次请求都回源算一次推荐结果后端压力会很大。我的方案是分级缓存冷启动用户的推荐结果缓存到一个默认推荐池里多个新用户共享同一份热榜推荐缓存避免每个新用户都触发一次完整推荐计算。这样做了之后我原本预计要上两台推荐计算后端最后一台服务器就扛住了全部请求量Redis缓存层的功劳占了一半以上。6.3 前端展示的扩展思路前端这块我没有做过重渲染就是一个简单的列表页推荐结果按排序分展示。但有一个小细节值得说一下每张推荐卡片上都展示了推荐理由标签比如“你可能感兴趣”“热门”“同类新闻延伸”。这个设计带来的点击率提升很明显——推荐理由相当于给用户一个点击的心理预期。如果想把系统做得更好看可以再加一个可视化大屏用ECharts展示当天热榜词、推荐点击率趋势、用户兴趣分类分布。我当时用Flask加ECharts做了个简易版面试和演示效果都很好。7. 性能优化与踩坑实录这些坑我替你踩过了7.1 数据倾斜ALS训练卡死的元凶大数据处理里最头疼的问题就是数据倾斜。我的行为日志里Top1%的热门新闻贡献了30%的点击量这些热点新闻作为物品在ALS训练时大量集中在某几个executor上别的executor都空闲了这几个executor还在疯狂计算任务直接卡死。解决数据倾斜我有两个经验对热点新闻做随机前缀打散。把点击量超过阈值的新闻ID加上随机后缀让它们分散到不同分区去计算。设置Spark的spark.sql.shuffle.partitions参数默认200数据量大时可以调大到500减少每个分区的数据量。7.2 隐式反馈的坑未点击不等于负反馈新闻推荐里用户没点击某条新闻不代表不喜欢。可能是因为没看到也可能是因为推荐位置偏下没注意。如果简单地把“曝光未点击”当成负样本模型会严重偏向热门标题。我的处理方式是设置置信度权重曝光未点击的样本权重设为0.1点击样本权重设为1.0完整阅读样本权重设为2.0。置信度越高模型训练时越重视这条样本。这样模型学到的是“点击行为更重要”而不是“曝光未点击是讨厌”。ALS的隐式反馈模型就支持这种方式把置信度直接作为训练权重传入。这个细节对推荐效果的影响非常明显推荐列表的点击率能提升20%到30%。7.3 离线指标和线上指标为什么经常对不上这是所有推荐系统都会遇到的问题。离线评估AUC和线上点击率经常不是一个走势。我一开始也很困惑后来才想明白原因离线评估拿的是历史数据而线上推荐结果被用户看到后用户的点击行为又会反过来影响模型训练这是一个动态闭环离线静态评估无法完全模拟。我的做法是离线评估只看召回率和排序稳定性真正的效果验证必须靠AB测试。我用了一个简单的流量切分方案80%的用户走默认推荐策略20%的用户走新策略对比两组的点击率和人均阅读时长。观察三天以上再决定新策略是否全量上线。经验总结不要迷信离线指标。推荐系统的最终评价标准只有一个——用户愿不愿点、愿不愿看。任何离线指标都只是参考线上数据才是答案。8. 做项目时的几个额外建议8.1 给毕业设计或课程设计同学的改造建议如果你正在做毕业设计这套系统可以直接按这个架构做简化版。爬虫只要能爬一个新闻源就行大数据部分可以用本地单机版的Hadoop加Spark推荐算法只做基于内容的TF-IDF加一个协同过滤展示层用Flask加一个简单网页。核心是跑通完整链路数据采集→数据存储→离线计算→推荐生成→Web展示。链路完整度远比算法复杂度重要评审老师看你的是工程思维和系统完整度不是炫技。8.2 面试时如何讲这个项目大数据面试时如果聊到新闻推荐系统不要上来就讲ALS原理。更好的切入方式是说明这个系统在什么场景下解决什么问题业务对推荐的实时性要求是什么数据量级多少你用什么架构解决过程中遇到什么性能瓶颈怎么排查和解决的。面试官对你的算法精度不会太在意他们更想听的是你对数据倾斜、缓存穿透、日志采集链路这些工程问题的思考。把这个项目的完整链路讲清楚已经能证明你具备独立完成一个大数据应用的能力。最后分享一个我个人的经验很多同学把精力全放在模型调参上其实对新闻推荐这种富文本场景数据质量对推荐效果的影响远大于算法本身。把爬虫采集的正文清洗干净、把重复内容去掉、把用户行为日志规范化这些基础工作做好了哪怕只用最简单的TF-IDF加热度排序效果都能超过很多花里胡哨的模型。先让数据干净起来再谈模型的精雕细琢。
RELATED

相关推荐

Elasticsearch脑裂事故复盘:从选举机制到生产恢复实践

Elasticsearch脑裂事故复盘:从选举机制到生产恢复实践

凌晨2点47分,手机上的告警电话把我吵醒。监控屏上Elasticsearch集群的状态从green瞬间跳到red,紧接着三个具备主节点候选资格的节点几乎同时开始刷discovery::zen相关的异常日志。不用等第二天看报告,我心里已经清楚:ES集群脑裂了…

📅 2026/10/9 8:37:45
空气动力学电子版资料:边界层、涡与CFD仿真核心要点解析

空气动力学电子版资料:边界层、涡与CFD仿真核心要点解析

简介:《空气动力学》电子版PDF是面向航空、宇宙、机械、船舶、汽车等领域学习者与设计人员的一份基础电子资料,目的在于系统呈现空气运动规律及其对物体的作用机理。资源为单个PDF文件,大小约26.98MB,支持全文检索与跨设备阅读&am…

📅 2026/10/9 8:37:45
PyTorch CyclicLR循环学习率:原理、参数与实战指南

PyTorch CyclicLR循环学习率:原理、参数与实战指南

1. 先搞懂一个反直觉的现象:Loss长期不动,问题可能不在模型而在学习率有段时间我在PyTorch里训练一个图像分类模型,loss卡在0.7附近怎么也下不去。学习率从0.1一路调到0.01、再到0.003,曲线要么震荡要么干脆不动。后来换成CyclicL…

📅 2026/10/9 8:37:45
MORE NEWS

更多资讯

📰

小波分解+BP神经网络风电功率预测实战指南

简介:本资源是一份面向电力系统、新能源预测及人工智能应用方向的科研与工程实践者的技术文档,聚焦风电功率不确定性带来的电网调度难题,提出融合小波分析与BP神经网络的高精度短期预测方法。文档系统阐述了小波分解(DB4四层&…

📰

Vue+ECharts动态地图实战:数据驱动着色与下钻联动

1. 项目背景与核心需求拆解1.1 为什么要在Vue项目里做动态地图做过数据大屏或者后台管理系统的朋友应该都有体会,静态地图早就满足不了业务需求了。所谓动态地图,核心诉求无非这么几类:地图区域能根据数据变化自动着色、点击某个省份能下钻到…

📰

浏览器插件开发避坑指南:Manifest V3实战通信与状态管理

1. 这不是教程,是我在三年里踩过27次坑后整理的浏览器插件开发实录“浏览器插件开发终极指南:从入门到精通”——这个标题听起来像极了那种点开前信心满满、读完后怀疑人生的文档。我第一次写插件时,也是被“5分钟上手”“一行代码搞定”这类…

📰

PHP集成活体识别全流程:签名验签与阈值配置实战

做风控的同行应该都有这种感觉:活体识别这东西,听起来就是个“成熟的第三方接口”,文档看着也简单,但真正要接进PHP业务系统,尤其是要承担合规审查责任的时候,细节全在坑里。我最近刚帮业务线做完一轮身份核…

📰

AI编程实战指南:从提示词到代码副驾驶的高效用法

很多人跟我说,AI写代码就是“人工智障”,让它写个排序算法都能跑出一堆莫名其妙的报错。我一开始也这么觉得,直到我花了两周时间认真研究了一下自己到底是怎么提问的,才反应过来:不是AI太蠢,是我根本没把它…

📰

锐捷云桌面部署实战:从选型到运维的踩坑经验分享

1. 从一台老旧PC的报废说起:为什么我开始折腾云桌面办公室角落里那台五年前的品牌机,开机要三分半,打开浏览器再卡两分钟,硬盘灯狂闪像在求救。IT运维的同事每次路过都要叹口气,说这台机器再撑半年就该进回收站了。但问…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

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

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

📞 💬