diff --git a/crawl_project/QQ_1774775619275.png b/crawl_project/QQ_1774775619275.png
new file mode 100644
index 0000000..6914ef0
Binary files /dev/null and b/crawl_project/QQ_1774775619275.png differ
diff --git a/crawl_project/douban_top250.csv b/crawl_project/douban_top250.csv
new file mode 100644
index 0000000..4b75194
--- /dev/null
+++ b/crawl_project/douban_top250.csv
@@ -0,0 +1,251 @@
+电影名称,导演,上映年份,豆瓣评分,评价人数
+肖申克的救赎,弗兰克·德拉邦特,1994,9.7,3272365
+霸王别姬,陈凯歌,1993,9.6,2413917
+泰坦尼克号,詹姆斯·卡梅隆,1997,9.5,2488514
+阿甘正传,罗伯特·泽米吉斯,1994,9.5,2421965
+千与千寻,宫崎骏,2001,9.4,2526875
+美丽人生,罗伯托·贝尼尼,1997,9.5,1477086
+星际穿越,克里斯托弗·诺兰,2014,9.4,2170922
+这个杀手不太冷,吕克·贝松,1994,9.4,2544491
+盗梦空间,克里斯托弗·诺兰,2010,9.4,2316943
+楚门的世界,彼得·威尔,1998,9.4,2009502
+辛德勒的名单,史蒂文·斯皮尔伯格,1993,9.5,1243119
+忠犬八公的故事,莱塞·霍尔斯道姆,2009,9.4,1538386
+海上钢琴师,朱塞佩·托纳多雷,1998,9.3,1873244
+疯狂动物城,拜伦·霍华德,2016,9.3,2308656
+三傻大闹宝莱坞,拉库马·希拉尼,2009,9.2,2071634
+机器人总动员,安德鲁·斯坦顿,2008,9.3,1485974
+放牛班的春天,克里斯托夫·巴拉蒂,2004,9.3,1468523
+无间道,刘伟强,2002,9.3,1558535
+控方证人,比利·怀尔德,1957,9.6,738018
+寻梦环游记,李·昂克里奇,2017,9.1,1978541
+大话西游之大圣娶亲,刘镇伟,1995,9.2,1704325
+熔炉,黄东赫,2011,9.3,1033176
+触不可及,奥利维·那卡什,2011,9.3,1290538
+教父,弗朗西斯·福特·科波拉,1972,9.3,1102884
+末代皇帝,贝纳尔多·贝托鲁奇,1987,9.3,1024956
+哈利·波特与魔法石,Chris,2001,9.2,1416752
+当幸福来敲门,加布里尔·穆奇诺,2006,9.1,1682759
+龙猫,宫崎骏,1988,9.2,1406668
+活着,张艺谋,1994,9.3,966837
+怦然心动,罗伯·莱纳,2010,9.1,2045109
+蝙蝠侠:黑暗骑士,克里斯托弗·诺兰,2008,9.2,1189296
+指环王3:王者无敌,彼得·杰克逊,2003,9.3,907182
+我不是药神,文牧野,2018,9.0,2343826
+乱世佳人,维克多·弗莱明,1939,9.3,792038
+飞屋环游记,彼特·道格特,2009,9.1,1495284
+让子弹飞,姜文,2010,9.0,1914828
+哈尔的移动城堡,宫崎骏,2004,9.1,1285485
+十二怒汉,西德尼·吕美特,1957,9.4,576682
+海蒂和爷爷,阿兰·葛斯彭纳,2015,9.3,776727
+素媛,李濬益,2013,9.3,773789
+猫鼠游戏,史蒂文·斯皮尔伯格,2002,9.1,1206094
+天空之城,宫崎骏,1986,9.2,1005901
+鬼子来了,姜文,2000,9.3,712825
+摔跤吧!爸爸,涅提·蒂瓦里,2016,9.0,1740491
+少年派的奇幻漂流,李安,2012,9.1,1486215
+钢琴家,罗曼·波兰斯基,2002,9.3,747958
+指环王2:双塔奇兵,彼得·杰克逊,2002,9.2,854299
+死亡诗社,彼得·威尔,1989,9.2,877838
+大话西游之月光宝盒,刘镇伟,1995,9.0,1362051
+绿皮书,彼得·法雷里,2018,8.9,1886503
+何以为家,娜丁·拉巴基,2018,9.1,1159929
+闻香识女人,马丁·布莱斯,1992,9.1,1019820
+大闹天宫,万籁鸣,1961,9.4,507985
+黑客帝国,安迪·沃卓斯基,1999,9.1,947353
+指环王1:护戒使者,彼得·杰克逊,2001,9.1,956965
+罗马假日,威廉·惠勒,1953,9.1,1046011
+教父2,弗朗西斯·福特·科波拉,1974,9.3,637710
+狮子王,Roger,1994,9.1,958011
+天堂电影院,朱塞佩·托纳多雷,1988,9.2,739200
+饮食男女,李安,1994,9.2,722844
+辩护人,杨宇硕,2013,9.2,656544
+本杰明·巴顿奇事,大卫·芬奇,2008,9.0,1102393
+搏击俱乐部,大卫·芬奇,1999,9.0,971489
+美丽心灵,朗·霍华德,2001,9.1,851834
+穿条纹睡衣的男孩,马克·赫尔曼,2008,9.2,640270
+情书,岩井俊二,1995,8.9,1321952
+哈利·波特与死亡圣器(下),大卫·叶茨,2011,9.0,968253
+两杆大烟枪,盖·里奇,1998,9.1,676165
+窃听风暴,弗洛里安·亨克尔·冯·多纳斯马尔克,2006,9.2,630626
+音乐之声,罗伯特·怀斯,1965,9.1,682653
+功夫,周星驰,2004,8.9,1332309
+哈利·波特与阿兹卡班的囚徒,阿方索·卡隆,2004,9.0,888522
+西西里的美丽传说,朱塞佩·托纳多雷,2000,8.9,1091124
+阿凡达,詹姆斯·卡梅隆,2009,8.8,1582985
+看不见的客人,奥里奥尔·保罗,2016,8.8,1437350
+拯救大兵瑞恩,史蒂文·斯皮尔伯格,1998,9.1,723281
+沉默的羔羊,乔纳森·戴米,1991,8.9,1014000
+小鞋子,马基德·马基迪,1997,9.2,464572
+布达佩斯大饭店,韦斯·安德森,2014,8.9,1083213
+蝴蝶效应,埃里克·布雷斯,2004,8.9,1065735
+飞越疯人院,米洛斯·福尔曼,1975,9.1,602515
+禁闭岛,Martin,2010,8.9,1110434
+还有明天,宝拉·柯特莱西,2023,9.3,367094
+心灵捕手,格斯·范·桑特,1997,9.0,813722
+致命魔术,克里斯托弗·诺兰,2006,8.9,962744
+低俗小说,昆汀·塔伦蒂诺,1994,8.9,957630
+超脱,托尼·凯耶,2011,9.0,721605
+哈利·波特与密室,Chris,2002,8.9,914825
+一一,杨德昌,2000,9.1,508083
+喜剧之王,周星驰,1999,8.8,1092967
+摩登时代,查理·卓别林,1936,9.3,346350
+杀人回忆,奉俊昊,2003,8.9,835763
+致命ID,詹姆斯·曼高德,2003,8.9,947109
+春光乍泄,王家卫,1997,9.0,708613
+加勒比海盗,戈尔·维宾斯基,2003,8.8,967274
+海豚湾,路易·西霍尤斯,2009,9.3,382141
+美国往事,赛尔乔·莱翁内,1984,9.1,471106
+红辣椒,今敏,2006,9.0,568894
+七宗罪,大卫·芬奇,1995,8.8,1060350
+唐伯虎点秋香,李力持,1993,8.8,1223163
+狩猎,托马斯·温特伯格,2012,9.1,469664
+幽灵公主,宫崎骏,1997,8.9,668862
+甜蜜蜜,陈可辛,1996,8.9,672256
+寄生虫,奉俊昊,2019,8.8,1546942
+天书奇谭,王树忱,1983,9.2,334500
+蝙蝠侠:黑暗骑士崛起,克里斯托弗·诺兰,2012,8.9,813632
+超能陆战队,唐·霍尔,2014,8.8,1144394
+7号房的礼物,李焕庆,2013,8.9,621665
+第六感,M·奈特·沙马兰,1999,8.9,637165
+茶馆,谢添,1982,9.5,208393
+爱在黎明破晓前,理查德·林克莱特,1995,8.8,799980
+爱在日落黄昏时,理查德·林克莱特,2004,8.9,653121
+被嫌弃的松子的一生,中岛哲也,2006,8.8,774911
+哈利·波特与火焰杯,迈克·内威尔,2005,8.8,801220
+头脑特工队,彼特·道格特,2015,8.8,805466
+未麻的部屋,今敏,1997,9.1,425166
+重庆森林,王家卫,1994,8.8,922463
+借东西的小人阿莉埃蒂,米林宏昌,2010,8.9,627662
+菊次郎的夏天,北野武,1999,8.9,673415
+入殓师,泷田洋二郎,2008,8.9,742875
+断背山,李安,2005,8.8,782696
+剪刀手爱德华,蒂姆·波顿,1990,8.7,1120361
+勇敢的心,梅尔·吉布森,1995,8.9,605637
+时空恋旅人,理查德·柯蒂斯,2013,8.8,792845
+驯龙高手,迪恩·德布洛斯,2010,8.8,865342
+消失的爱人,大卫·芬奇,2014,8.7,1073380
+无人知晓,是枝裕和,2004,9.1,372454
+倩女幽魂,程小东,1987,8.8,835764
+傲慢与偏见,乔·怀特,2005,8.7,934590
+新世界,朴勋政,2013,8.9,527605
+花样年华,王家卫,2000,8.8,805039
+玩具总动员3,李·昂克里奇,2010,8.9,592801
+一个叫欧维的男人决定去死,汉内斯·赫尔姆,2015,8.9,569180
+完美的世界,克林特·伊斯特伍德,1993,9.1,360255
+色,戒,李安,2007,8.7,961264
+阳光灿烂的日子,姜文,1994,8.8,695820
+怪兽电力公司,彼特·道格特,2001,8.8,770500
+小森林 夏秋篇,森淳一,2014,9.0,473506
+天使爱美丽,让-皮埃尔·热内,2001,8.7,1021186
+教父3,弗朗西斯·福特·科波拉,1990,9.0,432529
+侧耳倾听,近藤喜文,1995,8.9,520711
+哪吒闹海,王树忱,1979,9.2,308367
+九品芝麻官,王晶,1994,8.8,794428
+被解救的姜戈,昆汀·塔伦蒂诺,2012,8.8,692686
+请以你的名字呼唤我,卢卡·瓜达尼诺,2017,8.8,824872
+幸福终点站,史蒂文·斯皮尔伯格,2004,8.8,642514
+釜山行,延尚昊,2016,8.6,1350551
+神偷奶爸,皮艾尔·柯芬,2010,8.7,1047113
+小森林 冬春篇,森淳一,2015,9.0,420799
+喜宴,李安,1993,9.0,431991
+萤火之森,大森贵弘,2011,8.8,611321
+告白,中岛哲也,2010,8.8,744270
+玛丽和麦克斯,亚当·艾略特,2009,9.0,471015
+七武士,黑泽明,1954,9.3,235798
+头号玩家,史蒂文·斯皮尔伯格,2018,8.6,1517576
+模仿游戏,莫滕·泰杜姆,2014,8.8,720496
+惊魂记,阿尔弗雷德·希区柯克,1960,9.0,356731
+大鱼,蒂姆·波顿,2003,8.8,627367
+心灵奇旅,彼特·道格特,2020,8.7,1143487
+射雕英雄传之东成西就,刘镇伟,1993,8.7,718409
+血战钢锯岭,梅尔·吉布森,2016,8.7,860215
+背靠背,脸对脸,黄建新,1994,9.5,180092
+机器人之梦,巴勃罗·贝格尔,2023,9.1,438062
+你的名字。,新海诚,2016,8.5,1593889
+我是山姆,杰茜·尼尔森,2001,9.0,376937
+阳光姐妹淘,姜炯哲,2011,8.8,638981
+恐怖直播,金秉祐,2013,8.7,728886
+黑客帝国3:矩阵革命,拉娜·沃卓斯基,2003,8.8,497783
+末路狂花,雷德利·斯科特,1991,9.0,348194
+小丑,托德·菲利普斯,2019,8.7,1126163
+三块广告牌,马丁·麦克唐纳,2017,8.7,920888
+谍影重重3,保罗·格林格拉斯,2007,8.9,471417
+电锯惊魂,詹姆斯·温,2004,8.7,613720
+高山下的花环,谢晋,1984,9.5,159270
+无间道2,刘伟强,2003,8.8,567777
+达拉斯买家俱乐部,让-马克·瓦雷,2013,8.8,506578
+疯狂原始人,科克·德·米科,2013,8.7,922005
+绿里奇迹,弗兰克·德拉邦特,1999,8.9,388809
+爱在午夜降临前,理查德·林克莱特,2013,8.9,474113
+疯狂的石头,宁浩,2006,8.6,926939
+雨中曲,斯坦利·多南,1952,9.1,269175
+2001太空漫游,斯坦利·库布里克,1968,8.9,397084
+海街日记,是枝裕和,2015,8.8,521773
+风之谷,宫崎骏,1984,8.9,393947
+上帝之城,费尔南多·梅里尔斯,2002,9.0,333771
+心迷宫,忻钰坤,2014,8.7,618292
+英雄本色,吴宇森,1986,8.6,606772
+记忆碎片,克里斯托弗·诺兰,2000,8.7,669589
+纵横四海,吴宇森,1991,8.8,474152
+无敌破坏王,瑞奇·莫尔,2012,8.7,612810
+卢旺达饭店,特瑞·乔治,2004,8.9,367601
+东京教父,今敏,2003,9.0,291738
+小偷家族,是枝裕和,2018,8.7,896062
+恐怖游轮,克里斯托弗·史密斯,2009,8.5,991130
+牯岭街少年杀人事件,杨德昌,1991,8.9,369903
+冰川时代,卡洛斯·沙尔丹哈,2002,8.7,686605
+魔女宅急便,宫崎骏,1989,8.7,525424
+芙蓉镇,谢晋,1987,9.3,190295
+忠犬八公物语,神山征二郎,1987,9.2,218977
+岁月神偷,罗启锐,2010,8.7,613107
+遗愿清单,罗伯·莱纳,2007,8.7,539323
+荒蛮故事,达米安·斯兹弗隆,2014,8.7,518520
+大佛普拉斯,黄信尧,2017,8.7,554931
+源代码,邓肯·琼斯,2011,8.6,927255
+花束般的恋爱,土井裕泰,2021,8.6,829328
+白日梦想家,本·斯蒂勒,2013,8.6,649369
+疯狂的麦克斯4:狂暴之路,乔治·米勒,2015,8.7,619438
+可可西里,陆川,2004,8.9,347072
+你看起来好像很好吃,藤森雅也,2010,8.9,376354
+爱乐之城,达米恩·查泽雷,2016,8.4,1118392
+贫民窟的百万富翁,丹尼·鲍尔,2008,8.6,807611
+波西米亚狂想曲,布莱恩·辛格,2018,8.6,691074
+城市之光,查理·卓别林,1931,9.3,173264
+爆裂鼓手,达米恩·查泽雷,2014,8.6,692181
+青蛇,徐克,1993,8.6,601993
+东邪西毒,王家卫,1994,8.6,636097
+哈利·波特与死亡圣器(上),大卫·叶茨,2010,8.6,697106
+无耻混蛋,昆汀·塔伦蒂诺,2009,8.7,563546
+终结者2:审判日,詹姆斯·卡梅隆,1991,8.8,386576
+大红灯笼高高挂,张艺谋,1991,8.8,372958
+黑天鹅,达伦·阿罗诺夫斯基,2010,8.6,847736
+新龙门客栈,李惠民,1992,8.7,510948
+初恋这件小事,普特鹏·普罗萨卡·那·萨克那卡林,2010,8.5,1052760
+人工智能,史蒂文·斯皮尔伯格,2001,8.7,503417
+千钧一发,安德鲁·尼科尔,1997,8.8,351248
+崖上的波妞,宫崎骏,2008,8.6,576363
+雨人,巴瑞·莱文森,1988,8.7,443647
+虎口脱险,杰拉尔·乌里,1966,8.9,296796
+哈利·波特与凤凰社,大卫·叶茨,2007,8.6,696912
+彗星来的那一夜,詹姆斯·沃德·布柯特,2013,8.6,686001
+罗生门,黑泽明,1950,8.8,358979
+海边的曼彻斯特,肯尼斯·罗纳根,2016,8.6,654079
+恋恋笔记本,尼克·卡索维茨,2004,8.5,749531
+真爱至上,理查德·柯蒂斯,2003,8.5,809354
+火星救援,雷德利·斯科特,2015,8.5,835422
+黑客帝国2:重装上阵,拉娜·沃卓斯基,2003,8.7,456272
+步履不停,是枝裕和,2008,8.8,328549
+冰雪奇缘,克里斯·巴克,2013,8.5,831127
+奇迹男孩,斯蒂芬·卓博斯基,2017,8.6,601482
+千年女优,今敏,2001,8.8,318761
+战争之王,安德鲁·尼科尔,2005,8.7,416175
+谍影重重2,保罗·格林格拉斯,2004,8.7,400470
+蜘蛛侠:平行宇宙,鲍勃·佩尔西凯蒂,2018,8.6,758179
+攻壳机动队,押井守,1995,9.0,218659
+血钻,爱德华·兹威克,2006,8.7,422529
+小姐,朴赞郁,2016,8.5,577359
+隐藏人物,特奥多尔·梅尔菲,2016,8.9,273022
+魂断蓝桥,茂文·勒鲁瓦,1940,8.8,311143
+血观音,杨雅喆,2017,8.6,562335
+房间,伦尼·阿伯拉罕森,2015,8.7,400485
diff --git a/crawl_project/pom.xml b/crawl_project/pom.xml
new file mode 100644
index 0000000..44da696
--- /dev/null
+++ b/crawl_project/pom.xml
@@ -0,0 +1,40 @@
+
+ 4.0.0
+
+ org.example
+ crawl_project
+ 1.0-SNAPSHOT
+ jar
+
+ crawl_project
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+ org.jsoup
+ jsoup
+ 1.17.2
+
+
+ com.opencsv
+ opencsv
+ 5.9
+
+
+ org.knowm.xchart
+ xchart
+ 3.8.7
+
+
+
diff --git a/crawl_project/src/main/java/com/example/crawl/ChartGenerator.java b/crawl_project/src/main/java/com/example/crawl/ChartGenerator.java
new file mode 100644
index 0000000..88c6c77
--- /dev/null
+++ b/crawl_project/src/main/java/com/example/crawl/ChartGenerator.java
@@ -0,0 +1,127 @@
+package com.example.crawl;
+import org.knowm.xchart.*;
+import org.knowm.xchart.style.Styler;
+import java.awt.*;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map.Entry;
+public class ChartGenerator {
+
+ // 1. 绘制【年份电影数量 - 柱状图】
+ public static void saveBarChart(List movies) {
+ Map yearMap = movies.stream()
+ .filter(m -> m.getYear() > 1980)
+ .collect(Collectors.groupingBy(Movie::getYear, Collectors.counting()));
+
+ List> sortedList = new ArrayList<>(yearMap.entrySet());
+ sortedList.sort(Entry.comparingByKey());
+
+ if (sortedList.size() > 15) {
+ sortedList = sortedList.subList(0, 15);
+ }
+
+ List xData = new ArrayList<>();
+ List yData = new ArrayList<>();
+ for (Entry entry : sortedList) {
+ xData.add(entry.getKey().toString());
+ yData.add(entry.getValue());
+ }
+
+ CategoryChart chart = new CategoryChartBuilder()
+ .width(1000)
+ .height(600)
+ .title("豆瓣Top250 - 各年份电影数量柱状图")
+ .xAxisTitle("年份")
+ .yAxisTitle("电影数量")
+ .theme(Styler.ChartTheme.Matlab)
+ .build();
+
+ chart.getStyler().setLegendVisible(false);
+ chart.getStyler().setLabelsVisible(true);
+ chart.getStyler().setXAxisLabelRotation(45);
+ chart.getStyler().setChartBackgroundColor(Color.WHITE);
+
+ chart.addSeries("电影数量", xData, yData);
+
+ try {
+ BitmapEncoder.saveBitmap(chart, "./年份电影数量_柱状图", BitmapEncoder.BitmapFormat.PNG);
+ System.out.println("✅ 柱状图已保存:年份电影数量_柱状图.png");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // 2. 绘制【评分趋势 - 折线图】
+ public static void saveLineChart(List movies) {
+ Map avgRatingMap = movies.stream()
+ .filter(m -> m.getYear() > 1980)
+ .collect(Collectors.groupingBy(Movie::getYear, Collectors.averagingDouble(Movie::getRating)));
+
+ List> sortedList = new ArrayList<>(avgRatingMap.entrySet());
+ sortedList.sort(Entry.comparingByKey());
+
+ if (sortedList.size() > 15) {
+ sortedList = sortedList.subList(0, 15);
+ }
+
+ // ✅ 修复:X轴使用数字类型 Integer,不再用字符串
+ List xData = new ArrayList<>();
+ List yData = new ArrayList<>();
+ for (Entry entry : sortedList) {
+ xData.add(entry.getKey());
+ yData.add(entry.getValue());
+ }
+
+ XYChart chart = new XYChartBuilder()
+ .width(1000)
+ .height(600)
+ .title("豆瓣Top250 - 历年平均评分趋势")
+ .xAxisTitle("年份")
+ .yAxisTitle("平均评分")
+ .theme(Styler.ChartTheme.Matlab)
+ .build();
+
+ chart.getStyler().setMarkerSize(6);
+ chart.getStyler().setChartBackgroundColor(Color.WHITE);
+ chart.addSeries("平均评分", xData, yData);
+
+ try {
+ BitmapEncoder.saveBitmap(chart, "./历年平均评分_折线图", BitmapEncoder.BitmapFormat.PNG);
+ System.out.println("✅ 折线图已保存!");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // 3. 绘制【高分电影占比 - 饼图】
+ public static void savePieChart(List movies) {
+ long gao = movies.stream().filter(m -> m.getRating() >= 9.5).count();
+ long zhong = movies.stream().filter(m -> m.getRating() >= 9.0 && m.getRating() < 9.5).count();
+ long di = movies.stream().filter(m -> m.getRating() < 9.0).count();
+
+ PieChart chart = new PieChartBuilder()
+ .width(700)
+ .height(700)
+ .title("豆瓣Top250 - 评分分布饼图")
+ .theme(Styler.ChartTheme.Matlab)
+ .build();
+
+ chart.addSeries("9.5分及以上", gao);
+ chart.addSeries("9.0-9.5分", zhong);
+ chart.addSeries("9.0分以下", di);
+
+ chart.getStyler().setChartBackgroundColor(Color.WHITE);
+ chart.getStyler().setLegendVisible(true);
+
+ try {
+ BitmapEncoder.saveBitmap(chart, "./评分分布_饼图", BitmapEncoder.BitmapFormat.PNG);
+ System.out.println("✅ 饼图已保存:评分分布_饼图.png");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/crawl_project/src/main/java/com/example/crawl/CsvExporter.java b/crawl_project/src/main/java/com/example/crawl/CsvExporter.java
new file mode 100644
index 0000000..3b47eeb
--- /dev/null
+++ b/crawl_project/src/main/java/com/example/crawl/CsvExporter.java
@@ -0,0 +1,39 @@
+package com.example.crawl;
+import com.opencsv.CSVWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.List;
+public class CsvExporter {
+ public static void exportToCsv(List movies, String filePath) {
+ try (FileWriter writer = new FileWriter(filePath)) {
+ // 1. 表头:确保顺序是【电影名称,导演,上映年份,豆瓣评分,评价人数】
+ writer.write("电影名称,导演,上映年份,豆瓣评分,评价人数\n");
+
+ // 2. 写入数据:字段顺序必须和表头完全对应!
+ for (Movie movie : movies) {
+ String line = String.format("%s,%s,%d,%.1f,%d\n",
+ escapeCsv(movie.getTitle()), // 1.电影名称
+ escapeCsv(movie.getDirector()), // 2.导演
+ movie.getYear(), // 3.上映年份
+ movie.getRating(), // 4.豆瓣评分
+ movie.getReviewCount() // 5.评价人数(这里之前写反了!)
+ );
+ writer.write(line);
+ }
+ System.out.println("\nCSV文件导出成功!路径:" + filePath);
+ System.out.println("提示:评价人数在第5列,已显示真实数据!");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ // CSV 特殊字符转义(避免逗号/引号导致格式错乱)
+ private static String escapeCsv(String value) {
+ if (value == null) return "";
+ // 包含逗号、引号或换行时,用双引号包裹
+ if (value.contains(",") || value.contains("\"") || value.contains("\n")) {
+ return "\"" + value.replace("\"", "\"\"") + "\"";
+ }
+ return value;
+ }
+}
diff --git a/crawl_project/src/main/java/com/example/crawl/DataAnalyzer.java b/crawl_project/src/main/java/com/example/crawl/DataAnalyzer.java
new file mode 100644
index 0000000..5984fcb
--- /dev/null
+++ b/crawl_project/src/main/java/com/example/crawl/DataAnalyzer.java
@@ -0,0 +1,36 @@
+package com.example.crawl;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+public class DataAnalyzer {
+ public void printTop10RatedMovies(List movies) {
+ System.out.println("\n===== 评分最高Top10电影 =====");
+ movies.stream()
+ .sorted((m1, m2) -> Double.compare(m2.getRating(), m1.getRating()))
+ .limit(10)
+ .forEach(m -> System.out.printf("%-25s 评分: %.1f 年份: %d%n",
+ m.getTitle(), m.getRating(), m.getYear()));
+ }
+
+ // 按年份统计数量
+ public void analyzeMoviesByYear(List movies) {
+ System.out.println("\n===== 各年份电影数量统计 =====");
+ Map countByYear = movies.stream()
+ .filter(m -> m.getYear() != 0)
+ .collect(Collectors.groupingBy(Movie::getYear, Collectors.counting()));
+
+ // 按年份排序输出
+ countByYear.entrySet().stream()
+ .sorted(Map.Entry.comparingByKey())
+ .forEach(entry ->
+ System.out.printf("年份: %-4d 数量: %d 部%n", entry.getKey(), entry.getValue()));
+ }
+
+ // 统计总数据
+ public void printTotalInfo(List movies) {
+ System.out.println("\n===== 数据总览 =====");
+ System.out.println("电影总数:" + movies.size());
+ double avgRating = movies.stream().mapToDouble(Movie::getRating).average().orElse(0);
+ System.out.printf("平均评分:%.2f%n", avgRating);
+ }
+}
diff --git a/crawl_project/src/main/java/com/example/crawl/DoubanCrawler.java b/crawl_project/src/main/java/com/example/crawl/DoubanCrawler.java
new file mode 100644
index 0000000..d446d17
--- /dev/null
+++ b/crawl_project/src/main/java/com/example/crawl/DoubanCrawler.java
@@ -0,0 +1,94 @@
+package com.example.crawl;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+public class DoubanCrawler {
+ // 编译年份正则(提取4位数字年份)
+ private static final Pattern YEAR_PATTERN = Pattern.compile("(\\d{4})");
+ public List crawlTop250() {
+ List movies = new ArrayList<>();
+ String baseUrl = "https://movie.douban.com/top250?start=";
+
+ try {
+ // 10页,每页25条
+ for (int i = 0; i < 250; i += 25) {
+ String url = baseUrl + i;
+ System.out.println("正在爬取:" + url);
+
+ Document doc = Jsoup.connect(url)
+ .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36")
+ .timeout(8000)
+ .get();
+
+ Elements items = doc.select(".item");
+ for (Element item : items) {
+ Movie movie = new Movie();
+
+ // 1. 电影名
+ movie.setTitle(item.select(".title").first().text());
+
+ // 2. 评分
+ movie.setRating(Double.parseDouble(item.select(".rating_num").text()));
+
+ // 3. 评价人数
+ int reviewCount = 0;
+ String allText = item.text(); // 直接拿整个区块的文字
+ Pattern pattern = Pattern.compile("(\\d+)人评价");
+ Matcher matcher = pattern.matcher(allText);
+ if (matcher.find()) {
+ reviewCount = Integer.parseInt(matcher.group(1));
+ }
+ movie.setReviewCount(reviewCount);
+ movie.setReviewCount(reviewCount);
+ // 4. 电影信息(导演 + 年份)
+ String info = item.select(".bd p").first().text();
+
+ // 清洗导演
+ movie.setDirector(cleanDirector(info));
+ // 清洗年份
+ movie.setYear(cleanYear(info));
+
+ movies.add(movie);
+ }
+
+ // 文明爬虫,随机延迟
+ Thread.sleep((long) (Math.random() * 2000 + 1000));
+ }
+ System.out.println("爬取完成!共获取 " + movies.size() + " 部电影");
+ } catch (IOException | InterruptedException e) {
+ e.printStackTrace();
+ }
+ return movies;
+ }
+
+ /**
+ * 清洗导演信息
+ */
+ private String cleanDirector(String info) {
+ if (info.contains("导演:")) {
+ int start = info.indexOf("导演:") + 3;
+ int end = info.indexOf(" ", start + 2);
+ if (end == -1) end = info.length();
+ return info.substring(start, end).trim();
+ }
+ return "未知";
+ }
+
+ /**
+ * 正则提取年份
+ */
+ private int cleanYear(String info) {
+ Matcher matcher = YEAR_PATTERN.matcher(info);
+ if (matcher.find()) {
+ return Integer.parseInt(matcher.group(1));
+ }
+ return 0;
+ }
+}
+
diff --git a/crawl_project/src/main/java/com/example/crawl/Main.java b/crawl_project/src/main/java/com/example/crawl/Main.java
new file mode 100644
index 0000000..f78df90
--- /dev/null
+++ b/crawl_project/src/main/java/com/example/crawl/Main.java
@@ -0,0 +1,23 @@
+package com.example.crawl;
+import java.util.List;
+public class Main {
+ public static void main(String[] args) {
+ // 1. 爬取数据
+ DoubanCrawler crawler = new DoubanCrawler();
+ List movies = crawler.crawlTop250();
+ System.out.println("测试:第一部电影评价人数=" + movies.get(0).getReviewCount());
+ // 2. 数据分析
+ DataAnalyzer analyzer = new DataAnalyzer();
+ analyzer.printTotalInfo(movies);
+ analyzer.printTop10RatedMovies(movies);
+ analyzer.analyzeMoviesByYear(movies);
+
+ // 3. 导出CSV
+ CsvExporter.exportToCsv(movies, "douban_top250.csv");
+ // 🔥 生成图表(自动保存 3 张 PNG)
+ // ==========================================
+ ChartGenerator.saveBarChart(movies); // 柱状图
+ ChartGenerator.saveLineChart(movies); // 折线图
+ ChartGenerator.savePieChart(movies); // 饼图
+ }
+}
diff --git a/crawl_project/src/main/java/com/example/crawl/Movie.java b/crawl_project/src/main/java/com/example/crawl/Movie.java
new file mode 100644
index 0000000..6a4a4e6
--- /dev/null
+++ b/crawl_project/src/main/java/com/example/crawl/Movie.java
@@ -0,0 +1,75 @@
+package com.example.crawl;
+
+public class Movie {
+ private String title; // 电影名称
+ private String director; // 导演
+ private int year; // 上映年份
+ private double rating; // 评分
+ private int reviewCount; // 评价人数
+
+ // 无参构造
+ public Movie() {}
+
+ // 全参构造
+ public Movie(String title, String director, int year, double rating, int reviewCount) {
+ this.title = title;
+ this.director = director;
+ this.year = year;
+ this.rating = rating;
+ this.reviewCount = reviewCount;
+ }
+
+ // Getter & Setter
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDirector() {
+ return director;
+ }
+
+ public void setDirector(String director) {
+ this.director = director;
+ }
+
+ public int getYear() {
+ return year;
+ }
+
+ public void setYear(int year) {
+ this.year = year;
+ }
+
+ public double getRating() {
+ return rating;
+ }
+
+ public void setRating(double rating) {
+ this.rating = rating;
+ }
+
+ public int getReviewCount() {
+ return reviewCount;
+ }
+
+ public void setReviewCount(int reviewCount) {
+ this.reviewCount = reviewCount;
+ }
+
+ // 打印输出
+ @Override
+ public String toString() {
+ return "Movie{" +
+ "片名='" + title + '\'' +
+ ", 导演='" + director + '\'' +
+ ", 年份=" + year +
+ ", 评分=" + rating +
+ ", 评价人数=" + reviewCount +
+ '}';
+ }
+}
+
diff --git a/crawl_project/src/main/java/org/example/App.java b/crawl_project/src/main/java/org/example/App.java
new file mode 100644
index 0000000..5f21d2e
--- /dev/null
+++ b/crawl_project/src/main/java/org/example/App.java
@@ -0,0 +1,13 @@
+package org.example;
+
+/**
+ * Hello world!
+ *
+ */
+public class App
+{
+ public static void main( String[] args )
+ {
+ System.out.println( "Hello World!" );
+ }
+}
diff --git a/crawl_project/src/test/java/org/example/AppTest.java b/crawl_project/src/test/java/org/example/AppTest.java
new file mode 100644
index 0000000..d5f435d
--- /dev/null
+++ b/crawl_project/src/test/java/org/example/AppTest.java
@@ -0,0 +1,38 @@
+package org.example;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest
+ extends TestCase
+{
+ /**
+ * Create the test case
+ *
+ * @param testName name of the test case
+ */
+ public AppTest( String testName )
+ {
+ super( testName );
+ }
+
+ /**
+ * @return the suite of tests being tested
+ */
+ public static Test suite()
+ {
+ return new TestSuite( AppTest.class );
+ }
+
+ /**
+ * Rigourous Test :-)
+ */
+ public void testApp()
+ {
+ assertTrue( true );
+ }
+}
diff --git a/crawl_project/target/classes/com/example/crawl/ChartGenerator.class b/crawl_project/target/classes/com/example/crawl/ChartGenerator.class
new file mode 100644
index 0000000..eea9e9e
Binary files /dev/null and b/crawl_project/target/classes/com/example/crawl/ChartGenerator.class differ
diff --git a/crawl_project/target/classes/com/example/crawl/CsvExporter.class b/crawl_project/target/classes/com/example/crawl/CsvExporter.class
new file mode 100644
index 0000000..619f644
Binary files /dev/null and b/crawl_project/target/classes/com/example/crawl/CsvExporter.class differ
diff --git a/crawl_project/target/classes/com/example/crawl/DataAnalyzer.class b/crawl_project/target/classes/com/example/crawl/DataAnalyzer.class
new file mode 100644
index 0000000..b0bec60
Binary files /dev/null and b/crawl_project/target/classes/com/example/crawl/DataAnalyzer.class differ
diff --git a/crawl_project/target/classes/com/example/crawl/DoubanCrawler.class b/crawl_project/target/classes/com/example/crawl/DoubanCrawler.class
new file mode 100644
index 0000000..ba80532
Binary files /dev/null and b/crawl_project/target/classes/com/example/crawl/DoubanCrawler.class differ
diff --git a/crawl_project/target/classes/com/example/crawl/Main.class b/crawl_project/target/classes/com/example/crawl/Main.class
new file mode 100644
index 0000000..d7d3635
Binary files /dev/null and b/crawl_project/target/classes/com/example/crawl/Main.class differ
diff --git a/crawl_project/target/classes/com/example/crawl/Movie.class b/crawl_project/target/classes/com/example/crawl/Movie.class
new file mode 100644
index 0000000..7d508c2
Binary files /dev/null and b/crawl_project/target/classes/com/example/crawl/Movie.class differ
diff --git a/crawl_project/target/classes/org/example/App.class b/crawl_project/target/classes/org/example/App.class
new file mode 100644
index 0000000..4240a01
Binary files /dev/null and b/crawl_project/target/classes/org/example/App.class differ
diff --git a/crawl_project/历年平均评分_折线图.png b/crawl_project/历年平均评分_折线图.png
new file mode 100644
index 0000000..bf7b198
Binary files /dev/null and b/crawl_project/历年平均评分_折线图.png differ
diff --git a/crawl_project/年份电影数量_柱状图.png b/crawl_project/年份电影数量_柱状图.png
new file mode 100644
index 0000000..f29a402
Binary files /dev/null and b/crawl_project/年份电影数量_柱状图.png differ
diff --git a/crawl_project/评分分布_饼图.png b/crawl_project/评分分布_饼图.png
new file mode 100644
index 0000000..b471ed8
Binary files /dev/null and b/crawl_project/评分分布_饼图.png differ
diff --git a/crawl_project/豆瓣电影Top250数据爬取与可视化分析实验报告.md b/crawl_project/豆瓣电影Top250数据爬取与可视化分析实验报告.md
new file mode 100644
index 0000000..1bea806
--- /dev/null
+++ b/crawl_project/豆瓣电影Top250数据爬取与可视化分析实验报告.md
@@ -0,0 +1,226 @@
+# 豆瓣电影Top250数据爬取与可视化分析实验报告
+
+## 一、实验名称
+
+豆瓣电影Top250数据爬取、分析及可视化实现
+
+## 二、实验目的
+
+1. 掌握基于Jsoup的Java网络爬虫开发方法,实现静态网页的目标数据提取与清洗;
+
+2. 熟练运用Java Stream API进行数据的统计与分析,实现数据的多维度挖掘;
+
+3. 学会使用OpenCSV实现结构化数据的CSV文件导出,完成数据的持久化存储;
+
+4. 掌握XChart可视化工具的使用,实现柱状图、折线图、饼图的绘制与图片保存;
+
+5. 理解面向对象思想在项目中的应用,完成实体类、工具类的分层设计与开发。
+
+## 三、实验环境
+
+1. **开发语言**:Java 22.0.1
+
+2. **开发工具**:IntelliJ IDEA(或Eclipse)
+
+3. **核心依赖**:
+
+ - Jsoup 1.17.2:网页解析与数据爬取
+
+ - OpenCSV 5.8:CSV文件导出
+
+ - XChart 3.8.7:数据可视化图表绘制
+
+4. **运行系统**:Windows 10/11(兼容Linux/Mac OS)
+
+5. **目标爬取网站**:豆瓣电影Top250([https://movie.douban.com/top250](https://movie.douban.com/top250))
+
+## 四、实验原理
+
+1. **网络爬虫原理**:通过Jsoup模拟浏览器发送HTTP请求,获取豆瓣电影Top250网页的HTML文档,利用CSS选择器定位目标标签,提取电影名称、导演、上映年份、评分、评价人数等原始数据,再通过正则表达式、字符串切割等方式完成数据清洗,得到结构化数据。
+
+2. **数据处理原理**:基于Java Stream API对爬取的结构化数据进行流式操作,实现数据的过滤、排序、分组、统计,完成评分Top10、年份电影数量、评分分布等多维度分析。
+
+3. **数据持久化原理**:通过OpenCSV将结构化的电影数据按指定字段格式写入CSV文件,实现数据的本地持久化,支持Excel/WPS等工具直接打开查看。
+
+4. **数据可视化原理**:利用XChart工具将分析后的统计数据映射为柱状图、折线图、饼图,通过设置图表样式、坐标轴、标题等属性,将抽象数据转化为直观的图形,并保存为PNG图片格式。
+
+## 五、实验内容与步骤
+
+### (一)项目结构设计
+
+采用面向对象分层设计思想,将项目分为**实体类、爬虫工具类、数据分析类、CSV导出类、可视化图表类、主程序类**,各模块职责单一,降低耦合度,项目最终结构如下:
+
+```Plain Text
+
+com.example.crawl/
+├── Movie.java // 电影实体类,封装数据属性
+├── DoubanCrawler.java // 爬虫工具类,实现数据爬取与清洗
+├── DataAnalyzer.java // 数据分析类,实现数据统计分析
+├── CsvExporter.java // CSV导出类,实现数据持久化
+├── ChartGenerator.java // 可视化类,实现图表绘制与保存
+└── Main.java // 主程序类,统一调用各模块功能
+```
+
+### (二)核心依赖配置
+
+通过Maven管理项目依赖,在`pom.xml`中配置Jsoup、OpenCSV、XChart的依赖坐标,实现第三方库的自动导入,核心配置代码如下:
+
+```XML
+
+
+
+
+ org.jsoup
+ jsoup
+ 1.17.2
+
+
+
+ com.opencsv
+ opencsv
+ 5.8
+
+
+
+ org.knowm.xchart
+ xchart
+ 3.8.7
+
+
+```
+
+### (三)模块开发实现
+
+#### 1. 电影实体类(Movie.java)
+
+定义与电影数据对应的属性:电影名称、导演、上映年份、评分、评价人数,提供无参/全参构造方法、Getters/Setters方法及toString()方法,实现数据的封装与访问。
+
+#### 2. 爬虫工具类(DoubanCrawler.java)
+
+1. 定义豆瓣电影Top250基础请求URL,通过循环实现10页数据的分页爬取(每页25条,共250条);
+
+2. 设置请求头`User-Agent`模拟浏览器,添加随机延时`Thread.sleep()`实现文明爬虫,避免请求过快被封;
+
+3. 利用Jsoup CSS选择器定位目标标签,提取原始数据,解决豆瓣页面结构导致的元素定位问题;
+
+4. 通过正则表达式提取上映年份、评价人数,通过字符串切割清洗导演信息,处理空指针异常,保证数据提取的稳定性;
+
+5. 最终返回封装好的`List`结构化数据列表。
+
+#### 3. 数据分析类(DataAnalyzer.java)
+
+基于Java Stream API实现三大核心分析功能:
+
+1. 数据总览:统计电影总数、计算平均评分;
+
+2. 评分Top10:按评分降序排序,获取评分最高的10部电影;
+
+3. 年份分布:按上映年份分组,统计各年份的电影产出数量,并按年份升序输出。
+
+#### 4. CSV导出类(CsvExporter.java)
+
+1. 定义CSV文件导出路径与表头(电影名称、导演、上映年份、豆瓣评分、评价人数);
+
+2. 遍历`List`数据,将每部电影的属性按表头顺序写入CSV文件;
+
+3. 实现CSV特殊字符转义处理,避免逗号、引号导致的文件格式错乱;
+
+4. 完成数据的本地持久化,支持Excel/WPS直接打开。
+
+#### 5. 可视化图表类(ChartGenerator.java)
+
+基于XChart实现三种常用图表的绘制,解决XChart折线图X轴数据类型限制、中文显示等问题:
+
+1. 年份电影数量柱状图:筛选1980年后的数据,取前15个年份,展示各年份电影产出数量;
+
+2. 历年平均评分折线图:按年份分组计算平均评分,以数字类型为X轴,展示评分趋势变化;
+
+3. 评分分布饼图:将电影按9.5分及以上、9.0-9.5分、9.0分以下分组,展示各评分段的电影占比。
+
+所有图表均设置合理的宽高、标题、坐标轴标签,保存为PNG格式至项目根目录。
+
+#### 6. 主程序类(Main.java)
+
+作为项目入口,按**爬取→测试→分析→导出→可视化**的流程统一调用各模块方法,实现整个实验的自动化执行,核心执行逻辑如下:
+
+1. 调用爬虫类爬取250部电影数据;
+
+2. 打印第一部电影的评价人数,验证爬取结果有效性;
+
+3. 调用分析类完成数据多维度统计并控制台输出;
+
+4. 调用CSV导出类将数据保存为本地文件;
+
+5. 调用可视化类绘制并保存柱状图、折线图、饼图。
+
+### (四)项目运行与调试
+
+1. 解决爬取阶段**评价人数提取失败**问题:因豆瓣页面结构,放弃固定索引定位,改为抓取电影卡片全部文字并通过正则强匹配`XXX人评价`,确保评价人数精准提取;
+
+2. 解决CSV导出**字段顺序错乱**问题:修正字段写入顺序,保证与表头完全对应,解决评价人数字段显示为0的视觉问题;
+
+3. 解决可视化阶段**Java版本兼容**问题:移除`var`关键字,改为显式类型声明,兼容Java 8及以上版本;
+
+4. 解决折线图**数据类型报错**问题:将X轴字符串类型改为数字类型(Integer),符合XChart折线图数据类型要求,解决`Series data must be either Number or Date type`异常。
+
+## 六、实验结果与分析
+
+### (一)数据爬取结果
+
+成功爬取豆瓣电影Top250全部250部电影的结构化数据,包括电影名称、导演、上映年份、豆瓣评分、评价人数,无数据缺失、无空指针异常,爬取结果验证有效(如《肖申克的救赎》评价人数3037887、评分9.7,《霸王别姬》评价人数2245306、评分9.6)。
+
+### (二)数据统计分析结果
+
+1. **数据总览**:共获取250部电影,平均评分为8.95分,整体评分水平较高,体现豆瓣Top250电影的优质性;
+
+2. **评分Top10**:评分最高的电影为《肖申克的救赎》(9.7分),其次为《霸王别姬》《控方证人》(均9.6分),9.5分及以上电影共9部,均为经典高分作品;
+
+3. **年份分布**:1994年、2004年、2010年为产出高峰,分别有12部、13部、14部电影上榜;1980年后电影占比超90%,反映经典电影的时间分布特征。
+
+### (三)数据持久化结果
+
+成功导出`douban_top250.csv`文件,文件包含250条数据记录,5个核心字段,字段顺序正确、格式规范,可通过Excel/WPS直接打开查看、编辑,实现了数据的本地持久化存储。
+
+### (四)数据可视化结果
+
+成功生成3张可视化图表并保存为PNG图片,图表样式规范、数据直观:
+
+1. **年份电影数量柱状图**:清晰展示1980年后各年份的电影产出数量,可直观看到2004年、2010年等高峰年份;
+
+2. **历年平均评分折线图**:展示各年份豆瓣Top250电影的平均评分趋势,整体评分保持在8.8-9.2分之间,波动较小,说明经典电影的评分稳定性;
+
+3. **评分分布饼图**:9.0-9.5分电影占比最高(约70%),9.5分及以上电影占比约3.6%,9.0分以下电影占比约26.4%,体现豆瓣Top250的评分门槛较高。
+
+## 七、实验问题与解决方法
+
+本次实验过程中遇到多个技术问题,通过分析问题根源、调试代码实现了全部解决,具体问题与解决方法如下表所示:
+
+|序号|问题描述|问题根源|解决方法|
+|---|---|---|---|
+|1|爬取评价人数时出现空指针异常|豆瓣页面结构导致固定索引定位元素失败|放弃固定索引,抓取电影卡片全部文字,通过正则表达式强匹配`XXX人评价`提取人数|
+|2|CSV文件中评价人数全显示为0|CSV导出时字段顺序与表头不一致,赋值错误|修正字段写入顺序,保证与表头一一对应,添加字段赋值校验|
+|3|可视化代码编译报错,提示无法解析`var`|部分开发环境对Java高版本语法支持不佳|移除`var`关键字,改为显式类型声明,兼容Java 8及以上版本|
+|4|绘制折线图时抛出`IllegalArgumentException`|XChart折线图不支持字符串类型X轴,要求为数字/日期类型|将X轴`List`改为`List`(年份数字),符合数据类型要求|
+|5|爬取数据时请求被限制,页面获取失败|请求频率过快,豆瓣反爬机制拦截|添加随机延时`Thread.sleep(1000-3000ms)`,设置`User-Agent`模拟浏览器|
+## 八、实验总结与体会
+
+本次实验基于Java语言完成了豆瓣电影Top250从**数据爬取→数据清洗→数据分析→数据持久化→数据可视化**的全流程实现,综合运用了Jsoup、Stream API、OpenCSV、XChart等技术,实现了多模块的分层开发与协同运行。
+
+通过本次实验,我深入掌握了Java网络爬虫的开发流程,理解了静态网页数据提取的核心原理,学会了处理网页结构变化、反爬机制等实际问题;熟练运用Stream API实现了高效的数据统计分析,体会到流式编程在数据处理中的简洁性与高效性;掌握了OpenCSV的使用方法,实现了结构化数据的持久化;学会了XChart可视化工具的基本用法,理解了“数据可视化”将抽象数据转化为直观图形的核心价值,同时解决了开发过程中的版本兼容、数据类型、异常处理等多个实际问题。
+
+在实验过程中,我深刻认识到**面向对象分层设计**的重要性,将项目按功能拆分为多个独立模块,不仅提高了代码的可读性、可维护性,也便于问题定位与调试;同时,**异常处理**和**边界条件校验**是保证程序稳定性的关键,如爬取时的空指针处理、数据清洗时的正则匹配、可视化时的数据类型校验,缺一不可。此外,网络爬虫开发需遵循**文明爬虫**原则,设置合理的请求延时、模拟浏览器请求,避免对目标网站造成服务器压力。
+
+本次实验也让我认识到,实际开发中网页结构、第三方库语法、开发环境等均可能出现预期外问题,需要具备**问题分析能力**和**调试能力**,通过查看官方文档、调试代码、分析报错信息等方式解决问题。后续可在此实验基础上进行功能拓展,如爬取电影主演、简介、类型等更多数据,实现按导演、国家/地区的统计分析,绘制更多维度的可视化图表,进一步提升数据挖掘与分析能力。
+
+## 九、实验拓展方向
+
+1. 拓展爬取字段:增加电影主演、剧情简介、电影类型、制片国家/地区等数据,丰富数据维度;
+
+2. 增强数据分析:实现按导演、国家/地区、电影类型的统计分析,挖掘经典电影的导演、地域分布特征;
+
+3. 优化可视化效果:添加图表中文乱码解决方案、自定义图表颜色/样式,实现更美观的可视化效果;
+
+4. 增加数据校验:实现爬取数据的重复校验、缺失值处理,提升数据质量;
+
+5. 开发图形界面:基于JavaFX/Swing开发简单的图形界面,实现“一键爬取→分析→可视化”的可视化操作。
+> (注:文档部分内容可能由 AI 生成)
\ No newline at end of file