十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python爬虫实战:Flask+requests+Pandas猫眼电影数据采集与可视化分析

Python爬虫实战:Flask+requests+Pandas猫眼电影数据采集与可视化分析 这次我们来看一个完整的Python爬虫实战项目——基于FlaskrequestsPandas的猫眼电影数据爬取与可视化分析。这个项目不仅包含了数据采集的核心技术还涉及Web服务搭建和数据分析可视化适合想要系统学习Python爬虫和数据分析的开发者。项目最核心的价值在于一站式解决从数据获取到可视化展示的全流程。使用requests库进行网页数据抓取通过Pandas进行数据清洗和分析最后用Flask搭建Web服务展示可视化图表。整个项目代码结构清晰依赖简单在普通电脑上就能运行非常适合作为爬虫入门到进阶的实战案例。1. 核心能力速览能力项说明技术栈Python Flask requests Pandas Matplotlib数据来源猫眼电影网站公开数据主要功能电影数据爬取、数据清洗、数据分析、可视化图表生成硬件要求普通PC即可无需GPU内存4GB以上依赖管理pip安装标准库无特殊环境要求启动方式命令行启动Flask服务可视化展示Web页面图表展示支持多维度数据分析适合场景爬虫学习、数据分析练习、毕业设计、个人项目2. 适用场景与使用边界这个项目特别适合以下几类开发者Python初学者想要通过实战项目巩固requests、Pandas等库的使用需要完成课程设计或毕业设计的学生想要学习完整数据流程采集-清洗-分析-可视化的开发者需要快速搭建数据展示原型的项目人员使用边界提醒仅用于学习和技术交流目的爬取频率要合理避免对目标网站造成压力遵守网站robots.txt协议不得将爬取数据用于商业用途注意数据版权和个人隐私保护3. 环境准备与前置条件在开始项目之前需要确保开发环境满足以下要求3.1 系统要求Windows 10/11、macOS 10.14 或 Ubuntu 18.04至少4GB可用内存5GB可用磁盘空间3.2 Python环境Python 3.8或更高版本pip包管理工具最新版检查Python环境python --version pip --version3.3 必要依赖包项目需要以下Python库建议使用虚拟环境安装# 创建虚拟环境可选 python -m venv movie_env source movie_env/bin/activate # Linux/macOS movie_env\Scripts\activate # Windows # 安装核心依赖 pip install flask2.3.3 pip install requests2.31.0 pip install pandas2.0.3 pip install matplotlib3.7.2 pip install beautifulsoup44.12.24. 项目结构与代码实现4.1 项目目录结构movie_analysis/ ├── app.py # Flask主程序 ├── spider.py # 爬虫核心代码 ├── data_analysis.py # 数据分析模块 ├── templates/ # HTML模板 │ └── index.html ├── static/ # 静态资源 │ ├── css/ │ └── images/ ├── data/ # 数据存储 │ ├── raw_data.csv # 原始数据 │ └── cleaned_data.csv # 清洗后数据 └── charts/ # 生成图表4.2 爬虫核心代码实现首先实现数据爬取功能使用requests库模拟浏览器请求import requests import pandas as pd from bs4 import BeautifulSoup import time import random class MaoyanSpider: def __init__(self): self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8 } self.base_url https://maoyan.com/films def get_movie_list(self, offset0): 获取电影列表数据 url f{self.base_url}?offset{offset} try: response requests.get(url, headersself.headers, timeout10) response.raise_for_status() return self.parse_html(response.text) except requests.exceptions.RequestException as e: print(f请求失败: {e}) return [] def parse_html(self, html): 解析HTML页面提取电影信息 soup BeautifulSoup(html, html.parser) movies [] # 解析电影列表 movie_items soup.find_all(div, class_movie-item) for item in movie_items: try: movie {} # 提取电影名称 name_tag item.find(span, class_name) movie[name] name_tag.text if name_tag else 未知 # 提取评分 score_tag item.find(span, class_score) movie[score] float(score_tag.text) if score_tag else 0.0 # 提取上映时间 time_tag item.find(span, class_releasetime) movie[release_time] time_tag.text if time_tag else 未知 # 提取类型 type_tag item.find(span, class_type) movie[type] type_tag.text if type_tag else 未知 movies.append(movie) except Exception as e: print(f解析电影信息失败: {e}) continue return movies def crawl_multiple_pages(self, page_count10): 爬取多页数据 all_movies [] for page in range(page_count): print(f正在爬取第{page1}页...) movies self.get_movie_list(offsetpage*30) all_movies.extend(movies) # 添加随机延迟避免请求过快 time.sleep(random.uniform(1, 3)) return all_movies4.3 数据清洗与分析模块使用Pandas进行数据清洗和统计分析import pandas as pd import matplotlib.pyplot as plt import seaborn as sns from datetime import datetime class MovieAnalyzer: def __init__(self, data_filedata/raw_data.csv): self.df pd.read_csv(data_file) def clean_data(self): 数据清洗处理 # 去除重复数据 self.df self.df.drop_duplicates() # 处理缺失值 self.df[score] self.df[score].fillna(0) self.df[type] self.df[type].fillna(未知) # 提取年份信息 self.df[year] self.df[release_time].str.extract(r(\d{4})) self.df[year] pd.to_numeric(self.df[year], errorscoerce) # 评分分段 self.df[score_level] pd.cut(self.df[score], bins[0, 3, 6, 8, 10], labels[差评, 一般, 良好, 优秀]) return self.df def analyze_by_type(self): 按电影类型分析 type_analysis self.df.groupby(type).agg({ name: count, score: mean }).rename(columns{name: count, score: avg_score}) return type_analysis.sort_values(count, ascendingFalse) def analyze_by_year(self): 按年份分析 year_analysis self.df.groupby(year).agg({ name: count, score: mean }).rename(columns{name: count, score: avg_score}) return year_analysis.dropna() def create_visualizations(self): 创建可视化图表 plt.style.use(seaborn-v0_8) fig, axes plt.subplots(2, 2, figsize(15, 12)) # 1. 电影类型分布 type_counts self.df[type].value_counts().head(10) axes[0,0].pie(type_counts.values, labelstype_counts.index, autopct%1.1f%%) axes[0,0].set_title(电影类型分布) # 2. 评分分布直方图 axes[0,1].hist(self.df[score], bins20, alpha0.7, colorskyblue) axes[0,1].set_xlabel(评分) axes[0,1].set_ylabel(数量) axes[0,1].set_title(评分分布) # 3. 年度电影数量 year_counts self.df[year].value_counts().sort_index() axes[1,0].plot(year_counts.index, year_counts.values, markero) axes[1,0].set_xlabel(年份) axes[1,0].set_ylabel(电影数量) axes[1,0].set_title(年度电影数量趋势) # 4. 类型与评分关系 type_score self.df.groupby(type)[score].mean().sort_values(ascendingFalse).head(10) axes[1,1].barh(type_score.index, type_score.values, colorlightgreen) axes[1,1].set_xlabel(平均评分) axes[1,1].set_title(各类型电影平均评分) plt.tight_layout() plt.savefig(charts/movie_analysis.png, dpi300, bbox_inchestight) plt.close()4.4 Flask Web服务搭建创建Flask应用来展示数据分析结果from flask import Flask, render_template, jsonify import pandas as pd import os app Flask(__name__) app.route(/) def index(): 首页展示数据分析结果 try: # 加载分析结果 df pd.read_csv(data/cleaned_data.csv) # 基础统计信息 total_movies len(df) avg_score df[score].mean() max_score df[score].max() min_score df[score].min() # 类型统计 type_stats df[type].value_counts().head(5).to_dict() # 年度统计 year_stats df[year].value_counts().head(10).sort_index().to_dict() return render_template(index.html, total_moviestotal_movies, avg_scoreround(avg_score, 2), max_scoremax_score, min_scoremin_score, type_statstype_stats, year_statsyear_stats) except Exception as e: return f数据加载失败: {str(e)} app.route(/api/movie_data) def get_movie_data(): 提供电影数据API接口 df pd.read_csv(data/cleaned_data.csv) return jsonify(df.to_dict(orientrecords)) app.route(/api/type_analysis) def get_type_analysis(): 类型分析数据API df pd.read_csv(data/cleaned_data.csv) type_analysis df.groupby(type).agg({ name: count, score: mean }).rename(columns{name: count, score: avg_score}) return jsonify(type_analysis.reset_index().to_dict(orientrecords)) if __name__ __main__: # 确保目录存在 os.makedirs(data, exist_okTrue) os.makedirs(charts, exist_okTrue) os.makedirs(templates, exist_okTrue) os.makedirs(static/css, exist_okTrue) os.makedirs(static/images, exist_okTrue) app.run(host127.0.0.1, port5000, debugTrue)5. 完整执行流程5.1 数据爬取执行创建主执行文件main.pyfrom spider import MaoyanSpider from data_analysis import MovieAnalyzer import pandas as pd import os def main(): # 创建必要目录 os.makedirs(data, exist_okTrue) os.makedirs(charts, exist_okTrue) # 第一步爬取数据 print(开始爬取猫眼电影数据...) spider MaoyanSpider() movies spider.crawl_multiple_pages(page_count5) # 爬取5页数据 # 保存原始数据 df pd.DataFrame(movies) df.to_csv(data/raw_data.csv, indexFalse, encodingutf-8-sig) print(f爬取完成共获取{len(movies)}条数据) # 第二步数据清洗分析 print(开始数据清洗和分析...) analyzer MovieAnalyzer(data/raw_data.csv) cleaned_df analyzer.clean_data() cleaned_df.to_csv(data/cleaned_data.csv, indexFalse, encodingutf-8-sig) # 第三步生成可视化图表 print(生成可视化图表...) analyzer.create_visualizations() # 显示分析结果 type_analysis analyzer.analyze_by_type() year_analysis analyzer.analyze_by_year() print(\n 分析结果摘要 ) print(f电影总数: {len(cleaned_df)}) print(f平均评分: {cleaned_df[score].mean():.2f}) print(f电影类型数量: {cleaned_df[type].nunique()}) print(f数据时间跨度: {cleaned_df[year].min()} - {cleaned_df[year].max()}) print(\n热门电影类型TOP5:) print(type_analysis.head()) if __name__ __main__: main()5.2 启动Web服务创建启动脚本run.pyfrom app import app import webbrowser import threading import time def open_browser(): 自动打开浏览器 time.sleep(2) webbrowser.open(http://127.0.0.1:5000) if __name__ __main__: # 在独立线程中打开浏览器 threading.Thread(targetopen_browser).start() # 启动Flask应用 app.run(host127.0.0.1, port5000, debugTrue)6. 功能测试与效果验证6.1 爬虫功能测试测试爬虫的基本功能是否正常# test_spider.py import unittest from spider import MaoyanSpider class TestMaoyanSpider(unittest.TestCase): def setUp(self): self.spider MaoyanSpider() def test_get_movie_list(self): 测试获取电影列表功能 movies self.spider.get_movie_list(offset0) self.assertIsInstance(movies, list) if movies: # 如果有数据返回 self.assertIn(name, movies[0]) self.assertIn(score, movies[0]) def test_crawl_multiple_pages(self): 测试多页爬取功能 movies self.spider.crawl_multiple_pages(page_count2) self.assertTrue(len(movies) 60) # 2页最多60条数据 if __name__ __main__: unittest.main()6.2 数据分析验证验证数据分析结果的正确性# test_analysis.py import pandas as pd from data_analysis import MovieAnalyzer def test_analysis(): # 创建测试数据 test_data [ {name: 电影A, score: 8.5, type: 动作, release_time: 2023-01-01}, {name: 电影B, score: 7.2, type: 喜剧, release_time: 2023-02-01}, {name: 电影C, score: 9.1, type: 动作, release_time: 2023-03-01} ] df pd.DataFrame(test_data) df.to_csv(test_data.csv, indexFalse) analyzer MovieAnalyzer(test_data.csv) cleaned_df analyzer.clean_data() print(数据清洗验证:) print(f原始数据条数: {len(test_data)}) print(f清洗后数据条数: {len(cleaned_df)}) print(f是否有缺失值: {cleaned_df.isnull().sum().sum() 0}) type_analysis analyzer.analyze_by_type() print(\n类型分析验证:) print(type_analysis) test_analysis()7. Web界面设计与展示创建HTML模板文件templates/index.html!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title猫眼电影数据分析平台/title style body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; } .container { max-width: 1200px; margin: 0 auto; background: white; padding: 20px; border-radius: 8px; } .stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px; } .stat-card { background: #f8f9fa; padding: 20px; border-radius: 8px; text-align: center; } .chart-container { margin: 30px 0; } canvas { max-width: 100%; } /style /head body div classcontainer h1 猫眼电影数据分析平台/h1 !-- 统计信息卡片 -- div classstats-grid div classstat-card h3电影总数/h3 p stylefont-size: 2em; color: #007bff;{{ total_movies }}/p /div div classstat-card h3平均评分/h3 p stylefont-size: 2em; color: #28a745;{{ avg_score }}/p /div div classstat-card h3最高评分/h3 p stylefont-size: 2em; color: #dc3545;{{ max_score }}/p /div div classstat-card h3最低评分/h3 p stylefont-size: 2em; color: #ffc107;{{ min_score }}/p /div /div !-- 可视化图表区域 -- div classchart-container h2 数据分析图表/h2 img src{{ url_for(static, filenameimages/movie_analysis.png) }} alt电影数据分析图表 stylewidth: 100%; /div !-- 类型分布 -- div classchart-container h2 电影类型分布/h2 div idtypeChart/div /div !-- 数据表格 -- div classchart-container h2 电影数据列表/h2 div idmovieTable/div /div /div script // 使用Chart.js或ECharts进行动态图表展示 // 这里可以添加JavaScript代码来动态加载图表 /script /body /html8. 常见问题与排查方法8.1 爬虫相关问题问题现象可能原因排查方式解决方案请求返回403错误反爬虫机制触发检查请求头是否完整更新User-Agent添加Referer获取不到数据网页结构变化检查HTML解析逻辑更新选择器和解析方法连接超时网络问题或网站限制检查网络连接增加超时时间添加重试机制8.2 数据分析问题问题现象可能原因排查方式解决方案数据清洗失败数据格式不一致检查原始数据格式添加数据验证和异常处理图表生成失败依赖库版本问题检查matplotlib版本更新到兼容版本内存占用过高数据量过大监控内存使用分块处理数据使用生成器8.3 Flask服务问题问题现象可能原因排查方式解决方案端口被占用5000端口已被使用检查端口占用情况更换端口号模板找不到文件路径错误检查templates目录结构确保模板文件位置正确静态资源404静态文件配置错误检查static目录验证静态文件路径配置9. 性能优化与扩展建议9.1 爬虫性能优化# 优化后的爬虫类 class OptimizedMaoyanSpider(MaoyanSpider): def __init__(self): super().__init__() self.session requests.Session() # 使用会话保持 def crawl_with_retry(self, url, max_retries3): 带重试机制的爬取方法 for attempt in range(max_retries): try: response self.session.get(url, headersself.headers, timeout10) response.raise_for_status() return response.text except requests.exceptions.RequestException as e: if attempt max_retries - 1: raise e time.sleep(2 ** attempt) # 指数退避 def async_crawl(self, page_count10): 异步爬取提高效率 import asyncio import aiohttp async def fetch_page(session, page): url f{self.base_url}?offset{page*30} async with session.get(url, headersself.headers) as response: return await response.text() async def main(): async with aiohttp.ClientSession() as session: tasks [fetch_page(session, page) for page in range(page_count)] return await asyncio.gather(*tasks) return asyncio.run(main())9.2 数据分析扩展可以扩展的分析维度情感分析对电影评论进行情感倾向分析关联分析分析导演、演员与票房的关系预测模型基于历史数据预测电影评分实时监控定时爬取最新数据更新分析结果9.3 Web功能增强# 增强的Flask应用 app.route(/api/real_time_stats) def real_time_stats(): 实时统计接口 df pd.read_csv(data/cleaned_data.csv) # 实时计算各种统计指标 stats { total_movies: len(df), avg_score: round(df[score].mean(), 2), score_distribution: df[score_level].value_counts().to_dict(), latest_movies: df.nlargest(5, year).to_dict(records) } return jsonify(stats) app.route(/download/data) def download_data(): 数据下载接口 return send_file(data/cleaned_data.csv, as_attachmentTrue, download_namemaoyan_movies.csv)10. 项目部署与生产化10.1 使用Docker部署创建DockerfileFROM python:3.9-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . EXPOSE 5000 CMD [python, app.py]创建docker-compose.ymlversion: 3.8 services: movie-analysis: build: . ports: - 5000:5000 volumes: - ./data:/app/data - ./charts:/app/charts restart: unless-stopped10.2 添加日志监控import logging from logging.handlers import RotatingFileHandler def setup_logging(): 配置日志系统 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ RotatingFileHandler(app.log, maxBytes10485760, backupCount3), logging.StreamHandler() ] ) # 在Flask应用中使用 app.before_first_request def initialize(): setup_logging() logging.info(Flask应用启动完成)这个完整的猫眼电影数据分析项目展示了从数据采集到可视化展示的全流程每个环节都提供了详细的代码实现和说明。项目结构清晰代码可读性强非常适合作为Python爬虫和数据分析的学习案例。在实际使用中建议先小规模测试爬虫功能确保遵守网站的使用条款。数据分析部分可以根据实际需求进行定制扩展比如添加更多的统计维度或者更复杂的可视化图表。Flask Web服务提供了良好的数据展示界面也可以进一步扩展为完整的数据分析平台。
返回列表