新闻详情

新闻详情

首页 / 资讯中心 / 详情

Python+Flask淘宝商品爬虫系统:从Selenium采集到Spark分析的大数据实战

发布时间:2026/10/1 4:58:19来源:尧图网络
Python+Flask淘宝商品爬虫系统:从Selenium采集到Spark分析的大数据实战
如果你正在准备大数据方向的毕业设计或者想快速把爬虫、数据分析、可视化串成一条完整的技术链路那这套基于PythonFlask的淘宝商品爬虫系统值得你花时间拆一拆。我当初做这个项目最直接的感受是它不是一个简单的爬虫Demo而是把Selenium页面采集、Hadoop分布式存储、Spark数据处理、Echarts可视化四个环节串起来的完整案例。用一套真实可控的数据把从数据生产到数据消费的全过程走一遍既满足毕业设计对技术栈深度的要求也能让自己真正理解大数据项目是怎么落地的。很多同学会问淘宝商品数据用requests抓一抓不就行了为什么要上Selenium为什么要用Hadoop和Spark这套组合是不是有点“杀鸡用牛刀”说实话刚开始我也这么觉得。但当你真正把项目做下来就会发现电商页面的动态渲染、数据量的增长、分析需求的多样化都逼着你用更工程化的方案。这篇文章我就从项目设计、模块拆解、实操过程、问题排查到答辩经验把整个系统掰开揉碎讲清楚希望能给准备做类似课题的同学一个参考。1. 项目整体设计与技术选型1.1 一个商品爬虫系统为什么要扯上大数据先说清楚这个项目到底要解决什么问题。淘宝这类电商平台的商品数据是典型的非结构化数据网页里既有商品标题、价格、销量这些结构化字段又有店铺名称、评价等半结构化信息。如果只用requests抓静态HTML很难绕过动态加载的反爬机制如果抓下来就存进一个Excel又体现不出大数据处理的价值。毕业设计需要有足够的技术深度所以整个流程被我设计成了一条流水线Selenium负责采集Hadoop HDFS负责存储原始数据Spark负责清洗和分析Flask负责把结果暴露成接口Echarts负责最终的可视化展示。这条链路最大的好处是每个环节的数据都是下一环节的输入浑然一体。比如Selenium采集到的商品数据可能是脏乱差的有缺失、重复、格式不统一的问题这些原始数据不能直接用于分析需要先落到HDFS上再由Spark做ETL。等到Spark清洗完又会生成一些聚合结果比如价格区间分布、销量Top10商品、热门店铺排名等这些结果最适合用Echarts画成图表。而Flask在整个系统中扮演“连接器”的角色既提供数据接口又渲染前端页面把后端分析和前端展示打通。从毕业设计的角度看这套方案覆盖了数据采集、数据存储、分布式计算、Web开发、可视化五个方向几乎每个技术点都能单独拿出来讲。这也是为什么我最终选它不是因为它有多炫而是因为用它能把大学阶段接触过的核心技术全部整合到同一个项目里完成度看起来非常高。1.2 技术栈选型与各自的定位项目的技术栈可以分成六块每一块都有明确的定位。我用一个表格先列出大致分工再逐一说清楚选择理由。技术组件项目中的定位选择理由Python主开发语言语法简单、生态成熟尤其适合爬虫和数据处理FlaskWeb后端框架轻量、灵活可以快速搭建数据接口和页面Selenium动态网页爬虫能驱动真实浏览器处理JavaScript渲染和模拟操作Hadoop HDFS分布式存储存储大规模爬虫原始数据展示大数据文件系统能力Spark分布式计算基于内存的快速数据处理适合清洗、聚合分析Echarts数据可视化开源免费、交互性好能快速生成各类图表先聊Python。爬虫、数据分析、后端开发Python几乎是一通百通的选择。特别是需要调用PySpark、Flask、Selenium这些库时Python社区的支持最完善遇到问题也更容易搜到答案。Flask则是典型的“轻量选手”不需要像Django那样生成一大堆目录结构一个app.py跑起来就是服务适合毕业设计这种中小型项目。如果你用Flask开发过简单站点应该知道它对于学生党有多友好没有太多约束可以自由组织路由和模板。Selenium的选择可能是很多人最困惑的地方。淘宝商品列表页是典型的动态页面很多关键字段靠JavaScript异步加载直接拿requests去请求返回的HTML里根本找不到商品数据。Selenium通过驱动浏览器内核可以像真人一样打开页面、等待加载、点击按钮、滚动翻页因此能拿到渲染完成后的完整DOM。这个特性在反爬严格的环境下很有价值但也带来速度慢、资源占用高的问题后面我会详细讲怎么规避。Hadoop和Spark则是这整套方案里的“重武器”。Hadoop提供HDFS适合保存大量结构化和半结构化原始文件Spark则负责对这些数据进行高效处理。为什么不用MySQL一把梭MySQL当然可以存下小规模数据但毕业设计里如果出现“分布式”三个字HDFS和Spark就是最容易被答辩老师认可的组合。何况Spark的计算能力确实比单机Pandas强很多后续要扩展到更大规模数据也顺理成章。至于Echarts百度开源的可视化库图表类型多文档全而且一个script标签就能在浏览器里用起来前后端分离、模板渲染都支持做展示非常顺手。1.3 系统整体数据流向整个系统的数据流向我用文字描述如下Selenium爬虫每隔一段时间启动搜索指定的商品关键词比如“手机”“蓝牙耳机”逐页抓取商品标题、价格、销量、店铺名、商品链接等字段生成CSV或JSON格式的原始文件写入服务器本地临时目录。接着用Hadoop的hdfs dfs -put命令把这些文件上传到HDFS指定目录中。Spark作业启动后从HDFS读取原始数据完成去重、过滤缺失值、价格标准化、销量单位换算比如把“500人付款”转成数字然后按商品类目或价格区间做聚合统计最终把结果导出为Parquet或CSV文件。这些聚合结果会被Flask应用读取。Flask提供两个层面的功能一是对外暴露JSON接口比如/api/price_distribution返回价格分布数据二是通过Jinja2模板渲染一个包含Echarts图表的页面。浏览器加载页面时前端JavaScript发起Ajax请求从后端拿到JSON再调用Echarts初始化图表完成展示。如果你不想让前端跨域最简单的做法就是把Flask的模板和Echarts放在同一个服务下页面和接口同源没有任何跨域问题。这套流程我建议先在本地用单机模式跑通再往Hadoop和Spark上迁移。很多同学一上来就搭伪分布式集群结果环境问题就折腾一个星期。更稳妥的顺序是先写好爬虫存成CSV用Pandas分析出结果再用Flask做可视化。等这条“短链路”通了再把存储换成HDFS、计算换成Spark逐步替换组件。这样即使最后分布式环境出问题你手里已经有一个能演示的版本不会毕设答辩前一天手忙脚乱。2. 核心模块拆解与实现细节2.1 爬虫模块为什么非要Selenium不可Selenium在很多人印象里是自动化测试工具但它同样是非常好用的爬虫工具。我最初也想用requests配合解析Ajax接口的方式获取商品数据后来发现淘宝的页面接口带有复杂的签名参数而且登录态和Cookie的校验很严格单纯模拟接口难度极高。换用Selenium之后实现路径就变成了打开浏览器、输入关键词、点击搜索按钮、等待商品列表渲染、逐条提取数据。这其实是把操作浏览器的人类行为复制到脚本里能绕开很多只针对HTTP请求的反爬策略。当然Selenium也不是没有缺点。浏览器进程的启动、渲染、关闭都需要时间如果翻页多的话采集速度远比requests慢。同时一个没有经过任何优化的Selenium脚本非常容易被网站识别因为它的浏览器指纹、窗口大小、User-Agent都可能和真实用户有差异。我的做法是尽可能让浏览器“看起来正常”设置一个真实的用户代理指定窗口大小开启隐身模式模拟用户的滚动行为并且每次请求之间随机等待2到5秒。这样做不是为了绕过网站规则而是降低给对方服务器造成的压力也提升自己脚本的稳定性。爬虫模块的核心逻辑可以简化为以下几段代码。这里以Chrome浏览器和Selenium 4为例from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import random import csv options webdriver.ChromeOptions() options.add_argument(--user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36) options.add_argument(--window-size1280,800) options.add_argument(--disable-blink-featuresAutomationControlled) driver webdriver.Chrome(optionsoptions) def search_products(keyword, pages5): url fhttps://s.taobao.com/search?q{keyword} driver.get(url) # 等待首次加载完成 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, [class*Card--])) ) # 模拟向下滚动获取更多懒加载内容 driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) time.sleep(random.uniform(2, 4)) for page in range(pages): parse_page_items(driver) # 点击下一页 next_button driver.find_element(By.CSS_SELECTOR, [class*next]) next_button.click() time.sleep(random.uniform(3, 5))这里最关键的技巧是显式等待。淘宝商品列表的数据是异步加载的如果你在driver.get()之后立刻提取元素大概率会拿到一个空列表。使用WebDriverWait配合expected_conditions能让脚本等到特定元素出现后再继续执行稳定性会提高很多。另外翻页按钮的选择器一定要在改动后的页面上做实测不同时期的淘宝页面结构不一样建议用浏览器开发者工具重新定位元素。采集到的数据需要清洗后才方便存储。我在parse_page_items里会提取商品标题、价格字符串、销量文本、店铺名、商品链接等字段然后统一存成UTF-8编码的CSV文件。价格字段可能存在“价格区间”情况比如“299-349”这需要拆成最低价和最高价两个字段销量字段可能是“500人付款”“月销1000”等需要提取出数字并统一单位。这些清洗逻辑虽然也可以在Spark里做但我会在爬虫端先做一轮轻量清洗减少后续处理的压力。def parse_page_items(driver): items driver.find_elements(By.CSS_SELECTOR, [class*Card--]) for item in items: title item.find_element(By.CSS_SELECTOR, [class*Title--]).text price_text item.find_element(By.CSS_SELECTOR, [class*Price--]).text shop_name item.find_element(By.CSS_SELECTOR, [class*Shop--]).text sales_text item.find_element(By.CSS_SELECTOR, [class*Sales--]).text # 此处省略字段清洗逻辑最终写入CSV with open(products.csv, a, encodingutf-8-sig, newline) as f: writer csv.writer(f) writer.writerow([title, price_text, shop_name, sales_text])这段代码会触发网站的正常请求所以需要注意执行频率。对于毕业设计来说采集几十个商品关键词、每词抓5页就已经足够生成可视化数据完全没有必要用分布式爬虫大规模采集。另外如果登录淘宝账号才能查看更多数据可以选择手动扫码登录后保存Cookie不必硬撞验证码。后面在问题排查章节我会专门讲登录和Cookie的处理。2.2 数据清洗与存储HDFS和Spark怎么配合原始CSV文件通常包含大量杂质直接用Pandas处理也不是不行但为了体现Spark的分布式计算能力我选择把文件放到HDFS上用PySpark去处理。HDFS在这里的角色就是一个“大仓库”不管数据是CSV、JSON还是Parquet都能统一存储。Hadoop HDFS的优势在于水平扩展当数据量超过单机磁盘容量时可以通过增加节点解决而且它自带副本机制数据安全性更好。把采集到的文件上传到HDFS命令行很简单hdfs dfs -mkdir -p /user/taobao/products hdfs dfs -put ./products.csv /user/taobao/products/之后启动Spark作业先读取文件再执行ETL。下面是一段能直接运行的PySpark代码包含去重、过滤、字段转换和聚合from pyspark.sql import SparkSession from pyspark.sql.functions import col, split, regexp_extract, avg spark SparkSession.builder \ .appName(TaobaoDataAnalysis) \ .master(local[*]) \ .getOrCreate() df spark.read.option(header, True).csv(hdfs://localhost:9000/user/taobao/products/) # 去掉重复数据依据商品链接去重 df df.dropDuplicates([link]) # 过滤价格为空或为0的数据 df df.filter(col(price_min).isNotNull() col(price_min).cast(double) 0) # 把销售文本中的数字提取出来 df df.withColumn(sales_num, regexp_extract(col(sales_text), r(\d), 1).cast(int)) # 按店铺统计平均价格和总销量 shop_stats df.groupBy(shop_name).agg( avg(price_min).alias(avg_price), sum(sales_num).alias(total_sales) ) # 按价格区间统计商品数量 price_bucket df.withColumn( price_bucket, (col(price_min) / 100).cast(int) * 100 ).groupBy(price_bucket).count() shop_stats.write.mode(overwrite).parquet(hdfs://localhost:9000/user/taobao/result/shop_stats) price_bucket.write.mode(overwrite).parquet(hdfs://localhost:9000/user/taobao/result/price_bucket)这里有几个容易踩坑的地方。第一CSV中没有明确的SchemaSpark会把所有字段默认读成字符串所以后续要用cast转换成数值类型。第二价格字段如果是“299-349”要先在爬虫端或者Spark里把它拆成price_min和price_max两个字段否则后面的数值计算会直接报错。第三HDFS的端口号要根据实际集群配置调整比如Namenode RPC端口通常是9000或9820需要和core-site.xml保持一致。Spark计算结果保存成Parquet格式是一个很好的实践。Parquet是一种列式存储格式既能压缩数据又能在查询时只读需要的列效率高不少。Flask后端读取Parquet时可以用Pandas的read_parquet方法配合pyarrow依赖几行代码就能加载成DataFrame然后转成JSON输出。有人可能会问数据量这么小用Spark是不是过度设计了我的回答是如果只想做一个能跑的Demo那确实用Pandas就够了。但毕业设计的评分标准里“技术难度”是重要一项。Spark的引入能让系统架构更完整也为后续扩展打了基础。哪怕数据量不大你也完整地体验了分布式计算任务的开发流程答辩时能讲清楚分区、并行度和数据Shuffle这些概念就已经比大多数只做单机爬虫的同学高一个层次了。2.3 Flask后端接口设计比页面更重要Flask在这个项目里承担的是“后端服务”职责。很多初学者会把注意力放在前端页面上反复调CSS样式但我个人觉得Flask的设计重点应该是API接口。页面只要是整洁的图表能正常展示就已经达到毕业设计的要求了。真正需要打磨的是接口的输入输出输入什么参数、返回什么JSON结构、状态码怎么安排。一个典型的Flask应用可以这样组织from flask import Flask, jsonify, render_template import pandas as pd app Flask(__name__) app.route(/) def index(): return render_template(index.html) app.route(/api/price_distribution) def price_distribution(): # 从Parquet或CSV读取聚合结果 df pd.read_parquet(./result/price_bucket.parquet) data { buckets: df[price_bucket].tolist(), counts: df[count].tolist() } return jsonify(data) app.route(/api/shop_stats) def shop_stats(): df pd.read_parquet(./result/shop_stats.parquet) # 按总销量排序取前10 df df.sort_values(total_sales, ascendingFalse).head(10) data { shops: df[shop_name].tolist(), sales: df[total_sales].tolist() } return jsonify(data) if __name__ __main__: app.run(debugTrue, port5001)这段代码逻辑很直白/api/price_distribution返回价格区间分布/api/shop_stats返回销量Top10店铺。前端拿到这些JSON后交给Echarts渲染成柱状图和条形图。接口统一返回JSON好处是以后如果想把前端换成Vue或React后端完全不用改只要保持接口不变就能无缝对接。如果想让接口更规范可以增加参数支持。比如设计/api/trend?keyword手机用于返回某关键词下商品价格随时间的变化趋势。爬虫如果隔几天跑一次积累了多个时间点的数据这个接口就能通过筛选关键词查询对应趋势。注意此时返回的数据不能只是简单数组最好嵌套一层结构比如{ code: 0, message: success, data: { dates: [2024-05-01, 2024-05-08], avg_price: [329, 312], total_sales: [1023, 1245] } }统一包装接口能方便前端判断状态比如code0表示正常非0表示错误。这个习惯在真实项目中非常普遍毕业设计里用了会让老师觉得你具备工程思维。Flask的jsonify会自动处理字典和列表的序列化注意如果数据中包含中文记得设置app.config[JSON_AS_ASCII] False否则前端拿到的中文会被转成\uXXXX的Unicode编码。2.4 Echarts可视化从数据到图表的落地Echarts的可视化设计我认为核心是“用合适的图表表达合适的数据”。商品价格分布适合用直方图展示区间和数量店铺销量排名适合用横向条形图商品关键词的热度适合用词云不同价格段商品的平均销量适合用折线图。不一定图表越多越好关键是能让评委一眼看出你的分析结论。在Flask的模板里引入Echarts最简单的办法是下载Echarts的JavaScript文件放到static/js目录下然后在HTML里引用本地文件避免依赖外部CDN。接着初始化一个具备宽高的DOM容器比如div idpriceChart stylewidth: 600px; height: 400px;/div script src/static/js/echarts.min.js/script script var chart echarts.init(document.getElementById(priceChart)); fetch(/api/price_distribution) .then(res res.json()) .then(data { chart.setOption({ title: { text: 商品价格区间分布 }, tooltip: {}, xAxis: { data: data.buckets }, yAxis: { name: 商品数量 }, series: [{ name: 数量, type: bar, data: data.counts }] }); }); /script这个例子用到的是fetch从后端接口拿数据你也可以用jQuery的$.ajax或者原生XMLHttpRequest区别不大。需要注意的是Echarts图表必须有一个明确高度和宽度的DOM容器如果容器的宽度为0图表会无法渲染。很多人会遇到“明明数据对了就是不出图”的情况大概率就是容器宽度没设置或者echarts.init执行时容器还在隐藏状态。图表类型的选择还有一个小技巧商品价格分布用柱状图更直观因为价格区间是离散的店铺销量排名用横条图更容易对比长短而“不同价格敌段的平均销量”这种连续变化关系用折线图最合适。Echarts官网的示例非常丰富挑几个和你的数据接近的模板修改一下字段就能用。不要自己从零写配置项那样效率很低也容易漏掉必要的属性。3. 实操过程与关键环节实现3.1 环境准备Hadoop伪分布式和Spark本地模式这个项目对环境的要求比较多我建议先统一版本。Python建议3.8以上Hadoop可以用3.3.xSpark可以用3.3或3.4版本Selenium用4.xFlask用2.xEcharts用5.x。版本兼容问题非常重要尤其是Spark和JDK之间存在一个“对应关系”Spark 3.3需要JDK 8或JDK 11如果你电脑上装的是JDK 17很可能会启动报错所以最好先确认Java版本。Hadoop这里我选择伪分布式模式。所谓伪分布式就是在单台机器上同时运行Namenode、Datanode等进程虽然只有一个节点但能完整体验HDFS的读写流程和配置方式。你需要修改core-site.xml和hdfs-site.xml这两个文件配置NameNode的地址和数据块副本数。配置完成后记得先执行hdfs namenode -format格式化元数据然后再用start-dfs.sh启动所有HDFS进程。启动后可以通过jps命令检查进程是否存在。Spark则不用单独建集群用本地模式就行。设置master为local[*]Spark会使用本机所有CPU核心来执行任务。本地模式的好处是不需要额外启动Worker进程适合开发和调试。不过要注意的是如果本地内存不够Spark作业可能会频繁GC甚至直接崩溃可以在spark-defaults.conf或者SparkSession.builder里设置spark.driver.memory2g来调节。环境准备这一步很多同学会被各种依赖搞崩溃。我的经验是先写一个依赖清单pandas、pyspark、flask、selenium、pyarrow、numpy等然后一次性用pip install安装。Selenium还需要下载对应的浏览器驱动。如果你使用的是Chrome浏览器需要下载ChromeDriver版本要和浏览器版本匹配。ChromeDriver下载后需要放到Python环境路径下或者通过webdriver.Chrome(options)指定executable_path。这些细节虽然小但缺一个就会卡很久。3.2 爬虫脚本编写从搜索到翻页的完整逻辑爬虫脚本是整个系统最“接地气”的模块。我写爬虫时遵循两条原则第一先跑通单页再扩展多页第二每抓一页就打印日志方便观察进度。不要一上来就写一个很长的循环否则一旦定位元素出错很难定位问题。这里给出一个更完整的采集流程启动浏览器打开淘宝首页。定位搜索框输入关键词。点击搜索按钮等待商品列表加载。通过CSS选择器提取商品卡片。对每张卡片提取标题、价格、销量、店铺名、链接。判定是否存在下一页按钮如果有则点击继续重复第4步。达到指定页数后停止翻页关闭浏览器。核心代码如下timeout 10 wait WebDriverWait(driver, timeout) def grab_product_cards(): cards driver.find_elements(By.CSS_SELECTOR, [class*Card--]) result [] for card in cards: try: title card.find_element(By.CSS_SELECTOR, [class*Title--]).text price card.find_element(By.CSS_SELECTOR, [class*Price--]).text shop card.find_element(By.CSS_SELECTOR, [class*Shop--]).text link card.find_element(By.TAG_NAME, a).get_attribute(href) result.append({ title: title, price: price, shop: shop, link: link }) except Exception as e: print(f提取卡片失败: {e}) return result这里用了try...except包裹每条卡片的提取逻辑是为了防止某张卡片结构异常导致整个脚本中断。实际采集过程中偶尔会遇到平台改版个别选择器失效这种容错机制能让你在日志里看到具体哪一步出了问题而不是程序直接崩溃。翻页操作也要做双保险。有些页面顶部有“下一页”按钮有些页面需要滑动到底部才出现。我在脚本里统一用driver.find_element(By.CSS_SELECTOR, [class*next])去找如果找不到再尝试滚动页面。不要把翻页逻辑写得太死因为网页结构随时可能变化。采集完成后建议把数据分为多个CSV文件保存比如keyword_手机_page1.csv。万一中途被断掉你只需要重新运行脚本不需要从头再来。另外每次运行脚本前最好先清空输出目录避免不同关键词的数据混淆。3.3 Spark作业从HDFS读数据到分析结果Spark作业我建议单独写成一个脚本比如analysis.py通过spark-submit提交这样能隔离开发环境和运行环境。如果你用IDE直接运行PySpark有时会碰到Python解释器和Spark版本不匹配的问题而通过spark-submit来跑会更规范。一个完整的Spark分析任务可以包括以下几步读取HDFS上某关键词的原始CSV。删除重复数据过滤掉字段缺失的记录。将价格列转换为double类型将销量文本转为整型。计算价格区间分组、店铺维度聚合、关键词维度聚合。写回HDFS或本地目录。以上面价格区间分组为例输出格式是price_bucket count 0 320 100 512 200 481 300 156这个结果可以直接被Flask读取并返回给前端。如果不想每次页面刷新都触发Spark任务可以在Spark中把聚合结果保存为ParquetFlask端读取Parquet文件即可。这样Spark只需要在爬虫数据更新后运行一次比如每周跑一次而不是每次请求都跑性能上更合理。还有一个容易被忽略的优化点Spark读取CSV时可以预先定义Schema而不是依赖默认的字符串类型推断。预先定义Schema能让数据读取更稳定也能减少后续转换的麻烦尤其在字段缺失时不容易报错。from pyspark.sql.types import StructType, StructField, StringType, DoubleType schema StructType([ StructField(title, StringType(), True), StructField(price_min, DoubleType(), True), StructField(price_max, DoubleType(), True), StructField(shop, StringType(), True), StructField(sales_text, StringType(), True), ]) df spark.read.option(header, True).schema(schema).csv(hdfs://.../products.csv)当CSV中某个字段无法转换成Double时Spark会将其置为null之后过滤掉即可。用这种方式比把所有字段读成字符串再逐个cast要高效得多代码也更简洁。3.4 FlaskEcharts串联小步快跑先本地再集群在环境还没完全搭好的时候我建议先用本地文件把整条链路跑通。比如爬虫生成CSV后先用Pandas完成清洗和聚合把结果保存为shop_stats.csv。Flask先读取这个CSV返回给Echarts。这样你能快速看到可视化效果增强信心。等到Hadoop和Spark环境都稳定了再把数据流切换到HDFS和Spark。这种“小步快跑”的推进方式特别适合毕业设计因为环境问题往往是最耗时的如果一开始就卡在分布式环境上容易心态崩掉。具体的切换步骤是第一步爬虫生成CSVFlask直接读取CSVEcharts展示。第二步把CSV上传到HDFSSpark读取HDFS产出Parquet。第三步Flask改读Parquet前端逻辑不变。第四步将Spark作业过程加入系统文档完善架构图。前端页面可以做得简洁一些。我当时的页面布局是顶部一行关键词搜索框下方一排放四个图表分别是价格分布、销量Top店铺、商品关键词词云、价格-销量散点图。用Bootstrap或者简单的CSS网格布局都可以。这里不追求炫酷只要功能完整、表达清晰即可。注意Flask运行后默认监听5000端口如果你的电脑上已经占用可以通过app.run(port5001)指定其他端口。浏览器访问http://127.0.0.1:5001/如果能看到图表说明整条链路已经打通。再进一步可以尝试用curl http://127.0.0.1:5001/api/price_distribution验证接口返回的JSON格式这样能快速排查接口问题。4. 常见问题与排查技巧实录4.1 Selenium反爬虫与登录问题的6个处理办法我在实际调试中遇到最多的问题不是代码语法错误而是Selenium拿不到数据。最常见的有两种情况搜索后页面无法正常跳转或者商品列表加载不出来。下面是我总结的排查顺序先确认网络稳定淘宝页面加载是需要网络环境的。检查User-Agent是否设置成了标准浏览器标识不要用默认的Selenium标识。显式等待时间是否足够可以先把等待时间调到10秒测试。页面是否出现了滑块验证如果出现说明当前触发风控需要降低采集频率。是否使用了无头模式无头模式在部分页面上可能导致页面行为不一致建议先取消无头测试。登录问题主要出现在访问商品详情页时。搜索页通常可以不登录查看但详情页的部分数据需要登录。这时我的建议是在脚本中手动打开淘宝登录二维码页面用手机扫码登录然后保存Cookie。后续Selenium启动后加载这个Cookie就可以保持登录态。至于滑块验证建议不要尝试强制破解这不是项目核心而且有无合规风险手动处理或降低频率才是更稳妥的方式。4.2 中文编码与数据格式的坑中文乱码贯穿了整个项目的很多环节。首先是CSV写入如果直接使用utf-8编码用Excel打开会乱码解决办法是使用utf-8-sig编码。其次Flask返回JSON时中文通常会转成\uXXXX这不是乱码是JSON的正常表现但如果你希望前端直接显示中文需要设置JSON_AS_ASCIIFalse。Spark处理中文时偶尔会因为CSV里的分隔符和引号规则导致字段错位。比如商品标题中可能存在逗号或换行符这在CSV格式里是有特殊含义的。解决方法是在爬虫写入CSV时使用csv.writer它会自动处理字段里的特殊字符不要手动拼接字符串。另外Spark读取时不要随便指定分隔符默认逗号即可但需要确保CSV文件本身没有坏行。我遇到过一个很隐蔽的问题从HDFS读取CSV后第一行被当成表头实际数据里第一行是标题导致后面统计时“标题”字段错位。解决办法是统一用option(header, True)并保证爬虫输出的CSV第一行就是字段名。如果不想每次都处理表头可以在Spark中定义Schema并设置option(header, True)干净又保险。4.3 Hadoop和Spark环境配置的几个典型坑环境配置的坑往往比业务代码更让人头疼。我在搭建伪分布式时遇到的最大问题是启动HDFS后Namenode一直处于SafeMode状态导致上传文件失败。这种问题大多是数据目录的权限或手动格式化了多次造成的。解决办法是检查/tmp/hadoop-*目录是否存在冲突删除后重新格式化然后重启服务。Spark层面的坑则更集中典型问题常见原因解决办法找不到JAVA_HOMEJDK未安装或路径未配置在bashrc中设置JAVA_HOME并export无法连接HDFS端口配置错误或Namenode未启动检查core-site.xml和jps进程Python解释器报错使用了新的Python版本通过spark-submit --py-files运行或创建conda虚拟环境SparkSession启动失败内存不足或依赖冲突调整spark.driver.memory升级或降级PySpark版本还有一个非常常见的问题是本地文件系统上的products.csv和HDFS上的同名文件让Spark读取时混淆。我在代码里写HDFS路径时一定要写完整的hdfs://localhost:9000/...而不能简写成/user/taobao/...否则Spark会默认去本地文件路径找自然找不到。如果你是第一次接触Hadoop建议不要急着改造代码可以先在命令行上用hdfs dfs -ls、hdfs dfs -cat等命令确认文件是否真实存在于HDFS上。把这套命令用熟排查问题会顺手很多。4.4 Echarts图表不显示的排查路径Echarts图表不显示很多人第一反应是“我的配置有问题”但大多数时候问题出在数据或者DOM。我一般按下面这个顺序排查打开浏览器控制台Network面板看/api/price_distribution请求是否成功返回。看返回的JSON结构是否符合预期键名是否和前端代码一致。看Console面板有没有JavaScript报错比如Cannot read properties of undefined。看图表容器div的高度和宽度确保不是默认0。需要注意Echarts初始化时如果容器还没渲染完成图表会显示不出来。比如模板中把echarts.init放在/body结束标签之前通常没问题但如果放在head里DOM还没解析就会出错。解决办法是放在window.onload里或者使用$(document).ready。还有一个体验层面的问题多张图表同时初始化时如果页面被多次切换或重新渲染可能会出现图表残留。我的做法是在每次重新绘制前调用chart.dispose()销毁旧实例然后用新的数据重新初始化避免内存泄漏和图表重叠。5. 毕业设计答辩与后续扩展建议5.1 数据合规与使用边界这部分虽然不直接涉及代码但非常关键。淘宝商品数据虽然公开可见但擅自抓取并使用仍然存在合规风险。我的原则是只用于个人学习、研究和毕业设计展示控制抓取频率和数量不抓取用户个人信息不将数据用于商业用途。答辩陈述时要主动说明你已经理解这些边界并且做好了限流和脱敏处理。这样不仅让老师觉得你有责任感也能规避一些不必要的风险。具体到代码层面可以在爬虫脚本中设置单位时间内的最大请求数比如每10秒只访问一次页面单次采集量控制在几百条以内。另外保存的数据中用到的店铺名、商品标题都是公开信息尽量不要保存评论者昵称、头像等隐私字段。如果项目需要展示数据样本建议把链接和ID模糊化仅展示统计结果。5.2 答辩时怎么把系统讲得清楚答辩讲得好不好直接影响到老师对你项目的印象。我的建议是不要一上来讲代码细节而是先讲“我做了什么、用了什么技术、解决了什么问题”按照“爬虫采集 - 数据存储 - 分布式计算 - 可视化展示”这条主线串起来。每讲一个环节配合实际的截图或录屏证明系统真的能运行。你可以准备一页系统架构图用Visio或Draw.io画出来上面的每个组件都圈出来注明用途。答辩现场如果老师问“为什么用Spark不用Pandas”你可以这样回答当数据量达到几亿条时单机Pandas会内存溢出而Spark可以分布式处理同时代码逻辑并没有变复杂太多。即使你实际采集的数据量不大也要把这种“面向大规模数据的设计思路”讲清楚这是毕业设计和大作业最大的区别。提前演练一两个“踩坑经历”也很加分。比如你可以说“我在配置Hadoop伪分布式时遇到Namenode一直处于SafeMode的问题后来定位到是多次格式化导致数据目录冲突清理后重新初始化就好了”。这比空谈技术概念更能让老师相信项目是你真实做出来的。5.3 这个项目还可以怎么扩展这个系统做完后我最大的感受是它非常像一个“毛坯房”给后续各种大数据方向的技术都留了接口。如果你有余力可以从以下几个方面扩展引入实时流处理用Kafka作为消息队列Selenium采集到的数据实时发送到Kafka再由Spark Streaming或Flink消费处理形成实时价格监控系统。增加商品评论情感分析抓取商品评论数据利用NLP模型判断评论是正向还是负向再和销量数据关联分析。增加价格预测模块把历史价格数据按时间排列使用时间序列模型比如Prophet、ARIMA预测未来价格走势用Echarts画出预测曲线。容器化部署把Hadoop、Spark、Flask分别做成Docker容器通过Docker Compose一键启动这样老师演示时不用依赖你本机的复杂环境整个项目更容易复现。这些扩展都不是强制的但哪怕只实现其中一项项目的技术深度都会再上一个台阶。关键在于不要贪多先把主链路跑稳再逐步叠加新功能。从这个项目一路走下来我有个很深的体会毕业设计选型时不需要追求每个技术都选最流行的而是要选互相之间能“接得上”的。Selenium、Hadoop、Spark、Flask、Echarts这套组合虽然一开始搭建环境很折腾但一旦跑通你就会对数据采集、存储、计算、展示的完整链路有了实感。如果你也正在做类似课题建议先把基础环境稳定下来再一层一层往上加东西遇到坑不要慌大多数问题都在日志和控制台里能找到答案。给自己留足调试时间这个项目做完你的收获会比想象中多很多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

