拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于Python Flask与ECharts的天气数据爬取可视化系统实战

简介本资源是一套基于Python与Flask开发的山东省气象数据全链路分析系统面向气象爱好者、数据分析初学者及高校地理信息/环境科学相关专业学生解决实时天气抓取、历史数据存储与多维度可视化呈现的实际需求。系统完整实现从网页爬虫获取山东省各市实时与历史气象数据温度、湿度、风速、降水等经清洗入库后通过地图热力图、时间序列折线图、气象要素柱状图等交互式图表进行直观展示兼顾科研分析与大众化信息解读。压缩包共140个文件3.26MB含35个CSV气象数据集、31张可视化结果JPG图、19个前端JS交互脚本、12个PNG图标及10个CSS样式文件配合Bootstrap、Font Awesome等主流前端库构建响应式界面另有4个核心Python爬虫与Flask后端模块结构清晰、注释完备便于二次开发与教学复现。目前已有120人学习下载提供开箱即用的本地部署方案与完整前后端代码是理解Web数据可视化项目落地的典型实践案例。1. 项目概述与核心价值最近在做一个挺有意思的玩意儿一个专门针对山东省天气数据的爬取和可视化分析系统。起因很简单我之前想查查老家山东某个小县城过去几年的天气变化趋势发现要么是商业气象网站数据不全要么是API调用次数有限制要么就是数据格式不友好没法按自己的想法做深度分析。这让我觉得如果能自己动手把数据抓下来、存起来、再按需可视化那不就自由多了于是这个基于Python和Flask的“山东省天气数据爬取及可视化分析系统”就诞生了。这个系统本质上是一个Web应用它干三件核心的事第一从网络上自动、持续地抓取山东省各地市的实时和历史天气数据第二把这些结构化和非结构化的气象数据规规矩矩地存到数据库里方便后续查询和分析第三也是最有意思的部分就是通过一个友好的网页界面用地图、折线图、柱状图、热力图等多种形式把温度、湿度、风速、降水这些气象要素直观地展示出来让你一眼就能看出天气的时空分布规律和变化趋势。它适合谁呢如果你是Python初学者想找一个有完整链路爬虫-数据处理-Web开发-数据可视化的真实项目来练手这个项目再合适不过了涵盖了从后端到前端的多个核心技能点。如果你是对气象数据分析感兴趣的数据爱好者、学生或者需要本地化气象数据支持的小型研究或业务团队这个系统提供了一个可定制、可扩展的基础框架。当然前提是你得对Python有点基础知道怎么配环境、装库。2. 系统整体架构与技术选型2.1 核心架构设计思路整个系统的设计遵循了典型的数据流水线思维也就是“数据采集 - 数据存储 - 数据处理 - 数据展示”这条主线。但和简单的脚本不同我们需要把它工程化、服务化这就是引入Flask框架的原因。我的架构分为三个相对独立的模块通过数据库和Web服务器进行耦合数据爬取模块这是一个可以独立运行的脚本或定时任务。它负责访问目标天气数据源解析网页或调用API将获取到的山东省17个地市济南、青岛、烟台等的天气数据清洗成结构化格式。数据存储与处理模块核心是数据库。爬取到的数据会被持久化存储在这里。我选择的是关系型数据库因为它结构清晰便于做复杂查询比如“查询济南市2023年夏季所有日平均温度高于30度的日期”。同时这里也包含一些简单的数据预处理逻辑比如单位换算、异常值处理。Web可视化展示模块这是Flask的“主战场”。它负责提供网页界面接收用户的查询请求比如选择城市、时间范围、气象要素从数据库中取出相应的数据然后利用前端的图表库生成可视化图表最后渲染成HTML页面返回给用户。这种模块化设计的好处是解耦。爬虫挂了不影响网站访问你想换一种图表库也只需要改动前端部分甚至未来想增加新的数据源也只需要扩展爬虫模块。2.2 关键技术栈选型与理由后端框架Flask为什么选Flask而不是Django对于这个数据展示型的项目Django那套全功能的“全家桶”自带Admin、ORM、用户认证等显得有些重了。我们的核心需求是轻量、灵活、快速搭建RESTful API来提供数据。Flask的微框架特性正好契合它没有强制的项目结构用多少功能就引入多少扩展学习曲线平缓能让我更专注于业务逻辑本身。比如我需要提供JSON格式的天气数据给前端图表库用Flask的jsonify和路由功能几行代码就能搞定。爬虫库Requests BeautifulSoup4 / PyQueryRequests库是HTTP请求的“瑞士军刀”简单易用配合BeautifulSoup4或PyQuery进行HTML解析足以应对大多数静态天气网站。如果目标网站数据是通过JavaScript动态加载的那么可能需要引入Selenium或Playwright。考虑到天气数据通常更新不频繁且结构相对固定静态解析在大多数情况下是更高效稳定的选择。数据存储SQLite / MySQL项目初期或者个人学习使用SQLite是绝佳选择。它是一个文件数据库无需安装和配置独立的数据库服务用Python标准库sqlite3就能直接操作极大简化了部署复杂度。当数据量增大比如存储了十年以上的逐日数据或者需要多人协作时可以平滑迁移到MySQL或PostgreSQL。我建议初期用SQLite快速原型验证后期再根据需求升级。数据可视化ECharts / Plotly这是前端的核心。ECharts是百度开源的一个强大的图表库文档丰富社区活跃地图支持尤其是对中国省份城市的支持非常好这对于展示山东省内各地市的气象数据分布至关重要。它的热力图、折线图、柱状图等都能满足需求。Plotly也是一个优秀的选择它的交互性更强并且同时支持在Python后端生成静态图表和在前端动态交互。在这个项目中我选择让Flask后端只提供纯净的JSON数据由前端JavaScript调用ECharts库来渲染图表这样前后端分离更彻底用户体验也更流畅。前端框架简单的HTML/CSS/JavaScript Bootstrap为了快速搭建一个整洁美观的界面我引入了Bootstrap前端框架。它提供了现成的网格系统、按钮、表单和导航栏样式让我们不用在CSS上花费太多精力。页面布局主要用Bootstrap的栅格系统来划分区域比如左侧放城市选择面板中间主区域展示地图和图表。注意技术选型没有绝对的对错只有是否适合当前场景。这里的选择是基于“快速实现、易于学习、满足核心功能”的原则。如果你的项目对并发性能要求极高可能需要考虑异步框架如FastAPI和更强大的数据库优化。3. 核心模块实现细节拆解3.1 天气数据爬取模块的实现爬虫是这个系统的数据源头它的稳定性和准确性直接决定了后续所有分析的价值。我的目标是爬取山东省每个地级市的实时天气温度、湿度、风向风速、天气状况和历史数据每日最高/最低温、降水量等。第一步目标网站分析与数据源选择首先得找到可靠、免费且可持续访问的数据源。国内有一些提供天气数据的网站和API。在选择时我主要考虑几点1) 数据是否包含山东省所有地市2) 历史数据是否可获取3) 网站是否有反爬机制如频繁访问限制、验证码4) 数据更新频率。经过一番搜寻和测试我选定了一个结构清晰、反爬相对宽松的公益性气象数据网站作为主要数据源。切记爬取数据一定要遵守网站的robots.txt协议控制请求频率避免对对方服务器造成压力。第二步编写爬虫脚本核心代码结构如下import requests from bs4 import BeautifulSoup import pandas as pd import time import sqlite3 # 1. 定义目标城市列表山东省17地市 shandong_cities [‘济南‘ ‘青岛‘ ‘淄博‘ ...] # 2. 模拟请求头避免被识别为简单爬虫 headers { ‘User-Agent‘: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...‘ } def fetch_realtime_weather(city_name): 抓取单个城市的实时天气 # 构建对应城市的URL这里需要根据实际网站结构分析 url f‘http://example-weather-site.com/{city_name}‘ try: resp requests.get(url, headersheaders, timeout10) resp.encoding ‘utf-8‘ # 根据网站编码调整 soup BeautifulSoup(resp.text, ‘html.parser‘) # 3. 解析页面提取关键数据 # 这部分代码高度依赖目标网站的HTML结构需要仔细分析其标签和class temp soup.find(‘span‘, class_‘temp‘).text # 例如温度 humidity soup.find(‘div‘, id‘humidity‘).text # 湿度 # ... 提取其他要素 weather_data { ‘city‘: city_name, ‘temperature‘: temp, ‘humidity‘: humidity, ‘wind_speed‘: wind_speed, ‘update_time‘: ‘2023-10-27 14:00‘, # 实际应从页面解析或使用当前时间 ‘data_time‘: ‘2023-10-27 14:00‘ # 数据对应的时间点 } return weather_data except Exception as e: print(f“抓取{city_name}数据失败: {e}“) return None def save_to_db(data_dict, db_path‘weather.db‘): 将数据保存到SQLite数据库 conn sqlite3.connect(db_path) cursor conn.cursor() # 确保表存在这里以实时数据表为例 cursor.execute(‘‘‘CREATE TABLE IF NOT EXISTS realtime_weather (id INTEGER PRIMARY KEY AUTOINCREMENT, city TEXT, temperature REAL, humidity REAL, wind_speed REAL, update_time TIMESTAMP, data_time TIMESTAMP)‘‘‘) # 插入数据 cursor.execute(‘‘‘INSERT INTO realtime_weather (city, temperature, humidity, wind_speed, update_time, data_time) VALUES (?, ?, ?, ?, ?, ?)‘‘‘, (data_dict[‘city‘], data_dict[‘temperature‘], data_dict[‘humidity‘], data_dict[‘wind_speed‘], data_dict[‘update_time‘], data_dict[‘data_time‘])) conn.commit() conn.close() # 4. 主循环遍历所有城市抓取并保存 for city in shandong_cities: print(f‘正在抓取{city}的天气...‘) data fetch_realtime_weather(city) if data: save_to_db(data) # 礼貌性延时避免请求过快 time.sleep(2) print(‘所有城市实时天气数据抓取完成‘)第三步历史数据抓取策略历史数据通常以“日期”为维度进行查询。这就需要构造一个日期循环比如从2023-01-01循环到今天每天请求一次历史数据页面。URL模式通常是base_url city date。这里的关键点是异常处理和增量抓取。每次抓取前先查询数据库是否已存在该城市该日期的数据如果存在则跳过避免重复劳动和浪费资源。同时网络请求和页面结构可能变动必须用try...except包裹核心抓取逻辑并做好日志记录。第四步定时任务与自动化为了让数据保持更新需要让爬虫定时运行。在Linux服务器上最直接的方式是使用Crontab。你可以设置一个定时任务比如每小时运行一次实时数据爬虫每天凌晨运行一次历史数据更新爬虫。# 编辑crontab crontab -e # 添加以下行表示每小时的第5分钟运行一次爬虫脚本 5 * * * * /usr/bin/python3 /path/to/your/realtime_spider.py /path/to/spider.log 21在Windows上可以使用计划任务功能实现类似效果。更工程化的做法是使用APScheduler这样的Python库在Flask应用内部实现定时任务。3.2 数据存储与数据库设计数据库设计的好坏直接影响数据查询的效率和后续分析的便利性。我设计了两张核心表1. 实时天气数据表 (realtime_weather)这张表存储最新的、或按小时抓取的天气快照。CREATE TABLE realtime_weather ( id INTEGER PRIMARY KEY AUTOINCREMENT, city VARCHAR(20) NOT NULL, -- 城市名 temperature FLOAT, -- 温度摄氏度 humidity FLOAT, -- 湿度百分比 wind_speed FLOAT, -- 风速米/秒 wind_direction VARCHAR(10), -- 风向 weather_condition VARCHAR(20), -- 天气状况晴、多云、雨等 pressure FLOAT, -- 气压百帕 visibility FLOAT, -- 能见度公里 aqi INTEGER, -- 空气质量指数如有 data_time TIMESTAMP NOT NULL, -- 数据所代表的时间如2023-10-27 14:00:00 update_time TIMESTAMP DEFAULT (datetime(‘now‘, ‘localtime‘)) -- 数据入库时间 ); -- 为经常查询的字段创建索引大幅提升查询速度 CREATE INDEX idx_city ON realtime_weather (city); CREATE INDEX idx_data_time ON realtime_weather (data_time);2. 历史天气数据表 (history_weather)这张表存储每日的统计型数据如日最高/最低温、降水量等。CREATE TABLE history_weather ( id INTEGER PRIMARY KEY AUTOINCREMENT, city VARCHAR(20) NOT NULL, date DATE NOT NULL, -- 日期 max_temp FLOAT, -- 日最高温 min_temp FLOAT, -- 日最低温 avg_temp FLOAT, -- 日平均温 precipitation FLOAT, -- 降水量毫米 sunshine_hours FLOAT, -- 日照时长小时 avg_humidity FLOAT, -- 平均湿度 dominant_wind_direction VARCHAR(10), -- 主导风向 UNIQUE(city, date) -- 联合唯一约束防止同一城市同一天数据重复 ); CREATE INDEX idx_city_date ON history_weather (city, date);实操心得UNIQUE(city, date)这个约束非常重要。它能确保爬虫脚本在意外重复运行时数据库不会插入重复数据要么忽略要么更新使用INSERT OR REPLACE或ON CONFLICT子句。另外为city和date/data_time字段建立索引对于按城市和时间范围查询的性能提升是巨大的当数据量达到几十万条时有索引和没索引的查询速度可能是天壤之别。3.3 Flask后端API设计与实现Flask后端的作用是充当数据库和前端页面之间的桥梁提供数据接口。我们不需要复杂的页面渲染主要提供RESTful API。项目结构weather_visualization_system/ ├── app.py # Flask应用主入口 ├── config.py # 配置文件数据库路径、密钥等 ├── models.py # 数据库模型定义可使用SQLAlchemy ORM ├── spider/ # 爬虫模块目录 │ ├── __init__.py │ ├── realtime_spider.py │ └── history_spider.py ├── static/ # 静态文件CSS, JS, 图片 │ ├── css/ │ └── js/ ├── templates/ # 网页模板HTML │ └── index.html └── weather.db # SQLite数据库文件核心API路由示例 (app.py)from flask import Flask, render_template, jsonify, request import sqlite3 from datetime import datetime, timedelta app Flask(__name__) app.config.from_pyfile(‘config.py‘) # 从配置文件加载配置 def get_db_connection(): 获取数据库连接简单示例生产环境建议用连接池 conn sqlite3.connect(app.config[‘DATABASE‘]) # 设置返回字典格式的游标方便处理 conn.row_factory sqlite3.Row return conn app.route(‘/‘) def index(): 首页渲染主可视化页面 return render_template(‘index.html‘) app.route(‘/api/realtime‘) def get_realtime_data(): API获取所有城市的最新实时天气数据 conn get_db_connection() # 获取每个城市最新的一条记录 cursor conn.execute(‘‘‘ SELECT city, temperature, humidity, wind_speed, weather_condition, data_time FROM realtime_weather WHERE data_time (SELECT MAX(data_time) FROM realtime_weather WHERE city rw.city) GROUP BY city ORDER BY city ‘‘‘) # 另一种更高效的写法是使用窗口函数但SQLite版本需支持 rows cursor.fetchall() conn.close() # 将结果转换为字典列表 data [dict(row) for row in rows] return jsonify({‘code‘: 0, ‘msg‘: ‘success‘, ‘data‘: data}) app.route(‘/api/history‘) def get_history_data(): API获取指定城市、时间范围的历史数据 city request.args.get(‘city‘, ‘济南‘) start_date request.args.get(‘start‘, (datetime.now() - timedelta(days30)).strftime(‘%Y-%m-%d‘)) end_date request.args.get(‘end‘, datetime.now().strftime(‘%Y-%m-%d‘)) conn get_db_connection() cursor conn.execute(‘‘‘ SELECT date, avg_temp, max_temp, min_temp, precipitation FROM history_weather WHERE city ? AND date BETWEEN ? AND ? ORDER BY date ASC ‘‘‘, (city, start_date, end_date)) rows cursor.fetchall() conn.close() dates [row[‘date‘] for row in rows] avg_temps [row[‘avg_temp‘] for row in rows] # ... 组织其他数据 chart_data { ‘dates‘: dates, ‘avg_temps‘: avg_temps, ‘precipitations‘: [row[‘precipitation‘] for row in rows] } return jsonify({‘code‘: 0, ‘data‘: chart_data}) if __name__ ‘__main__‘: app.run(debugTrue) # 生产环境务必关闭debug模式这个后端非常简洁。/api/realtime接口用于在地图上展示全省实时天气状况/api/history接口用于绘制某个城市的温度、降水变化折线图。前端通过Ajax调用这些接口获取JSON数据。3.4 前端可视化界面与ECharts集成前端页面 (templates/index.html) 是用户交互的界面。我使用Bootstrap进行快速布局并引入ECharts库进行绘图。页面布局要点导航栏显示系统标题可能包含数据更新时间等信息。侧边栏控制面板使用Bootstrap的col-md-3或col-lg-2。这里放置城市选择下拉菜单单选或多选。时间范围选择器用于历史数据查询。气象要素选择复选框温度、湿度、降水、风速。图表类型切换按钮地图/折线图/柱状图/热力图。一个“查询/更新”按钮。主内容区使用Bootstrap的col-md-9或col-lg-10。这里是一个大的div容器用于承载ECharts图表。多个图表容器可以设计选项卡Tabs来切换显示不同的图表比如一个Tab放全省实时地图一个Tab放单城市历史趋势图。集成ECharts的核心JavaScript逻辑在HTML中引入ECharts JS文件。为每个图表容器div初始化一个ECharts实例。编写函数通过Fetch API或jQuery的$.ajax调用我们写好的Flask API (/api/realtime,/api/history)。将API返回的JSON数据按照ECharts要求的格式进行转换。调用ECharts实例的setOption方法渲染图表。示例绘制山东省实时温度地图!— 在HTML中 — div id“realtimeMap“ style“width: 100%; height: 600px;“/div script src“https://cdn.jsdelivr.net/npm/echarts5.4.3/dist/echarts.min.js“/script script src“https://cdn.jsdelivr.net/npm/echarts/map/js/china.js“/script !— 引入中国地图 — script // 初始化图表 var mapChart echarts.init(document.getElementById(‘realtimeMap‘)); // 从后端API获取数据 fetch(‘/api/realtime‘) .then(response response.json()) .then(result { if (result.code 0) { var cityData result.data; // 将数据转换为ECharts地图需要的格式 // 格式: [{name: ‘济南‘, value: [117.000923, 36.675807, 25]}, ...] // 其中value数组为[经度 纬度 数值如温度] var mapSeriesData []; // 这里需要一个城市名到经纬度的映射字典 var cityCoords { ‘济南‘: [117.000923, 36.675807], ‘青岛‘: [120.355173, 36.082982], // ... 其他城市 }; cityData.forEach(item { var coord cityCoords[item.city]; if (coord) { mapSeriesData.push({ name: item.city, value: coord.concat([item.temperature]) // 拼接经纬度和温度值 }); } }); // 配置项 var option { title: { text: ‘山东省实时温度分布‘, left: ‘center‘ }, tooltip: { trigger: ‘item‘, formatter: function(params) { return params.name ‘: ‘ params.value[2] ‘°C‘; } }, visualMap: { min: 0, max: 40, left: ‘left‘, top: ‘bottom‘, text: [‘高温‘, ‘低温‘], calculable: true, inRange: { color: [‘#313695‘, ‘#4575b4‘, ‘#74add1‘, ‘#abd9e9‘, ‘#e0f3f8‘, ‘#ffffbf‘, ‘#fee090‘, ‘#fdae61‘, ‘#f46d43‘, ‘#d73027‘, ‘#a50026‘] } }, series: [{ name: ‘温度‘, type: ‘map‘, map: ‘山东‘, // 需要注册‘山东‘地图可以从china.js细化或使用geoJSON // 或者使用 ‘china‘ 地图然后通过regions指定山东区域 roam: false, // 禁止拖动缩放 zoom: 1.2, // 初始缩放级别 label: { show: true }, emphasis: { label: { show: true } }, data: mapSeriesData }] }; // 使用刚指定的配置项和数据显示图表。 mapChart.setOption(option); } }) .catch(error console.error(‘Error fetching realtime data:‘, error)); // 响应窗口大小变化 window.addEventListener(‘resize‘, function() { mapChart.resize(); }); /script注意事项ECharts自带的中国地图只到省级。如果你想展示山东省内更详细的地市边界需要额外引入或自己制作山东省的GeoJSON地图数据并通过echarts.registerMap(‘山东‘, geoJsonData)进行注册。这是让地图可视化更精确的关键一步。4. 系统部署与优化实践4.1 本地运行与测试在开发完成后首先在本地进行完整测试。环境准备确保安装了所有依赖库。可以使用pip freeze requirements.txt命令生成依赖列表。启动Flask开发服务器在项目根目录运行python app.py。Flask默认会在http://127.0.0.1:5000启动服务。手动运行爬虫先运行一次爬虫脚本 (python spider/realtime_spider.py)确保有数据存入数据库。访问页面打开浏览器访问http://127.0.0.1:5000检查页面是否正常加载图表是否成功渲染交互功能如选择城市、时间是否正常。测试API直接访问http://127.0.0.1:5000/api/realtime查看返回的JSON数据是否正确。4.2 生产环境部署以Linux服务器NginxGunicorn为例Flask自带的开发服务器性能弱且不安全绝不能用于生产环境。生产部署通常需要WSGI服务器反向代理服务器的组合。安装Gunicornpip install gunicorn使用Gunicorn启动应用# 在项目根目录下运行-w 指定worker进程数通常为CPU核心数*21 gunicorn -w 4 -b 0.0.0.0:8000 app:app此时应用运行在8000端口。配置Nginx作为反向代理安装Nginx:sudo apt install nginx编辑Nginx站点配置文件例如/etc/nginx/sites-available/weatherserver { listen 80; server_name your_domain.com; # 你的域名或服务器IP location / { # 将请求转发给运行在8000端口的Gunicorn proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 可选的静态文件服务如果静态文件量大可由Nginx直接处理效率更高 location /static { alias /path/to/your/weather_visualization_system/static; expires 30d; } }创建软链接并重启Nginxsudo ln -s /etc/nginx/sites-available/weather /etc/nginx/sites-enabled/ sudo nginx -t # 测试配置 sudo systemctl restart nginx使用进程管理工具如Supervisor确保Gunicorn进程在后台稳定运行崩溃后自动重启。; /etc/supervisor/conf.d/weather.conf [program:weather_app] command/path/to/your/venv/bin/gunicorn -w 4 -b 127.0.0.1:8000 app:app directory/path/to/your/weather_visualization_system useryour_username autostarttrue autorestarttrue stderr_logfile/var/log/weather/err.log stdout_logfile/var/log/weather/out.log然后启动Supervisorsudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start weather_app4.3 性能优化与功能扩展思路当系统跑起来后可以从以下几个方向进行优化和增强数据库优化索引如前所述在city,date,data_time字段上建立索引。查询优化避免SELECT *只查询需要的字段。对于复杂的历史趋势查询可以考虑对数据进行预聚合比如提前计算好“月平均温度”存入另一张表用空间换时间。连接池在生产环境中使用如DBUtils或SQLAlchemy自带的连接池管理数据库连接避免频繁创建和销毁连接的开销。前端优化图表按需加载初始只加载核心图表如地图当用户点击具体城市或切换Tab时再通过Ajax加载对应的详细图表数据。数据缓存对于实时性要求不高的历史数据查询结果可以在前端或后端如使用Redis进行短时间缓存减少数据库压力。防抖与节流对频繁触发的事件如时间选择器的变化使用防抖Debounce或节流Throttle技术避免短时间内向服务器发送大量重复请求。功能扩展多数据源融合除了爬取网站可以尝试接入一些免费的公共气象API注意调用限制将多个来源的数据进行对比和融合提高数据的准确性和可靠性。预警功能在后端设置规则如“连续3天最高温35度”或“单日降水量50mm”当爬取到的新数据触发规则时通过邮件、微信机器人等方式发送预警通知。移动端适配利用Bootstrap的响应式特性使页面在手机和平板上也能良好显示。用户交互增强增加图表联动比如点击地图上的城市右侧自动显示该城市的详细历史趋势图。5. 常见问题与排查技巧实录在实际开发和部署过程中我踩过不少坑这里总结几个典型问题和解决方法。5.1 爬虫模块常见问题问题1爬虫运行一段时间后突然报错ConnectionError或Timeout。原因目标网站对频繁访问的IP进行了临时封锁或限速。解决增加延时在请求之间加入随机延时模拟人类操作。time.sleep(random.uniform(1, 3))。使用代理IP池如果数据量巨大可以考虑使用付费或免费的代理IP轮流使用不同IP发起请求。设置User-Agent轮换准备一个User-Agent列表每次请求随机选择一个。处理HTTP状态码在代码中检查响应状态码如果是403/429等说明可能被反爬应暂停一段时间或更换策略。使用更“友好”的库对于动态加载的网站可以尝试使用requests-html或Selenium但速度会慢很多。问题2页面结构变化导致数据解析失败。原因天气网站改版HTML标签或class名称变了。解决健壮的解析逻辑不要依赖过于具体的标签路径。多用find_all配合文本内容过滤或使用更灵活的CSS选择器。异常捕获与日志将解析代码包裹在try...except中一旦解析失败记录详细的错误信息和当时的HTML片段可保存到文件便于后续分析。定期检查将爬虫作为定时任务运行并监控其日志。一旦发现连续解析失败及时通知维护者。5.2 Flask后端与数据库问题问题3前端请求API返回500错误查看Flask日志发现数据库被锁 (sqlite3.OperationalError: database is locked)。原因SQLite在同一时间只允许一个写入操作。当爬虫脚本正在写入数据特别是大量历史数据插入的同时Flask应用尝试查询数据库就可能发生锁冲突。解决读写分离这是根本解决方法。将数据库迁移到MySQL或PostgreSQL它们能更好地处理并发。优化爬虫写入对于SQLite爬虫脚本应使用批量插入 (executemany) 而不是逐条插入并尽快提交事务减少锁持有时间。重试机制在Flask的数据库查询函数中加入简单的重试逻辑遇到database is locked错误时等待一小段时间再重试。错峰运行安排爬虫在访问低谷期如凌晨运行。问题4ECharts地图不显示或显示不完整。原因 a. GeoJSON地图数据未正确注册或路径错误。 b. 地图容器div的宽度或高度为0。 c. 数据格式不符合ECharts地图series的要求。排查打开浏览器开发者工具F12查看“网络(Network)”选项卡确认地图JS文件和GeoJSON数据文件是否成功加载状态码200。查看“控制台(Console)”是否有JavaScript报错。检查ECharts初始化代码确保DOM元素ID正确并且在该元素已经加载到页面后再执行初始化通常把JS代码放在/body标签前或使用$(document).ready()。仔细核对series.data的格式确保是[{name: ‘城市‘, value: [经度 纬度 数值]}, ...]。5.3 部署与性能问题问题5服务器内存占用越来越高最终导致应用崩溃。原因可能是内存泄漏。常见于 a. Flask应用配置了debugTrue在生产环境运行。 b. 全局变量不当引用导致对象无法被垃圾回收。 c. 数据库连接未正确关闭。解决生产环境务必关闭Debug模式设置app.run(debugFalse)并通过环境变量或配置文件管理。使用连接池管理数据库连接确保每次查询后连接被正确放回池中。使用专业工具监控用top,htop或ps命令监控进程内存使用情况。使用gunicorn的--max-requests和--max-requests-jitter参数让worker进程在处理一定数量的请求后重启可以释放积累的内存碎片。代码审查检查是否有全局列表、字典在无限增长。问题6图表加载速度慢特别是历史数据查询时间范围很长时。原因一次性从数据库查询出数万条记录传输到前端前端JS再处理渲染耗时很长。解决后端分页或聚合不要一次性返回所有数据。修改/api/history接口支持分页参数page,size或者在前端选择时间范围时提供“按日/按月/按年”聚合的选项后端直接返回聚合后的数据如月平均值数据量会大大减少。前端数据懒加载对于折线图可以采用“滚动加载”或“缩放加载”的模式初始只加载最近一年的数据当用户放大查看某段细节时再动态加载该时间段的高精度数据。启用数据库查询缓存对于不常变动的历史数据可以在应用层如使用Flask-Caching扩展配合Redis或数据库层设置查询缓存。这个项目从构思到实现再到一步步优化让我对Python全栈开发的各个环节——从数据获取、持久化、后端服务到前端展示——有了更扎实的实践理解。最大的体会是一个看似简单的“数据可视化”需求背后串联起来的技术点非常丰富任何一个环节的细节处理不当都可能影响最终的用户体验。比如爬虫的稳定性决定了数据的“质”数据库的设计影响了查询的“速”而前端图表的选择和配置则决定了信息的“形”。把这三者流畅地结合起来才是这个项目真正的价值所在。如果你正在学习Python不妨也找一个自己感兴趣领域的数据动手搭建一个这样的系统过程中遇到的每一个问题都是最好的学习材料。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门