You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

35 KiB

电影数据抓取与分析项目 - 流程文档

本文档面向 Java 初学者,详细说明项目的整体架构、数据流向和各模块职责。


1. 项目概述

1.1 项目目标

从豆瓣电影 Top 250 抓取影片数据,进行清洗、存储和多维度统计分析,最终以 Web 界面和图表形式展示结果。

1.2 技术栈

技术 版本 用途
Java 11 主要开发语言
Spring Boot 2.7.12 Web 框架、依赖注入
Spring Data JPA - 数据持久化
H2 Database - 内存数据库
Jsoup 1.15.3 HTML 解析与网页爬取
JFreeChart 1.5.3 图表生成
Apache Commons Math 3.6.1 统计计算(相关性分析)
Jackson - JSON 序列化
Thymeleaf - Web 模板引擎
Caffeine - 缓存

1.3 两种运行模式

本项目支持两种独立的运行模式:

┌─────────────────────────────────────────────────────────────┐
│                       运行模式选择                           │
├─────────────────────────────────────────────────────────────┤
│  模式一:独立控制台模式 (Main.java)                           │
│  - 命令:mvn exec:java -Dexec.mainClass="com.movieratings.Main"│
│  - 输出:控制台表格 + JSON 文件 + PNG 图表                     │
│  - 适用:数据分析演示、离线运行                               │
├─────────────────────────────────────────────────────────────┤
│  模式二:Spring Boot Web 模式 (MovieRatingsApplication.java) │
│  - 命令:mvn spring-boot:run                                 │
│  - 输出:Web 界面 (http://localhost:8080)                    │
│  - 适用:交互式查询、在线展示                                 │
└─────────────────────────────────────────────────────────────┘

2. 系统架构

2.1 整体架构图

┌─────────────────────────────────────────────────────────────────────────┐
│                            项目整体架构                                  │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌──────────────┐     ┌──────────────┐     ┌──────────────┐            │
│  │  豆瓣电影     │────▶│ MovieCrawler │────▶│    Movie     │            │
│  │  Top 250     │     │   (爬虫层)    │     │   (模型层)   │            │
│  └──────────────┘     └──────────────┘     └──────────────┘            │
│                                                   │                     │
│                              ┌────────────────────┼────────────────────┐│
│                              ▼                    ▼                    ▼│
│                   ┌──────────────────┐  ┌───────────────┐  ┌──────────┐│
│                   │   DataAnalyzer   │  │ MovieRepository│  │JSON/CSV  ││
│                   │    (分析层)      │  │  (持久化层)    │  │  (导出)  ││
│                   └──────────────────┘  └───────────────┘  └──────────┘│
│                              │                    │                     │
│                              ▼                    ▼                     │
│                   ┌──────────────────┐  ┌───────────────┐              │
│                   │   ResultDisplay  │  │  MovieService │              │
│                   │    (展示层)      │  │   (业务层)    │              │
│                   └──────────────────┘  └───────────────┘              │
│                              │                    │                     │
│                              ▼                    ▼                     │
│                   ┌──────────────────┐  ┌───────────────┐              │
│                   │ PNG 图表 + 控制台 │  │DirectorController│            │
│                   │   (独立模式)     │  │   (Web 控制器)  │             │
│                   └──────────────────┘  └───────────────┘              │
│                                                   │                     │
│                                                   ▼                     │
│                                         ┌───────────────┐              │
│                                         │  Thymeleaf    │              │
│                                         │  (Web 页面)   │              │
│                                         └───────────────┘              │
└─────────────────────────────────────────────────────────────────────────┘

2.2 包结构

com.movieratings
├── Main.java                    # 独立模式入口
├── MovieRatingsApplication.java # Spring Boot 入口
├── DataInitializer.java         # 启动时数据初始化
│
├── model/                       # 数据模型层
│   ├── Movie.java               # 电影实体类
│   └── DirectorStats.java       # 导演统计 DTO
│
├── crawler/                     # 爬虫层
│   └── MovieCrawler.java        # 网页爬取与解析
│
├── analysis/                    # 分析层
│   └── DataAnalyzer.java        # 数据统计分析
│
├── display/                     # 展示层
│   └── ResultDisplay.java       # 控制台输出与图表生成
│
├── repository/                  # 持久化层
│   └── MovieRepository.java     # JPA 数据访问接口
│
├── service/                     # 业务逻辑层
│   └── MovieService.java        # 业务服务
│
└── controller/                  # Web 控制层
    └── DirectorController.java  # HTTP 请求处理

3. 详细数据流程

3.1 模式一:独立控制台流程

┌─────────────────────────────────────────────────────────────────────┐
│                    独立模式执行流程 (Main.java)                       │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. 启动                                                            │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  MovieCrawler.crawl(50)                  │                      │
│  │  ────────────────────────                │                      │
│  │  • 连接豆瓣 Top 250 页面                  │                      │
│  │  • 解析 HTML (Jsoup)                     │                      │
│  │  • 提取:排名、标题、评分、年份、导演等     │                      │
│  │  • 返回 List<Movie>                      │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  DataAnalyzer (多维度分析)                │                      │
│  │  ────────────────────                    │                      │
│  │  • analyzeRatings()      → 评分统计       │                      │
│  │  • countMoviesByRatingRange() → 评分分布  │                      │
│  │  • findMostReviewed()    → 热门电影       │                      │
│  │  • analyzeYearRatingCorrelation() → 相关性│                      │
│  │  • getTopDirectors()     → 导演排行       │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  ResultDisplay (结果展示)                 │                      │
│  │  ──────────────────                      │                      │
│  │  • printMoviesTable()    → 控制台表格     │                      │
│  │  • printDirectorRanking() → 导演排行榜    │                      │
│  │  • generateRatingChart() → 评分分布图     │                      │
│  │  • generateScatterPlot() → 年份评分散点图 │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  数据导出                                 │                      │
│  │  ────────                                │                      │
│  │  • saveAsJson() → movies_data.json       │                      │
│  │  • exportToCSV() → movies_analysis.csv   │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  程序结束                                                            │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

3.2 模式二:Spring Boot Web 流程

┌─────────────────────────────────────────────────────────────────────┐
│                 Spring Boot Web 模式执行流程                         │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  1. 应用启动                                                         │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  MovieRatingsApplication.main()          │                      │
│  │  ────────────────────────────            │                      │
│  │  • 启动 Spring 容器                       │                      │
│  │  • 初始化 H2 内存数据库                    │                      │
│  │  • 配置 Caffeine 缓存                     │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  DataInitializer.run() (CommandLineRunner)│                      │
│  │  ──────────────────────────────          │                      │
│  │  • 调用 MovieCrawler.crawl(100)          │                      │
│  │  • 设置部分作品类型(电影/电视剧/纪录片)   │                      │
│  │  • 调用 MovieService.refreshData()       │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  MovieService.refreshData()              │                      │
│  │  ─────────────────────                   │                      │
│  │  • movieRepository.deleteAll()           │                      │
│  │  • movieRepository.saveAll(movies)       │                      │
│  │  • 清除缓存                              │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  应用就绪,等待 HTTP 请求                                            │
│                                                                     │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  2. 用户请求处理                                                     │
│                                                                     │
│  浏览器访问 http://localhost:8080/directors                         │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  DirectorController.showDirectorRankings()│                      │
│  │  ────────────────────────────────        │                      │
│  │  • 接收参数:name, type, page, size       │                      │
│  │  • 调用 MovieService.getDirectorRankings()│                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  MovieService.getDirectorRankings()      │                      │
│  │  ────────────────────────────            │                      │
│  │  • 检查缓存 (Caffeine)                   │                      │
│  │  • 缓存未命中 → 调用 Repository          │                      │
│  │  • 返回 Page<DirectorStats>              │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  MovieRepository.findDirectorRankings()  │                      │
│  │  ────────────────────────────────        │                      │
│  │  • 执行 JPQL 聚合查询                     │                      │
│  │  • GROUP BY director                     │                      │
│  │  • 返回导演统计数据                       │                      │
│  └──────────────────────────────────────────┘                      │
│     │                                                               │
│     ▼                                                               │
│  ┌──────────────────────────────────────────┐                      │
│  │  Thymeleaf 模板渲染                      │                      │
│  │  ────────────────                        │                      │
│  │  • director_rankings.html               │                      │
│  │  • 返回 HTML 页面给浏览器                 │                      │
│  └──────────────────────────────────────────┘                      │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

4. 核心模块详解

4.1 爬虫模块 (MovieCrawler)

职责:从豆瓣电影 Top 250 抓取数据

流程

┌────────────┐    ┌────────────┐    ┌────────────┐    ┌────────────┐
│ 构造请求URL │───▶│ 发送HTTP请求│───▶│ 解析HTML   │───▶│ 封装Movie  │
│ (分页参数)  │    │ (Jsoup)    │    │ (CSS选择器)│    │ 对象列表   │
└────────────┘    └────────────┘    └────────────┘    └────────────┘

关键代码

// 发送请求
Document doc = Jsoup.connect(url).userAgent(USER_AGENT).get();

// 解析数据
movie.setRank(Integer.parseInt(item.select(".pic em").text()));
movie.setTitle(item.select(".title").first().text());
movie.setRating(Double.parseDouble(item.select(".rating_num").text()));

数据提取字段

字段 提取方式
排名 .pic em
标题 .title
评分 .rating_num
海报 .pic img 的 src 属性
年份 正则匹配 \d{4}
导演 文本中提取 导演: xxx
国家 / 分割取倒数第二项
评价人数 正则匹配 ([\d,]+)人评价

4.2 数据模型 (Movie)

职责:定义电影数据的结构,使用 JPA 注解映射到数据库

类图

┌─────────────────────────────────────────┐
│              Movie                       │
├─────────────────────────────────────────┤
│ - id: Long           (主键, 自增)        │
│ - title: String      (标题)              │
│ - rating: double     (评分)              │
│ - releaseYear: int   (年份)              │
│ - rank: int          (排名)              │
│ - quote: String      (简评)              │
│ - director: String   (导演)              │
│ - reviewCount: int   (评价人数)          │
│ - country: String    (国家)              │
│ - boxOffice: double  (票房)              │
│ - type: String       (类型)              │
│ - posterUrl: String  (海报链接)          │
├─────────────────────────────────────────┤
│ + getter/setter 方法                    │
│ + toString(): String                    │
│ + equals(Object): boolean               │
│ + hashCode(): int                       │
└─────────────────────────────────────────┘

JPA 注解说明

  • @Entity — 标记为数据库实体
  • @Table(name = "movies") — 指定表名
  • @Id + @GeneratedValue — 主键自增策略

4.3 数据分析模块 (DataAnalyzer)

职责:对电影数据进行多维度统计分析

分析方法

┌──────────────────────────────────────────────────────────────────┐
│                      DataAnalyzer 方法                           │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│  analyzeRatings(List<Movie>)                                    │
│  └──▶ 返回 DoubleSummaryStatistics (平均值、最大值、最小值、计数)  │
│                                                                  │
│  countMoviesByRatingRange(List<Movie>)                          │
│  └──▶ 返回 Map<String, Long> (评分段 → 电影数量)                  │
│       评分段: 9.5-10.0, 9.0-9.4, 8.5-8.9, 8.5以下               │
│                                                                  │
│  findMostReviewed(List<Movie>, int n)                           │
│  └──▶ 返回评价人数最多的前 N 部电影                              │
│                                                                  │
│  analyzeYearRatingCorrelation(List<Movie>)                      │
│  └──▶ 返回 CorrelationResult (Pearson 相关系数 + 显著性检验)      │
│                                                                  │
│  getTopDirectors(List<Movie>, int topN)                         │
│  └──▶ 返回 List<DirectorStats> (按作品数排序的导演排行)           │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

内部类

  • DirectorStats — 导演统计结果(姓名、作品数、平均分、总票房)
  • CorrelationResult — 相关性分析结果(系数、p 值、显著性描述)

4.4 数据持久化层

MovieRepository 接口

┌─────────────────────────────────────────────────────────────────┐
│                  MovieRepository                                 │
│          extends JpaRepository<Movie, Long>                      │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  继承自 JpaRepository 的方法:                                   │
│  • save(Movie), saveAll(List<Movie>)                           │
│  • deleteAll(), deleteById(Long)                               │
│  • findById(Long), findAll()                                   │
│                                                                 │
│  自定义方法:                                                    │
│  • findDirectorRankings(name, type, pageable)                  │
│    └── JPQL 聚合查询,返回导演排行榜                            │
│  • findByDirector(String director)                             │
│    └── 按导演名查询作品列表                                     │
│  • findAllTypes()                                              │
│    └── 查询所有不同的作品类型                                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

JPQL 聚合查询示例

@Query("SELECT new com.movieratings.model.DirectorStats(" +
       "m.director, COUNT(m), MAX(m.posterUrl), AVG(m.rating), SUM(m.boxOffice)) " +
       "FROM Movie m " +
       "WHERE (:name IS NULL OR m.director LIKE %:name%) " +
       "GROUP BY m.director " +
       "ORDER BY COUNT(m) DESC")
Page<DirectorStats> findDirectorRankings(...);

4.5 业务服务层 (MovieService)

职责:封装业务逻辑,管理缓存和事务

方法说明

方法 功能 缓存策略
getDirectorRankings() 获取导演排行榜 @Cacheable 缓存 10 分钟
getAllTypes() 获取所有作品类型 @Cacheable 缓存
saveAll() 批量保存电影 @CacheEvict 清除缓存
refreshData() 清空并重新加载数据 @CacheEvict 清除缓存

4.6 Web 控制层 (DirectorController)

职责:处理 HTTP 请求,返回 Web 页面

路由映射

URL 方法 功能
GET / index() 重定向到导演排行
GET /directors showDirectorRankings() 导演排行榜页面
GET /director/{name} showDirectorMovies() 指定导演的作品列表

请求参数

GET /directors?name=张艺谋&type=电影&page=0&size=20

5. 数据流图

5.1 数据抓取流程

豆瓣服务器                    本地应用
    │                           │
    │  1. HTTP GET 请求         │
    │◀──────────────────────────│
    │                           │
    │  2. HTML 响应             │
    │──────────────────────────▶│
    │                           │
    │                      ┌────┴────┐
    │                      │ Jsoup   │
    │                      │ 解析    │
    │                      └────┬────┘
    │                           │
    │                      ┌────┴────┐
    │                      │ Movie   │
    │                      │ 对象    │
    │                      └────┬────┘
    │                           │
    │                      存入 List<Movie>

5.2 Web 请求处理流程

浏览器                       服务器
    │                           │
    │  GET /directors           │
    │──────────────────────────▶│
    │                      ┌────┴────┐
    │                      │Controller│
    │                      └────┬────┘
    │                           │
    │                      ┌────┴────┐
    │                      │ Service │
    │                      │ (检查缓存)│
    │                      └────┬────┘
    │                           │
    │                      ┌────┴────┐
    │                      │Repository│
    │                      │ (查询DB) │
    │                      └────┬────┘
    │                           │
    │                      ┌────┴────┐
    │                      │Thymeleaf│
    │                      │ 渲染    │
    │                      └────┬────┘
    │                           │
    │  HTML 响应                │
    │◀──────────────────────────│

6. 配置说明

6.1 数据库配置 (application.properties)

# H2 内存数据库
spring.datasource.url=jdbc:h2:mem:moviedb;DB_CLOSE_DELAY=-1
spring.h2.console.enabled=true   # 启用 H2 控制台

# JPA 配置
spring.jpa.hibernate.ddl-auto=update  # 自动创建表结构

6.2 缓存配置

spring.cache.type=caffeine
spring.cache.cache-names=directorRankings,movieTypes
spring.cache.caffeine.spec=expireAfterWrite=10m,maximumSize=100
  • 缓存 10 分钟后过期
  • 最大缓存 100 条记录

7. 运行指南

7.1 环境要求

  • JDK 11+
  • Maven 3.6+

7.2 运行命令

独立控制台模式

mvn clean compile exec:java -Dexec.mainClass="com.movieratings.Main"

Spring Boot Web 模式

mvn spring-boot:run

7.3 访问地址

功能 地址
导演排行榜 http://localhost:8080/directors
H2 数据库控制台 http://localhost:8080/h2-console
JDBC URL jdbc:h2:mem:moviedb

8. 输出文件说明

8.1 独立模式输出

文件 说明
movies_data.json 完整的电影数据 JSON
movies_analysis.csv 电影数据 CSV 表格
rating_distribution.png 评分分布柱状图
year_rating_scatter.png 年份-评分散点图

8.2 控制台输出示例

=== 电影数据抓取与分析项目开始 ===
正在抓取: https://movie.douban.com/top250?start=0&filter=
...

--- 电影抓取结果展示 (前 10 条展示) ---
--------------------------------------------------------------------------------------------------
| 排名 | 标题                 | 年份 | 评分 | 导演           | 评价人数   |
--------------------------------------------------------------------------------------------------
| 1    | 肖申克的救赎          | 1994 | 9.7  | 弗兰克·德拉邦特 | 2900000   |
| 2    | 霸王别姬             | 1993 | 9.6  | 陈凯歌         | 1900000   |
...

--- 基础统计分析报告 ---
总计分析电影数量: 50
平均评分: 8.92
最高评分: 9.70
最低评分: 8.50

--- 导演作品排行榜 (前 20) ---
------------------------------------------------------------------
| 导演                 | 作品数 | 平均分 | 总模拟票房      |
------------------------------------------------------------------
| 克里斯托弗·诺兰      | 7      | 8.9    | 1250000.00     |
| 斯蒂芬·斯皮尔伯格    | 6      | 8.7    | 980000.00      |
...

=== 项目执行完毕 ===

9. 扩展阅读

9.1 相关文档

  • OOP_封装_继承_多态.md — 项目中的面向对象概念详解
  • README.md — 项目基本信息
  • DEVELOPMENT.md — 开发日志

9.2 学习要点

  1. 爬虫技术:Jsoup 的使用、HTTP 请求、HTML 解析
  2. 数据持久化:JPA 实体映射、Repository 模式
  3. 业务分层:Controller → Service → Repository 架构
  4. 缓存机制:Caffeine 缓存的使用场景
  5. 数据分析:Stream API、统计计算、相关性分析
  6. 可视化:JFreeChart 图表生成

文档版本:1.0 更新日期:2026-04-09