资讯动态

Python Scrapy爬取网易新闻数据,实现全链路分析与可视化大屏

发布时间:2026/9/2 9:50:04 来源:尧图企业网站定制
简介本资源是一套完整的毕业设计项目面向计算机类专业本科生、研究生及Python初学者聚焦新闻数据采集、分析与可视化全流程实践。项目基于Scrapy框架爬取网易新闻实时数据存储至MySQL数据库后端采用Python3.6进行多维度统计分析前端使用Vue技术栈实现前后端分离并通过ECharts构建响应式数据大屏涵盖热度趋势、地域分布、话题词云等核心可视化模块。压缩包共556个文件含52个Python脚本爬虫/分析逻辑、69个Vue组件如IndexMain.vue、BreadCrumbs.vue等、39个JS与44个JPG/PNG图表资源辅以安装/运行批处理脚本、SQL建表语句及演示视频MP4整体大小31.49MB。已有140人学习下载提供完整可运行源码、详细文档说明与实操演示视频支持远程答疑与基础教学适合作为毕设、课程设计或数据分析进阶实战范例。1. 项目概述与核心价值最近在整理过往的毕业设计项目发现一个挺有意思的案例就是基于Python和Scrapy框架对网易新闻进行数据爬取然后做数据分析和可视化大屏展示。这个项目麻雀虽小五脏俱全几乎涵盖了数据工程里从数据采集、清洗、存储、分析到最终可视化呈现的全链路流程。对于刚入门数据科学或者想找一个完整项目练手的朋友来说是个非常不错的实践模板。它解决的不仅仅是“怎么把数据抓下来”的问题更重要的是如何将看似杂乱的新闻文本数据通过分析转化为有商业或社会洞察价值的可视化信息并最终通过一个酷炫的大屏直观地呈现出来。无论你是计算机、数据科学还是新闻传播相关专业的学生这个项目都能帮你把书本上的理论串联成实际可运行的代码和看得见的图表。2. 项目整体架构与技术选型解析2.1 为什么是网易新闻与Scrapy选择网易新闻作为数据源主要基于几个考量一是数据结构化程度相对较高新闻的标题、正文、发布时间、来源、评论数等字段清晰便于解析二是内容更新频繁数据量有保证适合做时序分析三是反爬措施处于中等强度既有一定的挑战性又不至于让新手无从下手非常适合作为教学和练手项目。技术栈的核心是Scrapy。为什么不直接用RequestsBeautifulSoup对于新闻网站这种页面结构相似、需要批量抓取大量页面的场景Scrapy框架的优势是碾压性的。它内置的异步处理引擎基于Twisted能极大提升爬取效率其清晰的Item、Pipeline、Middleware架构让代码结构非常规范便于维护和扩展。比如你可以轻松地通过配置实现请求延迟、自动重试、深度优先或广度优先爬取策略这些如果自己用Requests从头实现会非常繁琐且容易出错。2.2 数据处理与分析的技术栈搭配爬虫只是第一步拿到数据后的处理链条同样关键。这个项目典型的数据流是这样的数据采集层 (Scrapy)负责从网易新闻各个频道抓取原始HTML。数据解析与存储层在Scrapy的Spider中解析HTML提取结构化数据如标题、URL、时间、正文等并通过Item Pipeline存入数据库。这里常用MySQL或MongoDB。MySQL适合结构规整的关系型数据而MongoDB的文档模型对新闻正文这种可能含有不定长字段的内容更友好。数据分析层 (Pandas Jupyter Notebook)从数据库导出数据后使用Pandas进行数据清洗去重、处理缺失值、时间格式标准化和初步分析。Pandas强大的DataFrame操作和分组聚合功能是做描述性统计如每日新闻数量趋势、热门新闻来源分布的利器。可视化与展示层 (ECharts Flask/Django)这是项目的“门面”。分析结果需要通过Web页面展示。ECharts是百度开源的一个可视化库图表类型丰富交互性强特别适合制作数据大屏。后端可以选择轻量级的Flask来提供数据API接口或者功能更全面的Django。大屏的布局通常使用HTMLCSS进行栅格化设计通过Ajax请求后端API获取JSON格式的数据再由ECharts渲染成图表。注意在实际项目中如果数据量非常大或需要实时更新可能会引入Redis做缓存和去重或者使用Celery进行异步任务调度。但对于毕业设计级别的项目上述技术栈已经足够健壮和完整。2.3 核心难点与应对思路这个项目的难点主要集中在两个环节反爬虫对抗和非结构化文本数据处理。网易新闻这类大型网站通常会有基本的反爬措施比如检查请求头特别是User-Agent、对高频访问进行IP限制。在Scrapy中我们可以通过中间件Downloader Middleware轻松地随机切换User-Agent并设置合理的DOWNLOAD_DELAY下载延迟来模拟人类行为。更复杂的情况可能涉及动态加载Ajax和JavaScript渲染。早期这可能需要用Selenium或Splash但现在更优的解决方案是使用scrapy-playwright或scrapy-selenium中间件它们能无缝集成浏览器环境来处理动态内容。对于新闻正文虽然主体是文本但其中可能夹杂着小编寄语、广告链接、无关图片说明等“噪音”。简单的基于正则表达式或XPath/CSS选择器的提取方法可能不够鲁棒。这里可以引入一些自然语言处理NLP的预处理思想比如基于文本密度和标点符号的算法来提取核心正文或者使用像readability-lxml这样的专用库来智能提取文章主体内容。3. 爬虫核心实现与细节剖析3.1 Scrapy项目创建与Spider编写首先通过scrapy startproject news_163创建一个项目。核心的爬虫逻辑在Spider中。我们需要分析网易新闻的列表页和详情页URL规律。通常网易新闻各频道列表页的URL有固定模式例如国内新闻可能是https://news.163.com/domestic/并且会有分页参数。在Spider的start_requests方法中我们需要生成这些起始请求。import scrapy from news_163.items import NewsItem class NewsSpider(scrapy.Spider): name news_163 allowed_domains [news.163.com] start_urls [https://news.163.com/domestic/, https://news.163.com/world/] # 示例起始频道 def parse(self, response): # 解析列表页提取新闻详情页链接 news_links response.css(div.news_item h3 a::attr(href)).getall() for link in news_links: yield response.follow(link, callbackself.parse_news_detail) # 处理翻页如果存在 next_page response.css(a.next::attr(href)).get() if next_page: yield response.follow(next_page, callbackself.parse)关键点在于parse方法它负责解析列表页提取出所有新闻详情页的链接并使用response.follow发起新的请求指定回调函数parse_news_detail来处理详情页。3.2 详情页解析与Item封装在parse_news_detail方法中我们需要利用XPath或CSS选择器像手术刀一样精确地提取出需要的字段。def parse_news_detail(self, response): item NewsItem() # 使用CSS选择器提取数据更简洁直观 item[title] response.css(h1.post_title::text).get().strip() # 有时标题可能在meta标签里更准确 if not item[title]: item[title] response.css(meta[propertyog:title]::attr(content)).get().strip() item[url] response.url item[publish_time] response.css(div.post_time_source::text).re_first(r\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) item[source] response.css(a#ne_article_source::text).get(未知来源).strip() # 正文提取可能需要合并多个段落 content_list response.css(div.post_body p::text).getall() item[content] \n.join([p.strip() for p in content_list if p.strip()]) item[keywords] response.css(meta[namekeywords]::attr(content)).get() # 提取评论数可能通过API这里假设页面有显示 comment_count_text response.css(span.post_comment_count::text).get(0评) item[comment_count] int(re.search(r\d, comment_count_text).group()) if re.search(r\d, comment_count_text) else 0 yield item这里有几个细节需要注意字段备用方案比如标题优先从h1标签取如果取不到则从og:title这个meta标签取提高了代码的健壮性。时间格式化使用正则表达式re_first从字符串中精确匹配时间格式。正文清洗使用列表推导式将获取到的所有段落文本拼接起来并过滤掉空字符串。数字提取评论数往往是“1234评”这样的格式需要用正则表达式\d提取出纯数字部分。3.3 Item Pipeline设计与数据存储定义好的Item会依次经过配置的Pipeline进行处理。我们通常需要两个Pipeline一个用于数据清洗一个用于存储。# pipelines.py import pymongo from itemadapter import ItemAdapter from scrapy.exceptions import DropItem import hashlib class DuplicatesPipeline: 基于URL去重的Pipeline def __init__(self): self.url_seen set() def process_item(self, item, spider): adapter ItemAdapter(item) url adapter.get(url) # 生成URL的MD5指纹节省内存 url_md5 hashlib.md5(url.encode(utf-8)).hexdigest() if url_md5 in self.url_seen: raise DropItem(fDuplicate item found: {url}) else: self.url_seen.add(url_md5) return item class MongoPipeline: 存储到MongoDB的Pipeline def __init__(self, mongo_uri, mongo_db): self.mongo_uri mongo_uri self.mongo_db mongo_db classmethod def from_crawler(cls, crawler): # 从settings.py读取配置 return cls( mongo_uricrawler.settings.get(MONGO_URI), mongo_dbcrawler.settings.get(MONGO_DATABASE, news_163) ) def open_spider(self, spider): self.client pymongo.MongoClient(self.mongo_uri) self.db self.client[self.mongo_db] def close_spider(self, spider): self.client.close() def process_item(self, item, spider): # 决定存储到哪个集合表 collection_name item.get(channel, news) # 假设item里有channel字段 self.db[collection_name].insert_one(ItemAdapter(item).asdict()) return item在settings.py中启用并配置这些PipelineITEM_PIPELINES { news_163.pipelines.DuplicatesPipeline: 300, # 数字越小优先级越高 news_163.pipelines.MongoPipeline: 800, } MONGO_URI mongodb://localhost:27017 MONGO_DATABASE news_163实操心得去重Pipeline一定要放在存储Pipeline之前。去重逻辑可以基于URL也可以基于标题发布时间组合成一个唯一键。使用MD5哈希存储比直接存储URL字符串更节省内存。MongoDB的连接应该在爬虫开始时建立open_spider结束时关闭close_spider而不是在每个item处理时都连接断开这样效率更高。4. 数据分析从原始数据到业务洞察爬取到的原始数据存入数据库后接下来的任务就是让数据“说话”。我们使用Pandas进行数据分析。4.1 数据清洗与预处理首先从MongoDB中读取数据到Pandas DataFrame。import pandas as pd from pymongo import MongoClient import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 解决中文显示问题 plt.rcParams[axes.unicode_minus] False # 连接数据库 client MongoClient(localhost, 27017) db client[news_163] collection db[news] # 读取数据注意如果数据量巨大可能需要分批读取 df pd.DataFrame(list(collection.find())) client.close() # 数据预览与清洗 print(df.head()) print(df.info()) # 1. 删除MongoDB自带的_id字段 df.drop(columns[_id], inplaceTrue, errorsignore) # 2. 时间字段转换 df[publish_time] pd.to_datetime(df[publish_time], errorscoerce) # 3. 处理缺失值 df[title].fillna(无标题, inplaceTrue) df[content].fillna(, inplaceTrue) # 4. 去重基于URLPipeline已做这里可做二次保障 df.drop_duplicates(subset[url], inplaceTrue) # 5. 过滤无效数据如发布时间异常或内容过短的新闻 df df[df[publish_time].notna()] df df[df[content].str.len() 50]清洗完成后我们就得到了一个干净、结构化的DataFrame可以开始多维度分析了。4.2 多维度统计分析分析的角度可以有很多这里列举几个典型的1. 新闻发布时序分析# 按小时统计新闻发布数量 df[hour] df[publish_time].dt.hour hourly_counts df.groupby(hour).size() hourly_counts.plot(kindbar, figsize(10,6), title每日新闻发布小时分布) plt.xlabel(小时) plt.ylabel(新闻数量) plt.tight_layout() plt.show()这个分析可以揭示网易新闻编辑的活跃时间段通常早晚高峰是新闻发布的高峰期。2. 新闻来源媒体影响力分析# 统计各新闻来源的发布数量 source_counts df[source].value_counts().head(15) # 取前15 source_counts.plot(kindbarh, figsize(10,8), titleTop 15 新闻来源发布量) plt.xlabel(发布数量) plt.ylabel(新闻来源) plt.tight_layout() plt.show()这能看出哪些媒体机构是网易新闻平台的主要内容提供方。3. 新闻热度初步分析基于评论数# 找出评论数最多的Top 10新闻 top10_hot_news df.nlargest(10, comment_count)[[title, source, publish_time, comment_count]] print(top10_hot_news) # 可以进一步分析高评论新闻的主题共性需要文本分析见下文4.3 文本分析与主题挖掘对于新闻正文和标题的文本我们可以进行更深入的分析比如词频统计和情感倾向。1. 使用Jieba进行中文分词与词频统计import jieba from collections import Counter # 将所有标题合并成一个长文本 all_titles .join(df[title].dropna().tolist()) # 分词 words jieba.lcut(all_titles) # 过滤停用词和短词 with open(stopwords.txt, r, encodingutf-8) as f: stopwords set([line.strip() for line in f]) filtered_words [w for w in words if len(w) 1 and w not in stopwords and w ! ] # 统计词频 word_counts Counter(filtered_words).most_common(30) # 可视化 top_words, top_counts zip(*word_counts[:20]) plt.figure(figsize(12,8)) plt.barh(range(len(top_words)), top_counts, tick_labeltop_words) plt.gca().invert_yaxis() plt.title(新闻标题高频词Top 20) plt.xlabel(出现次数) plt.tight_layout() plt.show()通过高频词可以快速把握当前时间段内的新闻热点比如“疫情”、“经济”、“国际”等。2. 简单情感分析基于情感词典我们可以使用现有的中文情感词典如BosonNLP情感词典对新闻标题或正文进行简单的情感打分判断新闻的整体情感倾向是正面、负面还是中性。这能为后续的可视化提供“情感热度”维度。5. 可视化大屏设计与前端实现数据分析的结果最终需要通过一个直观、美观的仪表盘Dashboard呈现出来。我们采用Flask作为后端API服务ECharts作为前端图表库。5.1 Flask后端API搭建Flask后端负责从数据库或分析好的结果文件中读取数据并以JSON格式提供给前端。# app.py from flask import Flask, jsonify from flask_cors import CORS import pandas as pd from pymongo import MongoClient app Flask(__name__) CORS(app) # 允许跨域方便前端调试 client MongoClient(localhost, 27017) db client[news_163] app.route(/api/hourly_distribution) def get_hourly_distribution(): 获取小时发布分布数据 collection db[news] # 这里可以使用聚合管道也可以直接读取Pandas分析好的CSV pipeline [ {$project: {hour: {$hour: $publish_time}}}, {$group: {_id: $hour, count: {$sum: 1}}}, {$sort: {_id: 1}} ] results list(collection.aggregate(pipeline)) hours [str(r[_id]) for r in results] counts [r[count] for r in results] return jsonify({hours: hours, counts: counts}) app.route(/api/top_sources) def get_top_sources(): 获取Top新闻来源数据 collection db[news] pipeline [ {$group: {_id: $source, count: {$sum: 1}}}, {$sort: {count: -1}}, {$limit: 10} ] results list(collection.aggregate(pipeline)) sources [r[_id] for r in results] counts [r[count] for r in results] return jsonify({sources: sources, counts: counts}) app.route(/api/hot_news) def get_hot_news(): 获取热门新闻高评论数 collection db[news] results collection.find().sort(comment_count, -1).limit(5) news_list [] for r in results: news_list.append({ title: r.get(title), source: r.get(source), comment_count: r.get(comment_count, 0), url: r.get(url) }) return jsonify(news_list) if __name__ __main__: app.run(debugTrue, port5000)5.2 前端大屏布局与ECharts集成前端页面使用HTML/CSS/JS搭建采用灵活的栅格布局如Flexbox或CSS Grid来适配不同尺寸的大屏。使用Ajax或Fetch API调用上面写好的Flask API获取数据并用ECharts渲染图表。一个典型的ECharts柱状图初始化示例!DOCTYPE html html head meta charsetutf-8 title网易新闻数据可视化大屏/title script srchttps://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js/script style .dashboard { display: grid; grid-template-columns: repeat(2, 1fr); grid-gap: 20px; padding: 20px; background-color: #0f1c30; color: #fff; min-height: 100vh; } .chart-panel { background: rgba(255, 255, 255, 0.05); border-radius: 10px; padding: 15px; } .chart-title { font-size: 18px; margin-bottom: 15px; text-align: center; color: #4dc9ff; } #hourChart, #sourceChart { width: 100%; height: 400px; } /style /head body div classdashboard div classchart-panel div classchart-title新闻发布小时分布/div div idhourChart/div /div div classchart-panel div classchart-titleTop 10 新闻来源/div div idsourceChart/div /div !-- 可以继续添加更多图表区域 -- /div script // 初始化小时分布图表 var hourChart echarts.init(document.getElementById(hourChart)); fetch(http://localhost:5000/api/hourly_distribution) .then(response response.json()) .then(data { var option { tooltip: {}, xAxis: { type: category, data: data.hours, axisLabel: { color: #ccc } }, yAxis: { type: value, axisLabel: { color: #ccc } }, series: [{ data: data.counts, type: bar, itemStyle: { color: #5470c6 } }] }; hourChart.setOption(option); }); // 初始化来源分布图表横向柱状图 var sourceChart echarts.init(document.getElementById(sourceChart)); fetch(http://localhost:5000/api/top_sources) .then(response response.json()) .then(data { var option { tooltip: {}, grid: { left: 30% }, // 为Y轴标签留出空间 yAxis: { type: category, data: data.sources, axisLabel: { color: #ccc } }, xAxis: { type: value, axisLabel: { color: #ccc } }, series: [{ data: data.counts, type: bar, itemStyle: { color: #91cc75 } }] }; sourceChart.setOption(option); }); // 窗口大小变化时重置图表尺寸 window.addEventListener(resize, function() { hourChart.resize(); sourceChart.resize(); }); /script /body /html实操心得大屏设计的关键是信息密度和视觉层次。不要试图在一个屏幕上塞入所有图表而是选择最关键、最能反映核心洞察的3-5个指标。使用统一的配色方案如科技蓝、暗黑主题并确保图表类型与数据特性匹配趋势用折线图、分布用柱状图/饼图、关系用散点图。ECharts的dataset功能非常强大可以方便地管理多维度数据建议在复杂图表中使用。6. 项目部署与优化建议6.1 基础部署流程一个完整的项目部署通常包括以下几步环境准备在服务器如阿里云ECS、腾讯云CVM上安装Python、MongoDB、Node.js如果需要前端构建等依赖。代码上传与依赖安装将项目代码上传至服务器在虚拟环境如venv或conda中执行pip install -r requirements.txt安装Python依赖。数据库配置确保MongoDB服务已启动并创建好相应的数据库和用户权限。爬虫部署可以使用scrapyd来部署和管理Scrapy爬虫实现定时爬取。更简单的方案是使用系统的crontab定时任务定期执行爬虫命令如scrapy crawl news_163。Web服务部署使用gunicorn或uWSGI作为Flask应用的WSGI服务器配合Nginx做反向代理和静态文件服务。将前端HTML/CSS/JS文件放置在Nginx的静态目录下。域名与访问配置Nginx将特定域名或端口指向你的Flask应用和大屏页面。6.2 性能与稳定性优化爬虫优化并发控制在settings.py中调整CONCURRENT_REQUESTS并发请求数和DOWNLOAD_DELAY下载延迟在效率和友好性间取得平衡。中间件使用合理使用RetryMiddleware重试中间件和HttpProxyMiddleware代理中间件应对IP封锁。增量爬取设计爬虫时可以记录已爬取URL的指纹如MD5下次启动时跳过实现增量更新。Scrapy的JOBDIR设置可以支持断点续爬。数据分析优化数据预处理将清洗和特征计算的结果持久化如存为Parquet或Feather格式避免每次分析都从原始数据开始。使用更高效的工具如果数据量达到百万级以上Pandas可能力不从心可以考虑使用Dask或PySpark进行分布式分析。大屏优化数据缓存前端频繁请求的API可以在Flask端使用Flask-Caching扩展配合Redis进行缓存减少数据库压力。图表按需加载对于复杂的图表可以使用ECharts的lazy loading或者将数据分片加载。自动刷新使用JavaScript的setInterval定时调用API更新数据实现大屏的实时或准实时更新。6.3 常见问题与排查技巧爬虫被屏蔽返回403或验证码页面检查点首先检查请求头特别是User-Agent确保模拟了真实浏览器。可以尝试添加Referer、Accept-Language等头部。解决方案启用Scrapy的RotateUserAgentMiddleware。如果问题依旧考虑降低爬取频率或引入IP代理池。对于简单的验证码可以尝试使用OCR库如ddddocr自动识别复杂的则可能需要人工打码服务。数据解析失败字段为空检查点网站改版了这是最常见的原因。用浏览器开发者工具重新检查目标元素的CSS选择器或XPath路径是否还正确。解决方案编写更健壮的解析逻辑使用多个备选选择器并增加try...except块捕获异常。定期运行爬虫的测试用例。MongoDB写入速度慢检查点是否每条数据都立即插入insert_one解决方案在Pipeline中改用批量插入insert_many可以每积累100或500条item执行一次批量插入显著提升性能。前端图表不显示或数据显示错误检查点打开浏览器开发者工具的“网络(Network)”标签查看API请求是否成功返回的JSON数据结构是否符合ECharts的要求。解决方案确保Flask API返回的是标准的JSON使用jsonify并且数据格式如数组、对象与ECharts选项中的data字段期望的格式一致。检查是否有跨域问题CORS确保Flask已启用CORS支持。大屏在不同分辨率下布局错乱检查点是否使用了固定像素px宽度解决方案前端布局尽量使用百分比%、视口单位vw/vh或Flexbox/Grid布局使图表容器能自适应父容器大小。ECharts实例在window.onresize事件中调用resize()方法。这个项目从爬虫到可视化的全流程走下来你会对数据驱动的项目有一个非常立体和深刻的理解。它不仅仅是技术的堆砌更是问题拆解、方案设计、工程实现和结果呈现的综合体现。在实际操作中最花时间的往往不是写代码而是调试Debug和优化每一个环节都可能遇到意想不到的“坑”但解决它们的过程正是能力提升最快的时候。建议在完成基础功能后可以尝试加入更复杂的分析比如基于文本聚类如K-Means的新闻自动分类或者情感分析的时间序列趋势让你的大屏展示出更深层次的洞察力。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价