天气预报数据爬取与可视化:Hadoop+Hive全流程实战
做大数据的人十有八九都被课程设计或面试题里这套组合拳招呼过爬数据、上Hadoop、做可视化。这个“天气预报数据爬取与可视化分析”的项目标题本质上是把数据采集、分布式存储、离线计算和数据可视化串成一条完整链路看起来容易真跑通一遍才能体会到各个环节的门道。我前前后后带过几届学生搭这类项目也给自己练手重建过今天把整个流程、踩过的坑、关键的参数选择全部梳理出来给正在做课程设计、毕设或者准备大数据岗位面试的人一份能直接“抄作业”的参考。这项目该踩的坑我基本都踩过了——Hadoop版本选错导致各种兼容性报错、爬虫被反爬机制拦截、Hive跑数时数据倾斜、可视化大屏加载卡顿等等。下面按我的实操顺序从头到尾把完整方案讲清楚。1. 项目整体设计与技术选型思路1.1 项目到底要解决什么问题先别急着写代码。这个题目看着简单——“爬天气数据、存Hadoop、画图表”但真正考察的核心是你能不能把一条完整的大数据处理链路打通数据从哪来、用什么方式采集、怎么存储、怎么清洗、怎么分析、怎么展示。天气预报数据的特殊性在于数据量不算巨大但格式杂乱、来源多样、实时性要求分明。如果你只爬一天的数据那根本不需要Hadoop一个Python脚本加Excel就够了。所以要把它做成大数据项目就必须在数据规模和计算逻辑上做文章——比如爬取全国几百个城市一年甚至多年的历史天气数据单日数据量可能上GB这时候HDFS的分布式存储和Hive的批量计算才有用武之地。从实际课程设计和面试的角度看这个项目能展示的技能点非常全面爬虫能力Python requests BeautifulSoup、大数据基础Hadoop/HDFS/Hive、数据分析能力SQL/MapReduce思想、可视化能力ECharts等。这四个维度的技能组合恰好命中大数据岗位的基础能力要求这也是这个项目长盛不衰的原因。1.2 技术栈怎么选为什么是Hadoop而不是别的选Hadoop作为核心处理框架既有项目需求的必然性也有学习路径上的合理性。我见过不少人上来就质疑几GB的数据用MySQL或者Pandas不香吗说实话如果纯粹从处理效率看单机Pandas处理几GB的结构化数据完全没问题。但这个项目的关键不在“效率”而在“架构”——你要体现的是对分布式存储和分布式计算的理解。Hadoop生态里HDFS负责存储MapReduce或Hive负责计算。对于天气预报数据这种结构化文本数据Hive比直接写MapReduce要高效得多。原因很简单Hive把复杂的MapReduce逻辑封装成SQL让你能把精力集中在业务分析上而不是浪费在写Java代码上。我在实际项目中选用了Hadoop 2.7.6 Hive 1.2.1的经典组合因为这套版本在伪分布式模式下运行最稳定生态兼容性也最好。网上很多人推荐Hadoop 3.x但我个人经验是3.x的某些特性在单机伪分布式模式下容易出现权限和配置问题加上课程设计用的服务器普遍配置不高老版本反而跑得更顺。至于可视化部分我选择了ECharts。这几乎是国内数据可视化的事实标准文档全、例子多、图表类型丰富对前端基础要求也不高。你也可以用Superset或者Tableau但ECharts在“定制化”和“展示效果”上的灵活性是最好的。1.3 整体流程架构整个项目的执行流程我用一个简单的链路来梳理第一阶段Python爬虫从天气网站抓取各城市历史天气数据第二阶段原始数据清洗、统一格式生成标准CSV第三阶段数据上传到HDFS分布式文件系统第四阶段Hive建表、加载数据、写SQL做统计分析第五阶段分析结果导出用ECharts做可视化大屏每阶段的产出和验收标准也都比较明确爬虫阶段要有完整可用的数据集HDFS要有能通过Web界面看到的数据文件Hive要有能跑通的统计分析SQL可视化要有能展示分析结果的图表页面。这样拆分以后项目进度可控每个阶段的成果都能拿出来做阶段性展示。2. 环境准备Hadoop伪分布式集群搭建全记录2.1 版本选择与安装前准备做大数据项目环境搭建通常是第一道坎。我在实践中发现环境问题导致的报错占整个项目的60%以上所以这部分值得花时间讲透。先说版本选择。我的组合是CentOS 7.9 JDK 1.8 Hadoop 2.7.6 Hive 1.2.1。CentOS 7.9是目前兼容性最好的Linux发行版JDK 1.8是Hadoop 2.x系列的标配换新版本反而会出现编译和运行兼容问题。如果你用的是Ubuntu操作逻辑一样就是命令细节有差异。安装前需要确认三件事SSH是否安装、JDK是否配好JAVA_HOME、防火墙是否关闭。特别注意Hadoop的NameNode和DataNode之间通过SSH通信必须配置免密登录。第一次配的时候忘了生成SSH密钥结果每次启动Hadoop都要输密码而且集群节点之间通信会报权限错误。JDK安装和JAVA_HOME配置这块网上教程很多但有一点容易踩坑Hadoop的hadoop-env.sh脚本里固定写了JAVA_HOME${JAVA_HOME}但有时候系统环境变量没有正确传递导致Hadoop脚本找不到Java。我习惯的做法是直接在hadoop-env.sh里硬编码JAVA_HOME的绝对路径比如export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk。这样最保险不管你用什么用户启动Hadoop都不会出错。2.2 伪分布式搭建核心步骤伪分布式模式是在单台机器上模拟分布式环境核心思想是让NameNode、DataNode、SecondaryNameNode等角色跑在同一台机器上。以下是完整步骤第一步配置core-site.xml指定NameNode的地址。这里要注意hadoop.tmp.dir参数如果你不指定默认会用/tmp/hadoop-${user}系统重启后数据会被清理。我吃过这个亏重启服务器后数据全没了后来老老实实配置成/opt/hadoop/tmpconfiguration property namefs.defaultFS/name valuehdfs://localhost:9000/value /property property namehadoop.tmp.dir/name value/opt/hadoop/tmp/value /property /configuration第二步配置hdfs-site.xml设置副本数为1伪分布式只有一台机器副本数大于1反而会报错configuration property namedfs.replication/name value1/value /property property namedfs.namenode.name.dir/name value/opt/hadoop/data/namenode/value /property property namedfs.datanode.data.dir/name value/opt/hadoop/data/datanode/value /property /configuration第三步配置mapred-site.xml需要从模板改名指定使用YARN作为资源调度框架configuration property namemapreduce.framework.name/name valueyarn/value /property /configuration第四步配置yarn-site.xml设置ResourceManager的地址和NodeManager的参数。这里有个容易忽略的点需要在yarn-env.sh里设置YARN_HOME环境变量否则启动YARN时会报找不到脚本的错误。第五步格式化NameNode。这一步相当于文件系统的初始化会生成NameNode的元数据目录。注意格式化操作只能在首次运行时执行重复格式化会导致DataNode和NameNode的clusterID不一致hdfs namenode -format第六步启动服务用jps命令检查进程是否都起来了。正常状态下应该看到NameNode、DataNode、SecondaryNameNode、ResourceManager、NodeManager这5个进程。2.3 集群部署策略伪分布式还是完全分布式很多人在课程设计阶段纠结要不要搭完全分布式集群。我的建议是没有条件就踏实把伪分布式做好做精。伪分布式模式下HDFS的所有特性都能验证比如文件上传下载、分块存储、副本机制Hive和YARN也能跑。区别只在于没有多节点的数据均衡和容灾但对于课程设计和入门学习来说伪分布式完全够用。如果确实有多台机器推荐按照“1台NameNode 2台DataNode”的最小规模搭建完全分布式。核心改动是把core-site.xml里NameNode的地址改成master主机名hdfs-site.xml里把副本数从1改成2或3然后在slaves文件里添加所有DataNode的主机名。在完全分布式模式中最重要的一点是确保所有节点的/etc/hosts配置一致并且每个节点都能通过主机名互相访问否则集群启动会报节点间连接失败。我给学生的建议是优先保证伪分布式跑通完整链路有精力再拓展集群规模。因为项目验收看的是“完整度”而不是“节点数”。一个人用3台虚机搭集群通信问题排查起来费时费力反而影响核心功能的进度。3. 天气预报数据爬取模块实战3.1 数据源怎么选免费接口还是网页爬取说句大实话这个项目最花时间的地方不是Hadoop配置而是怎么拿到一份像样的天气数据。常见的方案有两条路子一是调用天气预报API接口二是直接爬取天气网站的网页数据。用API接口的优势是数据规范化程度高、字段完整比如和风天气、心知天气都提供免费的开发者接口。劣势是免费接口通常有每天调用次数的限制爬取大量历史数据需要开通付费权限。而且免费接口往往只提供实时天气和短期预报历史天气数据需要额外付费获取。网页爬取的优势是数据量大、不受次数限制比如中国天气网提供了非常完整的历史天气数据涵盖全国各城市每天的最高温、最低温、天气状况、风力风向等字段。劣势是数据格式不稳定、网页结构会调整、需要处理编码问题。我实际采用的是网页爬取为主、API接口为辅的策略历史数据从网页爬实时校准用API。选择中国天气网作为数据源还有一个关键原因——它不需要登录、反爬机制相对较弱、页面结构清晰规律。对爬虫新手来说学习成本低得多。3.2 爬虫代码实现requests BeautifulSoup讲一下核心爬虫逻辑。中国天气网的历史天气页面结构比较规律城市代码就是URL里的那一串数字。比如北京的城市代码是101010100可以直接通过URL拼接获取不同城市不同月份的数据。我的爬虫设计思路是这样的先准备一个城市代码清单然后循环遍历年份和月份构造请求URL解析HTML表格提取需要的字段。这里的关键是请求头的设置——目标网站会检查User-Agent默认的Python请求头很容易被拦截。我用的请求头长这样headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.8,en-US;q0.5,en;q0.3, }页面解析用BeautifulSoup定位表格节点from bs4 import BeautifulSoup import requests def parse_weather_page(html): soup BeautifulSoup(html, html.parser) weather_table soup.find(table) rows weather_table.find_all(tr) data_list [] for row in rows[1:]: # 跳过表头 cells row.find_all(td) if len(cells) 4: date cells[0].get_text().strip() high_temp cells[1].get_text().strip().replace(℃, ) low_temp cells[2].get_text().strip().replace(℃, ) weather cells[3].get_text().strip() wind cells[4].get_text().strip() if len(cells) 4 else data_list.append({ date: date, high_temp: high_temp, low_temp: low_temp, weather: weather, wind: wind }) return data_list这里有几个容易忽略的细节温度的提取注意去掉“℃”符号日期格式统一成“YYYY-MM-DD”风力字段可能包含多个空格分割的子字段。这些清洗工作看起来琐碎但直接影响后面Hive分析的数据质量。3.3 数据清洗与格式标准化爬下来的原始数据不能直接用至少要经过三级清洗第一级字段缺失处理。部分日期可能没有数据对应行直接丢弃避免影响统计结果。第二级数值类型转换。把温度、风力等字段从字符串转成数字或统一格式。特别注意温度可能为负值字符串包含负号。我用正则表达式提取数值“-5℃”要正确转成-5。第三级数据格式统一。日期统一成标准格式天气类型做归类整理比如“多云转晴”和“晴转多云”要识别为不同的组合类型做统计时按主天气来归类。清洗后的标准CSV格式我设计成这样的字段结构city_code,city_name,date,high_temp,low_temp,weather,wind 101010100,北京,2023-01-01,2,-8,晴,北风3级 101010100,北京,2023-01-02,0,-6,多云,东北风2级字段命名全部用下划线分隔这是Hive建表时的最佳实践方便后续写SQL时不用转义特殊字符。3.4 反爬应对与请求节流做爬虫最烦的就是被拦截。中国天气网虽然反爬机制不严格但高频请求还是会触发验证码。我采用的策略是“软节流”——每次请求之间加2-5秒的随机延时并且分类目分批次爬取比如先爬华北地区再爬华东地区。另外一个关键点网页编码问题。中国天气网老版页面用GBK编码新版用UTF-8。如果你发现爬下来的中文变成乱码第一反应应该是编码问题。用requests库时我习惯在拿到response后先检查apparent_encoding属性再做解码处理response requests.get(url, headersheaders, timeout10) response.encoding response.apparent_encoding爬取的数据先本地存一份备份再上传到HDFS。这个习惯很重要因为HDFS上的数据万一被误删或者格式有问题你还能回退到原始数据。我吃过一次亏单机伪分布式模式下hdfs dfs -rm -r误删了数据目录本地如果没有备份就要重新爬一遍。4. 数据入HDFS与Hive统计分析4.1 HDFS上传与目录设计数据清洗完成后就要上传到HDFS进行分布式存储。HDFS上的目录设计要遵循分区思想我的推荐结构是/weather/data/raw # 原始数据 /weather/data/clean # 清洗后数据 /weather/analysis # 分析结果输出这样设计的好处是职责清晰方便管理权限和数据生命周期。生产环境中的HDFS目录管理一般也遵循类似的规范先做目录规划再放数据避免数据堆积在一起无法追溯。上传命令很简单hdfs dfs -mkdir -p /weather/data/clean hdfs dfs -put clean_weather_data.csv /weather/data/clean/上传完成后可以通过Web界面验证在浏览器访问http://localhost:50070查看文件的状态。注意Hadoop 2.x的Web界面端口是500703.x换成了9870。如果你用的是2.7.6要用50070。4.2 Hive建表与数据加载Hive建表前需要先在HDFS里把数据准备好然后通过外部表或内部表方式加载。我最推荐的是外部表因为外部表删除时不会连带删除HDFS上的原始文件安全性更好。建表语句如下CREATE EXTERNAL TABLE IF NOT EXISTS weather_ods ( city_code STRING, city_name STRING, date STRING, high_temp INT, low_temp INT, weather STRING, wind STRING ) ROW FORMAT DELIMITED FIELDS TERMINATED BY , STORED AS TEXTFILE LOCATION /weather/data/clean;建表时特别要注意Hive表的字段类型和CSV里的数据类型必须严格匹配。温度我用的INT因为最高温度和最低温度都是整数天气和风力用STRING日期用STRING而不是DATE类型因为原始数据可能存在格式问题用了DATE类型如果解析失败整行数据就变成NULL。4.3 核心分析SQL与计算结果数据准备好以后分析的核心是SQL怎么写。我列几个最有价值、最能体现“数据分析能力”的统计口径。第一个年度平均温度趋势分析。按城市和月份做聚合看全年的温度变化曲线SELECT city_name, substr(date, 1, 7) AS month, AVG((high_temp low_temp) / 2) AS avg_temp FROM weather_ods WHERE city_name 北京 GROUP BY city_name, substr(date, 1, 7) ORDER BY month;第二个极端天气TOP排行。统计最高温度超过35℃的天数排行这个分析适合做全国热度图SELECT city_name, COUNT(*) AS hot_days FROM weather_ods WHERE high_temp 35 GROUP BY city_name ORDER BY hot_days DESC LIMIT 20;第三个天气类型统计。计算各类天气的出现次数和比例适合做饼图SELECT weather, COUNT(*) AS weather_count, ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 2) AS percentage FROM weather_ods GROUP BY weather ORDER BY weather_count DESC;写Hive SQL时有个常见误区——用MySQL的习惯写Hive最容易出问题的就是函数差异。Hive里求字符串长度用length()日期格式化用from_unixtime()和to_date()这和MySQL有不少区别。实际跑之前建议先在数据量小的表上测试语法不然报错debug很浪费时间。分析结果的导出也很关键。Hive查询结果可以直接落地到HDFS目录然后用命令拉回本地hive -e SELECT * FROM analysis_result | tr \t , analysis_result.csvHive默认的输出列分隔符是制表符\t如果要转成CSV格式需要用管道把制表符替换成逗号。这一步截了很多学生他们直接把hive的查询结果load到本地结果发现字段全是制表符分隔Excel里根本打不开。5. 可视化分析与展示实现5.1 工具选型对比ECharts、Tableau还是Superset可视化工具这块我对比了三个主流方案再说结论。Tableau优势是拖拽式操作不需要写代码适合快速出图但收费昂贵不适合课程设计展示。Superset是Apache孵化的开源BI工具支持多种数据源接入功能全面但部署较麻烦需要Docker或者复杂的前后端配置学习成本高。ECharts是纯前端图表库灵活的定制性是三者中最强的而且社区资源丰富适合做“数据大屏”这种展示效果极强的可视化页面。最终我选择的是ECharts因为数据大屏展示效果越好项目加分越明显。5.2 分析维度设计从数据中挖什么故事可视化不是把图表堆上去就完事关键是你的图表组合起来能不能讲一个完整的故事。我这个项目的分析展示设计为四个模块分别对应四个故事维度第一个维度是“全国气温分布概览”用地图热力图展示全国主要城市7月份的平均最高气温。这个图能直观呈现南北温差和火炉城市分布。第二个维度是“年度气温变化曲线”选中几个典型城市北京、上海、广州、哈尔滨展示全年的月均气温曲线南北气候差异一目了然。第三个维度是“天气类型分布”用饼图或环形图展示全年天气类型占比晴天、多云、阴天、雨雪天的比例关系清晰可见。第四个维度是“极端天气统计”柱状图展示高温天数排名前列的城市。这是我数据分析的一个亮点更贴合实际应用。5.3 图表具体实现与数据对接ECharts实现地图热力图时数据需要从分析结果CSV读取然后在前端JS里做JSON转换。数据结构大概是{ type: map, map: china, series: [{ type: map, mapType: china, data: [ {name: 北京, value: 31.5}, {name: 上海, value: 32.8}, {name: 广州, value: 33.5} ] }] }这里有个技术细节ECharts的地图数据是针对省份的而你的爬虫数据是城市级别的。处理方法是把城市映射到所属省份做聚合计算后再传。比如“苏州”映射到“江苏”“青岛”映射到“山东”。城市到省份的映射表可以提前准备成JSON避免前端做复杂的逻辑判断。折线图和柱状图的数据结构相对简单主要是注意X轴的类型设置。时间序列建议用type: time数值型用type: category。ECharts的官方示例都写着很清楚直接改数据和标题就能用。实际做可视化的时候建议先用静态数据把页面样式调好再接入真实分析结果。这样能避免调试时数据、样式混在一起难排查的状况。如果页面数据量过大出现卡顿优先考虑数据采样而不是提升电脑配置——我之前就遇到过图表加载卡顿后来发现是数据量太大导致ECharts一次性渲染的点太多做了数据聚合后问题立刻解决。6. 常见问题与兼容性踩坑实录6.1 高频报错的排查方案这一块是真正从实战中积累出来的经验我整理了一个问题速查表问题现象可能原因解决方案NameNode is not formatted初始化失败或未格式化重新执行hdfs namenode -formatJava heap space错误Hadoop默认堆内存太小修改hadoop-env.sh中的HADOOP_HEAPSIZE为1024DataNode clusterID mismatch重复格式化导致ID不一致清空data目录后重新格式化Hive Table schema解析失败CSv字段与Hive字段不对齐检查分隔符确认为逗号而不是制表符Permission deniedHDFS文件权限不匹配hdfs dfs -chmod -R 777 /weatherHive查询结果为空外部表LOCATION路径错误用hdfs dfs -ls确认路径存在且文件非空爬虫返回乱码网页编码解析错误用response.apparent_encoding动态获取编码最需要注意的是第3个问题。很多新手在启动Hadoop时发现DataNode报错然后反复执行格式化命令结果越搞越糟。正确做法是先停止所有进程清空dfs.datanode.data.dir指定的目录和dfs.namenode.name.dir指定的目录再格式化NameNode最后重新启动。6.2 数据倾斜问题与处理经验做天气预报数据统计想遇到数据倾斜其实不容易因为数据比较均匀。但如果你是做社交媒体或者电商类的分析就要特别注意了。数据倾斜的典型特征是MapReduce任务报错某些Reduce任务卡在100%不动。这种问题在Hive里最常见的解决方案是加hive.groupby.skewindatatrue参数或者在SQL里用COUNT(DISTINCT)改成先GROUP BY再聚合。天气预报项目里真正会遇到的性能瓶颈是往HDFS上传海量小文件。爬虫如果按天爬数据可能生成上千个小文件HDFS存储文件元数据的NameNode会承受压力。解决办法是用hdfs dfs -getmerge把多个小文件合并成大文件再上传或者在上传前用cat *.csv merged.csv在本地合并。这个细节体现了对HDFS小文件问题的理解面试时也是加分项。6.3 与最新技术栈的交汇Qt大数据表格性能与Hadoop生态既然热词里提到了“qt 表格大数据卡顿优化 tablewidget 到qtableview 自定义model”我就多聊几句大数据可视化展示层面的性能优化思路。虽然这在技术栈上和Hadoop不完全一样但大数据项目的最终呈现往往会有类似痛点——数据量一大传统控件卡成PPT。在Qt里从QTableWidget切换到QTableView 自定义QAbstractTableModel是解决大数据量表格卡顿的主流方案。QTableWidget本身一次性加载所有数据到内存几万行就开始卡QTableView配合QAbstractTableModel只渲染可见区域滚动时才动态加载这在数据量达到十万、百万行时性能差异非常明显。自定义Model的关键是正确实现rowCount()、columnCount()、data()三个方法核心逻辑是data()里根据index动态返回值而不是把数据复制到表格组件里。我之前做项目时也遇到可视化时数据量过大导致页面渲染卡顿的问题虽然不是Qt而是Web前端但底层思路一致——只在可视区域渲染滑动时按需加载。ECharts的dataZoom组件也能做类似的事情设置好smooth属性后大屏滚动流畅度会显著提升。实操总结与个人心得最后说点项目之外的体会。这个“天气预报数据爬取与可视化分析”看似是课程设计里的一个题目实际上训练的是你对大数据全流程的把控能力。从数据采集的细节处理到Hadoop生态的搭建再到分析维度的设计每个环节都有值得深挖的知识点。我自己在带这个项目时最大的感受是大多数人不是卡在某一个技术点太难而是对整体流程没有概念不知道每一步做完应该看到什么结果。所以我也很强调阶段性的结果验证——爬虫跑完要能看到CSV里有多少行数据Hadoop启动完要能看到5个Java进程Hive跑完要能看到分析结果表。每一步都看得见结果整体项目推进就会顺利很多。如果你正在做类似的课程设计或者准备大数据岗位面试建议不要只停留在“能跑通”的层面。多追问自己几个为什么为什么HDFS副本数要设成1Hive为什么要用外部表ECharts的地图数据为什么需要城市映射到省份这些“为什么”的答案恰恰是你和别人拉开差距的地方。