LLM推理加速器实战:架构选型、核心计算与部署调优 2026/10/1 6:00:32

LLM推理加速器实战:架构选型、核心计算与部署调优

1. 从“跑不动”到“跑得省”:LLM硬件加速器的核心命题大模型部署到生产环境之后,最先撞上的墙往往不是模型效果,而是推理成本和延迟。一个70B参数的模型,如果纯靠通用GPU做FP16推理,单次生成就要吃掉大量显存带宽&…

阅读更多 →
Vue3+Element Plus数字范围输入框组件封装实战 2026/10/1 6:00:32

Vue3+Element Plus数字范围输入框组件封装实战

做后台管理系统这些年,凡是涉及筛选条件、搜索表单、商品价格区间、时间区间,几乎都会碰到一个重复到让人想吐的场景:两个数字输入框并排,中间一个分隔符,左边最小值右边最大值,还要处理清空、边界限制、值…

阅读更多 →
GPU服务器故障排查实战:覆盖驱动、数据加载与硬件检修 2026/10/1 6:00:31

GPU服务器故障排查实战:覆盖驱动、数据加载与硬件检修

这几天帮一个客户收拾一台GPU服务器的烂摊子,现象特别典型:跑推理任务的时候,nvidia-smi里 GPU 利用率只有不到 30%,显存却顶在 12GB 左右不动,程序慢得让人怀疑人生,但 CPU 和内存占用又都不高&#xff0c…

阅读更多 →
Jev“哑巴模型”爆火:专注代码生成的编程专用大模型实战解析 2026/10/1 6:00:18

Jev“哑巴模型”爆火:专注代码生成的编程专用大模型实战解析

最近AI圈里有个词冒出来得特别猛——“Jev”。你要是这几天刷技术社区,大概率会看到“哑巴模型”“Jev密钥”“Jev在Codex里怎么配”这些字眼。我一朋友上来就问我:这Jev到底是个啥,怎么一夜之间全网都在聊?我去翻了一圈官网、社区…

阅读更多 →
Power BI Report Server企业级部署与SSRS深度集成指南 2026/10/1 6:00:18

Power BI Report Server企业级部署与SSRS深度集成指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
华为视频编辑服务UI SDK:Java短视频编辑源码与避坑指南 2026/10/1 6:00:18

华为视频编辑服务UI SDK:Java短视频编辑源码与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