From ad5d5ff3c4fb49415c0f1257eadb335a6a707b36 Mon Sep 17 00:00:00 2001 From: ZhengShiyi <1980003269@qq.com> Date: Tue, 19 May 2026 17:10:45 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4W10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- W10/CrawlerMain2/.gitignore | 29 ++ W10/CrawlerMain2/.idea/.gitignore | 10 + .../.idea/libraries/jcommon_1_0_24.xml | 9 + .../.idea/libraries/jfreechart_1_5_3.xml | 9 + .../.idea/libraries/jsoup_1_17_2.xml | 9 + .../.idea/libraries/kumo_core_1_12.xml | 9 + .../libraries/logback_classic_1_4_11.xml | 9 + .../.idea/libraries/logback_core_1_4_11.xml | 9 + .../.idea/libraries/slf4j_api_2_0_9.xml | 9 + W10/CrawlerMain2/.idea/misc.xml | 6 + W10/CrawlerMain2/.idea/modules.xml | 8 + W10/CrawlerMain2/CrawlerMain2.iml | 18 ++ W10/CrawlerMain2/src/CrawlerMain.java | 299 ++++++++++++++++++ W10/CrawlerMain2/src/logback.xml | 12 + W10/策略模式类结构检查.docx | Bin 0 -> 19053 bytes 15 files changed, 445 insertions(+) create mode 100644 W10/CrawlerMain2/.gitignore create mode 100644 W10/CrawlerMain2/.idea/.gitignore create mode 100644 W10/CrawlerMain2/.idea/libraries/jcommon_1_0_24.xml create mode 100644 W10/CrawlerMain2/.idea/libraries/jfreechart_1_5_3.xml create mode 100644 W10/CrawlerMain2/.idea/libraries/jsoup_1_17_2.xml create mode 100644 W10/CrawlerMain2/.idea/libraries/kumo_core_1_12.xml create mode 100644 W10/CrawlerMain2/.idea/libraries/logback_classic_1_4_11.xml create mode 100644 W10/CrawlerMain2/.idea/libraries/logback_core_1_4_11.xml create mode 100644 W10/CrawlerMain2/.idea/libraries/slf4j_api_2_0_9.xml create mode 100644 W10/CrawlerMain2/.idea/misc.xml create mode 100644 W10/CrawlerMain2/.idea/modules.xml create mode 100644 W10/CrawlerMain2/CrawlerMain2.iml create mode 100644 W10/CrawlerMain2/src/CrawlerMain.java create mode 100644 W10/CrawlerMain2/src/logback.xml create mode 100644 W10/策略模式类结构检查.docx diff --git a/W10/CrawlerMain2/.gitignore b/W10/CrawlerMain2/.gitignore new file mode 100644 index 0000000..f68d109 --- /dev/null +++ b/W10/CrawlerMain2/.gitignore @@ -0,0 +1,29 @@ +### IntelliJ IDEA ### +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/.gitignore b/W10/CrawlerMain2/.idea/.gitignore new file mode 100644 index 0000000..7d05e99 --- /dev/null +++ b/W10/CrawlerMain2/.idea/.gitignore @@ -0,0 +1,10 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml +# 基于编辑器的 HTTP 客户端请求 +/httpRequests/ +# 依赖于环境的 Maven 主目录路径 +/mavenHomeManager.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/W10/CrawlerMain2/.idea/libraries/jcommon_1_0_24.xml b/W10/CrawlerMain2/.idea/libraries/jcommon_1_0_24.xml new file mode 100644 index 0000000..cef0a8d --- /dev/null +++ b/W10/CrawlerMain2/.idea/libraries/jcommon_1_0_24.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/libraries/jfreechart_1_5_3.xml b/W10/CrawlerMain2/.idea/libraries/jfreechart_1_5_3.xml new file mode 100644 index 0000000..6fdf9d7 --- /dev/null +++ b/W10/CrawlerMain2/.idea/libraries/jfreechart_1_5_3.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/libraries/jsoup_1_17_2.xml b/W10/CrawlerMain2/.idea/libraries/jsoup_1_17_2.xml new file mode 100644 index 0000000..90ce41d --- /dev/null +++ b/W10/CrawlerMain2/.idea/libraries/jsoup_1_17_2.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/libraries/kumo_core_1_12.xml b/W10/CrawlerMain2/.idea/libraries/kumo_core_1_12.xml new file mode 100644 index 0000000..c74069d --- /dev/null +++ b/W10/CrawlerMain2/.idea/libraries/kumo_core_1_12.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/libraries/logback_classic_1_4_11.xml b/W10/CrawlerMain2/.idea/libraries/logback_classic_1_4_11.xml new file mode 100644 index 0000000..54a73cf --- /dev/null +++ b/W10/CrawlerMain2/.idea/libraries/logback_classic_1_4_11.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/libraries/logback_core_1_4_11.xml b/W10/CrawlerMain2/.idea/libraries/logback_core_1_4_11.xml new file mode 100644 index 0000000..fbdb3a1 --- /dev/null +++ b/W10/CrawlerMain2/.idea/libraries/logback_core_1_4_11.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/libraries/slf4j_api_2_0_9.xml b/W10/CrawlerMain2/.idea/libraries/slf4j_api_2_0_9.xml new file mode 100644 index 0000000..7c49634 --- /dev/null +++ b/W10/CrawlerMain2/.idea/libraries/slf4j_api_2_0_9.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/misc.xml b/W10/CrawlerMain2/.idea/misc.xml new file mode 100644 index 0000000..3653b1f --- /dev/null +++ b/W10/CrawlerMain2/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/.idea/modules.xml b/W10/CrawlerMain2/.idea/modules.xml new file mode 100644 index 0000000..8824534 --- /dev/null +++ b/W10/CrawlerMain2/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/CrawlerMain2.iml b/W10/CrawlerMain2/CrawlerMain2.iml new file mode 100644 index 0000000..e0317a0 --- /dev/null +++ b/W10/CrawlerMain2/CrawlerMain2.iml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/W10/CrawlerMain2/src/CrawlerMain.java b/W10/CrawlerMain2/src/CrawlerMain.java new file mode 100644 index 0000000..a6aafec --- /dev/null +++ b/W10/CrawlerMain2/src/CrawlerMain.java @@ -0,0 +1,299 @@ +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +// 1. 抽象策略接口(去掉public,因为文件名是CrawlerMain.java) +interface Crawler { + List startCrawl(); +} + +// 2. 抽象模板父类 +abstract class BaseCrawler implements Crawler { + protected final String baseUrl; + protected static final Logger logger = LoggerFactory.getLogger(BaseCrawler.class); + + protected BaseCrawler(String baseUrl) { + this.baseUrl = baseUrl; + } + + protected Document getPage(String url) throws Exception { + logger.info("正在请求页面:{}", url); + return Jsoup.connect(url) + .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)") + .timeout(15000) + .get(); + } + + @Override + public abstract List startCrawl(); +} + +// 3. 实体类 +class Movie { + private final String title; + private final String rating; + + public Movie(String title, String rating) { + this.title = title; + this.rating = rating; + } + + public String getTitle() { return title; } + public double getRatingDouble() { return Double.parseDouble(rating); } + public String getRating() { return rating; } + + @Override + public String toString() { + return "电影:《" + title + "》 | 评分:" + rating; + } +} + +class Hero { + private final String name; + public Hero(String name) { this.name = name; } + public String getName() { return name; } + @Override + public String toString() { return "英雄:" + name; } +} + +class Weather { + private final String province; + private final String city; + private final String temperature; + private final String condition; + + public Weather(String province, String city, String temperature, String condition) { + this.province = province; + this.city = city; + this.temperature = temperature; + this.condition = condition; + } + + public String getProvince() { return province; } + public String getCity() { return city; } + public String getTemperature() { return temperature; } + public String getCondition() { return condition; } + + @Override + public String toString() { + return "省份:" + province + " | 城市:" + city + " | 天气:" + condition + " | 温度:" + temperature; + } +} + +// 4. 具体策略类 +class MovieCrawler extends BaseCrawler { + private static final Logger logger = LoggerFactory.getLogger(MovieCrawler.class); + + public MovieCrawler() { + super("https://movie.douban.com/top250"); + } + + @Override + public List startCrawl() { + List list = new ArrayList<>(); + logger.info("开始爬取豆瓣电影Top250"); + try { + for (int i = 0; i < 250; i += 25) { + Document doc = getPage(baseUrl + "?start=" + i); + Elements items = doc.select(".item"); + for (Element e : items) { + String title = e.select(".title").first().text().split("/")[0].trim(); + String rating = e.select(".rating_num").text(); + list.add(new Movie(title, rating)); + } + Thread.sleep(1000); + } + logger.info("豆瓣电影爬取完成,共{}条数据", list.size()); + } catch (Exception e) { + logger.error("电影爬取失败", e); + } + return list; + } +} + +class HeroCrawler extends BaseCrawler { + private static final Logger logger = LoggerFactory.getLogger(HeroCrawler.class); + + public HeroCrawler() { + super("https://pvp.qq.com/web201605/herolist.shtml"); + } + + @Override + public List startCrawl() { + List list = new ArrayList<>(); + logger.info("开始爬取王者荣耀英雄数据"); + try { + Document doc = getPage(baseUrl); + Elements heros = doc.select("ul.herolist li a"); + for (Element h : heros) { + String name = h.text().trim(); + if (!name.isEmpty()) { + list.add(new Hero(name)); + } + } + logger.info("英雄爬取完成,共{}条数据", list.size()); + } catch (Exception e) { + logger.error("英雄爬取失败", e); + } + return list; + } +} + +class WeatherCrawler extends BaseCrawler { + private static final Logger logger = LoggerFactory.getLogger(WeatherCrawler.class); + + private static final String[][] cities = { + {"北京","北京","101010100"},{"上海","上海","101020100"},{"天津","天津","101030100"},{"重庆","重庆","101040100"}, + {"河北","石家庄","101090101"},{"山西","太原","101100101"},{"辽宁","沈阳","101070101"},{"吉林","长春","101060101"}, + {"黑龙江","哈尔滨","101050101"},{"江苏","南京","101190101"},{"浙江","杭州","101210101"},{"安徽","合肥","101220101"}, + {"福建","福州","101230101"},{"江西","南昌","101240101"},{"山东","济南","101120101"},{"河南","郑州","101180101"}, + {"湖北","武汉","101200101"},{"湖南","长沙","101250101"},{"广东","广州","101280101"},{"海南","海口","101310101"}, + {"四川","成都","101270101"},{"贵州","贵阳","101260101"},{"云南","昆明","101290101"},{"陕西","西安","101110101"}, + {"甘肃","兰州","101160101"},{"青海","西宁","101150101"},{"内蒙古","呼和浩特","101080101"},{"广西","南宁","101300101"}, + {"西藏","拉萨","101140101"},{"宁夏","银川","101170101"},{"新疆","乌鲁木齐","101130101"}, + {"香港","香港","101320101"},{"澳门","澳门","101330101"},{"台湾","台北","101340101"} + }; + + public WeatherCrawler() { + super("https://www.weather.com.cn/weather/"); + } + + @Override + public List startCrawl() { + List list = new ArrayList<>(); + logger.info("开始爬取全国天气数据"); + try { + for (String[] city : cities) { + String province = city[0]; + String cityName = city[1]; + String code = city[2]; + Document doc = getPage(baseUrl + code + ".shtml"); + Element today = doc.select("ul.t li").first(); + if (today != null) { + String temp = today.select(".tem").text(); + String wea = today.select(".wea").text(); + list.add(new Weather(province, cityName, temp, wea)); + } + Thread.sleep(500); + } + logger.info("天气数据爬取完成,共{}条数据", list.size()); + } catch (Exception e) { + logger.error("天气爬取失败", e); + } + return list; + } +} + +// 5. 策略上下文Context +class CrawlerContext { + private Crawler crawlerStrategy; + private static final Logger logger = LoggerFactory.getLogger(CrawlerContext.class); + + public void setCrawlerStrategy(Crawler crawlerStrategy) { + this.crawlerStrategy = crawlerStrategy; + } + + public List executeCrawl() { + if (crawlerStrategy == null) { + logger.error("未设置爬取策略"); + return new ArrayList<>(); + } + return crawlerStrategy.startCrawl(); + } +} + +// 6. 工具类 +final class DataUtil { + private static final String PATH = "D:\\Java爬虫\\"; + private static final Logger logger = LoggerFactory.getLogger(DataUtil.class); + + private DataUtil() {} + + public static void initFolder() { + File dir = new File(PATH); + if (!dir.exists()) { + boolean created = dir.mkdirs(); + if (created) { + logger.info("创建目录:{}", PATH); + } + } + } + + public static void saveText(String fileName, String content) throws IOException { + if (content == null || content.isBlank()) { + logger.warn("保存文件内容为空,跳过:{}", fileName); + return; + } + try (FileWriter fw = new FileWriter(PATH + fileName)) { + fw.write(content); + } + logger.info("文件保存成功:{}", fileName); + } + + public static void addAll(String fileName, List dataList) throws IOException { + if (dataList == null || dataList.isEmpty()) { + logger.warn("批量数据为空,跳过保存:{}", fileName); + return; + } + StringBuilder sb = new StringBuilder(); + dataList.forEach(item -> sb.append(item).append("\r\n")); + saveText(fileName, sb.toString()); + } + + public static void analyzeData(List movieList, List heroList) { + if (movieList == null || heroList == null) { + logger.error("分析数据列表为空"); + return; + } + logger.info("===== 执行数据分析 ====="); + double sum = 0; + for (Movie movie : movieList) { + sum += movie.getRatingDouble(); + } + double avg = sum / movieList.size(); + System.out.println("电影平均评分:" + String.format("%.2f", avg)); + System.out.println("8.5分以上电影数量:" + movieList.stream().filter(m -> m.getRatingDouble() >= 8.5).count()); + System.out.println("英雄总数量:" + heroList.size()); + logger.info("数据分析结束"); + } +} + +// 7. 主程序(必须和文件名一致,public) +public class CrawlerMain { + private static final Logger logger = LoggerFactory.getLogger(CrawlerMain.class); + + public static void main(String[] args) { + logger.info("===== 爬虫程序启动 ====="); + CrawlerContext context = new CrawlerContext(); + + context.setCrawlerStrategy(new MovieCrawler()); + List movieList = (List) context.executeCrawl(); + + context.setCrawlerStrategy(new HeroCrawler()); + List heroList = (List) context.executeCrawl(); + + context.setCrawlerStrategy(new WeatherCrawler()); + List weatherList = (List) context.executeCrawl(); + + try { + DataUtil.initFolder(); + DataUtil.addAll("电影数据.txt", movieList); + DataUtil.addAll("英雄数据.txt", heroList); + DataUtil.addAll("天气数据.txt", weatherList); + DataUtil.analyzeData(movieList, heroList); + logger.info("===== 全部任务执行完成 ====="); + System.out.println("✅ 数据已全部保存至 D:\\Java爬虫"); + } catch (Exception e) { + logger.error("程序运行异常", e); + } + } +} \ No newline at end of file diff --git a/W10/CrawlerMain2/src/logback.xml b/W10/CrawlerMain2/src/logback.xml new file mode 100644 index 0000000..0564736 --- /dev/null +++ b/W10/CrawlerMain2/src/logback.xml @@ -0,0 +1,12 @@ + + + + + %d{HH:mm:ss} %-5level - %msg%n + + + + + + + \ No newline at end of file diff --git a/W10/策略模式类结构检查.docx b/W10/策略模式类结构检查.docx new file mode 100644 index 0000000000000000000000000000000000000000..256ee9b04a4f854d6d436f6991227cfc6a229cf0 GIT binary patch literal 19053 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

@@71^#tQgnvRiKOL|CZG(ir!vEFL|DRw0fc(op z;Qx)=|6e)%)z|x7V-Oe}(_mXXKyo60(25|HXsk zulT=C&;AqdMfG3V|KG{lzk>fdsQ4!sjOQQVzYH<{%HglQ-hXl^