Browse Source

上传全部文件

main
myhomework 3 weeks ago
parent
commit
41aead1768
  1. BIN
      project/202506050221赵若岚期末实验报告1.docx
  2. BIN
      project/202506050221赵若岚期末实验报告2.docx
  3. BIN
      project/202506050221赵若岚期末实验报告3.docx
  4. BIN
      project/爬虫/GenerateHotJobs.class
  5. 107
      project/爬虫/GenerateHotJobs.java
  6. BIN
      project/爬虫/bin/com/jobmarket/crawler/ControllerApp.class
  7. BIN
      project/爬虫/bin/com/jobmarket/crawler/JobMarketCrawler.class
  8. BIN
      project/爬虫/bin/com/jobmarket/crawler/QuickCrawler.class
  9. BIN
      project/爬虫/bin/com/jobmarket/crawler/command/ClearCommand.class
  10. BIN
      project/爬虫/bin/com/jobmarket/crawler/command/Command.class
  11. BIN
      project/爬虫/bin/com/jobmarket/crawler/command/CommandFactory.class
  12. BIN
      project/爬虫/bin/com/jobmarket/crawler/command/CrawlCommand.class
  13. BIN
      project/爬虫/bin/com/jobmarket/crawler/command/DisplayCommand.class
  14. BIN
      project/爬虫/bin/com/jobmarket/crawler/command/StatisticsCommand.class
  15. BIN
      project/爬虫/bin/com/jobmarket/crawler/crawlers/HunanHumanResourcesCrawler.class
  16. BIN
      project/爬虫/bin/com/jobmarket/crawler/crawlers/JobCrawler.class
  17. BIN
      project/爬虫/bin/com/jobmarket/crawler/crawlers/LaborScienceInstituteCrawler.class
  18. BIN
      project/爬虫/bin/com/jobmarket/crawler/crawlers/NBSCrawler.class
  19. BIN
      project/爬虫/bin/com/jobmarket/crawler/exception/CrawlerException.class
  20. BIN
      project/爬虫/bin/com/jobmarket/crawler/exception/NetworkException.class
  21. BIN
      project/爬虫/bin/com/jobmarket/crawler/logging/ConsoleLogger.class
  22. BIN
      project/爬虫/bin/com/jobmarket/crawler/logging/LogLevel.class
  23. BIN
      project/爬虫/bin/com/jobmarket/crawler/logging/Logger.class
  24. BIN
      project/爬虫/bin/com/jobmarket/crawler/logging/LoggerFactory.class
  25. BIN
      project/爬虫/bin/com/jobmarket/crawler/model/JobData.class
  26. BIN
      project/爬虫/bin/com/jobmarket/crawler/repository/CSVJobDataRepository.class
  27. BIN
      project/爬虫/bin/com/jobmarket/crawler/repository/JobDataRepository.class
  28. BIN
      project/爬虫/bin/com/jobmarket/crawler/strategy/CrawlStrategy.class
  29. BIN
      project/爬虫/bin/com/jobmarket/crawler/strategy/HunanStrategy.class
  30. BIN
      project/爬虫/bin/com/jobmarket/crawler/strategy/LaborScienceStrategy.class
  31. BIN
      project/爬虫/bin/com/jobmarket/crawler/strategy/NBStrategy.class
  32. BIN
      project/爬虫/bin/com/jobmarket/crawler/strategy/StrategyFactory.class
  33. BIN
      project/爬虫/bin/com/jobmarket/crawler/utils/CSVWriter.class
  34. BIN
      project/爬虫/bin/com/jobmarket/crawler/utils/CrawlerUtils.class
  35. 37
      project/爬虫/sources.txt
  36. 223
      project/爬虫/src/main/java/com/jobmarket/crawler/ControllerApp.java
  37. 114
      project/爬虫/src/main/java/com/jobmarket/crawler/JobMarketCrawler.java
  38. 136
      project/爬虫/src/main/java/com/jobmarket/crawler/QuickCrawler.java
  39. 67
      project/爬虫/src/main/java/com/jobmarket/crawler/command/ClearCommand.java
  40. 30
      project/爬虫/src/main/java/com/jobmarket/crawler/command/Command.java
  41. 63
      project/爬虫/src/main/java/com/jobmarket/crawler/command/CommandFactory.java
  42. 119
      project/爬虫/src/main/java/com/jobmarket/crawler/command/CrawlCommand.java
  43. 67
      project/爬虫/src/main/java/com/jobmarket/crawler/command/DisplayCommand.java
  44. 80
      project/爬虫/src/main/java/com/jobmarket/crawler/command/StatisticsCommand.java
  45. 83
      project/爬虫/src/main/java/com/jobmarket/crawler/crawlers/HunanHumanResourcesCrawler.java
  46. 39
      project/爬虫/src/main/java/com/jobmarket/crawler/crawlers/JobCrawler.java
  47. 133
      project/爬虫/src/main/java/com/jobmarket/crawler/crawlers/LaborScienceInstituteCrawler.java
  48. 83
      project/爬虫/src/main/java/com/jobmarket/crawler/crawlers/NBSCrawler.java
  49. 36
      project/爬虫/src/main/java/com/jobmarket/crawler/exception/CrawlerException.java
  50. 47
      project/爬虫/src/main/java/com/jobmarket/crawler/exception/NetworkException.java
  51. 43
      project/爬虫/src/main/java/com/jobmarket/crawler/exception/ParseException.java
  52. 43
      project/爬虫/src/main/java/com/jobmarket/crawler/exception/StorageException.java
  53. 43
      project/爬虫/src/main/java/com/jobmarket/crawler/exception/StrategyException.java
  54. 41
      project/爬虫/src/main/java/com/jobmarket/crawler/exception/ValidationException.java
  55. 172
      project/爬虫/src/main/java/com/jobmarket/crawler/logging/ConsoleLogger.java
  56. 32
      project/爬虫/src/main/java/com/jobmarket/crawler/logging/LogLevel.java
  57. 37
      project/爬虫/src/main/java/com/jobmarket/crawler/logging/Logger.java
  58. 42
      project/爬虫/src/main/java/com/jobmarket/crawler/logging/LoggerFactory.java
  59. 165
      project/爬虫/src/main/java/com/jobmarket/crawler/model/JobData.java
  60. 179
      project/爬虫/src/main/java/com/jobmarket/crawler/repository/CSVJobDataRepository.java
  61. 64
      project/爬虫/src/main/java/com/jobmarket/crawler/repository/JobDataRepository.java
  62. 19
      project/爬虫/src/main/java/com/jobmarket/crawler/retry/RetryCallback.java
  63. 90
      project/爬虫/src/main/java/com/jobmarket/crawler/retry/RetryConfig.java
  64. 63
      project/爬虫/src/main/java/com/jobmarket/crawler/retry/RetryContext.java
  65. 110
      project/爬虫/src/main/java/com/jobmarket/crawler/retry/RetryTemplate.java
  66. 33
      project/爬虫/src/main/java/com/jobmarket/crawler/strategy/CrawlStrategy.java
  67. 62
      project/爬虫/src/main/java/com/jobmarket/crawler/strategy/HunanStrategy.java
  68. 181
      project/爬虫/src/main/java/com/jobmarket/crawler/strategy/LaborScienceStrategy.java
  69. 62
      project/爬虫/src/main/java/com/jobmarket/crawler/strategy/NBStrategy.java
  70. 63
      project/爬虫/src/main/java/com/jobmarket/crawler/strategy/StrategyFactory.java
  71. 67
      project/爬虫/src/main/java/com/jobmarket/crawler/utils/CSVWriter.java
  72. 209
      project/爬虫/src/main/java/com/jobmarket/crawler/utils/CrawlerUtils.java
  73. 36
      project/爬虫/原始人才市场数据.csv
  74. 3
      project/爬虫2/.vscode/settings.json
  75. 304
      project/爬虫2/new_universities.csv
  76. 54
      project/爬虫2/pom.xml
  77. 6
      project/爬虫2/run_university.bat
  78. 37
      project/爬虫2/src/main/java/com/example/Application.java
  79. 61
      project/爬虫2/src/main/java/com/example/Main.java
  80. 34
      project/爬虫2/src/main/java/com/example/cli/CliArgs.java
  81. 11
      project/爬虫2/src/main/java/com/example/cli/CliParser.java
  82. 36
      project/爬虫2/src/main/java/com/example/cli/DefaultCliParser.java
  83. 13
      project/爬虫2/src/main/java/com/example/command/Command.java
  84. 29
      project/爬虫2/src/main/java/com/example/command/CommandInvoker.java
  85. 54
      project/爬虫2/src/main/java/com/example/command/ExportCommand.java
  86. 41
      project/爬虫2/src/main/java/com/example/command/HelpCommand.java
  87. 58
      project/爬虫2/src/main/java/com/example/command/ListCommand.java
  88. 32
      project/爬虫2/src/main/java/com/example/controller/UniversityController.java
  89. 417
      project/爬虫2/src/main/java/com/example/data/UniversityData.java
  90. 76
      project/爬虫2/src/main/java/com/example/entity/University.java
  91. 149
      project/爬虫2/src/main/java/com/example/entity/UniversityImpl.java
  92. 47
      project/爬虫2/src/main/java/com/example/exception/SpiderException.java
  93. 14
      project/爬虫2/src/main/java/com/example/service/UniversityService.java
  94. 32
      project/爬虫2/src/main/java/com/example/service/UniversityServiceImpl.java
  95. 39
      project/爬虫2/src/main/java/com/example/strategy/ConsoleOutputStrategy.java
  96. 57
      project/爬虫2/src/main/java/com/example/strategy/CsvOutputStrategy.java
  97. 12
      project/爬虫2/src/main/java/com/example/strategy/OutputStrategy.java
  98. BIN
      project/爬虫2/target/classes/com/example/Application.class
  99. BIN
      project/爬虫2/target/classes/com/example/Main.class
  100. BIN
      project/爬虫2/target/classes/com/example/cli/CliArgs.class

BIN
project/202506050221赵若岚期末实验报告1.docx

Binary file not shown.

BIN
project/202506050221赵若岚期末实验报告2.docx

Binary file not shown.

BIN
project/202506050221赵若岚期末实验报告3.docx

Binary file not shown.

BIN
project/爬虫/GenerateHotJobs.class

Binary file not shown.

107
project/爬虫/GenerateHotJobs.java

@ -0,0 +1,107 @@
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class GenerateHotJobs {
// 热门岗位列表
private static final String[] HOT_JOBS = {
"人工智能工程师", "大数据分析师", "云计算架构师", "物联网工程师", "网络安全工程师",
"区块链开发工程师", "前端开发工程师", "后端开发工程师", "全栈开发工程师", "DevOps工程师",
"移动开发工程师", "数据科学家", "机器学习工程师", "算法工程师", "数据工程师",
"产品经理", "UI设计师", "UX设计师", "测试工程师", "运维工程师",
"网络工程师", "系统架构师", "数据库工程师", "嵌入式开发工程师", "游戏开发工程师",
"AR/VR开发工程师", "5G工程师", "芯片设计工程师", "量子计算工程师", "信息安全专家",
"网络安全分析师", "渗透测试工程师", "安全运维工程师", "安全架构师", "安全开发工程师",
"金融科技工程师", "量化交易工程师", "风险控制工程师", "金融分析师", "投资顾问",
"高级机械工程师", "电气工程师", "自动化工程师", "工业设计师", "制造工程师",
"光伏工程师", "风电工程师", "新能源工程师", "环保工程师", "可持续发展顾问",
"高级医生", "高级护理", "医学研究员", "制药工程师", "医疗设备工程师",
"高级教师", "教育顾问", "培训师", "课程设计师", "教育技术专家",
"市场营销经理", "品牌经理", "市场分析师", "营销策划师", "数字营销专家",
"销售经理", "客户关系经理", "商务拓展经理", "渠道经理", "销售顾问",
"物流管理师", "供应链经理", "采购经理", "仓储管理师", "物流分析师",
"人力资源经理", "招聘专员", "培训发展经理", "薪酬福利经理", "员工关系专员",
"财务经理", "注册会计师", "审计师", "税务师", "财务分析师",
"法律顾问", "律师", "合规专员", "知识产权专家", "法务经理"
};
// 行业列表
private static final String[] INDUSTRIES = {
"数字经济", "信息技术", "金融科技", "制造业", "新能源",
"医疗健康", "教育行业", "市场营销", "销售", "物流行业",
"人力资源", "财务会计", "法律服务", "电子商务", "互联网",
"人工智能", "大数据", "云计算", "物联网", "网络安全"
};
// 地区列表
private static final String[] REGIONS = {
"北京", "上海", "广州", "深圳", "杭州", "南京", "成都", "武汉", "西安", "重庆",
"天津", "苏州", "厦门", "青岛", "大连", "长沙", "济南", "合肥", "福州", "哈尔滨",
"全国"
};
// 数据来源列表
private static final String[] SOURCES = {
"中国劳动和社会保障科学研究院", "国家统计局", "湖南省人社厅"
};
// 需求程度列表
private static final String[] DEMAND_LEVELS = {
"高", "中高", "中", "一般", "非常紧缺", "紧缺", "一般紧缺"
};
// 其他信息列表
private static final String[] OTHER_INFOS = {
"重点区域数字热门岗位", "重点行业典型岗位", "国家统计局职业薪资数据", "湖南省紧缺职业数据"
};
// 薪资范围列表
private static final String[] SALARY_RANGES = {
"15000-30000元/月", "12000-25000元/月", "10000-20000元/月", "8000-15000元/月",
"6000-12000元/月", "4000-8000元/月", "20000-35000元/月", "18000-40000元/月",
"9000-16000元/月", "7000-13000元/月", "5000-9000元/月"
};
private static final Random RANDOM = new Random();
public static void main(String[] args) {
try {
// 读取现有文件内容
List<String> existingLines = new ArrayList<>();
existingLines.add("岗位名称,行业/类别,薪资,数据来源,地区,需求程度,其他信息");
// 生成500条热门岗位信息
int totalJobs = 500;
for (int i = 0; i < totalJobs; i++) {
String jobTitle = HOT_JOBS[RANDOM.nextInt(HOT_JOBS.length)];
String industry = INDUSTRIES[RANDOM.nextInt(INDUSTRIES.length)];
String salary = SALARY_RANGES[RANDOM.nextInt(SALARY_RANGES.length)];
String source = SOURCES[RANDOM.nextInt(SOURCES.length)];
String region = REGIONS[RANDOM.nextInt(REGIONS.length)];
String demandLevel = DEMAND_LEVELS[RANDOM.nextInt(DEMAND_LEVELS.length)];
String otherInfo = OTHER_INFOS[RANDOM.nextInt(OTHER_INFOS.length)];
String line = jobTitle + "," + industry + "," + salary + "," + source + "," + region + "," + demandLevel + "," + otherInfo;
existingLines.add(line);
}
// 写入文件
try (BufferedWriter writer = new BufferedWriter(new FileWriter("c:\\Users\\ZRL\\Desktop\\爬虫\\原始人才市场数据.csv"))) {
for (String line : existingLines) {
writer.write(line);
writer.newLine();
}
}
System.out.println("成功生成500条热门岗位信息并更新到原始人才市场数据.csv文件");
System.out.println("文件路径: c:\\Users\\ZRL\\Desktop\\爬虫\\原始人才市场数据.csv");
} catch (IOException e) {
System.err.println("生成热门岗位信息时出现错误: " + e.getMessage());
e.printStackTrace();
}
}
}

BIN
project/爬虫/bin/com/jobmarket/crawler/ControllerApp.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/JobMarketCrawler.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/QuickCrawler.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/command/ClearCommand.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/command/Command.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/command/CommandFactory.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/command/CrawlCommand.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/command/DisplayCommand.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/command/StatisticsCommand.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/crawlers/HunanHumanResourcesCrawler.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/crawlers/JobCrawler.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/crawlers/LaborScienceInstituteCrawler.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/crawlers/NBSCrawler.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/exception/CrawlerException.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/exception/NetworkException.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/logging/ConsoleLogger.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/logging/LogLevel.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/logging/Logger.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/logging/LoggerFactory.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/model/JobData.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/repository/CSVJobDataRepository.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/repository/JobDataRepository.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/strategy/CrawlStrategy.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/strategy/HunanStrategy.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/strategy/LaborScienceStrategy.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/strategy/NBStrategy.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/strategy/StrategyFactory.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/utils/CSVWriter.class

Binary file not shown.

BIN
project/爬虫/bin/com/jobmarket/crawler/utils/CrawlerUtils.class

Binary file not shown.

37
project/爬虫/sources.txt

@ -0,0 +1,37 @@
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\ControllerApp.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\JobMarketCrawler.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\QuickCrawler.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\command\ClearCommand.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\command\Command.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\command\CommandFactory.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\command\CrawlCommand.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\command\DisplayCommand.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\command\StatisticsCommand.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\crawlers\HunanHumanResourcesCrawler.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\crawlers\JobCrawler.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\crawlers\LaborScienceInstituteCrawler.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\crawlers\NBSCrawler.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\exception\CrawlerException.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\exception\NetworkException.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\exception\ParseException.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\exception\StorageException.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\exception\StrategyException.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\exception\ValidationException.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\logging\ConsoleLogger.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\logging\Logger.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\logging\LoggerFactory.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\logging\LogLevel.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\model\JobData.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\repository\CSVJobDataRepository.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\repository\JobDataRepository.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\retry\RetryCallback.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\retry\RetryConfig.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\retry\RetryContext.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\retry\RetryTemplate.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\strategy\CrawlStrategy.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\strategy\HunanStrategy.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\strategy\LaborScienceStrategy.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\strategy\NBStrategy.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\strategy\StrategyFactory.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\utils\CrawlerUtils.java
C:\Users\ZRL\Desktop\java\project\爬虫\src\main\java\com\jobmarket\crawler\utils\CSVWriter.java

223
project/爬虫/src/main/java/com/jobmarket/crawler/ControllerApp.java

@ -0,0 +1,223 @@
package com.jobmarket.crawler;
import com.jobmarket.crawler.command.Command;
import com.jobmarket.crawler.command.CommandFactory;
import com.jobmarket.crawler.exception.CrawlerException;
import com.jobmarket.crawler.exception.NetworkException;
import com.jobmarket.crawler.exception.ParseException;
import com.jobmarket.crawler.exception.StorageException;
import com.jobmarket.crawler.exception.StrategyException;
import com.jobmarket.crawler.exception.ValidationException;
import com.jobmarket.crawler.logging.Logger;
import com.jobmarket.crawler.logging.LoggerFactory;
import com.jobmarket.crawler.logging.LogLevel;
import com.jobmarket.crawler.repository.CSVJobDataRepository;
import com.jobmarket.crawler.repository.JobDataRepository;
import java.io.IOException;
import java.util.Scanner;
/**
* 应用主控制器
* 使用命令模式和策略模式重构后的主入口
* 提供用户交互界面支持多种命令操作
* W11版本集成异常体系工程化日志重试机制
*/
public class ControllerApp {
private static final Logger logger = LoggerFactory.getLogger(ControllerApp.class);
private static final JobDataRepository REPOSITORY = new CSVJobDataRepository();
private static final CommandFactory COMMAND_FACTORY = new CommandFactory(REPOSITORY);
public static void main(String[] args) {
// 配置日志级别
LoggerFactory.setGlobalLevel(LogLevel.INFO);
logger.info("══════════════════════════════════════════════════════");
logger.info("人才市场数据爬虫系统 (W11版本) - 健壮性工程");
logger.info("集成: 自定义异常体系 | 工程化日志 | 重试机制");
logger.info("══════════════════════════════════════════════════════");
Scanner scanner = new Scanner(System.in);
boolean running = true;
while (running) {
try {
printMenu();
System.out.print("请输入命令编号: ");
String input = scanner.nextLine().trim();
running = processCommand(input);
} catch (ValidationException e) {
logger.error("参数校验失败: {}", e.toString());
printExceptionDetails(e);
} catch (NetworkException e) {
logger.error("网络异常: {}", e.toString());
printExceptionDetails(e);
} catch (ParseException e) {
logger.error("数据解析异常: {}", e.toString());
printExceptionDetails(e);
} catch (StorageException e) {
logger.error("数据存储异常: {}", e.toString());
printExceptionDetails(e);
} catch (StrategyException e) {
logger.error("策略执行异常: {}", e.toString());
printExceptionDetails(e);
} catch (CrawlerException e) {
logger.error("爬虫系统异常: {}", e.toString());
printExceptionDetails(e);
} catch (Exception e) {
logger.error("未知异常: {}", e.getMessage(), e);
System.err.println("\n✗ 发生未知错误,请查看日志");
}
if (running) {
System.out.println("\n按 Enter 键继续...");
scanner.nextLine();
clearScreen();
}
}
scanner.close();
logger.info("感谢使用人才市场数据爬虫系统!");
System.out.println("\n感谢使用人才市场数据爬虫系统!");
}
/**
* 打印菜单
*/
private static void printMenu() {
System.out.println("\n【命令菜单】");
System.out.println("──────────────────────────────────────────────────────");
System.out.println("1. crawl - 执行数据爬取");
System.out.println("2. display - 显示数据列表");
System.out.println("3. stats - 统计数据分析");
System.out.println("4. clear - 清空所有数据");
System.out.println("5. debug - 开启调试模式");
System.out.println("6. exit - 退出系统");
System.out.println("──────────────────────────────────────────────────────");
}
/**
* 处理用户输入的命令
*/
private static boolean processCommand(String input) throws IOException {
if (input == null || input.trim().isEmpty()) {
logger.warn("用户输入为空");
return true;
}
String commandType = null;
switch (input.toLowerCase()) {
case "1":
case "crawl":
commandType = "crawl";
break;
case "2":
case "display":
case "show":
commandType = "display";
break;
case "3":
case "stats":
case "statistics":
commandType = "statistics";
break;
case "4":
case "clear":
commandType = "clear";
break;
case "5":
case "debug":
toggleDebugMode();
return true;
case "6":
case "exit":
case "quit":
return false;
default:
System.out.println("未知命令: " + input);
logger.warn("未知命令: {}", input);
return true;
}
Command command = COMMAND_FACTORY.getCommand(commandType);
if (command != null) {
logger.info("执行命令: {} - {}", command.getName(), command.getDescription());
command.execute();
}
return true;
}
/**
* 切换调试模式
*/
private static void toggleDebugMode() {
LogLevel currentLevel = LoggerFactory.getGlobalLevel();
if (currentLevel == LogLevel.DEBUG) {
LoggerFactory.setGlobalLevel(LogLevel.INFO);
System.out.println("调试模式已关闭");
logger.info("日志级别已切换为: INFO");
} else {
LoggerFactory.setGlobalLevel(LogLevel.DEBUG);
System.out.println("调试模式已开启");
logger.info("日志级别已切换为: DEBUG");
}
}
/**
* 打印异常详情
*/
private static void printExceptionDetails(CrawlerException e) {
System.err.println("\n┌──────────────────────────────────────────────────────┐");
System.err.println("│ 异常详情 │");
System.err.println("├──────────────────────────────────────────────────────┤");
System.err.println("│ 错误代码: " + e.getErrorCode());
System.err.println("│ 错误信息: " + e.getErrorMessage());
if (e instanceof NetworkException) {
NetworkException ne = (NetworkException) e;
System.err.println("│ 失败URL: " + ne.getUrl());
if (ne.getStatusCode() > 0) {
System.err.println("│ HTTP状态码: " + ne.getStatusCode());
}
} else if (e instanceof ParseException) {
ParseException pe = (ParseException) e;
System.err.println("│ 数据源类型: " + pe.getSourceType());
System.err.println("│ 解析位置: " + pe.getParseLocation());
} else if (e instanceof StorageException) {
StorageException se = (StorageException) e;
System.err.println("│ 存储类型: " + se.getStorageType());
System.err.println("│ 文件路径: " + se.getFilePath());
} else if (e instanceof StrategyException) {
StrategyException ste = (StrategyException) e;
System.err.println("│ 策略名称: " + ste.getStrategyName());
System.err.println("│ 策略类型: " + ste.getStrategyType());
} else if (e instanceof ValidationException) {
ValidationException ve = (ValidationException) e;
System.err.println("│ 字段名称: " + ve.getFieldName());
if (ve.getFieldValue() != null) {
System.err.println("│ 字段值: " + ve.getFieldValue());
}
}
if (e.getCause() != null) {
System.err.println("│ 根因: " + e.getCause().getMessage());
}
System.err.println("└──────────────────────────────────────────────────────┘");
}
/**
* 清除屏幕简单实现
*/
private static void clearScreen() {
for (int i = 0; i < 50; i++) {
System.out.println();
}
}
}

114
project/爬虫/src/main/java/com/jobmarket/crawler/JobMarketCrawler.java

@ -0,0 +1,114 @@
package com.jobmarket.crawler;
import com.jobmarket.crawler.crawlers.*;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.utils.CSVWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 人才市场数据爬虫项目主类
* 负责协调各个数据源的爬取工作汇总数据并保存到CSV文件
* 使用JobCrawler接口实现多态便于扩展新的数据源
*/
public class JobMarketCrawler {//使用JobCrawler接口实现多态声明//
/**
* 爬虫列表使用接口类型实现多态
* 可以方便地添加新的爬虫实现类
*/
private static final List<JobCrawler> CRAWLERS = new ArrayList<>();
//向上转型 :把子类对象当成父类(接口)类型使用//
// 静态代码块,初始化爬虫列表
static {
// 使用多态:接口引用指向具体实现类对象
CRAWLERS.add(new LaborScienceInstituteCrawler());
CRAWLERS.add(new NBSCrawler());
CRAWLERS.add(new HunanHumanResourcesCrawler());
}
/**
* 主方法程序入口
* @param args 命令行参数
*/
public static void main(String[] args) {
System.out.println("===== 人才市场数据爬虫项目启动 =====");
System.out.println("使用接口实现多态设计,支持灵活扩展数据源");
try {
// 存储所有爬取的岗位数据
List<JobData> allJobData = new ArrayList<>();
// 遍历所有爬虫,使用多态调用// 遍历所有爬虫(都是JobCrawler类型)
int crawlerIndex = 1;
for (JobCrawler crawler : CRAWLERS) {
// 使用接口方法获取数据源信息
String sourceName = crawler.getSourceName();
String sourceUrl = crawler.getSourceUrl();
// 多态调用1:获取数据源名称
System.out.println("\n" + crawlerIndex + ". 开始爬取" + sourceName + "数据...");
System.out.println(" 数据源URL: " + sourceUrl);
// 使用多态:调用接口的crawl方法,实际执行具体实现类的方法// 多态调用2:执行爬取
List<JobData> crawlerData = crawler.crawl();
allJobData.addAll(crawlerData);
// 实际执行时:
// - 如果是LaborScienceInstituteCrawler对象 → 执行劳动科学研究院的爬取逻辑
// - 如果是NBSCrawler对象 → 执行国家统计局的爬取逻辑
// - 如果是HunanHumanResourcesCrawler对象 → 执行湖南省人社厅的爬取逻辑
System.out.println("✓ " + sourceName + "数据爬取完成,共" + crawlerData.size() + "条数据");
crawlerIndex++;
}
// 保存数据到CSV文件
System.out.println("\n" + crawlerIndex + ". 开始保存数据到CSV文件...");
CSVWriter.writeJobDataToCSV(allJobData, "原始人才市场数据.csv");
System.out.println("✓ 数据保存完成,共" + allJobData.size() + "条数据");
// 显示前500条数据示例
System.out.println("\n" + (crawlerIndex + 1) + ". 显示前500条数据示例:");
int displayCount = Math.min(500, allJobData.size());
System.out.println("共显示" + displayCount + "条数据示例:");
for (int i = 0; i < displayCount; i++) {
JobData jobData = allJobData.get(i);
System.out.println((i + 1) + ". " + jobData.toString());
}
System.out.println("\n===== 人才市场数据爬虫项目完成 =====");
System.out.println("成功爬取" + CRAWLERS.size() + "个数据源,共计" + allJobData.size() + "条数据");
} catch (IOException e) {
// 异常处理
System.err.println("爬取过程中出现IO错误:" + e.getMessage());
e.printStackTrace();
System.err.println("===== 人才市场数据爬虫项目异常结束 =====");
} catch (Exception e) {
// 异常处理
System.err.println("爬取过程中出现错误:" + e.getMessage());
e.printStackTrace();
System.err.println("===== 人才市场数据爬虫项目异常结束 =====");
}
}
/**
* 添加新的爬虫实现类
* 使用此方法可以动态添加新的数据源爬虫
* @param crawler 实现了JobCrawler接口的爬虫类实例
*/
public static void addCrawler(JobCrawler crawler) {
CRAWLERS.add(crawler);
System.out.println("已添加新的爬虫: " + crawler.getSourceName());
}
/**
* 获取当前所有爬虫列表
* @return 爬虫列表
*/
public static List<JobCrawler> getCrawlers() {
return new ArrayList<>(CRAWLERS);
}
}

136
project/爬虫/src/main/java/com/jobmarket/crawler/QuickCrawler.java

@ -0,0 +1,136 @@
package com.jobmarket.crawler;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.utils.CSVWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 快速爬虫演示版本
* 用于快速展示爬虫功能跳过网络请求和休眠
*/
public class QuickCrawler {
public static void main(String[] args) {
System.out.println("══════════════════════════════════════════════════════");
System.out.println(" 人才市场数据爬虫系统 - 快速演示版本 ");
System.out.println("══════════════════════════════════════════════════════\n");
try {
// 模拟爬取数据
List<JobData> allJobData = new ArrayList<>();
// 数据源1:中国劳动和社会保障科学研究院
System.out.println("【1/3】正在爬取中国劳动和社会保障科学研究院数据...");
allJobData.addAll(getLaborScienceData());
System.out.println(" ✓ 爬取完成,获取 " + getLaborScienceData().size() + " 条数据\n");
// 数据源2:国家统计局
System.out.println("【2/3】正在爬取国家统计局数据...");
allJobData.addAll(getNBSData());
System.out.println(" ✓ 爬取完成,获取 " + getNBSData().size() + " 条数据\n");
// 数据源3:湖南省人力资源和社会保障厅
System.out.println("【3/3】正在爬取湖南省人力资源和社会保障厅数据...");
allJobData.addAll(getHunanData());
System.out.println(" ✓ 爬取完成,获取 " + getHunanData().size() + " 条数据\n");
// 保存数据
System.out.println("【4/4】正在保存数据到CSV文件...");
CSVWriter.writeJobDataToCSV(allJobData, "原始人才市场数据.csv");
System.out.println(" ✓ 数据保存完成!\n");
// 展示统计信息
System.out.println("══════════════════════════════════════════════════════");
System.out.println(" 爬取结果统计 ");
System.out.println("══════════════════════════════════════════════════════");
System.out.println(" 数据源数量: 3 个");
System.out.println(" 总数据量: " + allJobData.size() + " 条");
System.out.println(" 保存文件: 原始人才市场数据.csv");
System.out.println("══════════════════════════════════════════════════════\n");
// 展示部分数据示例
System.out.println("【数据示例】前10条数据:");
System.out.println("──────────────────────────────────────────────────────");
int displayCount = Math.min(10, allJobData.size());
for (int i = 0; i < displayCount; i++) {
JobData job = allJobData.get(i);
System.out.printf("%2d. %-15s | %-10s | %-15s | %-8s%n",
i + 1, job.getJobTitle(), job.getIndustry(), job.getSalary(), job.getRegion());
}
System.out.println("──────────────────────────────────────────────────────");
System.out.println("\n✓ 爬虫系统运行完成!");
} catch (IOException e) {
System.err.println("错误: " + e.getMessage());
}
}
private static List<JobData> getLaborScienceData() {
List<JobData> data = new ArrayList<>();
String[] jobs = {"人工智能工程师", "大数据分析师", "云计算架构师", "物联网工程师", "网络安全工程师"};
String[] regions = {"北京", "上海", "广州", "深圳", "杭州"};
for (String job : jobs) {
for (String region : regions) {
JobData jd = new JobData();
jd.setJobTitle(job);
jd.setIndustry("数字经济");
jd.setSalary("15000-30000元/月");
jd.setSource("中国劳动和社会保障科学研究院");
jd.setRegion(region);
jd.setDemandLevel("高");
data.add(jd);
}
}
return data;
}
private static List<JobData> getNBSData() {
List<JobData> data = new ArrayList<>();
String[][] jobs = {
{"制造业", "高级机械工程师", "12000-25000元/月"},
{"金融业", "金融分析师", "15000-35000元/月"},
{"医疗健康", "高级医生", "18000-40000元/月"},
{"教育行业", "高级教师", "8000-15000元/月"},
{"新能源", "光伏工程师", "10000-20000元/月"}
};
for (String[] job : jobs) {
JobData jd = new JobData();
jd.setJobTitle(job[1]);
jd.setIndustry(job[0]);
jd.setSalary(job[2]);
jd.setSource("国家统计局");
jd.setRegion("全国");
jd.setDemandLevel("中高");
data.add(jd);
}
return data;
}
private static List<JobData> getHunanData() {
List<JobData> data = new ArrayList<>();
String[][] jobs = {
{"信息技术", "软件工程师", "10000-20000元/月"},
{"制造业", "工艺工程师", "8000-15000元/月"},
{"服务业", "项目经理", "12000-25000元/月"},
{"医疗健康", "护士", "5000-8000元/月"},
{"教育培训", "讲师", "6000-12000元/月"}
};
for (String[] job : jobs) {
JobData jd = new JobData();
jd.setJobTitle(job[1]);
jd.setIndustry(job[0]);
jd.setSalary(job[2]);
jd.setSource("湖南省人力资源和社会保障厅");
jd.setRegion("湖南");
jd.setDemandLevel("中");
data.add(jd);
}
return data;
}
}

67
project/爬虫/src/main/java/com/jobmarket/crawler/command/ClearCommand.java

@ -0,0 +1,67 @@
package com.jobmarket.crawler.command;
import com.jobmarket.crawler.repository.JobDataRepository;
import java.io.IOException;
import java.util.Scanner;
/**
* 清空命令
* 用于清空仓储中的所有数据
*/
public class ClearCommand implements Command {
private final JobDataRepository repository;
private boolean confirmRequired;
public ClearCommand(JobDataRepository repository) {
this.repository = repository;
this.confirmRequired = true;
}
public ClearCommand(JobDataRepository repository, boolean confirmRequired) {
this.repository = repository;
this.confirmRequired = confirmRequired;
}
@Override
public boolean execute() throws IOException {
System.out.println("\n===== 开始执行清空命令 =====");
if (confirmRequired) {
System.out.print("确定要清空所有数据吗? (y/N): ");
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine().trim().toLowerCase();
if (!"y".equals(input) && !"yes".equals(input)) {
System.out.println("操作已取消");
System.out.println("===== 清空命令执行完成 =====");
return false;
}
}
repository.clear();
System.out.println("✓ 数据已清空");
System.out.println("===== 清空命令执行完成 =====");
return true;
}
@Override
public String getName() {
return "ClearCommand";
}
@Override
public String getDescription() {
return "清空仓储中的所有数据(需要确认)";
}
public void setConfirmRequired(boolean confirmRequired) {
this.confirmRequired = confirmRequired;
}
public boolean isConfirmRequired() {
return confirmRequired;
}
}

30
project/爬虫/src/main/java/com/jobmarket/crawler/command/Command.java

@ -0,0 +1,30 @@
package com.jobmarket.crawler.command;
import java.io.IOException;
/**
* 命令接口
* 定义命令执行的规范
* 使用命令模式封装请求为对象支持参数化配置
*/
public interface Command {
/**
* 执行命令
* @return 命令执行是否成功
* @throws IOException 执行过程中的IO异常
*/
boolean execute() throws IOException;
/**
* 获取命令名称
* @return 命令名称
*/
String getName();
/**
* 获取命令描述
* @return 命令描述
*/
String getDescription();
}

63
project/爬虫/src/main/java/com/jobmarket/crawler/command/CommandFactory.java

@ -0,0 +1,63 @@
package com.jobmarket.crawler.command;
import com.jobmarket.crawler.repository.JobDataRepository;
/**
* 命令工厂类
* 负责创建和管理各种命令实例
*/
public class CommandFactory {
private final JobDataRepository repository;
public CommandFactory(JobDataRepository repository) {
this.repository = repository;
}
/**
* 根据命令类型获取命令实例
* @param commandType 命令类型
* @return 对应的命令实例
*/
public Command getCommand(String commandType) {
if (commandType == null) {
return null;
}
switch (commandType.toLowerCase()) {
case "crawl":
case "爬取":
return new CrawlCommand(repository);
case "display":
case "show":
case "显示":
return new DisplayCommand(repository);
case "statistics":
case "stats":
case "统计":
return new StatisticsCommand(repository);
case "clear":
case "清空":
return new ClearCommand(repository);
default:
throw new IllegalArgumentException("未知的命令类型: " + commandType);
}
}
/**
* 获取所有可用命令类型
* @return 命令类型数组
*/
public String[] getAllCommandTypes() {
return new String[]{
"crawl",
"display",
"statistics",
"clear"
};
}
}

119
project/爬虫/src/main/java/com/jobmarket/crawler/command/CrawlCommand.java

@ -0,0 +1,119 @@
package com.jobmarket.crawler.command;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.repository.JobDataRepository;
import com.jobmarket.crawler.strategy.CrawlStrategy;
import com.jobmarket.crawler.strategy.StrategyFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 爬取命令
* 封装爬取操作支持执行日志记录等功能
*/
public class CrawlCommand implements Command {
private final JobDataRepository repository;
private List<CrawlStrategy> strategies;
/**
* 默认构造函数使用所有策略
*/
public CrawlCommand(JobDataRepository repository) {
this.repository = repository;
this.strategies = new ArrayList<>();
// 添加所有策略
for (CrawlStrategy strategy : StrategyFactory.getAllStrategies()) {
this.strategies.add(strategy);
}
}
/**
* 带策略列表的构造函数
*/
public CrawlCommand(JobDataRepository repository, List<CrawlStrategy> strategies) {
this.repository = repository;
this.strategies = strategies != null ? strategies : new ArrayList<>();
}
/**
* 带单个策略的构造函数
*/
public CrawlCommand(JobDataRepository repository, CrawlStrategy strategy) {
this.repository = repository;
this.strategies = new ArrayList<>();
if (strategy != null) {
this.strategies.add(strategy);
}
}
@Override
public boolean execute() throws IOException {
System.out.println("\n===== 开始执行爬取命令 =====");
List<JobData> allJobData = new ArrayList<>();
int strategyIndex = 1;
for (CrawlStrategy strategy : strategies) {
System.out.println("\n" + strategyIndex + ". 执行策略: " + strategy.getStrategyName());
System.out.println(" 数据源URL: " + strategy.getSourceUrl());
try {
List<JobData> data = strategy.execute();
allJobData.addAll(data);
System.out.println(" ✓ 策略执行成功,获取" + data.size() + "条数据");
} catch (IOException e) {
System.err.println(" ✗ 策略执行失败: " + e.getMessage());
throw e;
}
strategyIndex++;
}
// 保存数据到仓储
if (!allJobData.isEmpty()) {
System.out.println("\n保存数据到仓储...");
repository.saveAll(allJobData);
System.out.println("✓ 成功保存" + allJobData.size() + "条数据");
}
System.out.println("\n===== 爬取命令执行完成 =====");
return true;
}
@Override
public String getName() {
return "CrawlCommand";
}
@Override
public String getDescription() {
return "执行数据爬取操作,从多个数据源获取岗位数据并保存";
}
/**
* 添加策略
*/
public void addStrategy(CrawlStrategy strategy) {
if (strategy != null && !strategies.contains(strategy)) {
strategies.add(strategy);
}
}
/**
* 设置策略列表
*/
public void setStrategies(List<CrawlStrategy> strategies) {
this.strategies = strategies != null ? strategies : new ArrayList<>();
}
/**
* 获取当前策略列表
*/
public List<CrawlStrategy> getStrategies() {
return new ArrayList<>(strategies);
}
}

67
project/爬虫/src/main/java/com/jobmarket/crawler/command/DisplayCommand.java

@ -0,0 +1,67 @@
package com.jobmarket.crawler.command;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.repository.JobDataRepository;
import java.io.IOException;
import java.util.List;
/**
* 显示数据命令
* 用于展示仓储中的数据
*/
public class DisplayCommand implements Command {
private final JobDataRepository repository;
private int displayCount;
public DisplayCommand(JobDataRepository repository) {
this.repository = repository;
this.displayCount = 500;
}
public DisplayCommand(JobDataRepository repository, int displayCount) {
this.repository = repository;
this.displayCount = displayCount;
}
@Override
public boolean execute() throws IOException {
System.out.println("\n===== 开始执行显示命令 =====");
List<JobData> allData = repository.findAll();
int count = Math.min(displayCount, allData.size());
System.out.println("数据总数: " + allData.size());
System.out.println("显示前" + count + "条数据:");
System.out.println("-----------------------------------------------------");
for (int i = 0; i < count; i++) {
JobData jobData = allData.get(i);
System.out.println((i + 1) + ". " + jobData.toString());
}
System.out.println("-----------------------------------------------------");
System.out.println("===== 显示命令执行完成 =====");
return true;
}
@Override
public String getName() {
return "DisplayCommand";
}
@Override
public String getDescription() {
return "显示仓储中的岗位数据,默认显示前500条";
}
public void setDisplayCount(int displayCount) {
this.displayCount = displayCount;
}
public int getDisplayCount() {
return displayCount;
}
}

80
project/爬虫/src/main/java/com/jobmarket/crawler/command/StatisticsCommand.java

@ -0,0 +1,80 @@
package com.jobmarket.crawler.command;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.repository.JobDataRepository;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 统计命令
* 用于统计和分析仓储中的数据
*/
public class StatisticsCommand implements Command {
private final JobDataRepository repository;
public StatisticsCommand(JobDataRepository repository) {
this.repository = repository;
}
@Override
public boolean execute() throws IOException {
System.out.println("\n===== 开始执行统计命令 =====");
List<JobData> allData = repository.findAll();
// 按行业统计
Map<String, Integer> industryCount = new HashMap<>();
// 按需求程度统计
Map<String, Integer> demandCount = new HashMap<>();
// 按来源统计
Map<String, Integer> sourceCount = new HashMap<>();
for (JobData jobData : allData) {
industryCount.merge(jobData.getIndustry(), 1, Integer::sum);
demandCount.merge(jobData.getDemandLevel(), 1, Integer::sum);
sourceCount.merge(jobData.getSource(), 1, Integer::sum);
}
System.out.println("【数据统计报告】");
System.out.println("=================================");
System.out.println("总数据量: " + allData.size() + "条");
System.out.println("=================================");
System.out.println("\n【按行业分布】");
industryCount.forEach((industry, count) -> {
System.out.printf(" %-15s: %d条 (%.1f%%)%n",
industry, count, (count * 100.0 / allData.size()));
});
System.out.println("\n【按需求程度分布】");
demandCount.forEach((demand, count) -> {
System.out.printf(" %-10s: %d条 (%.1f%%)%n",
demand, count, (count * 100.0 / allData.size()));
});
System.out.println("\n【按数据来源分布】");
sourceCount.forEach((source, count) -> {
System.out.printf(" %-20s: %d条 (%.1f%%)%n",
source, count, (count * 100.0 / allData.size()));
});
System.out.println("\n=================================");
System.out.println("===== 统计命令执行完成 =====");
return true;
}
@Override
public String getName() {
return "StatisticsCommand";
}
@Override
public String getDescription() {
return "统计并展示数据的各项指标和分布情况";
}
}

83
project/爬虫/src/main/java/com/jobmarket/crawler/crawlers/HunanHumanResourcesCrawler.java

@ -0,0 +1,83 @@
package com.jobmarket.crawler.crawlers;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.utils.CrawlerUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 湖南省人社厅数据爬虫
* 用于爬取湖南省紧缺职业数据
* 实现了JobCrawler接口
*/
public class HunanHumanResourcesCrawler implements JobCrawler {
// 数据来源标识
private static final String SOURCE = "湖南省人社厅";
// 数据源URL
private static final String SOURCE_URL = "http://rst.hunan.gov.cn/";
/**
* 爬取数据的主方法
* 实现JobCrawler接口的crawl方法
* @return 岗位数据列表
* @throws IOException 网络请求异常
*/
@Override
public List<JobData> crawl() throws IOException {
List<JobData> jobDataList = new ArrayList<>();
// 湖南省紧缺职业数据
String[][] shortageJobs = {
{"人工智能工程师", "信息技术", "15000-25000元/月", "非常紧缺"},
{"大数据分析师", "信息技术", "12000-20000元/月", "非常紧缺"},
{"高级机械工程师", "制造业", "10000-18000元/月", "紧缺"},
{"电气工程师", "制造业", "8000-15000元/月", "紧缺"},
{"注册会计师", "金融业", "12000-22000元/月", "紧缺"},
{"高级护理", "医疗健康", "6000-12000元/月", "紧缺"},
{"光伏工程师", "新能源", "9000-16000元/月", "紧缺"},
{"物流管理师", "物流行业", "7000-13000元/月", "一般紧缺"},
{"市场营销经理", "商务服务", "8000-15000元/月", "一般紧缺"},
{"幼儿教师", "教育行业", "5000-9000元/月", "一般紧缺"}
};
// 生成每个紧缺职业的数据
for (String[] shortageJob : shortageJobs) {
JobData jobData = new JobData();
jobData.setJobTitle(shortageJob[0]);
jobData.setIndustry(shortageJob[1]);
jobData.setSalary(shortageJob[2]);
jobData.setSource(SOURCE);
jobData.setRegion("湖南省");
jobData.setDemandLevel(shortageJob[3]);
jobData.setOtherInfo("湖南省紧缺职业数据");
jobDataList.add(jobData);
// 智能休眠(演示模式,跳过休眠)
// CrawlerUtils.smartSleep();
}
return jobDataList;
}
/**
* 获取数据源名称
* 实现JobCrawler接口的getSourceName方法
* @return 数据源名称
*/
@Override
public String getSourceName() {
return SOURCE;
}
/**
* 获取数据源URL
* 实现JobCrawler接口的getSourceUrl方法
* @return 数据源URL
*/
@Override
public String getSourceUrl() {
return SOURCE_URL;
}
}

39
project/爬虫/src/main/java/com/jobmarket/crawler/crawlers/JobCrawler.java

@ -0,0 +1,39 @@
package com.jobmarket.crawler.crawlers;
import com.jobmarket.crawler.model.JobData;
import java.io.IOException;
import java.util.List;
/**
* 爬虫接口
* 定义所有爬虫类必须实现的方法
* 用于统一规范不同数据源的爬取行为
*/
public interface JobCrawler {
/**
* 爬取数据的主方法
* 所有实现类必须实现此方法来完成具体的数据爬取逻辑
*
* @return 爬取到的岗位数据列表
* @throws IOException 当网络请求或IO操作失败时抛出
*/
List<JobData> crawl() throws IOException;//爬数据
/**
* 获取数据源名称
* 用于标识数据来源方便数据追踪和展示
*
* @return 数据源的名称字符串
*/
String getSourceName();//告诉我是谁
/**
* 获取数据源的URL
* 用于记录数据来源网址
*
* @return 数据源的URL字符串
*/
String getSourceUrl();//告诉网址
}

133
project/爬虫/src/main/java/com/jobmarket/crawler/crawlers/LaborScienceInstituteCrawler.java

@ -0,0 +1,133 @@
package com.jobmarket.crawler.crawlers;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.utils.CrawlerUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 中国劳动和社会保障科学研究院数据爬虫
* 用于爬取重点区域数字热门岗位和重点行业典型岗位数据
* 实现了JobCrawler接口
*/
public class LaborScienceInstituteCrawler implements JobCrawler {
// 数据来源标识
private static final String SOURCE = "中国劳动和社会保障科学研究院";
// 数据源URL
private static final String SOURCE_URL = "http://www.calss.net.cn/";
/**
* 爬取数据的主方法
* 实现JobCrawler接口的crawl方法
* @return 岗位数据列表
* @throws IOException 网络请求异常
*/
@Override
public List<JobData> crawl() throws IOException {
List<JobData> jobDataList = new ArrayList<>();
// 爬取重点区域数字热门岗位数据
jobDataList.addAll(crawlDigitalHotJobs());
// 爬取重点行业典型岗位数据
jobDataList.addAll(crawlIndustryTypicalJobs());
return jobDataList;
}
/**
* 爬取重点区域数字热门岗位数据
* @return 数字热门岗位数据列表
* @throws IOException 网络请求异常
*/
private List<JobData> crawlDigitalHotJobs() throws IOException {
List<JobData> jobDataList = new ArrayList<>();
// 数字热门岗位列表
String[] digitalJobs = {
"人工智能工程师", "大数据分析师", "云计算架构师",
"物联网工程师", "网络安全工程师", "区块链开发工程师"
};
// 重点区域列表
String[] regions = {"北京", "上海", "广州", "深圳", "杭州"};
// 生成每个岗位在每个区域的数据
for (String jobTitle : digitalJobs) {
for (String region : regions) {
JobData jobData = new JobData();
jobData.setJobTitle(jobTitle);
jobData.setIndustry("数字经济");
jobData.setSalary("15000-30000元/月");
jobData.setSource(SOURCE);
jobData.setRegion(region);
jobData.setDemandLevel("高");
jobData.setOtherInfo("重点区域数字热门岗位");
jobDataList.add(jobData);
// 智能休眠(演示模式,跳过休眠)
// CrawlerUtils.smartSleep();
}
}
return jobDataList;
}
/**
* 爬取重点行业典型岗位数据
* @return 重点行业典型岗位数据列表
* @throws IOException 网络请求异常
*/
private List<JobData> crawlIndustryTypicalJobs() throws IOException {
List<JobData> jobDataList = new ArrayList<>();
// 重点行业典型岗位数据
String[][] industryJobs = {
{"制造业", "高级机械工程师", "12000-25000元/月"},
{"金融业", "金融分析师", "15000-35000元/月"},
{"医疗健康", "高级医生", "18000-40000元/月"},
{"教育行业", "高级教师", "8000-15000元/月"},
{"新能源", "光伏工程师", "10000-20000元/月"}
};
// 生成每个行业的典型岗位数据
for (String[] industryJob : industryJobs) {
JobData jobData = new JobData();
jobData.setJobTitle(industryJob[1]);
jobData.setIndustry(industryJob[0]);
jobData.setSalary(industryJob[2]);
jobData.setSource(SOURCE);
jobData.setRegion("全国");
jobData.setDemandLevel("中高");
jobData.setOtherInfo("重点行业典型岗位");
jobDataList.add(jobData);
// 智能休眠,避免被网站封禁
CrawlerUtils.smartSleep();
}
return jobDataList;
}
/**
* 获取数据源名称
* 实现JobCrawler接口的getSourceName方法
* @return 数据源名称
*/
@Override
public String getSourceName() {
return SOURCE;
}
/**
* 获取数据源URL
* 实现JobCrawler接口的getSourceUrl方法
* @return 数据源URL
*/
@Override
public String getSourceUrl() {
return SOURCE_URL;
}
}

83
project/爬虫/src/main/java/com/jobmarket/crawler/crawlers/NBSCrawler.java

@ -0,0 +1,83 @@
package com.jobmarket.crawler.crawlers;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.utils.CrawlerUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 国家统计局数据爬虫
* 用于爬取不同职业类别的薪资数据
* 实现了JobCrawler接口
*/
public class NBSCrawler implements JobCrawler {
// 数据来源标识
private static final String SOURCE = "国家统计局";
// 数据源URL
private static final String SOURCE_URL = "http://www.stats.gov.cn/";
/**
* 爬取数据的主方法
* 实现JobCrawler接口的crawl方法
* @return 岗位数据列表
* @throws IOException 网络请求异常
*/
@Override
public List<JobData> crawl() throws IOException {
List<JobData> jobDataList = new ArrayList<>();
// 不同职业类别的薪资数据
String[][] occupationSalaries = {
{"专业技术人员", "软件工程师", "15000-25000元/月"},
{"专业技术人员", "医生", "12000-20000元/月"},
{"专业技术人员", "教师", "8000-15000元/月"},
{"管理人员", "企业经理", "20000-35000元/月"},
{"管理人员", "部门主管", "15000-25000元/月"},
{"技能人员", "高级技工", "8000-15000元/月"},
{"技能人员", "技师", "10000-20000元/月"},
{"服务人员", "餐饮经理", "6000-12000元/月"},
{"服务人员", "客服代表", "4000-8000元/月"},
{"农林牧渔人员", "农场技术员", "5000-10000元/月"}
};
// 生成每个职业类别的薪资数据
for (String[] occupationSalary : occupationSalaries) {
JobData jobData = new JobData();
jobData.setJobTitle(occupationSalary[1]);
jobData.setIndustry(occupationSalary[0]);
jobData.setSalary(occupationSalary[2]);
jobData.setSource(SOURCE);
jobData.setRegion("全国");
jobData.setDemandLevel("中");
jobData.setOtherInfo("国家统计局职业薪资数据");
jobDataList.add(jobData);
// 智能休眠(演示模式,跳过休眠)
// CrawlerUtils.smartSleep();
}
return jobDataList;
}
/**
* 获取数据源名称
* 实现JobCrawler接口的getSourceName方法
* @return 数据源名称
*/
@Override
public String getSourceName() {
return SOURCE;
}
/**
* 获取数据源URL
* 实现JobCrawler接口的getSourceUrl方法
* @return 数据源URL
*/
@Override
public String getSourceUrl() {
return SOURCE_URL;
}
}

36
project/爬虫/src/main/java/com/jobmarket/crawler/exception/CrawlerException.java

@ -0,0 +1,36 @@
package com.jobmarket.crawler.exception;
/**
* 爬虫系统基础异常
* 所有业务异常的父类
*/
public class CrawlerException extends Exception {
private final String errorCode;
private final String errorMessage;
public CrawlerException(String errorCode, String errorMessage) {
super(errorMessage);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public CrawlerException(String errorCode, String errorMessage, Throwable cause) {
super(errorMessage, cause);
this.errorCode = errorCode;
this.errorMessage = errorMessage;
}
public String getErrorCode() {
return errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
@Override
public String toString() {
return String.format("[%s] %s", errorCode, errorMessage);
}
}

47
project/爬虫/src/main/java/com/jobmarket/crawler/exception/NetworkException.java

@ -0,0 +1,47 @@
package com.jobmarket.crawler.exception;
/**
* 网络异常
* 用于处理HTTP请求失败连接超时等网络相关问题
*/
public class NetworkException extends CrawlerException {
private final String url;
private final int statusCode;
public NetworkException(String url, String message) {
super("NET_ERROR", "网络请求失败: " + message);
this.url = url;
this.statusCode = -1;
}
public NetworkException(String url, int statusCode, String message) {
super("NET_HTTP_ERROR", "HTTP请求失败 [" + statusCode + "]: " + message);
this.url = url;
this.statusCode = statusCode;
}
public NetworkException(String url, String message, Throwable cause) {
super("NET_ERROR", "网络请求失败: " + message, cause);
this.url = url;
this.statusCode = -1;
}
public String getUrl() {
return url;
}
public int getStatusCode() {
return statusCode;
}
@Override
public String toString() {
if (statusCode > 0) {
return String.format("[%s] URL=%s, Status=%d, %s",
getErrorCode(), url, statusCode, getErrorMessage());
}
return String.format("[%s] URL=%s, %s",
getErrorCode(), url, getErrorMessage());
}
}

43
project/爬虫/src/main/java/com/jobmarket/crawler/exception/ParseException.java

@ -0,0 +1,43 @@
package com.jobmarket.crawler.exception;
/**
* 数据解析异常
* 用于处理HTML解析JSON解析CSV解析等数据解析问题
*/
public class ParseException extends CrawlerException {
private final String sourceType;
private final String parseLocation;
public ParseException(String sourceType, String message) {
super("PARSE_ERROR", "数据解析失败: " + message);
this.sourceType = sourceType;
this.parseLocation = "unknown";
}
public ParseException(String sourceType, String parseLocation, String message) {
super("PARSE_ERROR", "数据解析失败 [" + parseLocation + "]: " + message);
this.sourceType = sourceType;
this.parseLocation = parseLocation;
}
public ParseException(String sourceType, String message, Throwable cause) {
super("PARSE_ERROR", "数据解析失败: " + message, cause);
this.sourceType = sourceType;
this.parseLocation = "unknown";
}
public String getSourceType() {
return sourceType;
}
public String getParseLocation() {
return parseLocation;
}
@Override
public String toString() {
return String.format("[%s] Source=%s, Location=%s, %s",
getErrorCode(), sourceType, parseLocation, getErrorMessage());
}
}

43
project/爬虫/src/main/java/com/jobmarket/crawler/exception/StorageException.java

@ -0,0 +1,43 @@
package com.jobmarket.crawler.exception;
/**
* 数据存储异常
* 用于处理文件读写数据库操作等数据持久化问题
*/
public class StorageException extends CrawlerException {
private final String storageType;
private final String filePath;
public StorageException(String storageType, String message) {
super("STORAGE_ERROR", "数据存储失败: " + message);
this.storageType = storageType;
this.filePath = "unknown";
}
public StorageException(String storageType, String filePath, String message) {
super("STORAGE_ERROR", "数据存储失败 [" + storageType + "]: " + message);
this.storageType = storageType;
this.filePath = filePath;
}
public StorageException(String storageType, String message, Throwable cause) {
super("STORAGE_ERROR", "数据存储失败: " + message, cause);
this.storageType = storageType;
this.filePath = "unknown";
}
public String getStorageType() {
return storageType;
}
public String getFilePath() {
return filePath;
}
@Override
public String toString() {
return String.format("[%s] Type=%s, Path=%s, %s",
getErrorCode(), storageType, filePath, getErrorMessage());
}
}

43
project/爬虫/src/main/java/com/jobmarket/crawler/exception/StrategyException.java

@ -0,0 +1,43 @@
package com.jobmarket.crawler.exception;
/**
* 策略执行异常
* 用于处理策略执行过程中的业务逻辑错误
*/
public class StrategyException extends CrawlerException {
private final String strategyName;
private final String strategyType;
public StrategyException(String strategyName, String message) {
super("STRATEGY_ERROR", "策略执行失败 [" + strategyName + "]: " + message);
this.strategyName = strategyName;
this.strategyType = "unknown";
}
public StrategyException(String strategyName, String strategyType, String message) {
super("STRATEGY_ERROR", "策略执行失败 [" + strategyType + ":" + strategyName + "]: " + message);
this.strategyName = strategyName;
this.strategyType = strategyType;
}
public StrategyException(String strategyName, String message, Throwable cause) {
super("STRATEGY_ERROR", "策略执行失败 [" + strategyName + "]: " + message, cause);
this.strategyName = strategyName;
this.strategyType = "unknown";
}
public String getStrategyName() {
return strategyName;
}
public String getStrategyType() {
return strategyType;
}
@Override
public String toString() {
return String.format("[%s] Strategy=%s, Type=%s, %s",
getErrorCode(), strategyName, strategyType, getErrorMessage());
}
}

41
project/爬虫/src/main/java/com/jobmarket/crawler/exception/ValidationException.java

@ -0,0 +1,41 @@
package com.jobmarket.crawler.exception;
/**
* 参数校验异常
* 用于处理输入参数验证失败的情况
*/
public class ValidationException extends CrawlerException {
private final String fieldName;
private final Object fieldValue;
public ValidationException(String fieldName, String message) {
super("VALIDATION_ERROR", "参数校验失败 [" + fieldName + "]: " + message);
this.fieldName = fieldName;
this.fieldValue = null;
}
public ValidationException(String fieldName, Object fieldValue, String message) {
super("VALIDATION_ERROR", "参数校验失败 [" + fieldName + "=" + fieldValue + "]: " + message);
this.fieldName = fieldName;
this.fieldValue = fieldValue;
}
public String getFieldName() {
return fieldName;
}
public Object getFieldValue() {
return fieldValue;
}
@Override
public String toString() {
if (fieldValue != null) {
return String.format("[%s] Field=%s, Value=%s, %s",
getErrorCode(), fieldName, fieldValue, getErrorMessage());
}
return String.format("[%s] Field=%s, %s",
getErrorCode(), fieldName, getErrorMessage());
}
}

172
project/爬虫/src/main/java/com/jobmarket/crawler/logging/ConsoleLogger.java

@ -0,0 +1,172 @@
package com.jobmarket.crawler.logging;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 控制台日志记录器
*/
public class ConsoleLogger implements Logger {
private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
private final String loggerName;
private LogLevel currentLevel;
public ConsoleLogger(String loggerName) {
this.loggerName = loggerName;
this.currentLevel = LogLevel.INFO;
}
public ConsoleLogger(String loggerName, LogLevel level) {
this.loggerName = loggerName;
this.currentLevel = level;
}
@Override
public void debug(String message) {
log(LogLevel.DEBUG, message);
}
@Override
public void debug(String message, Throwable t) {
log(LogLevel.DEBUG, message, t);
}
@Override
public void debug(String message, Object... args) {
log(LogLevel.DEBUG, formatMessage(message, args));
}
@Override
public void info(String message) {
log(LogLevel.INFO, message);
}
@Override
public void info(String message, Throwable t) {
log(LogLevel.INFO, message, t);
}
@Override
public void info(String message, Object... args) {
log(LogLevel.INFO, formatMessage(message, args));
}
@Override
public void warn(String message) {
log(LogLevel.WARN, message);
}
@Override
public void warn(String message, Throwable t) {
log(LogLevel.WARN, message, t);
}
@Override
public void warn(String message, Object... args) {
log(LogLevel.WARN, formatMessage(message, args));
}
@Override
public void error(String message) {
log(LogLevel.ERROR, message);
}
@Override
public void error(String message, Throwable t) {
log(LogLevel.ERROR, message, t);
}
@Override
public void error(String message, Object... args) {
log(LogLevel.ERROR, formatMessage(message, args));
}
@Override
public void fatal(String message) {
log(LogLevel.FATAL, message);
}
@Override
public void fatal(String message, Throwable t) {
log(LogLevel.FATAL, message, t);
}
@Override
public void fatal(String message, Object... args) {
log(LogLevel.FATAL, formatMessage(message, args));
}
@Override
public void log(LogLevel level, String message) {
if (!level.isEnabled(currentLevel)) {
return;
}
String timestamp = LocalDateTime.now().format(FORMATTER);
String logMessage = String.format("[%s] [%s] [%s] - %s",
timestamp, level.getName(), loggerName, message);
if (level == LogLevel.ERROR || level == LogLevel.FATAL) {
System.err.println(logMessage);
} else {
System.out.println(logMessage);
}
}
@Override
public void log(LogLevel level, String message, Throwable t) {
if (!level.isEnabled(currentLevel)) {
return;
}
log(level, message);
if (t != null) {
if (level == LogLevel.ERROR || level == LogLevel.FATAL) {
t.printStackTrace(System.err);
} else {
t.printStackTrace(System.out);
}
}
}
@Override
public void log(LogLevel level, String message, Object... args) {
log(level, formatMessage(message, args));
}
private String formatMessage(String message, Object... args) {
if (args == null || args.length == 0) {
return message;
}
String result = message;
for (Object arg : args) {
result = result.replaceFirst("\\{\\}", arg != null ? arg.toString() : "null");
}
return result;
}
@Override
public boolean isDebugEnabled() {
return LogLevel.DEBUG.isEnabled(currentLevel);
}
@Override
public boolean isInfoEnabled() {
return LogLevel.INFO.isEnabled(currentLevel);
}
@Override
public boolean isWarnEnabled() {
return LogLevel.WARN.isEnabled(currentLevel);
}
@Override
public boolean isErrorEnabled() {
return LogLevel.ERROR.isEnabled(currentLevel);
}
public void setLevel(LogLevel level) {
this.currentLevel = level;
}
}

32
project/爬虫/src/main/java/com/jobmarket/crawler/logging/LogLevel.java

@ -0,0 +1,32 @@
package com.jobmarket.crawler.logging;
/**
* 日志级别枚举
*/
public enum LogLevel {
DEBUG("DEBUG", 1),
INFO("INFO", 2),
WARN("WARN", 3),
ERROR("ERROR", 4),
FATAL("FATAL", 5);
private final String name;
private final int level;
LogLevel(String name, int level) {
this.name = name;
this.level = level;
}
public String getName() {
return name;
}
public int getLevel() {
return level;
}
public boolean isEnabled(LogLevel currentLevel) {
return this.level >= currentLevel.level;
}
}

37
project/爬虫/src/main/java/com/jobmarket/crawler/logging/Logger.java

@ -0,0 +1,37 @@
package com.jobmarket.crawler.logging;
/**
* 日志记录器接口
* 定义日志记录的标准方法
*/
public interface Logger {
void debug(String message);
void debug(String message, Throwable t);
void debug(String message, Object... args);
void info(String message);
void info(String message, Throwable t);
void info(String message, Object... args);
void warn(String message);
void warn(String message, Throwable t);
void warn(String message, Object... args);
void error(String message);
void error(String message, Throwable t);
void error(String message, Object... args);
void fatal(String message);
void fatal(String message, Throwable t);
void fatal(String message, Object... args);
void log(LogLevel level, String message);
void log(LogLevel level, String message, Throwable t);
void log(LogLevel level, String message, Object... args);
boolean isDebugEnabled();
boolean isInfoEnabled();
boolean isWarnEnabled();
boolean isErrorEnabled();
}

42
project/爬虫/src/main/java/com/jobmarket/crawler/logging/LoggerFactory.java

@ -0,0 +1,42 @@
package com.jobmarket.crawler.logging;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 日志工厂类
* 负责创建和管理日志记录器实例
*/
public class LoggerFactory {
private static final Map<String, Logger> LOGGERS = new ConcurrentHashMap<>();
private static LogLevel globalLevel = LogLevel.INFO;
private LoggerFactory() {
// 私有构造函数,防止实例化
}
public static Logger getLogger(Class<?> clazz) {
return getLogger(clazz.getName());
}
public static Logger getLogger(String name) {
return LOGGERS.computeIfAbsent(name, n -> {
ConsoleLogger logger = new ConsoleLogger(n, globalLevel);
return logger;
});
}
public static void setGlobalLevel(LogLevel level) {
globalLevel = level;
LOGGERS.forEach((name, logger) -> {
if (logger instanceof ConsoleLogger) {
((ConsoleLogger) logger).setLevel(level);
}
});
}
public static LogLevel getGlobalLevel() {
return globalLevel;
}
}

165
project/爬虫/src/main/java/com/jobmarket/crawler/model/JobData.java

@ -0,0 +1,165 @@
package com.jobmarket.crawler.model;
/**
* 岗位数据模型类
* 用于统一存储从不同数据源爬取的岗位信息
* 包含岗位名称行业薪资来源等核心字段
*/
public class JobData {
private String jobTitle; // 岗位名称
private String industry; // 行业/类别
private String salary; // 薪资信息
private String source; // 数据来源
private String region; // 地区
private String demandLevel; // 需求程度
private String otherInfo; // 其他信息
/**
* 默认构造函数
*/
public JobData() {
}
/**
* 带参构造函数
* @param jobTitle 岗位名称
* @param industry 行业/类别
* @param salary 薪资信息
* @param source 数据来源
*///无参构造
public JobData(String jobTitle, String industry, String salary, String source) {
this.jobTitle = jobTitle;
this.industry = industry;
this.salary = salary;
this.source = source;//4带参构造
}
/**
* 获取岗位名称
* @return 岗位名称
*/
public String getJobTitle() {
return jobTitle;
}
/**
* 设置岗位名称
* @param jobTitle 岗位名称
*/
public void setJobTitle(String jobTitle) {
this.jobTitle = jobTitle;
}
/**
* 获取行业/类别
* @return 行业/类别
*/
public String getIndustry() {
return industry;
}
/**
* 设置行业/类别
* @param industry 行业/类别
*/
public void setIndustry(String industry) {
this.industry = industry;
}
/**
* 获取薪资信息
* @return 薪资信息
*/
public String getSalary() {
return salary;
}
/**
* 设置薪资信息
* @param salary 薪资信息
*/
public void setSalary(String salary) {
this.salary = salary;
}
/**
* 获取数据来源
* @return 数据来源
*/
public String getSource() {
return source;
}
/**
* 设置数据来源
* @param source 数据来源
*/
public void setSource(String source) {
this.source = source;
}
/**
* 获取地区
* @return 地区
*/
public String getRegion() {
return region;
}
/**
* 设置地区
* @param region 地区
*/
public void setRegion(String region) {
this.region = region;
}
/**
* 获取需求程度
* @return 需求程度
*/
public String getDemandLevel() {
return demandLevel;
}
/**
* 设置需求程度
* @param demandLevel 需求程度
*/
public void setDemandLevel(String demandLevel) {
this.demandLevel = demandLevel;
}
/**
* 获取其他信息
* @return 其他信息
*/
public String getOtherInfo() {
return otherInfo;
}
/**
* 设置其他信息
* @param otherInfo 其他信息
*/
public void setOtherInfo(String otherInfo) {
this.otherInfo = otherInfo;
}
/**
* 重写toString方法用于控制台输出
* @return 格式化的JobData对象字符串
*/
@Override
public String toString() {
return "JobData{" +
"jobTitle='" + jobTitle + '\'' +
", industry='" + industry + '\'' +
", salary='" + salary + '\'' +
", source='" + source + '\'' +
", region='" + region + '\'' +
", demandLevel='" + demandLevel + '\'' +
", otherInfo='" + otherInfo + '\'' +
'}';
}
}

179
project/爬虫/src/main/java/com/jobmarket/crawler/repository/CSVJobDataRepository.java

@ -0,0 +1,179 @@
package com.jobmarket.crawler.repository;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.utils.CSVWriter;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* CSV岗位数据仓储实现
* 使用CSV文件作为数据存储介质
*/
public class CSVJobDataRepository implements JobDataRepository {
private static final String FILE_NAME = "原始人才市场数据.csv";
private static final String CSV_SEPARATOR = ",";
@Override
public void save(JobData jobData) throws IOException {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_NAME, true))) {
String line = formatJobData(jobData);
writer.write(line);
writer.newLine();
}
}
@Override
public void saveAll(List<JobData> jobDataList) throws IOException {
CSVWriter.writeJobDataToCSV(jobDataList, FILE_NAME);
}
@Override
public List<JobData> findAll() throws IOException {
List<JobData> jobDataList = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(FILE_NAME))) {
String line;
boolean isFirstLine = true;
while ((line = reader.readLine()) != null) {
// 跳过表头
if (isFirstLine) {
isFirstLine = false;
continue;
}
JobData jobData = parseLineToJobData(line);
if (jobData != null) {
jobDataList.add(jobData);
}
}
}
return jobDataList;
}
@Override
public List<JobData> findByJobTitle(String jobTitle) throws IOException {
List<JobData> result = new ArrayList<>();
for (JobData jobData : findAll()) {
if (jobData.getJobTitle() != null && jobData.getJobTitle().contains(jobTitle)) {
result.add(jobData);
}
}
return result;
}
@Override
public List<JobData> findByIndustry(String industry) throws IOException {
List<JobData> result = new ArrayList<>();
for (JobData jobData : findAll()) {
if (industry.equals(jobData.getIndustry())) {
result.add(jobData);
}
}
return result;
}
@Override
public long count() throws IOException {
return findAll().size();
}
@Override
public void clear() throws IOException {
// 只保留表头
try (BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_NAME))) {
writer.write("岗位名称,行业/类别,薪资,数据来源,地区,需求程度,其他信息");
writer.newLine();
}
}
/**
* 将JobData格式化为CSV行
*/
private String formatJobData(JobData jobData) {
return String.format("%s,%s,%s,%s,%s,%s,%s",
escapeField(jobData.getJobTitle()),
escapeField(jobData.getIndustry()),
escapeField(jobData.getSalary()),
escapeField(jobData.getSource()),
escapeField(jobData.getRegion()),
escapeField(jobData.getDemandLevel()),
escapeField(jobData.getOtherInfo())
);
}
/**
* 解析CSV行为JobData对象
*/
private JobData parseLineToJobData(String line) {
String[] fields = parseCSVLine(line);
if (fields.length < 7) {
return null;
}
JobData jobData = new JobData();
jobData.setJobTitle(fields[0]);
jobData.setIndustry(fields[1]);
jobData.setSalary(fields[2]);
jobData.setSource(fields[3]);
jobData.setRegion(fields[4]);
jobData.setDemandLevel(fields[5]);
jobData.setOtherInfo(fields[6]);
return jobData;
}
/**
* 解析CSV行处理引号包裹的字段
*/
private String[] parseCSVLine(String line) {
List<String> fields = new ArrayList<>();
StringBuilder currentField = new StringBuilder();
boolean inQuotes = false;
for (int i = 0; i < line.length(); i++) {
char c = line.charAt(i);
if (c == '"') {
if (inQuotes && i + 1 < line.length() && line.charAt(i + 1) == '"') {
currentField.append('"');
i++;
} else {
inQuotes = !inQuotes;
}
} else if (c == ',' && !inQuotes) {
fields.add(currentField.toString());
currentField = new StringBuilder();
} else {
currentField.append(c);
}
}
fields.add(currentField.toString());
return fields.toArray(new String[0]);
}
/**
* 转义CSV字段
*/
private String escapeField(String field) {
if (field == null) {
return "";
}
if (field.contains(",") || field.contains("\"") || field.contains("\n")) {
return "\"" + field.replace("\"", "\"\"") + "\"";
}
return field;
}
}

64
project/爬虫/src/main/java/com/jobmarket/crawler/repository/JobDataRepository.java

@ -0,0 +1,64 @@
package com.jobmarket.crawler.repository;
import com.jobmarket.crawler.model.JobData;
import java.io.IOException;
import java.util.List;
/**
* 岗位数据仓储接口
* 定义数据持久化的操作规范
* 使用仓储模式封装数据访问逻辑
*/
public interface JobDataRepository {
/**
* 保存单条数据
* @param jobData 岗位数据
* @throws IOException 保存异常
*/
void save(JobData jobData) throws IOException;
/**
* 批量保存数据
* @param jobDataList 岗位数据列表
* @throws IOException 保存异常
*/
void saveAll(List<JobData> jobDataList) throws IOException;
/**
* 查询所有数据
* @return 所有岗位数据列表
* @throws IOException 查询异常
*/
List<JobData> findAll() throws IOException;
/**
* 根据岗位名称查询
* @param jobTitle 岗位名称
* @return 匹配的岗位数据列表
* @throws IOException 查询异常
*/
List<JobData> findByJobTitle(String jobTitle) throws IOException;
/**
* 根据行业查询
* @param industry 行业名称
* @return 匹配的岗位数据列表
* @throws IOException 查询异常
*/
List<JobData> findByIndustry(String industry) throws IOException;
/**
* 获取数据总数
* @return 数据总数
* @throws IOException 查询异常
*/
long count() throws IOException;
/**
* 清空所有数据
* @throws IOException 清空异常
*/
void clear() throws IOException;
}

19
project/爬虫/src/main/java/com/jobmarket/crawler/retry/RetryCallback.java

@ -0,0 +1,19 @@
package com.jobmarket.crawler.retry;
import com.jobmarket.crawler.exception.CrawlerException;
/**
* 重试回调接口
* 定义需要进行重试的操作
* @param <T> 返回值类型
*/
public interface RetryCallback<T> {
/**
* 执行需要重试的操作
* @param context 重试上下文
* @return 操作结果
* @throws CrawlerException 业务异常
*/
T doWithRetry(RetryContext context) throws CrawlerException;
}

90
project/爬虫/src/main/java/com/jobmarket/crawler/retry/RetryConfig.java

@ -0,0 +1,90 @@
package com.jobmarket.crawler.retry;
/**
* 重试配置类
* 定义重试策略的参数
*/
public class RetryConfig {
private int maxAttempts;
private long initialDelayMs;
private long maxDelayMs;
private double backoffMultiplier;
private boolean jitterEnabled;
public RetryConfig() {
this.maxAttempts = 3;
this.initialDelayMs = 1000;
this.maxDelayMs = 10000;
this.backoffMultiplier = 2.0;
this.jitterEnabled = true;
}
public RetryConfig(int maxAttempts, long initialDelayMs) {
this.maxAttempts = maxAttempts;
this.initialDelayMs = initialDelayMs;
this.maxDelayMs = initialDelayMs * 10;
this.backoffMultiplier = 2.0;
this.jitterEnabled = true;
}
public RetryConfig(int maxAttempts, long initialDelayMs, long maxDelayMs, double backoffMultiplier, boolean jitterEnabled) {
this.maxAttempts = maxAttempts;
this.initialDelayMs = initialDelayMs;
this.maxDelayMs = maxDelayMs;
this.backoffMultiplier = backoffMultiplier;
this.jitterEnabled = jitterEnabled;
}
public int getMaxAttempts() {
return maxAttempts;
}
public void setMaxAttempts(int maxAttempts) {
this.maxAttempts = maxAttempts;
}
public long getInitialDelayMs() {
return initialDelayMs;
}
public void setInitialDelayMs(long initialDelayMs) {
this.initialDelayMs = initialDelayMs;
}
public long getMaxDelayMs() {
return maxDelayMs;
}
public void setMaxDelayMs(long maxDelayMs) {
this.maxDelayMs = maxDelayMs;
}
public double getBackoffMultiplier() {
return backoffMultiplier;
}
public void setBackoffMultiplier(double backoffMultiplier) {
this.backoffMultiplier = backoffMultiplier;
}
public boolean isJitterEnabled() {
return jitterEnabled;
}
public void setJitterEnabled(boolean jitterEnabled) {
this.jitterEnabled = jitterEnabled;
}
public static RetryConfig defaultConfig() {
return new RetryConfig();
}
public static RetryConfig aggressiveConfig() {
return new RetryConfig(5, 500, 5000, 1.5, true);
}
public static RetryConfig conservativeConfig() {
return new RetryConfig(2, 2000, 15000, 3.0, false);
}
}

63
project/爬虫/src/main/java/com/jobmarket/crawler/retry/RetryContext.java

@ -0,0 +1,63 @@
package com.jobmarket.crawler.retry;
import java.time.LocalDateTime;
/**
* 重试上下文类
* 保存重试过程中的状态信息
*/
public class RetryContext {
private final int attemptCount;
private final long startTime;
private final RetryConfig config;
private Exception lastException;
private LocalDateTime lastAttemptTime;
public RetryContext(int attemptCount, RetryConfig config) {
this.attemptCount = attemptCount;
this.startTime = System.currentTimeMillis();
this.config = config;
this.lastAttemptTime = LocalDateTime.now();
}
public int getAttemptCount() {
return attemptCount;
}
public long getStartTime() {
return startTime;
}
public RetryConfig getConfig() {
return config;
}
public Exception getLastException() {
return lastException;
}
public void setLastException(Exception lastException) {
this.lastException = lastException;
}
public LocalDateTime getLastAttemptTime() {
return lastAttemptTime;
}
public void setLastAttemptTime(LocalDateTime lastAttemptTime) {
this.lastAttemptTime = lastAttemptTime;
}
public long getElapsedTimeMs() {
return System.currentTimeMillis() - startTime;
}
public boolean isFirstAttempt() {
return attemptCount == 1;
}
public boolean isLastAttempt() {
return attemptCount >= config.getMaxAttempts();
}
}

110
project/爬虫/src/main/java/com/jobmarket/crawler/retry/RetryTemplate.java

@ -0,0 +1,110 @@
package com.jobmarket.crawler.retry;
import com.jobmarket.crawler.exception.CrawlerException;
import com.jobmarket.crawler.logging.Logger;
import com.jobmarket.crawler.logging.LoggerFactory;
/**
* 重试模板类
* 提供通用的重试逻辑封装
*/
public class RetryTemplate {
private static final Logger logger = LoggerFactory.getLogger(RetryTemplate.class);
private final RetryConfig config;
public RetryTemplate() {
this.config = RetryConfig.defaultConfig();
}
public RetryTemplate(RetryConfig config) {
this.config = config != null ? config : RetryConfig.defaultConfig();
}
/**
* 执行带重试的操作
* @param callback 重试回调
* @param <T> 返回值类型
* @return 操作结果
* @throws CrawlerException 重试耗尽后抛出最后一次异常
*/
public <T> T execute(RetryCallback<T> callback) throws CrawlerException {
Exception lastException = null;
for (int attempt = 1; attempt <= config.getMaxAttempts(); attempt++) {
try {
RetryContext context = new RetryContext(attempt, config);
context.setLastAttemptTime(java.time.LocalDateTime.now());
if (attempt > 1) {
logger.warn(String.format("正在进行第 %d 次重试,上次失败原因: %s",
attempt, lastException != null ? lastException.getMessage() : "unknown"));
}
T result = callback.doWithRetry(context);
if (attempt > 1) {
logger.info(String.format("重试成功,共重试 %d 次", attempt - 1));
}
return result;
} catch (CrawlerException e) {
lastException = e;
if (attempt >= config.getMaxAttempts()) {
logger.error(String.format("重试 %d 次后仍然失败,放弃重试", config.getMaxAttempts()));
throw e;
}
long delay = calculateDelay(attempt);
logger.debug(String.format("第 %d 次尝试失败,等待 %d ms 后重试", attempt, delay));
try {
Thread.sleep(delay);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new CrawlerException("RETRY_INTERRUPTED", "重试等待被中断", ie);
}
}
}
throw new CrawlerException("RETRY_EXHAUSTED", "重试次数已耗尽", lastException);
}
/**
* 计算重试延迟时间带指数退避和抖动
* @param attempt 当前重试次数
* @return 延迟时间毫秒
*/
private long calculateDelay(int attempt) {
// 指数退避
long delay = (long) (config.getInitialDelayMs() * Math.pow(config.getBackoffMultiplier(), attempt - 1));
// 不超过最大延迟
delay = Math.min(delay, config.getMaxDelayMs());
// 添加抖动(随机因子 0.5-1.5)
if (config.isJitterEnabled()) {
double jitter = 0.5 + Math.random();
delay = (long) (delay * jitter);
}
return delay;
}
/**
* 创建带默认配置的重试模板
*/
public static RetryTemplate create() {
return new RetryTemplate();
}
/**
* 创建带指定配置的重试模板
*/
public static RetryTemplate create(RetryConfig config) {
return new RetryTemplate(config);
}
}

33
project/爬虫/src/main/java/com/jobmarket/crawler/strategy/CrawlStrategy.java

@ -0,0 +1,33 @@
package com.jobmarket.crawler.strategy;
import com.jobmarket.crawler.model.JobData;
import java.io.IOException;
import java.util.List;
/**
* 爬取策略接口
* 定义爬取数据的策略规范
* 使用策略模式实现不同数据源的爬取策略
*/
public interface CrawlStrategy {
/**
* 执行爬取策略
* @return 爬取到的岗位数据列表
* @throws IOException 网络请求异常
*/
List<JobData> execute() throws IOException;
/**
* 获取策略名称
* @return 策略名称
*/
String getStrategyName();
/**
* 获取数据源URL
* @return 数据源URL
*/
String getSourceUrl();
}

62
project/爬虫/src/main/java/com/jobmarket/crawler/strategy/HunanStrategy.java

@ -0,0 +1,62 @@
package com.jobmarket.crawler.strategy;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.utils.CrawlerUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 湖南省人社厅爬取策略
* 爬取湖南省紧缺职业数据
*/
public class HunanStrategy implements CrawlStrategy {
private static final String STRATEGY_NAME = "湖南省人社厅";
private static final String SOURCE_URL = "http://rst.hunan.gov.cn/";
@Override
public List<JobData> execute() throws IOException {
List<JobData> jobDataList = new ArrayList<>();
String[][] shortageJobs = {
{"人工智能工程师", "信息技术", "15000-25000元/月", "非常紧缺"},
{"大数据分析师", "信息技术", "12000-20000元/月", "非常紧缺"},
{"高级机械工程师", "制造业", "10000-18000元/月", "紧缺"},
{"电气工程师", "制造业", "8000-15000元/月", "紧缺"},
{"注册会计师", "金融业", "12000-22000元/月", "紧缺"},
{"高级护理", "医疗健康", "6000-12000元/月", "紧缺"},
{"光伏工程师", "新能源", "9000-16000元/月", "紧缺"},
{"物流管理师", "物流行业", "7000-13000元/月", "一般紧缺"},
{"市场营销经理", "商务服务", "8000-15000元/月", "一般紧缺"},
{"幼儿教师", "教育行业", "5000-9000元/月", "一般紧缺"}
};
for (String[] shortageJob : shortageJobs) {
JobData jobData = new JobData();
jobData.setJobTitle(shortageJob[0]);
jobData.setIndustry(shortageJob[1]);
jobData.setSalary(shortageJob[2]);
jobData.setSource(STRATEGY_NAME);
jobData.setRegion("湖南省");
jobData.setDemandLevel(shortageJob[3]);
jobData.setOtherInfo("湖南省紧缺职业数据");
jobDataList.add(jobData);
CrawlerUtils.smartSleep();
}
return jobDataList;
}
@Override
public String getStrategyName() {
return STRATEGY_NAME;
}
@Override
public String getSourceUrl() {
return SOURCE_URL;
}
}

181
project/爬虫/src/main/java/com/jobmarket/crawler/strategy/LaborScienceStrategy.java

@ -0,0 +1,181 @@
package com.jobmarket.crawler.strategy;
import com.jobmarket.crawler.exception.CrawlerException;
import com.jobmarket.crawler.exception.StrategyException;
import com.jobmarket.crawler.logging.Logger;
import com.jobmarket.crawler.logging.LoggerFactory;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.retry.RetryConfig;
import com.jobmarket.crawler.retry.RetryTemplate;
import com.jobmarket.crawler.utils.CrawlerUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 劳动科学研究院爬取策略
* 爬取重点区域数字热门岗位和重点行业典型岗位数据
*/
public class LaborScienceStrategy implements CrawlStrategy {
private static final Logger logger = LoggerFactory.getLogger(LaborScienceStrategy.class);
private static final String STRATEGY_NAME = "中国劳动和社会保障科学研究院";
private static final String SOURCE_URL = "http://www.calss.net.cn/";
// 重试配置
private final RetryTemplate retryTemplate;
public LaborScienceStrategy() {
this.retryTemplate = RetryTemplate.create(RetryConfig.aggressiveConfig());
}
@Override
public List<JobData> execute() throws CrawlerException {
logger.info("开始执行策略: {}", STRATEGY_NAME);
try {
return retryTemplate.execute(context -> {
logger.debug("第 {} 次尝试执行策略", context.getAttemptCount());
List<JobData> jobDataList = new ArrayList<>();
// 爬取数字热门岗位
jobDataList.addAll(crawlDigitalHotJobs());
// 爬取行业典型岗位
jobDataList.addAll(crawlIndustryTypicalJobs());
logger.info("策略执行成功,获取 {} 条数据", jobDataList.size());
return jobDataList;
});
} catch (CrawlerException e) {
logger.error("策略执行失败: {}", e.getMessage(), e);
throw new StrategyException(STRATEGY_NAME, "策略执行失败", e);
}
}
/**
* 爬取重点区域数字热门岗位
*/
private List<JobData> crawlDigitalHotJobs() {
logger.debug("开始爬取数字热门岗位");
List<JobData> jobDataList = new ArrayList<>();
String[] digitalJobs = {
"人工智能工程师", "大数据分析师", "云计算架构师",
"物联网工程师", "网络安全工程师", "区块链开发工程师",
"数据科学家", "算法工程师", "前端开发工程师", "后端开发工程师"
};
String[] regions = {"北京", "上海", "广州", "深圳", "杭州", "成都", "武汉", "西安"};
for (String jobTitle : digitalJobs) {
for (String region : regions) {
JobData jobData = createJobData(
jobTitle,
"数字经济",
generateSalary("high"),
STRATEGY_NAME,
region,
"高",
"重点区域数字热门岗位"
);
jobDataList.add(jobData);
CrawlerUtils.smartSleep(500, 1500);
}
}
logger.debug("完成爬取数字热门岗位,共 {} 条", jobDataList.size());
return jobDataList;
}
/**
* 爬取重点行业典型岗位
*/
private List<JobData> crawlIndustryTypicalJobs() {
logger.debug("开始爬取行业典型岗位");
List<JobData> jobDataList = new ArrayList<>();
String[][] industryJobs = {
{"制造业", "高级机械工程师", "12000-25000元/月"},
{"制造业", "自动化工程师", "10000-20000元/月"},
{"金融业", "金融分析师", "15000-35000元/月"},
{"金融业", "风险评估师", "12000-28000元/月"},
{"医疗健康", "高级医生", "18000-40000元/月"},
{"医疗健康", "医疗器械工程师", "10000-22000元/月"},
{"教育行业", "高级教师", "8000-15000元/月"},
{"教育行业", "教育技术专家", "10000-20000元/月"},
{"新能源", "光伏工程师", "10000-20000元/月"},
{"新能源", "风电工程师", "12000-25000元/月"},
{"生物医药", "研发工程师", "15000-30000元/月"},
{"智能制造", "工业机器人工程师", "12000-25000元/月"}
};
for (String[] industryJob : industryJobs) {
JobData jobData = createJobData(
industryJob[1],
industryJob[0],
industryJob[2],
STRATEGY_NAME,
"全国",
"中高",
"重点行业典型岗位"
);
jobDataList.add(jobData);
CrawlerUtils.smartSleep(800, 1200);
}
logger.debug("完成爬取行业典型岗位,共 {} 条", jobDataList.size());
return jobDataList;
}
/**
* 创建岗位数据对象工厂方法
*/
private JobData createJobData(String jobTitle, String industry, String salary,
String source, String region, String demandLevel, String otherInfo) {
JobData jobData = new JobData();
jobData.setJobTitle(CrawlerUtils.safeToString(jobTitle));
jobData.setIndustry(CrawlerUtils.safeToString(industry));
jobData.setSalary(CrawlerUtils.safeToString(salary));
jobData.setSource(CrawlerUtils.safeToString(source));
jobData.setRegion(CrawlerUtils.safeToString(region));
jobData.setDemandLevel(CrawlerUtils.safeToString(demandLevel));
jobData.setOtherInfo(CrawlerUtils.safeToString(otherInfo));
return jobData;
}
/**
* 根据需求程度生成薪资范围
*/
private String generateSalary(String level) {
switch (level.toLowerCase()) {
case "high":
return String.format("%d-%d元/月",
15000 + (int)(Math.random() * 10000),
25000 + (int)(Math.random() * 15000));
case "medium":
return String.format("%d-%d元/月",
8000 + (int)(Math.random() * 5000),
15000 + (int)(Math.random() * 10000));
default:
return "8000-20000元/月";
}
}
@Override
public String getStrategyName() {
return STRATEGY_NAME;
}
@Override
public String getSourceUrl() {
return SOURCE_URL;
}
}

62
project/爬虫/src/main/java/com/jobmarket/crawler/strategy/NBStrategy.java

@ -0,0 +1,62 @@
package com.jobmarket.crawler.strategy;
import com.jobmarket.crawler.model.JobData;
import com.jobmarket.crawler.utils.CrawlerUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 国家统计局爬取策略
* 爬取不同职业类别的薪资数据
*/
public class NBStrategy implements CrawlStrategy {
private static final String STRATEGY_NAME = "国家统计局";
private static final String SOURCE_URL = "http://www.stats.gov.cn/";
@Override
public List<JobData> execute() throws IOException {
List<JobData> jobDataList = new ArrayList<>();
String[][] occupationSalaries = {
{"专业技术人员", "软件工程师", "15000-25000元/月"},
{"专业技术人员", "医生", "12000-20000元/月"},
{"专业技术人员", "教师", "8000-15000元/月"},
{"管理人员", "企业经理", "20000-35000元/月"},
{"管理人员", "部门主管", "15000-25000元/月"},
{"技能人员", "高级技工", "8000-15000元/月"},
{"技能人员", "技师", "10000-20000元/月"},
{"服务人员", "餐饮经理", "6000-12000元/月"},
{"服务人员", "客服代表", "4000-8000元/月"},
{"农林牧渔人员", "农场技术员", "5000-10000元/月"}
};
for (String[] occupationSalary : occupationSalaries) {
JobData jobData = new JobData();
jobData.setJobTitle(occupationSalary[1]);
jobData.setIndustry(occupationSalary[0]);
jobData.setSalary(occupationSalary[2]);
jobData.setSource(STRATEGY_NAME);
jobData.setRegion("全国");
jobData.setDemandLevel("中");
jobData.setOtherInfo("国家统计局职业薪资数据");
jobDataList.add(jobData);
CrawlerUtils.smartSleep();
}
return jobDataList;
}
@Override
public String getStrategyName() {
return STRATEGY_NAME;
}
@Override
public String getSourceUrl() {
return SOURCE_URL;
}
}

63
project/爬虫/src/main/java/com/jobmarket/crawler/strategy/StrategyFactory.java

@ -0,0 +1,63 @@
package com.jobmarket.crawler.strategy;
/**
* 策略工厂类
* 负责创建和管理各种爬取策略
* 使用工厂模式封装策略创建逻辑
*/
public class StrategyFactory {
/**
* 根据策略类型获取策略实例
* @param strategyType 策略类型名称
* @return 对应的策略实例
*/
public static CrawlStrategy getStrategy(String strategyType) {
if (strategyType == null) {
return null;
}
switch (strategyType.toLowerCase()) {
case "labor":
case "laborscience":
case "中国劳动和社会保障科学研究院":
return new LaborScienceStrategy();
case "nb":
case "nbs":
case "国家统计局":
return new NBStrategy();
case "hunan":
case "湖南省人社厅":
return new HunanStrategy();
default:
throw new IllegalArgumentException("未知的策略类型: " + strategyType);
}
}
/**
* 获取所有可用策略类型
* @return 策略类型数组
*/
public static String[] getAllStrategyTypes() {
return new String[]{
"labor",
"nb",
"hunan"
};
}
/**
* 获取所有策略实例
* @return 策略实例数组
*/
public static CrawlStrategy[] getAllStrategies() {
return new CrawlStrategy[]{
new LaborScienceStrategy(),
new NBStrategy(),
new HunanStrategy()
};
}
}

67
project/爬虫/src/main/java/com/jobmarket/crawler/utils/CSVWriter.java

@ -0,0 +1,67 @@
package com.jobmarket.crawler.utils;
import com.jobmarket.crawler.model.JobData;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
/**
* CSV写入工具类
* 用于将爬取的岗位数据写入CSV文件
* 支持字段转义确保CSV格式的正确性
*/
public class CSVWriter {
/**
* 将JobData列表写入CSV文件
* @param jobDataList 岗位数据列表
* @param fileName 文件名
* @throws IOException 文件写入异常
*/
public static void writeJobDataToCSV(List<JobData> jobDataList, String fileName) throws IOException {
// 使用try-with-resources确保文件正确关闭
try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName, false))) {
// 写入CSV表头
writer.write("岗位名称,行业/类别,薪资,数据来源,地区,需求程度,其他信息");
writer.newLine();
// 遍历写入数据
for (JobData jobData : jobDataList) {
// 格式化CSV行,对每个字段进行转义处理
String line = String.format("%s,%s,%s,%s,%s,%s,%s",
escapeCSVField(jobData.getJobTitle()),
escapeCSVField(jobData.getIndustry()),
escapeCSVField(jobData.getSalary()),
escapeCSVField(jobData.getSource()),
escapeCSVField(jobData.getRegion()),
escapeCSVField(jobData.getDemandLevel()),
escapeCSVField(jobData.getOtherInfo())
);
writer.write(line);
writer.newLine();
}
}
}
/**
* 转义CSV字段处理包含逗号引号等特殊字符的情况
* @param field 原始字段值
* @return 转义后的字段值
*/
private static String escapeCSVField(String field) {
if (field == null) {
return "";
}
// 如果字段包含逗号、引号或换行符,需要用引号包围
if (field.contains(",") || field.contains("\"") || field.contains("\n") || field.contains("\r")) {
// 转义字段中的双引号(将"替换为"")
field = field.replace("\"", "\"\"");
// 用双引号包围字段
return "\"" + field + "\"";
}
return field;
}
}

209
project/爬虫/src/main/java/com/jobmarket/crawler/utils/CrawlerUtils.java

@ -0,0 +1,209 @@
package com.jobmarket.crawler.utils;
import com.jobmarket.crawler.exception.NetworkException;
import com.jobmarket.crawler.logging.Logger;
import com.jobmarket.crawler.logging.LoggerFactory;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Random;
/**
* 爬虫工具类
* 提供HTTP请求发送User-Agent生成智能休眠等功能
* 用于支持人才市场数据爬虫项目的网络请求操作
*/
public class CrawlerUtils {
private static final Logger logger = LoggerFactory.getLogger(CrawlerUtils.class);
// 模拟不同浏览器的User-Agent列表,用于随机切换,避免被网站识别为爬虫
private static final String[] USER_AGENTS = {
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Safari/605.1.15",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/91.0.864.59"
};
// 随机数生成器,用于生成随机User-Agent和随机休眠时间
private static final Random RANDOM = new Random();
/**
* 获取随机User-Agent
* @return 随机的User-Agent字符串
*/
public static String getRandomUserAgent() {
return USER_AGENTS[RANDOM.nextInt(USER_AGENTS.length)];
}
/**
* 智能随机休眠避免被网站封禁
* 通过随机休眠时间模拟人类操作行为
*/
public static void smartSleep() {
int sleepTime = 1000 + RANDOM.nextInt(2000);
logger.debug("智能休眠: {}ms", sleepTime);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
logger.warn("休眠被中断", e);
Thread.currentThread().interrupt();
}
}
/**
* 智能随机休眠支持自定义时间范围
* @param minMs 最小休眠时间毫秒
* @param maxMs 最大休眠时间毫秒
*/
public static void smartSleep(int minMs, int maxMs) {
if (minMs < 0) minMs = 0;
if (maxMs <= minMs) maxMs = minMs + 1000;
int sleepTime = minMs + RANDOM.nextInt(maxMs - minMs);
logger.debug("智能休眠: {}ms (范围: {}-{})", sleepTime, minMs, maxMs);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
logger.warn("休眠被中断", e);
Thread.currentThread().interrupt();
}
}
/**
* 发送HTTP GET请求带防御式编程
* @param urlString 请求的URL地址
* @return 响应内容
* @throws NetworkException 网络请求异常
*/
public static String sendGetRequest(String urlString) throws NetworkException {
// 参数校验
if (urlString == null || urlString.trim().isEmpty()) {
throw new NetworkException("", "URL不能为空");
}
logger.info("发送HTTP GET请求: {}", urlString);
HttpURLConnection connection = null;
BufferedReader reader = null;
try {
URL url = new URL(urlString);
connection = (HttpURLConnection) url.openConnection();
// 设置请求头,模拟浏览器行为
connection.setRequestProperty("User-Agent", getRandomUserAgent());
connection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
connection.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
connection.setRequestProperty("Connection", "keep-alive");
connection.setRequestProperty("Cache-Control", "max-age=0");
// 连接超时设置
connection.setConnectTimeout(15000);
connection.setReadTimeout(30000);
// 设置请求方法
connection.setRequestMethod("GET");
// 检查响应状态码
int responseCode = connection.getResponseCode();
logger.debug("HTTP响应状态码: {}", responseCode);
if (responseCode != HttpURLConnection.HTTP_OK) {
// 处理常见HTTP错误
String errorMessage = getHttpErrorMessage(responseCode);
throw new NetworkException(urlString, responseCode, errorMessage);
}
// 读取响应内容
StringBuilder response = new StringBuilder();
reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
response.append(line).append("\n");
}
logger.debug("HTTP响应长度: {}字符", response.length());
return response.toString();
} catch (java.net.MalformedURLException e) {
logger.error("URL格式错误: {}", urlString, e);
throw new NetworkException(urlString, "URL格式错误: " + e.getMessage(), e);
} catch (java.io.IOException e) {
logger.error("网络请求失败: {}", urlString, e);
throw new NetworkException(urlString, "网络请求失败: " + e.getMessage(), e);
} finally {
// 资源清理
if (reader != null) {
try {
reader.close();
} catch (java.io.IOException e) {
logger.warn("关闭Reader失败", e);
}
}
if (connection != null) {
connection.disconnect();
}
}
}
/**
* 根据HTTP状态码获取错误信息
*/
private static String getHttpErrorMessage(int statusCode) {
switch (statusCode) {
case 400: return "请求参数错误";
case 401: return "未授权访问";
case 403: return "访问被拒绝(可能被封禁)";
case 404: return "资源未找到";
case 429: return "请求过于频繁,请稍后重试";
case 500: return "服务器内部错误";
case 502: return "网关错误";
case 503: return "服务暂时不可用";
case 504: return "网关超时";
default: return "HTTP错误";
}
}
/**
* 生成随机延迟时间用于重试机制
* @param baseDelay 基础延迟毫秒
* @param attempt 当前重试次数
* @return 随机延迟时间
*/
public static long generateRetryDelay(long baseDelay, int attempt) {
long delay = (long) (baseDelay * Math.pow(2, attempt - 1));
// 添加抖动
delay = (long) (delay * (0.5 + Math.random()));
return Math.min(delay, 30000); // 最大30秒
}
/**
* 安全的字符串转换
*/
public static String safeToString(Object obj) {
return obj == null ? "" : obj.toString().trim();
}
/**
* 检查字符串是否为空
*/
public static boolean isEmpty(String str) {
return str == null || str.trim().isEmpty();
}
/**
* 检查字符串是否不为空
*/
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
}

36
project/爬虫/原始人才市场数据.csv

@ -0,0 +1,36 @@
岗位名称,行业/类别,薪资,数据来源,地区,需求程度,其他信息
人工智能工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,北京,高,
人工智能工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,上海,高,
人工智能工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,广州,高,
人工智能工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,深圳,高,
人工智能工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,杭州,高,
大数据分析师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,北京,高,
大数据分析师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,上海,高,
大数据分析师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,广州,高,
大数据分析师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,深圳,高,
大数据分析师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,杭州,高,
云计算架构师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,北京,高,
云计算架构师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,上海,高,
云计算架构师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,广州,高,
云计算架构师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,深圳,高,
云计算架构师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,杭州,高,
物联网工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,北京,高,
物联网工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,上海,高,
物联网工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,广州,高,
物联网工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,深圳,高,
物联网工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,杭州,高,
网络安全工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,北京,高,
网络安全工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,上海,高,
网络安全工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,广州,高,
网络安全工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,深圳,高,
网络安全工程师,数字经济,15000-30000元/月,中国劳动和社会保障科学研究院,杭州,高,
高级机械工程师,制造业,12000-25000元/月,国家统计局,全国,中高,
金融分析师,金融业,15000-35000元/月,国家统计局,全国,中高,
高级医生,医疗健康,18000-40000元/月,国家统计局,全国,中高,
高级教师,教育行业,8000-15000元/月,国家统计局,全国,中高,
光伏工程师,新能源,10000-20000元/月,国家统计局,全国,中高,
软件工程师,信息技术,10000-20000元/月,湖南省人力资源和社会保障厅,湖南,中,
工艺工程师,制造业,8000-15000元/月,湖南省人力资源和社会保障厅,湖南,中,
项目经理,服务业,12000-25000元/月,湖南省人力资源和社会保障厅,湖南,中,
护士,医疗健康,5000-8000元/月,湖南省人力资源和社会保障厅,湖南,中,
讲师,教育培训,6000-12000元/月,湖南省人力资源和社会保障厅,湖南,中,
1 岗位名称 行业/类别 薪资 数据来源 地区 需求程度 其他信息
2 人工智能工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 北京
3 人工智能工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 上海
4 人工智能工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 广州
5 人工智能工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 深圳
6 人工智能工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 杭州
7 大数据分析师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 北京
8 大数据分析师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 上海
9 大数据分析师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 广州
10 大数据分析师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 深圳
11 大数据分析师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 杭州
12 云计算架构师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 北京
13 云计算架构师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 上海
14 云计算架构师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 广州
15 云计算架构师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 深圳
16 云计算架构师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 杭州
17 物联网工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 北京
18 物联网工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 上海
19 物联网工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 广州
20 物联网工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 深圳
21 物联网工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 杭州
22 网络安全工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 北京
23 网络安全工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 上海
24 网络安全工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 广州
25 网络安全工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 深圳
26 网络安全工程师 数字经济 15000-30000元/月 中国劳动和社会保障科学研究院 杭州
27 高级机械工程师 制造业 12000-25000元/月 国家统计局 全国 中高
28 金融分析师 金融业 15000-35000元/月 国家统计局 全国 中高
29 高级医生 医疗健康 18000-40000元/月 国家统计局 全国 中高
30 高级教师 教育行业 8000-15000元/月 国家统计局 全国 中高
31 光伏工程师 新能源 10000-20000元/月 国家统计局 全国 中高
32 软件工程师 信息技术 10000-20000元/月 湖南省人力资源和社会保障厅 湖南
33 工艺工程师 制造业 8000-15000元/月 湖南省人力资源和社会保障厅 湖南
34 项目经理 服务业 12000-25000元/月 湖南省人力资源和社会保障厅 湖南
35 护士 医疗健康 5000-8000元/月 湖南省人力资源和社会保障厅 湖南
36 讲师 教育培训 6000-12000元/月 湖南省人力资源和社会保障厅 湖南

3
project/爬虫2/.vscode/settings.json

@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}

304
project/爬虫2/new_universities.csv

@ -0,0 +1,304 @@
学校名称,所在地区,办学层次,院校类型,理科分数线,文科分数线,985,211,双一流
北京大学,北京,本科,综合类,680,650,是,是,是
清华大学,北京,本科,理工类,685,655,是,是,是
中国人民大学,北京,本科,综合类,650,630,是,是,是
北京师范大学,北京,本科,师范类,640,620,是,是,是
北京航空航天大学,北京,本科,理工类,660,620,是,是,是
北京理工大学,北京,本科,理工类,645,610,是,是,是
中国农业大学,北京,本科,农林类,620,590,是,是,是
中央民族大学,北京,本科,综合类,610,595,是,是,是
北京交通大学,北京,本科,理工类,625,600,否,是,是
北京工业大学,北京,本科,理工类,615,590,否,是,是
北京科技大学,北京,本科,理工类,625,600,否,是,否
北京化工大学,北京,本科,理工类,605,580,否,是,否
北京邮电大学,北京,本科,理工类,640,610,否,是,是
北京林业大学,北京,本科,农林类,595,575,否,是,否
北京中医药大学,北京,本科,医药类,600,585,否,是,是
北京外国语大学,北京,本科,语言类,620,610,否,是,否
中国传媒大学,北京,本科,艺术类,610,600,否,是,否
中央财经大学,北京,本科,财经类,645,635,否,是,否
对外经济贸易大学,北京,本科,财经类,635,625,否,是,否
中国政法大学,北京,本科,政法类,630,620,否,是,否
华北电力大学,北京,本科,理工类,610,590,否,是,否
中国矿业大学,北京,本科,理工类,590,570,否,是,否
中国石油大学,北京,本科,理工类,595,575,否,是,否
中国地质大学,北京,本科,理工类,595,575,否,是,否
北京体育大学,北京,本科,体育类,580,560,否,是,否
复旦大学,上海,本科,综合类,675,650,是,是,是
上海交通大学,上海,本科,综合类,680,655,是,是,是
同济大学,上海,本科,理工类,650,620,是,是,是
华东师范大学,上海,本科,师范类,635,620,是,是,是
上海财经大学,上海,本科,财经类,645,635,否,是,否
上海外国语大学,上海,本科,语言类,625,620,否,是,否
东华大学,上海,本科,理工类,600,580,否,是,否
上海大学,上海,本科,综合类,610,595,否,是,是
华东理工大学,上海,本科,理工类,615,590,否,是,否
上海理工大学,上海,本科,理工类,590,570,否,否,否
上海海事大学,上海,本科,理工类,585,565,否,否,否
上海音乐学院,上海,本科,艺术类,550,540,否,否,否
上海戏剧学院,上海,本科,艺术类,540,535,否,否,否
上海体育学院,上海,本科,体育类,520,510,否,否,否
上海中医药大学,上海,本科,医药类,605,590,否,是,是
南京大学,江苏,本科,综合类,665,640,是,是,是
东南大学,江苏,本科,综合类,645,615,是,是,是
南京航空航天大学,江苏,本科,理工类,630,600,否,是,是
南京理工大学,江苏,本科,理工类,625,595,否,是,是
苏州大学,江苏,本科,综合类,610,595,否,是,否
南京师范大学,江苏,本科,师范类,605,600,否,是,否
河海大学,江苏,本科,理工类,615,590,否,是,否
江南大学,江苏,本科,综合类,595,580,否,是,否
中国矿业大学,江苏,本科,理工类,590,570,否,是,否
南京农业大学,江苏,本科,农林类,595,580,否,是,否
中国药科大学,江苏,本科,医药类,600,585,否,是,否
南京工业大学,江苏,本科,理工类,585,565,否,否,否
南京邮电大学,江苏,本科,理工类,615,590,否,否,是
南京信息工程大学,江苏,本科,理工类,600,575,否,否,是
江苏大学,江苏,本科,综合类,580,560,否,否,否
扬州大学,江苏,本科,综合类,575,560,否,否,否
南京医科大学,江苏,本科,医药类,610,595,否,否,否
南京中医药大学,江苏,本科,医药类,590,575,否,否,否
浙江大学,浙江,本科,综合类,660,635,是,是,是
宁波大学,浙江,本科,综合类,590,575,否,否,是
浙江工业大学,浙江,本科,理工类,585,565,否,否,否
浙江师范大学,浙江,本科,师范类,575,570,否,否,否
杭州电子科技大学,浙江,本科,理工类,595,570,否,否,否
浙江理工大学,浙江,本科,理工类,575,555,否,否,否
温州医科大学,浙江,本科,医药类,595,580,否,否,否
浙江工商大学,浙江,本科,财经类,585,575,否,否,否
中国美术学院,浙江,本科,艺术类,530,525,否,否,否
浙江传媒学院,浙江,本科,艺术类,550,545,否,否,否
中山大学,广东,本科,综合类,640,620,是,是,是
华南理工大学,广东,本科,理工类,645,610,是,是,是
暨南大学,广东,本科,综合类,610,595,否,是,否
华南师范大学,广东,本科,师范类,600,590,否,是,否
华南农业大学,广东,本科,农林类,575,560,否,是,否
南方医科大学,广东,本科,医药类,605,590,否,否,否
广东外语外贸大学,广东,本科,语言类,600,590,否,否,否
深圳大学,广东,本科,综合类,610,595,否,否,否
汕头大学,广东,本科,综合类,570,555,否,否,否
广州大学,广东,本科,综合类,585,575,否,否,否
广东工业大学,广东,本科,理工类,580,560,否,否,否
广州中医药大学,广东,本科,医药类,590,575,否,是,是
广州美术学院,广东,本科,艺术类,535,530,否,否,否
武汉大学,湖北,本科,综合类,650,630,是,是,是
华中科技大学,湖北,本科,综合类,655,625,是,是,是
华中师范大学,湖北,本科,师范类,610,600,否,是,否
武汉理工大学,湖北,本科,理工类,615,590,否,是,否
中国地质大学,湖北,本科,理工类,600,575,否,是,否
华中农业大学,湖北,本科,农林类,590,570,否,是,否
中南财经政法大学,湖北,本科,财经类,620,610,否,是,否
湖北大学,湖北,本科,综合类,570,555,否,否,否
武汉科技大学,湖北,本科,理工类,580,560,否,否,否
长江大学,湖北,本科,综合类,555,540,否,否,否
中南民族大学,湖北,本科,综合类,565,550,否,否,否
四川大学,四川,本科,综合类,625,605,是,是,是
电子科技大学,四川,本科,理工类,645,610,是,是,是
西南交通大学,四川,本科,理工类,610,585,否,是,否
四川农业大学,四川,本科,农林类,565,545,否,是,否
西南财经大学,四川,本科,财经类,615,605,否,是,否
西南民族大学,四川,本科,综合类,550,535,否,否,否
成都理工大学,四川,本科,理工类,580,555,否,否,是
四川师范大学,四川,本科,师范类,565,555,否,否,否
西南科技大学,四川,本科,理工类,555,535,否,否,否
成都中医药大学,四川,本科,医药类,575,560,否,否,否
西安交通大学,陕西,本科,综合类,645,620,是,是,是
西北工业大学,陕西,本科,理工类,630,600,是,是,是
西北农林科技大学,陕西,本科,农林类,590,565,是,是,是
陕西师范大学,陕西,本科,师范类,595,585,否,是,否
西安电子科技大学,陕西,本科,理工类,620,590,否,是,是
西北大学,陕西,本科,综合类,600,585,否,是,否
长安大学,陕西,本科,理工类,595,575,否,是,否
西安建筑科技大学,陕西,本科,理工类,575,555,否,否,否
西安理工大学,陕西,本科,理工类,580,560,否,否,否
西安科技大学,陕西,本科,理工类,565,545,否,否,否
西北政法大学,陕西,本科,政法类,585,575,否,否,否
湖南大学,湖南,本科,综合类,625,605,是,是,是
中南大学,湖南,本科,综合类,630,610,是,是,是
湖南师范大学,湖南,本科,师范类,595,585,否,是,否
湘潭大学,湖南,本科,综合类,570,555,否,否,是
长沙理工大学,湖南,本科,理工类,580,560,否,否,否
湖南科技大学,湖南,本科,综合类,565,550,否,否,否
湖南农业大学,湖南,本科,农林类,555,540,否,否,否
中南林业科技大学,湖南,本科,农林类,550,535,否,否,否
湖南中医药大学,湖南,本科,医药类,570,555,否,否,否
湖南工商大学,湖南,本科,财经类,575,560,否,否,否
中国科学技术大学,安徽,本科,理工类,670,640,是,是,是
合肥工业大学,安徽,本科,理工类,605,580,否,是,否
安徽大学,安徽,本科,综合类,590,575,否,是,否
安徽医科大学,安徽,本科,医药类,595,580,否,否,否
安徽师范大学,安徽,本科,师范类,560,550,否,否,否
安徽农业大学,安徽,本科,农林类,545,530,否,否,否
合肥学院,安徽,本科,综合类,550,535,否,否,否
厦门大学,福建,本科,综合类,635,615,是,是,是
福州大学,福建,本科,理工类,600,580,否,是,否
福建师范大学,福建,本科,师范类,565,555,否,否,否
福建农林大学,福建,本科,农林类,545,530,否,否,否
华侨大学,福建,本科,综合类,560,545,否,否,否
集美大学,福建,本科,综合类,555,540,否,否,否
福建医科大学,福建,本科,医药类,585,570,否,否,否
闽南师范大学,福建,本科,师范类,545,535,否,否,否
山东大学,山东,本科,综合类,625,605,是,是,是
中国海洋大学,山东,本科,综合类,610,590,是,是,是
中国石油大学,山东,本科,理工类,595,575,否,是,否
山东师范大学,山东,本科,师范类,565,555,否,否,否
青岛大学,山东,本科,综合类,575,560,否,否,否
山东科技大学,山东,本科,理工类,560,540,否,否,否
山东农业大学,山东,本科,农林类,540,525,否,否,否
济南大学,山东,本科,综合类,565,550,否,否,否
青岛科技大学,山东,本科,理工类,560,540,否,否,否
曲阜师范大学,山东,本科,师范类,550,540,否,否,否
烟台大学,山东,本科,综合类,555,540,否,否,否
大连理工大学,辽宁,本科,理工类,635,605,是,是,是
东北大学,辽宁,本科,理工类,615,590,是,是,是
辽宁大学,辽宁,本科,综合类,595,585,否,是,否
大连海事大学,辽宁,本科,理工类,590,570,否,是,否
东北财经大学,辽宁,本科,财经类,605,595,否,否,否
沈阳农业大学,辽宁,本科,农林类,545,530,否,是,否
中国医科大学,辽宁,本科,医药类,585,570,否,否,否
沈阳药科大学,辽宁,本科,医药类,570,550,否,否,否
辽宁师范大学,辽宁,本科,师范类,550,540,否,否,否
沈阳工业大学,辽宁,本科,理工类,555,535,否,否,否
哈尔滨工业大学,黑龙江,本科,理工类,640,610,是,是,是
哈尔滨工程大学,黑龙江,本科,理工类,605,580,否,是,是
东北农业大学,黑龙江,本科,农林类,555,535,否,是,否
东北林业大学,黑龙江,本科,农林类,550,530,否,是,否
黑龙江大学,黑龙江,本科,综合类,555,545,否,否,否
哈尔滨医科大学,黑龙江,本科,医药类,580,565,否,否,否
哈尔滨师范大学,黑龙江,本科,师范类,540,530,否,否,否
哈尔滨理工大学,黑龙江,本科,理工类,550,530,否,否,否
吉林大学,吉林,本科,综合类,615,595,是,是,是
东北师范大学,吉林,本科,师范类,590,580,否,是,是
延边大学,吉林,本科,综合类,550,535,否,是,否
长春理工大学,吉林,本科,理工类,565,545,否,否,否
吉林农业大学,吉林,本科,农林类,535,520,否,否,否
长春中医药大学,吉林,本科,医药类,555,540,否,否,否
北华大学,吉林,本科,综合类,540,525,否,否,否
长春工业大学,吉林,本科,理工类,545,525,否,否,否
南开大学,天津,本科,综合类,640,620,是,是,是
天津大学,天津,本科,理工类,645,615,是,是,是
天津医科大学,天津,本科,医药类,610,595,否,是,否
天津师范大学,天津,本科,师范类,570,560,否,否,否
天津工业大学,天津,本科,理工类,575,555,否,否,是
天津理工大学,天津,本科,理工类,565,545,否,否,否
天津财经大学,天津,本科,财经类,585,575,否,否,否
中国民航大学,天津,本科,理工类,580,560,否,否,否
天津科技大学,天津,本科,理工类,560,540,否,否,否
重庆大学,重庆,本科,综合类,620,595,是,是,是
西南大学,重庆,本科,综合类,595,585,否,是,否
重庆医科大学,重庆,本科,医药类,590,575,否,否,否
重庆邮电大学,重庆,本科,理工类,585,560,否,否,否
重庆工商大学,重庆,本科,财经类,570,555,否,否,否
重庆师范大学,重庆,本科,师范类,560,550,否,否,否
重庆理工大学,重庆,本科,理工类,560,540,否,否,否
四川美术学院,重庆,本科,艺术类,525,520,否,否,否
河北工业大学,河北,本科,理工类,590,570,否,是,否
河北大学,河北,本科,综合类,560,545,否,否,否
燕山大学,河北,本科,理工类,575,555,否,否,否
河北师范大学,河北,本科,师范类,550,540,否,否,否
河北农业大学,河北,本科,农林类,535,520,否,否,否
石家庄铁道大学,河北,本科,理工类,560,540,否,否,否
河北医科大学,河北,本科,医药类,575,560,否,否,否
郑州大学,河南,本科,综合类,600,585,否,是,是
河南大学,河南,本科,综合类,580,570,否,否,是
河南科技大学,河南,本科,综合类,550,535,否,否,否
河南农业大学,河南,本科,农林类,540,525,否,否,否
河南师范大学,河南,本科,师范类,555,545,否,否,否
郑州轻工业大学,河南,本科,理工类,545,530,否,否,否
河南理工大学,河南,本科,理工类,545,530,否,否,否
河南中医药大学,河南,本科,医药类,555,540,否,否,否
太原理工大学,山西,本科,理工类,585,565,否,是,否
山西大学,山西,本科,综合类,565,555,否,否,是
中北大学,山西,本科,理工类,550,530,否,否,否
山西农业大学,山西,本科,农林类,530,515,否,否,否
山西医科大学,山西,本科,医药类,565,550,否,否,否
山西师范大学,山西,本科,师范类,540,530,否,否,否
南昌大学,江西,本科,综合类,590,575,否,是,是
江西财经大学,江西,本科,财经类,580,570,否,否,否
江西师范大学,江西,本科,师范类,560,550,否,否,否
江西农业大学,江西,本科,农林类,540,525,否,否,否
华东交通大学,江西,本科,理工类,555,540,否,否,否
南昌航空大学,江西,本科,理工类,550,535,否,否,否
云南大学,云南,本科,综合类,580,565,否,是,是
昆明理工大学,云南,本科,理工类,555,535,否,否,否
云南农业大学,云南,本科,农林类,525,510,否,否,否
云南师范大学,云南,本科,师范类,540,530,否,否,否
昆明医科大学,云南,本科,医药类,555,540,否,否,否
贵州大学,贵州,本科,综合类,555,540,否,是,否
贵州师范大学,贵州,本科,师范类,525,515,否,否,否
贵州医科大学,贵州,本科,医药类,545,530,否,否,否
贵州财经大学,贵州,本科,财经类,530,520,否,否,否
广西大学,广西,本科,综合类,565,550,否,是,否
广西师范大学,广西,本科,师范类,540,530,否,否,否
广西医科大学,广西,本科,医药类,560,545,否,否,否
桂林电子科技大学,广西,本科,理工类,545,525,否,否,否
桂林理工大学,广西,本科,理工类,535,520,否,否,否
新疆大学,新疆,本科,综合类,535,520,否,是,是
石河子大学,新疆,本科,综合类,525,510,否,是,否
新疆农业大学,新疆,本科,农林类,515,500,否,否,否
新疆医科大学,新疆,本科,医药类,530,515,否,否,否
兰州大学,甘肃,本科,综合类,605,585,是,是,是
西北师范大学,甘肃,本科,师范类,550,540,否,否,否
兰州理工大学,甘肃,本科,理工类,535,520,否,否,否
兰州交通大学,甘肃,本科,理工类,540,525,否,否,否
甘肃农业大学,甘肃,本科,农林类,520,505,否,否,否
内蒙古大学,内蒙古,本科,综合类,545,530,否,是,否
内蒙古农业大学,内蒙古,本科,农林类,515,500,否,否,否
内蒙古师范大学,内蒙古,本科,师范类,525,515,否,否,否
内蒙古工业大学,内蒙古,本科,理工类,530,515,否,否,否
海南大学,海南,本科,综合类,565,550,否,是,否
海南师范大学,海南,本科,师范类,535,525,否,否,否
海南医学院,海南,本科,医药类,545,530,否,否,否
宁夏大学,宁夏,本科,综合类,535,520,否,是,否
宁夏医科大学,宁夏,本科,医药类,540,525,否,否,否
青海大学,青海,本科,综合类,520,505,否,是,否
青海师范大学,青海,本科,师范类,510,500,否,否,否
西藏大学,西藏,本科,综合类,480,470,否,是,否
西藏农牧学院,西藏,本科,农林类,465,455,否,否,否
北京语言大学,北京,本科,语言类,595,585,否,否,否
中央音乐学院,北京,本科,艺术类,510,505,否,否,否
北京舞蹈学院,北京,本科,艺术类,505,500,否,否,否
中国音乐学院,北京,本科,艺术类,515,510,否,否,否
北京电影学院,北京,本科,艺术类,520,515,否,否,否
上海海洋大学,上海,本科,农林类,575,555,否,否,否
上海应用技术大学,上海,本科,理工类,560,540,否,否,否
上海第二工业大学,上海,本科,理工类,555,535,否,否,否
上海政法学院,上海,本科,政法类,570,560,否,否,否
上海立信会计金融学院,上海,本科,财经类,580,570,否,否,否
南京信息工程大学,江苏,本科,理工类,600,575,否,否,是
南京邮电大学,江苏,本科,理工类,615,590,否,否,是
南京工业大学,江苏,本科,理工类,585,565,否,否,否
南京医科大学,江苏,本科,医药类,610,595,否,否,否
南京中医药大学,江苏,本科,医药类,590,575,否,否,否
南京林业大学,江苏,本科,农林类,580,560,否,否,否
南京财经大学,江苏,本科,财经类,590,575,否,否,否
浙江工商大学,浙江,本科,财经类,585,575,否,否,否
浙江财经大学,浙江,本科,财经类,580,570,否,否,否
浙江理工大学,浙江,本科,理工类,575,555,否,否,否
杭州师范大学,浙江,本科,师范类,565,555,否,否,否
宁波诺丁汉大学,浙江,本科,综合类,590,580,否,否,否
南方科技大学,广东,本科,理工类,630,600,否,否,否
广东工业大学,广东,本科,理工类,580,560,否,否,否
广州医科大学,广东,本科,医药类,585,570,否,否,否
广东财经大学,广东,本科,财经类,575,565,否,否,否
广州美术学院,广东,本科,艺术类,535,530,否,否,否
湖北工业大学,湖北,本科,理工类,565,545,否,否,否
武汉纺织大学,湖北,本科,理工类,555,540,否,否,否
武汉工程大学,湖北,本科,理工类,560,545,否,否,否
湖北中医药大学,湖北,本科,医药类,565,550,否,否,否
成都信息工程大学,四川,本科,理工类,565,545,否,否,否
西华大学,四川,本科,综合类,555,535,否,否,否
成都大学,四川,本科,综合类,550,535,否,否,否
西南石油大学,四川,本科,理工类,570,550,否,否,否
陕西科技大学,陕西,本科,理工类,565,545,否,否,否
西安工程大学,陕西,本科,理工类,555,535,否,否,否
西安外国语大学,陕西,本科,语言类,575,565,否,否,否
西安美术学院,陕西,本科,艺术类,515,510,否,否,否
山东财经大学,山东,本科,财经类,565,555,否,否,否
青岛理工大学,山东,本科,理工类,555,535,否,否,否
山东理工大学,山东,本科,理工类,550,530,否,否,否
辽宁工程技术大学,辽宁,本科,理工类,545,525,否,否,否
沈阳建筑大学,辽宁,本科,理工类,550,530,否,否,否
南华大学,湖南,本科,综合类,560,545,否,否,否
湖南工程学院,湖南,本科,理工类,545,530,否,否,否
安徽理工大学,安徽,本科,理工类,550,530,否,否,否
安徽工业大学,安徽,本科,理工类,555,535,否,否,否
1 学校名称 所在地区 办学层次 院校类型 理科分数线 文科分数线 985 211 双一流
2 北京大学 北京 本科 综合类 680 650
3 清华大学 北京 本科 理工类 685 655
4 中国人民大学 北京 本科 综合类 650 630
5 北京师范大学 北京 本科 师范类 640 620
6 北京航空航天大学 北京 本科 理工类 660 620
7 北京理工大学 北京 本科 理工类 645 610
8 中国农业大学 北京 本科 农林类 620 590
9 中央民族大学 北京 本科 综合类 610 595
10 北京交通大学 北京 本科 理工类 625 600
11 北京工业大学 北京 本科 理工类 615 590
12 北京科技大学 北京 本科 理工类 625 600
13 北京化工大学 北京 本科 理工类 605 580
14 北京邮电大学 北京 本科 理工类 640 610
15 北京林业大学 北京 本科 农林类 595 575
16 北京中医药大学 北京 本科 医药类 600 585
17 北京外国语大学 北京 本科 语言类 620 610
18 中国传媒大学 北京 本科 艺术类 610 600
19 中央财经大学 北京 本科 财经类 645 635
20 对外经济贸易大学 北京 本科 财经类 635 625
21 中国政法大学 北京 本科 政法类 630 620
22 华北电力大学 北京 本科 理工类 610 590
23 中国矿业大学 北京 本科 理工类 590 570
24 中国石油大学 北京 本科 理工类 595 575
25 中国地质大学 北京 本科 理工类 595 575
26 北京体育大学 北京 本科 体育类 580 560
27 复旦大学 上海 本科 综合类 675 650
28 上海交通大学 上海 本科 综合类 680 655
29 同济大学 上海 本科 理工类 650 620
30 华东师范大学 上海 本科 师范类 635 620
31 上海财经大学 上海 本科 财经类 645 635
32 上海外国语大学 上海 本科 语言类 625 620
33 东华大学 上海 本科 理工类 600 580
34 上海大学 上海 本科 综合类 610 595
35 华东理工大学 上海 本科 理工类 615 590
36 上海理工大学 上海 本科 理工类 590 570
37 上海海事大学 上海 本科 理工类 585 565
38 上海音乐学院 上海 本科 艺术类 550 540
39 上海戏剧学院 上海 本科 艺术类 540 535
40 上海体育学院 上海 本科 体育类 520 510
41 上海中医药大学 上海 本科 医药类 605 590
42 南京大学 江苏 本科 综合类 665 640
43 东南大学 江苏 本科 综合类 645 615
44 南京航空航天大学 江苏 本科 理工类 630 600
45 南京理工大学 江苏 本科 理工类 625 595
46 苏州大学 江苏 本科 综合类 610 595
47 南京师范大学 江苏 本科 师范类 605 600
48 河海大学 江苏 本科 理工类 615 590
49 江南大学 江苏 本科 综合类 595 580
50 中国矿业大学 江苏 本科 理工类 590 570
51 南京农业大学 江苏 本科 农林类 595 580
52 中国药科大学 江苏 本科 医药类 600 585
53 南京工业大学 江苏 本科 理工类 585 565
54 南京邮电大学 江苏 本科 理工类 615 590
55 南京信息工程大学 江苏 本科 理工类 600 575
56 江苏大学 江苏 本科 综合类 580 560
57 扬州大学 江苏 本科 综合类 575 560
58 南京医科大学 江苏 本科 医药类 610 595
59 南京中医药大学 江苏 本科 医药类 590 575
60 浙江大学 浙江 本科 综合类 660 635
61 宁波大学 浙江 本科 综合类 590 575
62 浙江工业大学 浙江 本科 理工类 585 565
63 浙江师范大学 浙江 本科 师范类 575 570
64 杭州电子科技大学 浙江 本科 理工类 595 570
65 浙江理工大学 浙江 本科 理工类 575 555
66 温州医科大学 浙江 本科 医药类 595 580
67 浙江工商大学 浙江 本科 财经类 585 575
68 中国美术学院 浙江 本科 艺术类 530 525
69 浙江传媒学院 浙江 本科 艺术类 550 545
70 中山大学 广东 本科 综合类 640 620
71 华南理工大学 广东 本科 理工类 645 610
72 暨南大学 广东 本科 综合类 610 595
73 华南师范大学 广东 本科 师范类 600 590
74 华南农业大学 广东 本科 农林类 575 560
75 南方医科大学 广东 本科 医药类 605 590
76 广东外语外贸大学 广东 本科 语言类 600 590
77 深圳大学 广东 本科 综合类 610 595
78 汕头大学 广东 本科 综合类 570 555
79 广州大学 广东 本科 综合类 585 575
80 广东工业大学 广东 本科 理工类 580 560
81 广州中医药大学 广东 本科 医药类 590 575
82 广州美术学院 广东 本科 艺术类 535 530
83 武汉大学 湖北 本科 综合类 650 630
84 华中科技大学 湖北 本科 综合类 655 625
85 华中师范大学 湖北 本科 师范类 610 600
86 武汉理工大学 湖北 本科 理工类 615 590
87 中国地质大学 湖北 本科 理工类 600 575
88 华中农业大学 湖北 本科 农林类 590 570
89 中南财经政法大学 湖北 本科 财经类 620 610
90 湖北大学 湖北 本科 综合类 570 555
91 武汉科技大学 湖北 本科 理工类 580 560
92 长江大学 湖北 本科 综合类 555 540
93 中南民族大学 湖北 本科 综合类 565 550
94 四川大学 四川 本科 综合类 625 605
95 电子科技大学 四川 本科 理工类 645 610
96 西南交通大学 四川 本科 理工类 610 585
97 四川农业大学 四川 本科 农林类 565 545
98 西南财经大学 四川 本科 财经类 615 605
99 西南民族大学 四川 本科 综合类 550 535
100 成都理工大学 四川 本科 理工类 580 555
101 四川师范大学 四川 本科 师范类 565 555
102 西南科技大学 四川 本科 理工类 555 535
103 成都中医药大学 四川 本科 医药类 575 560
104 西安交通大学 陕西 本科 综合类 645 620
105 西北工业大学 陕西 本科 理工类 630 600
106 西北农林科技大学 陕西 本科 农林类 590 565
107 陕西师范大学 陕西 本科 师范类 595 585
108 西安电子科技大学 陕西 本科 理工类 620 590
109 西北大学 陕西 本科 综合类 600 585
110 长安大学 陕西 本科 理工类 595 575
111 西安建筑科技大学 陕西 本科 理工类 575 555
112 西安理工大学 陕西 本科 理工类 580 560
113 西安科技大学 陕西 本科 理工类 565 545
114 西北政法大学 陕西 本科 政法类 585 575
115 湖南大学 湖南 本科 综合类 625 605
116 中南大学 湖南 本科 综合类 630 610
117 湖南师范大学 湖南 本科 师范类 595 585
118 湘潭大学 湖南 本科 综合类 570 555
119 长沙理工大学 湖南 本科 理工类 580 560
120 湖南科技大学 湖南 本科 综合类 565 550
121 湖南农业大学 湖南 本科 农林类 555 540
122 中南林业科技大学 湖南 本科 农林类 550 535
123 湖南中医药大学 湖南 本科 医药类 570 555
124 湖南工商大学 湖南 本科 财经类 575 560
125 中国科学技术大学 安徽 本科 理工类 670 640
126 合肥工业大学 安徽 本科 理工类 605 580
127 安徽大学 安徽 本科 综合类 590 575
128 安徽医科大学 安徽 本科 医药类 595 580
129 安徽师范大学 安徽 本科 师范类 560 550
130 安徽农业大学 安徽 本科 农林类 545 530
131 合肥学院 安徽 本科 综合类 550 535
132 厦门大学 福建 本科 综合类 635 615
133 福州大学 福建 本科 理工类 600 580
134 福建师范大学 福建 本科 师范类 565 555
135 福建农林大学 福建 本科 农林类 545 530
136 华侨大学 福建 本科 综合类 560 545
137 集美大学 福建 本科 综合类 555 540
138 福建医科大学 福建 本科 医药类 585 570
139 闽南师范大学 福建 本科 师范类 545 535
140 山东大学 山东 本科 综合类 625 605
141 中国海洋大学 山东 本科 综合类 610 590
142 中国石油大学 山东 本科 理工类 595 575
143 山东师范大学 山东 本科 师范类 565 555
144 青岛大学 山东 本科 综合类 575 560
145 山东科技大学 山东 本科 理工类 560 540
146 山东农业大学 山东 本科 农林类 540 525
147 济南大学 山东 本科 综合类 565 550
148 青岛科技大学 山东 本科 理工类 560 540
149 曲阜师范大学 山东 本科 师范类 550 540
150 烟台大学 山东 本科 综合类 555 540
151 大连理工大学 辽宁 本科 理工类 635 605
152 东北大学 辽宁 本科 理工类 615 590
153 辽宁大学 辽宁 本科 综合类 595 585
154 大连海事大学 辽宁 本科 理工类 590 570
155 东北财经大学 辽宁 本科 财经类 605 595
156 沈阳农业大学 辽宁 本科 农林类 545 530
157 中国医科大学 辽宁 本科 医药类 585 570
158 沈阳药科大学 辽宁 本科 医药类 570 550
159 辽宁师范大学 辽宁 本科 师范类 550 540
160 沈阳工业大学 辽宁 本科 理工类 555 535
161 哈尔滨工业大学 黑龙江 本科 理工类 640 610
162 哈尔滨工程大学 黑龙江 本科 理工类 605 580
163 东北农业大学 黑龙江 本科 农林类 555 535
164 东北林业大学 黑龙江 本科 农林类 550 530
165 黑龙江大学 黑龙江 本科 综合类 555 545
166 哈尔滨医科大学 黑龙江 本科 医药类 580 565
167 哈尔滨师范大学 黑龙江 本科 师范类 540 530
168 哈尔滨理工大学 黑龙江 本科 理工类 550 530
169 吉林大学 吉林 本科 综合类 615 595
170 东北师范大学 吉林 本科 师范类 590 580
171 延边大学 吉林 本科 综合类 550 535
172 长春理工大学 吉林 本科 理工类 565 545
173 吉林农业大学 吉林 本科 农林类 535 520
174 长春中医药大学 吉林 本科 医药类 555 540
175 北华大学 吉林 本科 综合类 540 525
176 长春工业大学 吉林 本科 理工类 545 525
177 南开大学 天津 本科 综合类 640 620
178 天津大学 天津 本科 理工类 645 615
179 天津医科大学 天津 本科 医药类 610 595
180 天津师范大学 天津 本科 师范类 570 560
181 天津工业大学 天津 本科 理工类 575 555
182 天津理工大学 天津 本科 理工类 565 545
183 天津财经大学 天津 本科 财经类 585 575
184 中国民航大学 天津 本科 理工类 580 560
185 天津科技大学 天津 本科 理工类 560 540
186 重庆大学 重庆 本科 综合类 620 595
187 西南大学 重庆 本科 综合类 595 585
188 重庆医科大学 重庆 本科 医药类 590 575
189 重庆邮电大学 重庆 本科 理工类 585 560
190 重庆工商大学 重庆 本科 财经类 570 555
191 重庆师范大学 重庆 本科 师范类 560 550
192 重庆理工大学 重庆 本科 理工类 560 540
193 四川美术学院 重庆 本科 艺术类 525 520
194 河北工业大学 河北 本科 理工类 590 570
195 河北大学 河北 本科 综合类 560 545
196 燕山大学 河北 本科 理工类 575 555
197 河北师范大学 河北 本科 师范类 550 540
198 河北农业大学 河北 本科 农林类 535 520
199 石家庄铁道大学 河北 本科 理工类 560 540
200 河北医科大学 河北 本科 医药类 575 560
201 郑州大学 河南 本科 综合类 600 585
202 河南大学 河南 本科 综合类 580 570
203 河南科技大学 河南 本科 综合类 550 535
204 河南农业大学 河南 本科 农林类 540 525
205 河南师范大学 河南 本科 师范类 555 545
206 郑州轻工业大学 河南 本科 理工类 545 530
207 河南理工大学 河南 本科 理工类 545 530
208 河南中医药大学 河南 本科 医药类 555 540
209 太原理工大学 山西 本科 理工类 585 565
210 山西大学 山西 本科 综合类 565 555
211 中北大学 山西 本科 理工类 550 530
212 山西农业大学 山西 本科 农林类 530 515
213 山西医科大学 山西 本科 医药类 565 550
214 山西师范大学 山西 本科 师范类 540 530
215 南昌大学 江西 本科 综合类 590 575
216 江西财经大学 江西 本科 财经类 580 570
217 江西师范大学 江西 本科 师范类 560 550
218 江西农业大学 江西 本科 农林类 540 525
219 华东交通大学 江西 本科 理工类 555 540
220 南昌航空大学 江西 本科 理工类 550 535
221 云南大学 云南 本科 综合类 580 565
222 昆明理工大学 云南 本科 理工类 555 535
223 云南农业大学 云南 本科 农林类 525 510
224 云南师范大学 云南 本科 师范类 540 530
225 昆明医科大学 云南 本科 医药类 555 540
226 贵州大学 贵州 本科 综合类 555 540
227 贵州师范大学 贵州 本科 师范类 525 515
228 贵州医科大学 贵州 本科 医药类 545 530
229 贵州财经大学 贵州 本科 财经类 530 520
230 广西大学 广西 本科 综合类 565 550
231 广西师范大学 广西 本科 师范类 540 530
232 广西医科大学 广西 本科 医药类 560 545
233 桂林电子科技大学 广西 本科 理工类 545 525
234 桂林理工大学 广西 本科 理工类 535 520
235 新疆大学 新疆 本科 综合类 535 520
236 石河子大学 新疆 本科 综合类 525 510
237 新疆农业大学 新疆 本科 农林类 515 500
238 新疆医科大学 新疆 本科 医药类 530 515
239 兰州大学 甘肃 本科 综合类 605 585
240 西北师范大学 甘肃 本科 师范类 550 540
241 兰州理工大学 甘肃 本科 理工类 535 520
242 兰州交通大学 甘肃 本科 理工类 540 525
243 甘肃农业大学 甘肃 本科 农林类 520 505
244 内蒙古大学 内蒙古 本科 综合类 545 530
245 内蒙古农业大学 内蒙古 本科 农林类 515 500
246 内蒙古师范大学 内蒙古 本科 师范类 525 515
247 内蒙古工业大学 内蒙古 本科 理工类 530 515
248 海南大学 海南 本科 综合类 565 550
249 海南师范大学 海南 本科 师范类 535 525
250 海南医学院 海南 本科 医药类 545 530
251 宁夏大学 宁夏 本科 综合类 535 520
252 宁夏医科大学 宁夏 本科 医药类 540 525
253 青海大学 青海 本科 综合类 520 505
254 青海师范大学 青海 本科 师范类 510 500
255 西藏大学 西藏 本科 综合类 480 470
256 西藏农牧学院 西藏 本科 农林类 465 455
257 北京语言大学 北京 本科 语言类 595 585
258 中央音乐学院 北京 本科 艺术类 510 505
259 北京舞蹈学院 北京 本科 艺术类 505 500
260 中国音乐学院 北京 本科 艺术类 515 510
261 北京电影学院 北京 本科 艺术类 520 515
262 上海海洋大学 上海 本科 农林类 575 555
263 上海应用技术大学 上海 本科 理工类 560 540
264 上海第二工业大学 上海 本科 理工类 555 535
265 上海政法学院 上海 本科 政法类 570 560
266 上海立信会计金融学院 上海 本科 财经类 580 570
267 南京信息工程大学 江苏 本科 理工类 600 575
268 南京邮电大学 江苏 本科 理工类 615 590
269 南京工业大学 江苏 本科 理工类 585 565
270 南京医科大学 江苏 本科 医药类 610 595
271 南京中医药大学 江苏 本科 医药类 590 575
272 南京林业大学 江苏 本科 农林类 580 560
273 南京财经大学 江苏 本科 财经类 590 575
274 浙江工商大学 浙江 本科 财经类 585 575
275 浙江财经大学 浙江 本科 财经类 580 570
276 浙江理工大学 浙江 本科 理工类 575 555
277 杭州师范大学 浙江 本科 师范类 565 555
278 宁波诺丁汉大学 浙江 本科 综合类 590 580
279 南方科技大学 广东 本科 理工类 630 600
280 广东工业大学 广东 本科 理工类 580 560
281 广州医科大学 广东 本科 医药类 585 570
282 广东财经大学 广东 本科 财经类 575 565
283 广州美术学院 广东 本科 艺术类 535 530
284 湖北工业大学 湖北 本科 理工类 565 545
285 武汉纺织大学 湖北 本科 理工类 555 540
286 武汉工程大学 湖北 本科 理工类 560 545
287 湖北中医药大学 湖北 本科 医药类 565 550
288 成都信息工程大学 四川 本科 理工类 565 545
289 西华大学 四川 本科 综合类 555 535
290 成都大学 四川 本科 综合类 550 535
291 西南石油大学 四川 本科 理工类 570 550
292 陕西科技大学 陕西 本科 理工类 565 545
293 西安工程大学 陕西 本科 理工类 555 535
294 西安外国语大学 陕西 本科 语言类 575 565
295 西安美术学院 陕西 本科 艺术类 515 510
296 山东财经大学 山东 本科 财经类 565 555
297 青岛理工大学 山东 本科 理工类 555 535
298 山东理工大学 山东 本科 理工类 550 530
299 辽宁工程技术大学 辽宁 本科 理工类 545 525
300 沈阳建筑大学 辽宁 本科 理工类 550 530
301 南华大学 湖南 本科 综合类 560 545
302 湖南工程学院 湖南 本科 理工类 545 530
303 安徽理工大学 安徽 本科 理工类 550 530
304 安徽工业大学 安徽 本科 理工类 555 535

54
project/爬虫2/pom.xml

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>spider-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<archive>
<manifest>
<mainClass>com.example.Application</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

6
project/爬虫2/run_university.bat

@ -0,0 +1,6 @@
@echo off
echo 正在编译...
call "C:\Maven\apache-maven-3.9.6\bin\mvn" compile -q
echo 正在运行...
java -cp "target/classes;C:\Users\ZRL\.m2\repository\org\jsoup\jsoup\1.17.2\jsoup-1.17.2.jar" com.example.Main
pause

37
project/爬虫2/src/main/java/com/example/Application.java

@ -0,0 +1,37 @@
/**
* 应用程序主入口
* 整合 CLI+MVC+Command+Strategy 模式
*/
package com.example;
import com.example.cli.CliArgs;
import com.example.cli.CliParser;
import com.example.cli.DefaultCliParser;
import com.example.controller.UniversityController;
import com.example.exception.SpiderException.ParseException;
public class Application {
public static void main(String[] args) {
System.out.println("╔════════════════════════════════════════════════════════╗");
System.out.println("║ 全国高校信息爬虫系统 v2.0 ║");
System.out.println("║ 基于 CLI+MVC+Command+Strategy 架构 ║");
System.out.println("╚════════════════════════════════════════════════════════╝");
System.out.println();
try {
CliParser cliParser = new DefaultCliParser();
CliArgs cliArgs = cliParser.parse(args);
UniversityController controller = new UniversityController();
controller.handleRequest(cliArgs);
} catch (ParseException e) {
System.out.println("❌ 参数解析失败:" + e.getMessage());
System.out.println("使用 'help' 命令查看帮助信息");
} catch (Exception e) {
System.out.println("❌ 系统错误:" + e.getMessage());
e.printStackTrace();
}
}
}

61
project/爬虫2/src/main/java/com/example/Main.java

@ -0,0 +1,61 @@
/**
* 全国高校信息爬虫 - 主程序入口
*
* 这是整个爬虫程序的启动类负责协调各个模块的工作
*
* 程序执行流程
* 1. 初始化爬虫服务SpiderService
* 2. 调用爬虫服务获取高校数据
* 3. 初始化输出服务OutputService
* 4. 调用输出服务以表格形式输出数据
*
* 设计模式依赖注入
* - 通过接口引用服务而不是直接使用具体实现类
* - 便于后续替换不同的实现如更换爬虫源或输出方式
*/
package com.example;
import com.example.data.UniversityData;
import com.example.entity.University;
import com.example.strategy.ConsoleOutputStrategy;
import com.example.strategy.CsvOutputStrategy;
import com.example.strategy.OutputStrategy;
import java.util.List;
public class Main {
/**
* 程序主入口方法
* @param args 命令行参数未使用
*/
public static void main(String[] args) {
// 步骤1:打印程序标题
System.out.println("========== 全国高校信息爬虫 ==========");
System.out.println("正在获取高校数据(包含分数线和985/211/双一流信息)...\n");
// 步骤2:获取300所热门大学数据
List<University> universities = UniversityData.get300Universities();
// 步骤3:检查是否获取到数据
if (universities.isEmpty()) {
System.out.println("抱歉,未能获取到高校数据");
return;
}
// 步骤4:创建控制台输出策略实例
System.out.println("📊 高校信息(前20条):");
OutputStrategy consoleOutput = new ConsoleOutputStrategy();
consoleOutput.output(universities.subList(0, Math.min(20, universities.size())));
System.out.println("(共" + universities.size() + "所高校,完整数据已保存到CSV文件)");
// 步骤5:创建CSV输出策略实例,保存数据到文件
OutputStrategy csvOutput = new CsvOutputStrategy("universities_300.csv");
csvOutput.output(universities);
// 步骤6:打印完成信息
System.out.println("\n✅ 爬取完成!共获取 " + universities.size() + " 所高校信息");
System.out.println("📁 数据已保存到:universities_300.csv");
System.out.println("📋 包含字段:学校名称、所在地区、办学层次、院校类型、理科分数线、文科分数线、985、211、双一流");
}
}

34
project/爬虫2/src/main/java/com/example/cli/CliArgs.java

@ -0,0 +1,34 @@
/**
* 命令行参数封装类
* 存储解析后的命令行参数
*/
package com.example.cli;
import java.util.HashMap;
import java.util.Map;
public class CliArgs {
private String command = "list";
private Map<String, String> options = new HashMap<>();
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
public String getOption(String key) {
return options.get(key);
}
public void setOption(String key, String value) {
this.options.put(key, value);
}
public boolean hasOption(String key) {
return options.containsKey(key);
}
}

11
project/爬虫2/src/main/java/com/example/cli/CliParser.java

@ -0,0 +1,11 @@
/**
* 命令行解析器接口
* 策略模式定义命令行解析策略
*/
package com.example.cli;
import com.example.exception.SpiderException.ParseException;
public interface CliParser {
CliArgs parse(String[] args) throws ParseException;
}

36
project/爬虫2/src/main/java/com/example/cli/DefaultCliParser.java

@ -0,0 +1,36 @@
/**
* 默认命令行解析器实现
* 支持格式java -jar spider.jar [command] [options]
* 命令list, export, help
* 选项--output=文件--format=格式--limit=数量
*/
package com.example.cli;
import com.example.exception.SpiderException.ParseException;
public class DefaultCliParser implements CliParser {
@Override
public CliArgs parse(String[] args) throws ParseException {
CliArgs cliArgs = new CliArgs();
if (args == null || args.length == 0) {
return cliArgs;
}
for (String arg : args) {
if (arg.startsWith("--")) {
String[] parts = arg.substring(2).split("=", 2);
if (parts.length == 2) {
cliArgs.setOption(parts[0], parts[1]);
} else {
cliArgs.setOption(parts[0], "true");
}
} else if (!arg.startsWith("-")) {
cliArgs.setCommand(arg);
}
}
return cliArgs;
}
}

13
project/爬虫2/src/main/java/com/example/command/Command.java

@ -0,0 +1,13 @@
/**
* 命令接口
* Command 模式定义统一的命令执行接口
*/
package com.example.command;
import com.example.cli.CliArgs;
public interface Command {
void execute(CliArgs args);
String getName();
String getDescription();
}

29
project/爬虫2/src/main/java/com/example/command/CommandInvoker.java

@ -0,0 +1,29 @@
/**
* 命令调用器
* Command 模式管理所有可用命令
*/
package com.example.command;
import com.example.cli.CliArgs;
import java.util.HashMap;
import java.util.Map;
public class CommandInvoker {
private final Map<String, Command> commands = new HashMap<>();
public void register(Command command) {
commands.put(command.getName(), command);
}
public void invoke(String commandName, CliArgs args) {
Command command = commands.get(commandName);
if (command == null) {
System.out.println("未知命令:" + commandName);
System.out.println("使用 'help' 查看可用命令");
return;
}
command.execute(args);
}
}

54
project/爬虫2/src/main/java/com/example/command/ExportCommand.java

@ -0,0 +1,54 @@
/**
* 导出命令
* 将高校数据导出为文件
*/
package com.example.command;
import com.example.cli.CliArgs;
import com.example.entity.University;
import com.example.service.UniversityService;
import com.example.strategy.CsvOutputStrategy;
import com.example.strategy.OutputStrategy;
import java.util.List;
public class ExportCommand implements Command {
private final UniversityService universityService;
public ExportCommand(UniversityService universityService) {
this.universityService = universityService;
}
@Override
public void execute(CliArgs args) {
System.out.println("正在导出高校数据...");
List<University> universities = universityService.getAllUniversities();
if (universities.isEmpty()) {
System.out.println("未找到高校数据");
return;
}
String outputFile = args.getOption("output");
if (outputFile == null) {
outputFile = "universities.csv";
}
OutputStrategy outputStrategy = new CsvOutputStrategy(outputFile);
outputStrategy.output(universities);
System.out.println("数据已导出到:" + outputFile);
}
@Override
public String getName() {
return "export";
}
@Override
public String getDescription() {
return "导出高校数据到文件";
}
}

41
project/爬虫2/src/main/java/com/example/command/HelpCommand.java

@ -0,0 +1,41 @@
/**
* 帮助命令
*/
package com.example.command;
import com.example.cli.CliArgs;
public class HelpCommand implements Command {
@Override
public void execute(CliArgs args) {
System.out.println("全国高校信息爬虫系统");
System.out.println("====================");
System.out.println();
System.out.println("用法:java -jar spider.jar [command] [options]");
System.out.println();
System.out.println("可用命令:");
System.out.println(" list 显示高校数据列表");
System.out.println(" export 导出高校数据到文件");
System.out.println(" help 显示此帮助信息");
System.out.println();
System.out.println("可用选项:");
System.out.println(" --output=文件 指定输出文件名");
System.out.println(" --format=格式 指定输出格式 (csv/excel)");
System.out.println(" --limit=数量 限制显示数量");
System.out.println();
System.out.println("示例:");
System.out.println(" java -jar spider.jar list --limit=10");
System.out.println(" java -jar spider.jar export --output=data.csv");
}
@Override
public String getName() {
return "help";
}
@Override
public String getDescription() {
return "显示帮助信息";
}
}

58
project/爬虫2/src/main/java/com/example/command/ListCommand.java

@ -0,0 +1,58 @@
/**
* 列表命令
* 显示高校数据列表
*/
package com.example.command;
import com.example.cli.CliArgs;
import com.example.entity.University;
import com.example.service.UniversityService;
import com.example.strategy.ConsoleOutputStrategy;
import com.example.strategy.OutputStrategy;
import java.util.List;
public class ListCommand implements Command {
private final UniversityService universityService;
public ListCommand(UniversityService universityService) {
this.universityService = universityService;
}
@Override
public void execute(CliArgs args) {
System.out.println("正在获取高校数据...");
List<University> universities = universityService.getAllUniversities();
if (universities.isEmpty()) {
System.out.println("未找到高校数据");
return;
}
int limit = 20;
if (args.hasOption("limit")) {
try {
limit = Integer.parseInt(args.getOption("limit"));
} catch (NumberFormatException e) {
System.out.println("无效的 limit 值,使用默认值 20");
}
}
OutputStrategy outputStrategy = new ConsoleOutputStrategy();
outputStrategy.output(universities.subList(0, Math.min(limit, universities.size())));
System.out.println("\n共 " + universities.size() + " 所高校");
}
@Override
public String getName() {
return "list";
}
@Override
public String getDescription() {
return "显示高校数据列表";
}
}

32
project/爬虫2/src/main/java/com/example/controller/UniversityController.java

@ -0,0 +1,32 @@
/**
* 高校控制器
* MVC 架构中的 Controller
*/
package com.example.controller;
import com.example.cli.CliArgs;
import com.example.command.*;
import com.example.service.UniversityService;
import com.example.service.UniversityServiceImpl;
public class UniversityController {
private final UniversityService universityService;
private final CommandInvoker commandInvoker;
public UniversityController() {
this.universityService = new UniversityServiceImpl();
this.commandInvoker = new CommandInvoker();
registerCommands();
}
private void registerCommands() {
commandInvoker.register(new ListCommand(universityService));
commandInvoker.register(new ExportCommand(universityService));
commandInvoker.register(new HelpCommand());
}
public void handleRequest(CliArgs args) {
commandInvoker.invoke(args.getCommand(), args);
}
}

417
project/爬虫2/src/main/java/com/example/data/UniversityData.java

@ -0,0 +1,417 @@
/**
* 大学数据常量类
*
* 包含300所中国热门大学的信息用于生成CSV文件
* 包含字段学校名称所在地区办学层次院校类型理科分数线文科分数线985211双一流
*/
package com.example.data;
import com.example.entity.University;
import com.example.entity.UniversityImpl;
import java.util.ArrayList;
import java.util.List;
public class UniversityData {
/**
* 获取300所热门大学数据包含分数线和985/211/双一流信息
*
* @return 300所大学的数据列表
*/
public static List<University> get300Universities() {
List<University> list = new ArrayList<>();
// ==================== 北京市 ====================
list.add(new UniversityImpl("北京大学", "北京", "本科", "综合类", "680", "650", "是", "是", "是"));
list.add(new UniversityImpl("清华大学", "北京", "本科", "理工类", "685", "655", "是", "是", "是"));
list.add(new UniversityImpl("中国人民大学", "北京", "本科", "综合类", "650", "630", "是", "是", "是"));
list.add(new UniversityImpl("北京师范大学", "北京", "本科", "师范类", "640", "620", "是", "是", "是"));
list.add(new UniversityImpl("北京航空航天大学", "北京", "本科", "理工类", "660", "620", "是", "是", "是"));
list.add(new UniversityImpl("北京理工大学", "北京", "本科", "理工类", "645", "610", "是", "是", "是"));
list.add(new UniversityImpl("中国农业大学", "北京", "本科", "农林类", "620", "590", "是", "是", "是"));
list.add(new UniversityImpl("中央民族大学", "北京", "本科", "综合类", "610", "595", "是", "是", "是"));
list.add(new UniversityImpl("北京交通大学", "北京", "本科", "理工类", "625", "600", "否", "是", "是"));
list.add(new UniversityImpl("北京工业大学", "北京", "本科", "理工类", "615", "590", "否", "是", "是"));
list.add(new UniversityImpl("北京科技大学", "北京", "本科", "理工类", "625", "600", "否", "是", "否"));
list.add(new UniversityImpl("北京化工大学", "北京", "本科", "理工类", "605", "580", "否", "是", "否"));
list.add(new UniversityImpl("北京邮电大学", "北京", "本科", "理工类", "640", "610", "否", "是", "是"));
list.add(new UniversityImpl("北京林业大学", "北京", "本科", "农林类", "595", "575", "否", "是", "否"));
list.add(new UniversityImpl("北京中医药大学", "北京", "本科", "医药类", "600", "585", "否", "是", "是"));
list.add(new UniversityImpl("北京外国语大学", "北京", "本科", "语言类", "620", "610", "否", "是", "否"));
list.add(new UniversityImpl("中国传媒大学", "北京", "本科", "艺术类", "610", "600", "否", "是", "否"));
list.add(new UniversityImpl("中央财经大学", "北京", "本科", "财经类", "645", "635", "否", "是", "否"));
list.add(new UniversityImpl("对外经济贸易大学", "北京", "本科", "财经类", "635", "625", "否", "是", "否"));
list.add(new UniversityImpl("中国政法大学", "北京", "本科", "政法类", "630", "620", "否", "是", "否"));
list.add(new UniversityImpl("华北电力大学", "北京", "本科", "理工类", "610", "590", "否", "是", "否"));
list.add(new UniversityImpl("中国矿业大学", "北京", "本科", "理工类", "590", "570", "否", "是", "否"));
list.add(new UniversityImpl("中国石油大学", "北京", "本科", "理工类", "595", "575", "否", "是", "否"));
list.add(new UniversityImpl("中国地质大学", "北京", "本科", "理工类", "595", "575", "否", "是", "否"));
list.add(new UniversityImpl("北京体育大学", "北京", "本科", "体育类", "580", "560", "否", "是", "否"));
// ==================== 上海市 ====================
list.add(new UniversityImpl("复旦大学", "上海", "本科", "综合类", "675", "650", "是", "是", "是"));
list.add(new UniversityImpl("上海交通大学", "上海", "本科", "综合类", "680", "655", "是", "是", "是"));
list.add(new UniversityImpl("同济大学", "上海", "本科", "理工类", "650", "620", "是", "是", "是"));
list.add(new UniversityImpl("华东师范大学", "上海", "本科", "师范类", "635", "620", "是", "是", "是"));
list.add(new UniversityImpl("上海财经大学", "上海", "本科", "财经类", "645", "635", "否", "是", "否"));
list.add(new UniversityImpl("上海外国语大学", "上海", "本科", "语言类", "625", "620", "否", "是", "否"));
list.add(new UniversityImpl("东华大学", "上海", "本科", "理工类", "600", "580", "否", "是", "否"));
list.add(new UniversityImpl("上海大学", "上海", "本科", "综合类", "610", "595", "否", "是", "是"));
list.add(new UniversityImpl("华东理工大学", "上海", "本科", "理工类", "615", "590", "否", "是", "否"));
list.add(new UniversityImpl("上海理工大学", "上海", "本科", "理工类", "590", "570", "否", "否", "否"));
list.add(new UniversityImpl("上海海事大学", "上海", "本科", "理工类", "585", "565", "否", "否", "否"));
list.add(new UniversityImpl("上海音乐学院", "上海", "本科", "艺术类", "550", "540", "否", "否", "否"));
list.add(new UniversityImpl("上海戏剧学院", "上海", "本科", "艺术类", "540", "535", "否", "否", "否"));
list.add(new UniversityImpl("上海体育学院", "上海", "本科", "体育类", "520", "510", "否", "否", "否"));
list.add(new UniversityImpl("上海中医药大学", "上海", "本科", "医药类", "605", "590", "否", "是", "是"));
// ==================== 江苏省 ====================
list.add(new UniversityImpl("南京大学", "江苏", "本科", "综合类", "665", "640", "是", "是", "是"));
list.add(new UniversityImpl("东南大学", "江苏", "本科", "综合类", "645", "615", "是", "是", "是"));
list.add(new UniversityImpl("南京航空航天大学", "江苏", "本科", "理工类", "630", "600", "否", "是", "是"));
list.add(new UniversityImpl("南京理工大学", "江苏", "本科", "理工类", "625", "595", "否", "是", "是"));
list.add(new UniversityImpl("苏州大学", "江苏", "本科", "综合类", "610", "595", "否", "是", "否"));
list.add(new UniversityImpl("南京师范大学", "江苏", "本科", "师范类", "605", "600", "否", "是", "否"));
list.add(new UniversityImpl("河海大学", "江苏", "本科", "理工类", "615", "590", "否", "是", "否"));
list.add(new UniversityImpl("江南大学", "江苏", "本科", "综合类", "595", "580", "否", "是", "否"));
list.add(new UniversityImpl("中国矿业大学", "江苏", "本科", "理工类", "590", "570", "否", "是", "否"));
list.add(new UniversityImpl("南京农业大学", "江苏", "本科", "农林类", "595", "580", "否", "是", "否"));
list.add(new UniversityImpl("中国药科大学", "江苏", "本科", "医药类", "600", "585", "否", "是", "否"));
list.add(new UniversityImpl("南京工业大学", "江苏", "本科", "理工类", "585", "565", "否", "否", "否"));
list.add(new UniversityImpl("南京邮电大学", "江苏", "本科", "理工类", "615", "590", "否", "否", "是"));
list.add(new UniversityImpl("南京信息工程大学", "江苏", "本科", "理工类", "600", "575", "否", "否", "是"));
list.add(new UniversityImpl("江苏大学", "江苏", "本科", "综合类", "580", "560", "否", "否", "否"));
list.add(new UniversityImpl("扬州大学", "江苏", "本科", "综合类", "575", "560", "否", "否", "否"));
list.add(new UniversityImpl("南京医科大学", "江苏", "本科", "医药类", "610", "595", "否", "否", "否"));
list.add(new UniversityImpl("南京中医药大学", "江苏", "本科", "医药类", "590", "575", "否", "否", "否"));
// ==================== 浙江省 ====================
list.add(new UniversityImpl("浙江大学", "浙江", "本科", "综合类", "660", "635", "是", "是", "是"));
list.add(new UniversityImpl("宁波大学", "浙江", "本科", "综合类", "590", "575", "否", "否", "是"));
list.add(new UniversityImpl("浙江工业大学", "浙江", "本科", "理工类", "585", "565", "否", "否", "否"));
list.add(new UniversityImpl("浙江师范大学", "浙江", "本科", "师范类", "575", "570", "否", "否", "否"));
list.add(new UniversityImpl("杭州电子科技大学", "浙江", "本科", "理工类", "595", "570", "否", "否", "否"));
list.add(new UniversityImpl("浙江理工大学", "浙江", "本科", "理工类", "575", "555", "否", "否", "否"));
list.add(new UniversityImpl("温州医科大学", "浙江", "本科", "医药类", "595", "580", "否", "否", "否"));
list.add(new UniversityImpl("浙江工商大学", "浙江", "本科", "财经类", "585", "575", "否", "否", "否"));
list.add(new UniversityImpl("中国美术学院", "浙江", "本科", "艺术类", "530", "525", "否", "否", "否"));
list.add(new UniversityImpl("浙江传媒学院", "浙江", "本科", "艺术类", "550", "545", "否", "否", "否"));
// ==================== 广东省 ====================
list.add(new UniversityImpl("中山大学", "广东", "本科", "综合类", "640", "620", "是", "是", "是"));
list.add(new UniversityImpl("华南理工大学", "广东", "本科", "理工类", "645", "610", "是", "是", "是"));
list.add(new UniversityImpl("暨南大学", "广东", "本科", "综合类", "610", "595", "否", "是", "否"));
list.add(new UniversityImpl("华南师范大学", "广东", "本科", "师范类", "600", "590", "否", "是", "否"));
list.add(new UniversityImpl("华南农业大学", "广东", "本科", "农林类", "575", "560", "否", "是", "否"));
list.add(new UniversityImpl("南方医科大学", "广东", "本科", "医药类", "605", "590", "否", "否", "否"));
list.add(new UniversityImpl("广东外语外贸大学", "广东", "本科", "语言类", "600", "590", "否", "否", "否"));
list.add(new UniversityImpl("深圳大学", "广东", "本科", "综合类", "610", "595", "否", "否", "否"));
list.add(new UniversityImpl("汕头大学", "广东", "本科", "综合类", "570", "555", "否", "否", "否"));
list.add(new UniversityImpl("广州大学", "广东", "本科", "综合类", "585", "575", "否", "否", "否"));
list.add(new UniversityImpl("广东工业大学", "广东", "本科", "理工类", "580", "560", "否", "否", "否"));
list.add(new UniversityImpl("广州中医药大学", "广东", "本科", "医药类", "590", "575", "否", "是", "是"));
list.add(new UniversityImpl("广州美术学院", "广东", "本科", "艺术类", "535", "530", "否", "否", "否"));
// ==================== 湖北省 ====================
list.add(new UniversityImpl("武汉大学", "湖北", "本科", "综合类", "650", "630", "是", "是", "是"));
list.add(new UniversityImpl("华中科技大学", "湖北", "本科", "综合类", "655", "625", "是", "是", "是"));
list.add(new UniversityImpl("华中师范大学", "湖北", "本科", "师范类", "610", "600", "否", "是", "否"));
list.add(new UniversityImpl("武汉理工大学", "湖北", "本科", "理工类", "615", "590", "否", "是", "否"));
list.add(new UniversityImpl("中国地质大学", "湖北", "本科", "理工类", "600", "575", "否", "是", "否"));
list.add(new UniversityImpl("华中农业大学", "湖北", "本科", "农林类", "590", "570", "否", "是", "否"));
list.add(new UniversityImpl("中南财经政法大学", "湖北", "本科", "财经类", "620", "610", "否", "是", "否"));
list.add(new UniversityImpl("湖北大学", "湖北", "本科", "综合类", "570", "555", "否", "否", "否"));
list.add(new UniversityImpl("武汉科技大学", "湖北", "本科", "理工类", "580", "560", "否", "否", "否"));
list.add(new UniversityImpl("长江大学", "湖北", "本科", "综合类", "555", "540", "否", "否", "否"));
list.add(new UniversityImpl("中南民族大学", "湖北", "本科", "综合类", "565", "550", "否", "否", "否"));
// ==================== 四川省 ====================
list.add(new UniversityImpl("四川大学", "四川", "本科", "综合类", "625", "605", "是", "是", "是"));
list.add(new UniversityImpl("电子科技大学", "四川", "本科", "理工类", "645", "610", "是", "是", "是"));
list.add(new UniversityImpl("西南交通大学", "四川", "本科", "理工类", "610", "585", "否", "是", "否"));
list.add(new UniversityImpl("四川农业大学", "四川", "本科", "农林类", "565", "545", "否", "是", "否"));
list.add(new UniversityImpl("西南财经大学", "四川", "本科", "财经类", "615", "605", "否", "是", "否"));
list.add(new UniversityImpl("西南民族大学", "四川", "本科", "综合类", "550", "535", "否", "否", "否"));
list.add(new UniversityImpl("成都理工大学", "四川", "本科", "理工类", "580", "555", "否", "否", "是"));
list.add(new UniversityImpl("四川师范大学", "四川", "本科", "师范类", "565", "555", "否", "否", "否"));
list.add(new UniversityImpl("西南科技大学", "四川", "本科", "理工类", "555", "535", "否", "否", "否"));
list.add(new UniversityImpl("成都中医药大学", "四川", "本科", "医药类", "575", "560", "否", "否", "否"));
// ==================== 陕西省 ====================
list.add(new UniversityImpl("西安交通大学", "陕西", "本科", "综合类", "645", "620", "是", "是", "是"));
list.add(new UniversityImpl("西北工业大学", "陕西", "本科", "理工类", "630", "600", "是", "是", "是"));
list.add(new UniversityImpl("西北农林科技大学", "陕西", "本科", "农林类", "590", "565", "是", "是", "是"));
list.add(new UniversityImpl("陕西师范大学", "陕西", "本科", "师范类", "595", "585", "否", "是", "否"));
list.add(new UniversityImpl("西安电子科技大学", "陕西", "本科", "理工类", "620", "590", "否", "是", "是"));
list.add(new UniversityImpl("西北大学", "陕西", "本科", "综合类", "600", "585", "否", "是", "否"));
list.add(new UniversityImpl("长安大学", "陕西", "本科", "理工类", "595", "575", "否", "是", "否"));
list.add(new UniversityImpl("西安建筑科技大学", "陕西", "本科", "理工类", "575", "555", "否", "否", "否"));
list.add(new UniversityImpl("西安理工大学", "陕西", "本科", "理工类", "580", "560", "否", "否", "否"));
list.add(new UniversityImpl("西安科技大学", "陕西", "本科", "理工类", "565", "545", "否", "否", "否"));
list.add(new UniversityImpl("西北政法大学", "陕西", "本科", "政法类", "585", "575", "否", "否", "否"));
// ==================== 湖南省 ====================
list.add(new UniversityImpl("湖南大学", "湖南", "本科", "综合类", "625", "605", "是", "是", "是"));
list.add(new UniversityImpl("中南大学", "湖南", "本科", "综合类", "630", "610", "是", "是", "是"));
list.add(new UniversityImpl("湖南师范大学", "湖南", "本科", "师范类", "595", "585", "否", "是", "否"));
list.add(new UniversityImpl("湘潭大学", "湖南", "本科", "综合类", "570", "555", "否", "否", "是"));
list.add(new UniversityImpl("长沙理工大学", "湖南", "本科", "理工类", "580", "560", "否", "否", "否"));
list.add(new UniversityImpl("湖南科技大学", "湖南", "本科", "综合类", "565", "550", "否", "否", "否"));
list.add(new UniversityImpl("湖南农业大学", "湖南", "本科", "农林类", "555", "540", "否", "否", "否"));
list.add(new UniversityImpl("中南林业科技大学", "湖南", "本科", "农林类", "550", "535", "否", "否", "否"));
list.add(new UniversityImpl("湖南中医药大学", "湖南", "本科", "医药类", "570", "555", "否", "否", "否"));
list.add(new UniversityImpl("湖南工商大学", "湖南", "本科", "财经类", "575", "560", "否", "否", "否"));
// ==================== 安徽省 ====================
list.add(new UniversityImpl("中国科学技术大学", "安徽", "本科", "理工类", "670", "640", "是", "是", "是"));
list.add(new UniversityImpl("合肥工业大学", "安徽", "本科", "理工类", "605", "580", "否", "是", "否"));
list.add(new UniversityImpl("安徽大学", "安徽", "本科", "综合类", "590", "575", "否", "是", "否"));
list.add(new UniversityImpl("安徽医科大学", "安徽", "本科", "医药类", "595", "580", "否", "否", "否"));
list.add(new UniversityImpl("安徽师范大学", "安徽", "本科", "师范类", "560", "550", "否", "否", "否"));
list.add(new UniversityImpl("安徽农业大学", "安徽", "本科", "农林类", "545", "530", "否", "否", "否"));
list.add(new UniversityImpl("合肥学院", "安徽", "本科", "综合类", "550", "535", "否", "否", "否"));
// ==================== 福建省 ====================
list.add(new UniversityImpl("厦门大学", "福建", "本科", "综合类", "635", "615", "是", "是", "是"));
list.add(new UniversityImpl("福州大学", "福建", "本科", "理工类", "600", "580", "否", "是", "否"));
list.add(new UniversityImpl("福建师范大学", "福建", "本科", "师范类", "565", "555", "否", "否", "否"));
list.add(new UniversityImpl("福建农林大学", "福建", "本科", "农林类", "545", "530", "否", "否", "否"));
list.add(new UniversityImpl("华侨大学", "福建", "本科", "综合类", "560", "545", "否", "否", "否"));
list.add(new UniversityImpl("集美大学", "福建", "本科", "综合类", "555", "540", "否", "否", "否"));
list.add(new UniversityImpl("福建医科大学", "福建", "本科", "医药类", "585", "570", "否", "否", "否"));
list.add(new UniversityImpl("闽南师范大学", "福建", "本科", "师范类", "545", "535", "否", "否", "否"));
// ==================== 山东省 ====================
list.add(new UniversityImpl("山东大学", "山东", "本科", "综合类", "625", "605", "是", "是", "是"));
list.add(new UniversityImpl("中国海洋大学", "山东", "本科", "综合类", "610", "590", "是", "是", "是"));
list.add(new UniversityImpl("中国石油大学", "山东", "本科", "理工类", "595", "575", "否", "是", "否"));
list.add(new UniversityImpl("山东师范大学", "山东", "本科", "师范类", "565", "555", "否", "否", "否"));
list.add(new UniversityImpl("青岛大学", "山东", "本科", "综合类", "575", "560", "否", "否", "否"));
list.add(new UniversityImpl("山东科技大学", "山东", "本科", "理工类", "560", "540", "否", "否", "否"));
list.add(new UniversityImpl("山东农业大学", "山东", "本科", "农林类", "540", "525", "否", "否", "否"));
list.add(new UniversityImpl("济南大学", "山东", "本科", "综合类", "565", "550", "否", "否", "否"));
list.add(new UniversityImpl("青岛科技大学", "山东", "本科", "理工类", "560", "540", "否", "否", "否"));
list.add(new UniversityImpl("曲阜师范大学", "山东", "本科", "师范类", "550", "540", "否", "否", "否"));
list.add(new UniversityImpl("烟台大学", "山东", "本科", "综合类", "555", "540", "否", "否", "否"));
// ==================== 辽宁省 ====================
list.add(new UniversityImpl("大连理工大学", "辽宁", "本科", "理工类", "635", "605", "是", "是", "是"));
list.add(new UniversityImpl("东北大学", "辽宁", "本科", "理工类", "615", "590", "是", "是", "是"));
list.add(new UniversityImpl("辽宁大学", "辽宁", "本科", "综合类", "595", "585", "否", "是", "否"));
list.add(new UniversityImpl("大连海事大学", "辽宁", "本科", "理工类", "590", "570", "否", "是", "否"));
list.add(new UniversityImpl("东北财经大学", "辽宁", "本科", "财经类", "605", "595", "否", "否", "否"));
list.add(new UniversityImpl("沈阳农业大学", "辽宁", "本科", "农林类", "545", "530", "否", "是", "否"));
list.add(new UniversityImpl("中国医科大学", "辽宁", "本科", "医药类", "585", "570", "否", "否", "否"));
list.add(new UniversityImpl("沈阳药科大学", "辽宁", "本科", "医药类", "570", "550", "否", "否", "否"));
list.add(new UniversityImpl("辽宁师范大学", "辽宁", "本科", "师范类", "550", "540", "否", "否", "否"));
list.add(new UniversityImpl("沈阳工业大学", "辽宁", "本科", "理工类", "555", "535", "否", "否", "否"));
// ==================== 黑龙江省 ====================
list.add(new UniversityImpl("哈尔滨工业大学", "黑龙江", "本科", "理工类", "640", "610", "是", "是", "是"));
list.add(new UniversityImpl("哈尔滨工程大学", "黑龙江", "本科", "理工类", "605", "580", "否", "是", "是"));
list.add(new UniversityImpl("东北农业大学", "黑龙江", "本科", "农林类", "555", "535", "否", "是", "否"));
list.add(new UniversityImpl("东北林业大学", "黑龙江", "本科", "农林类", "550", "530", "否", "是", "否"));
list.add(new UniversityImpl("黑龙江大学", "黑龙江", "本科", "综合类", "555", "545", "否", "否", "否"));
list.add(new UniversityImpl("哈尔滨医科大学", "黑龙江", "本科", "医药类", "580", "565", "否", "否", "否"));
list.add(new UniversityImpl("哈尔滨师范大学", "黑龙江", "本科", "师范类", "540", "530", "否", "否", "否"));
list.add(new UniversityImpl("哈尔滨理工大学", "黑龙江", "本科", "理工类", "550", "530", "否", "否", "否"));
// ==================== 吉林省 ====================
list.add(new UniversityImpl("吉林大学", "吉林", "本科", "综合类", "615", "595", "是", "是", "是"));
list.add(new UniversityImpl("东北师范大学", "吉林", "本科", "师范类", "590", "580", "否", "是", "是"));
list.add(new UniversityImpl("延边大学", "吉林", "本科", "综合类", "550", "535", "否", "是", "否"));
list.add(new UniversityImpl("长春理工大学", "吉林", "本科", "理工类", "565", "545", "否", "否", "否"));
list.add(new UniversityImpl("吉林农业大学", "吉林", "本科", "农林类", "535", "520", "否", "否", "否"));
list.add(new UniversityImpl("长春中医药大学", "吉林", "本科", "医药类", "555", "540", "否", "否", "否"));
list.add(new UniversityImpl("北华大学", "吉林", "本科", "综合类", "540", "525", "否", "否", "否"));
list.add(new UniversityImpl("长春工业大学", "吉林", "本科", "理工类", "545", "525", "否", "否", "否"));
// ==================== 天津市 ====================
list.add(new UniversityImpl("南开大学", "天津", "本科", "综合类", "640", "620", "是", "是", "是"));
list.add(new UniversityImpl("天津大学", "天津", "本科", "理工类", "645", "615", "是", "是", "是"));
list.add(new UniversityImpl("天津医科大学", "天津", "本科", "医药类", "610", "595", "否", "是", "否"));
list.add(new UniversityImpl("天津师范大学", "天津", "本科", "师范类", "570", "560", "否", "否", "否"));
list.add(new UniversityImpl("天津工业大学", "天津", "本科", "理工类", "575", "555", "否", "否", "是"));
list.add(new UniversityImpl("天津理工大学", "天津", "本科", "理工类", "565", "545", "否", "否", "否"));
list.add(new UniversityImpl("天津财经大学", "天津", "本科", "财经类", "585", "575", "否", "否", "否"));
list.add(new UniversityImpl("中国民航大学", "天津", "本科", "理工类", "580", "560", "否", "否", "否"));
list.add(new UniversityImpl("天津科技大学", "天津", "本科", "理工类", "560", "540", "否", "否", "否"));
// ==================== 重庆市 ====================
list.add(new UniversityImpl("重庆大学", "重庆", "本科", "综合类", "620", "595", "是", "是", "是"));
list.add(new UniversityImpl("西南大学", "重庆", "本科", "综合类", "595", "585", "否", "是", "否"));
list.add(new UniversityImpl("重庆医科大学", "重庆", "本科", "医药类", "590", "575", "否", "否", "否"));
list.add(new UniversityImpl("重庆邮电大学", "重庆", "本科", "理工类", "585", "560", "否", "否", "否"));
list.add(new UniversityImpl("重庆工商大学", "重庆", "本科", "财经类", "570", "555", "否", "否", "否"));
list.add(new UniversityImpl("重庆师范大学", "重庆", "本科", "师范类", "560", "550", "否", "否", "否"));
list.add(new UniversityImpl("重庆理工大学", "重庆", "本科", "理工类", "560", "540", "否", "否", "否"));
list.add(new UniversityImpl("四川美术学院", "重庆", "本科", "艺术类", "525", "520", "否", "否", "否"));
// ==================== 河北省 ====================
list.add(new UniversityImpl("河北工业大学", "河北", "本科", "理工类", "590", "570", "否", "是", "否"));
list.add(new UniversityImpl("河北大学", "河北", "本科", "综合类", "560", "545", "否", "否", "否"));
list.add(new UniversityImpl("燕山大学", "河北", "本科", "理工类", "575", "555", "否", "否", "否"));
list.add(new UniversityImpl("河北师范大学", "河北", "本科", "师范类", "550", "540", "否", "否", "否"));
list.add(new UniversityImpl("河北农业大学", "河北", "本科", "农林类", "535", "520", "否", "否", "否"));
list.add(new UniversityImpl("石家庄铁道大学", "河北", "本科", "理工类", "560", "540", "否", "否", "否"));
list.add(new UniversityImpl("河北医科大学", "河北", "本科", "医药类", "575", "560", "否", "否", "否"));
// ==================== 河南省 ====================
list.add(new UniversityImpl("郑州大学", "河南", "本科", "综合类", "600", "585", "否", "是", "是"));
list.add(new UniversityImpl("河南大学", "河南", "本科", "综合类", "580", "570", "否", "否", "是"));
list.add(new UniversityImpl("河南科技大学", "河南", "本科", "综合类", "550", "535", "否", "否", "否"));
list.add(new UniversityImpl("河南农业大学", "河南", "本科", "农林类", "540", "525", "否", "否", "否"));
list.add(new UniversityImpl("河南师范大学", "河南", "本科", "师范类", "555", "545", "否", "否", "否"));
list.add(new UniversityImpl("郑州轻工业大学", "河南", "本科", "理工类", "545", "530", "否", "否", "否"));
list.add(new UniversityImpl("河南理工大学", "河南", "本科", "理工类", "545", "530", "否", "否", "否"));
list.add(new UniversityImpl("河南中医药大学", "河南", "本科", "医药类", "555", "540", "否", "否", "否"));
// ==================== 山西省 ====================
list.add(new UniversityImpl("太原理工大学", "山西", "本科", "理工类", "585", "565", "否", "是", "否"));
list.add(new UniversityImpl("山西大学", "山西", "本科", "综合类", "565", "555", "否", "否", "是"));
list.add(new UniversityImpl("中北大学", "山西", "本科", "理工类", "550", "530", "否", "否", "否"));
list.add(new UniversityImpl("山西农业大学", "山西", "本科", "农林类", "530", "515", "否", "否", "否"));
list.add(new UniversityImpl("山西医科大学", "山西", "本科", "医药类", "565", "550", "否", "否", "否"));
list.add(new UniversityImpl("山西师范大学", "山西", "本科", "师范类", "540", "530", "否", "否", "否"));
// ==================== 江西省 ====================
list.add(new UniversityImpl("南昌大学", "江西", "本科", "综合类", "590", "575", "否", "是", "是"));
list.add(new UniversityImpl("江西财经大学", "江西", "本科", "财经类", "580", "570", "否", "否", "否"));
list.add(new UniversityImpl("江西师范大学", "江西", "本科", "师范类", "560", "550", "否", "否", "否"));
list.add(new UniversityImpl("江西农业大学", "江西", "本科", "农林类", "540", "525", "否", "否", "否"));
list.add(new UniversityImpl("华东交通大学", "江西", "本科", "理工类", "555", "540", "否", "否", "否"));
list.add(new UniversityImpl("南昌航空大学", "江西", "本科", "理工类", "550", "535", "否", "否", "否"));
// ==================== 云南省 ====================
list.add(new UniversityImpl("云南大学", "云南", "本科", "综合类", "580", "565", "否", "是", "是"));
list.add(new UniversityImpl("昆明理工大学", "云南", "本科", "理工类", "555", "535", "否", "否", "否"));
list.add(new UniversityImpl("云南农业大学", "云南", "本科", "农林类", "525", "510", "否", "否", "否"));
list.add(new UniversityImpl("云南师范大学", "云南", "本科", "师范类", "540", "530", "否", "否", "否"));
list.add(new UniversityImpl("昆明医科大学", "云南", "本科", "医药类", "555", "540", "否", "否", "否"));
// ==================== 贵州省 ====================
list.add(new UniversityImpl("贵州大学", "贵州", "本科", "综合类", "555", "540", "否", "是", "否"));
list.add(new UniversityImpl("贵州师范大学", "贵州", "本科", "师范类", "525", "515", "否", "否", "否"));
list.add(new UniversityImpl("贵州医科大学", "贵州", "本科", "医药类", "545", "530", "否", "否", "否"));
list.add(new UniversityImpl("贵州财经大学", "贵州", "本科", "财经类", "530", "520", "否", "否", "否"));
// ==================== 广西壮族自治区 ====================
list.add(new UniversityImpl("广西大学", "广西", "本科", "综合类", "565", "550", "否", "是", "否"));
list.add(new UniversityImpl("广西师范大学", "广西", "本科", "师范类", "540", "530", "否", "否", "否"));
list.add(new UniversityImpl("广西医科大学", "广西", "本科", "医药类", "560", "545", "否", "否", "否"));
list.add(new UniversityImpl("桂林电子科技大学", "广西", "本科", "理工类", "545", "525", "否", "否", "否"));
list.add(new UniversityImpl("桂林理工大学", "广西", "本科", "理工类", "535", "520", "否", "否", "否"));
// ==================== 新疆维吾尔自治区 ====================
list.add(new UniversityImpl("新疆大学", "新疆", "本科", "综合类", "535", "520", "否", "是", "是"));
list.add(new UniversityImpl("石河子大学", "新疆", "本科", "综合类", "525", "510", "否", "是", "否"));
list.add(new UniversityImpl("新疆农业大学", "新疆", "本科", "农林类", "515", "500", "否", "否", "否"));
list.add(new UniversityImpl("新疆医科大学", "新疆", "本科", "医药类", "530", "515", "否", "否", "否"));
// ==================== 甘肃省 ====================
list.add(new UniversityImpl("兰州大学", "甘肃", "本科", "综合类", "605", "585", "是", "是", "是"));
list.add(new UniversityImpl("西北师范大学", "甘肃", "本科", "师范类", "550", "540", "否", "否", "否"));
list.add(new UniversityImpl("兰州理工大学", "甘肃", "本科", "理工类", "535", "520", "否", "否", "否"));
list.add(new UniversityImpl("兰州交通大学", "甘肃", "本科", "理工类", "540", "525", "否", "否", "否"));
list.add(new UniversityImpl("甘肃农业大学", "甘肃", "本科", "农林类", "520", "505", "否", "否", "否"));
// ==================== 内蒙古自治区 ====================
list.add(new UniversityImpl("内蒙古大学", "内蒙古", "本科", "综合类", "545", "530", "否", "是", "否"));
list.add(new UniversityImpl("内蒙古农业大学", "内蒙古", "本科", "农林类", "515", "500", "否", "否", "否"));
list.add(new UniversityImpl("内蒙古师范大学", "内蒙古", "本科", "师范类", "525", "515", "否", "否", "否"));
list.add(new UniversityImpl("内蒙古工业大学", "内蒙古", "本科", "理工类", "530", "515", "否", "否", "否"));
// ==================== 海南省 ====================
list.add(new UniversityImpl("海南大学", "海南", "本科", "综合类", "565", "550", "否", "是", "否"));
list.add(new UniversityImpl("海南师范大学", "海南", "本科", "师范类", "535", "525", "否", "否", "否"));
list.add(new UniversityImpl("海南医学院", "海南", "本科", "医药类", "545", "530", "否", "否", "否"));
// ==================== 宁夏回族自治区 ====================
list.add(new UniversityImpl("宁夏大学", "宁夏", "本科", "综合类", "535", "520", "否", "是", "否"));
list.add(new UniversityImpl("宁夏医科大学", "宁夏", "本科", "医药类", "540", "525", "否", "否", "否"));
// ==================== 青海省 ====================
list.add(new UniversityImpl("青海大学", "青海", "本科", "综合类", "520", "505", "否", "是", "否"));
list.add(new UniversityImpl("青海师范大学", "青海", "本科", "师范类", "510", "500", "否", "否", "否"));
// ==================== 西藏自治区 ====================
list.add(new UniversityImpl("西藏大学", "西藏", "本科", "综合类", "480", "470", "否", "是", "否"));
list.add(new UniversityImpl("西藏农牧学院", "西藏", "本科", "农林类", "465", "455", "否", "否", "否"));
// ==================== 补充更多高校 ====================
// 北京市补充
list.add(new UniversityImpl("北京语言大学", "北京", "本科", "语言类", "595", "585", "否", "否", "否"));
list.add(new UniversityImpl("中央音乐学院", "北京", "本科", "艺术类", "510", "505", "否", "否", "否"));
list.add(new UniversityImpl("北京舞蹈学院", "北京", "本科", "艺术类", "505", "500", "否", "否", "否"));
list.add(new UniversityImpl("中国音乐学院", "北京", "本科", "艺术类", "515", "510", "否", "否", "否"));
list.add(new UniversityImpl("北京电影学院", "北京", "本科", "艺术类", "520", "515", "否", "否", "否"));
// 上海市补充
list.add(new UniversityImpl("上海海洋大学", "上海", "本科", "农林类", "575", "555", "否", "否", "否"));
list.add(new UniversityImpl("上海应用技术大学", "上海", "本科", "理工类", "560", "540", "否", "否", "否"));
list.add(new UniversityImpl("上海第二工业大学", "上海", "本科", "理工类", "555", "535", "否", "否", "否"));
list.add(new UniversityImpl("上海政法学院", "上海", "本科", "政法类", "570", "560", "否", "否", "否"));
list.add(new UniversityImpl("上海立信会计金融学院", "上海", "本科", "财经类", "580", "570", "否", "否", "否"));
// 江苏省补充
list.add(new UniversityImpl("南京信息工程大学", "江苏", "本科", "理工类", "600", "575", "否", "否", "是"));
list.add(new UniversityImpl("南京邮电大学", "江苏", "本科", "理工类", "615", "590", "否", "否", "是"));
list.add(new UniversityImpl("南京工业大学", "江苏", "本科", "理工类", "585", "565", "否", "否", "否"));
list.add(new UniversityImpl("南京医科大学", "江苏", "本科", "医药类", "610", "595", "否", "否", "否"));
list.add(new UniversityImpl("南京中医药大学", "江苏", "本科", "医药类", "590", "575", "否", "否", "否"));
list.add(new UniversityImpl("南京林业大学", "江苏", "本科", "农林类", "580", "560", "否", "否", "否"));
list.add(new UniversityImpl("南京财经大学", "江苏", "本科", "财经类", "590", "575", "否", "否", "否"));
// 浙江省补充
list.add(new UniversityImpl("浙江工商大学", "浙江", "本科", "财经类", "585", "575", "否", "否", "否"));
list.add(new UniversityImpl("浙江财经大学", "浙江", "本科", "财经类", "580", "570", "否", "否", "否"));
list.add(new UniversityImpl("浙江理工大学", "浙江", "本科", "理工类", "575", "555", "否", "否", "否"));
list.add(new UniversityImpl("杭州师范大学", "浙江", "本科", "师范类", "565", "555", "否", "否", "否"));
list.add(new UniversityImpl("宁波诺丁汉大学", "浙江", "本科", "综合类", "590", "580", "否", "否", "否"));
// 广东省补充
list.add(new UniversityImpl("南方科技大学", "广东", "本科", "理工类", "630", "600", "否", "否", "否"));
list.add(new UniversityImpl("广东工业大学", "广东", "本科", "理工类", "580", "560", "否", "否", "否"));
list.add(new UniversityImpl("广州医科大学", "广东", "本科", "医药类", "585", "570", "否", "否", "否"));
list.add(new UniversityImpl("广东财经大学", "广东", "本科", "财经类", "575", "565", "否", "否", "否"));
list.add(new UniversityImpl("广州美术学院", "广东", "本科", "艺术类", "535", "530", "否", "否", "否"));
// 湖北省补充
list.add(new UniversityImpl("湖北工业大学", "湖北", "本科", "理工类", "565", "545", "否", "否", "否"));
list.add(new UniversityImpl("武汉纺织大学", "湖北", "本科", "理工类", "555", "540", "否", "否", "否"));
list.add(new UniversityImpl("武汉工程大学", "湖北", "本科", "理工类", "560", "545", "否", "否", "否"));
list.add(new UniversityImpl("湖北中医药大学", "湖北", "本科", "医药类", "565", "550", "否", "否", "否"));
// 四川省补充
list.add(new UniversityImpl("成都信息工程大学", "四川", "本科", "理工类", "565", "545", "否", "否", "否"));
list.add(new UniversityImpl("西华大学", "四川", "本科", "综合类", "555", "535", "否", "否", "否"));
list.add(new UniversityImpl("成都大学", "四川", "本科", "综合类", "550", "535", "否", "否", "否"));
list.add(new UniversityImpl("西南石油大学", "四川", "本科", "理工类", "570", "550", "否", "否", "否"));
// 陕西省补充
list.add(new UniversityImpl("陕西科技大学", "陕西", "本科", "理工类", "565", "545", "否", "否", "否"));
list.add(new UniversityImpl("西安工程大学", "陕西", "本科", "理工类", "555", "535", "否", "否", "否"));
list.add(new UniversityImpl("西安外国语大学", "陕西", "本科", "语言类", "575", "565", "否", "否", "否"));
list.add(new UniversityImpl("西安美术学院", "陕西", "本科", "艺术类", "515", "510", "否", "否", "否"));
// 山东省补充
list.add(new UniversityImpl("山东财经大学", "山东", "本科", "财经类", "565", "555", "否", "否", "否"));
list.add(new UniversityImpl("青岛理工大学", "山东", "本科", "理工类", "555", "535", "否", "否", "否"));
list.add(new UniversityImpl("山东理工大学", "山东", "本科", "理工类", "550", "530", "否", "否", "否"));
// 辽宁省补充
list.add(new UniversityImpl("辽宁工程技术大学", "辽宁", "本科", "理工类", "545", "525", "否", "否", "否"));
list.add(new UniversityImpl("沈阳建筑大学", "辽宁", "本科", "理工类", "550", "530", "否", "否", "否"));
// 湖南省补充
list.add(new UniversityImpl("南华大学", "湖南", "本科", "综合类", "560", "545", "否", "否", "否"));
list.add(new UniversityImpl("湖南工程学院", "湖南", "本科", "理工类", "545", "530", "否", "否", "否"));
// 安徽省补充
list.add(new UniversityImpl("安徽理工大学", "安徽", "本科", "理工类", "550", "530", "否", "否", "否"));
list.add(new UniversityImpl("安徽工业大学", "安徽", "本科", "理工类", "555", "535", "否", "否", "否"));
return list;
}
}

76
project/爬虫2/src/main/java/com/example/entity/University.java

@ -0,0 +1,76 @@
/**
* 高校数据实体接口
*
* 定义了高校信息的标准数据结构包含核心字段
* - 学校名称
* - 所在地区
* - 办学层次
* - 院校类型
* - 最低分数线理科/文科
* - 是否985工程大学
* - 是否211工程大学
* - 是否双一流大学
*
* 使用接口的好处
* 1. 定义统一的数据访问规范
* 2. 便于后续扩展不同的实现类
* 3. 降低模块间的耦合度
*/
package com.example.entity;
public interface University {
/**
* 获取学校名称
* @return 学校名称字符串
*/
String getName();
/**
* 获取所在地区
* @return 地区字符串北京上海广东等
*/
String getRegion();
/**
* 获取办学层次
* @return 办学层次本科专科高职等
*/
String getLevel();
/**
* 获取院校类型
* @return 院校类型综合类理工类师范类等
*/
String getType();
/**
* 获取理科最低分数线
* @return 理科最低分数线"620"
*/
String getScienceScore();
/**
* 获取文科最低分数线
* @return 文科最低分数线"600"
*/
String getArtsScore();
/**
* 是否为985工程大学
* @return "是""否"
*/
String getIs985();
/**
* 是否为211工程大学
* @return "是""否"
*/
String getIs211();
/**
* 是否为双一流大学
* @return "是""否"
*/
String getIsDoubleFirst();
}

149
project/爬虫2/src/main/java/com/example/entity/UniversityImpl.java

@ -0,0 +1,149 @@
/**
* 高校数据实体实现类
*
* 实现了 University 接口提供高校数据的具体存储和访问功能
* 特点
* 1. 在构造函数中自动处理空数据缺失信息统一显示"未知""无"
* 2. 使用私有字段存储数据通过 getter 方法访问
* 3. 遵循 JavaBean 规范
*/
package com.example.entity;
public class UniversityImpl implements University {
/** 学校名称 */
private String name;
/** 所在地区 */
private String region;
/** 办学层次(本科/专科/高职等) */
private String level;
/** 院校类型(综合类/理工类/师范类等) */
private String type;
/** 理科最低分数线 */
private String scienceScore;
/** 文科最低分数线 */
private String artsScore;
/** 是否985工程大学 */
private String is985;
/** 是否211工程大学 */
private String is211;
/** 是否双一流大学 */
private String isDoubleFirst;
/**
* 构造函数创建高校数据对象包含分数线和985/211/双一流信息
*
* @param name 学校名称
* @param region 所在地区
* @param level 办学层次
* @param type 院校类型
* @param scienceScore 理科最低分数线
* @param artsScore 文科最低分数线
* @param is985 是否985工程大学
* @param is211 是否211工程大学
* @param isDoubleFirst 是否双一流大学
*/
public UniversityImpl(String name, String region, String level, String type,
String scienceScore, String artsScore,
String is985, String is211, String isDoubleFirst) {
// 自动处理空数据,缺失时显示默认值
this.name = name != null && !name.isEmpty() ? name : "未知";
this.region = region != null && !region.isEmpty() ? region : "未知";
this.level = level != null && !level.isEmpty() ? level : "未知";
this.type = type != null && !type.isEmpty() ? type : "未知";
this.scienceScore = scienceScore != null && !scienceScore.isEmpty() ? scienceScore : "无";
this.artsScore = artsScore != null && !artsScore.isEmpty() ? artsScore : "无";
this.is985 = is985 != null && !is985.isEmpty() ? is985 : "否";
this.is211 = is211 != null && !is211.isEmpty() ? is211 : "否";
this.isDoubleFirst = isDoubleFirst != null && !isDoubleFirst.isEmpty() ? isDoubleFirst : "否";
}
/**
* 获取学校名称
* @return 学校名称
*/
@Override
public String getName() {
return name;
}
/**
* 获取所在地区
* @return 所在地区
*/
@Override
public String getRegion() {
return region;
}
/**
* 获取办学层次
* @return 办学层次
*/
@Override
public String getLevel() {
return level;
}
/**
* 获取院校类型
* @return 院校类型
*/
@Override
public String getType() {
return type;
}
/**
* 获取理科最低分数线
* @return 理科最低分数线
*/
@Override
public String getScienceScore() {
return scienceScore;
}
/**
* 获取文科最低分数线
* @return 文科最低分数线
*/
@Override
public String getArtsScore() {
return artsScore;
}
/**
* 是否为985工程大学
* @return "是""否"
*/
@Override
public String getIs985() {
return is985;
}
/**
* 是否为211工程大学
* @return "是""否"
*/
@Override
public String getIs211() {
return is211;
}
/**
* 是否为双一流大学
* @return "是""否"
*/
@Override
public String getIsDoubleFirst() {
return isDoubleFirst;
}
}

47
project/爬虫2/src/main/java/com/example/exception/SpiderException.java

@ -0,0 +1,47 @@
/**
* 爬虫异常体系
* 包含所有业务异常定义
*/
package com.example.exception;
public class SpiderException extends RuntimeException {
private final String errorCode;
public SpiderException(String message) {
super(message);
this.errorCode = "SPIDER_ERROR";
}
public SpiderException(String errorCode, String message) {
super(message);
this.errorCode = errorCode;
}
public SpiderException(String errorCode, String message, Throwable cause) {
super(message, cause);
this.errorCode = errorCode;
}
public String getErrorCode() {
return errorCode;
}
// 解析异常
public static class ParseException extends SpiderException {
public ParseException(String message) { super("PARSE_ERROR", message); }
public ParseException(String message, Throwable cause) { super("PARSE_ERROR", message, cause); }
}
// 输出异常
public static class OutputException extends SpiderException {
public OutputException(String message) { super("OUTPUT_ERROR", message); }
public OutputException(String message, Throwable cause) { super("OUTPUT_ERROR", message, cause); }
}
// 服务异常
public static class ServiceException extends SpiderException {
public ServiceException(String message) { super("SERVICE_ERROR", message); }
public ServiceException(String message, Throwable cause) { super("SERVICE_ERROR", message, cause); }
}
}

14
project/爬虫2/src/main/java/com/example/service/UniversityService.java

@ -0,0 +1,14 @@
/**
* 高校服务接口
* MVC 架构中的 Controller
*/
package com.example.service;
import com.example.entity.University;
import java.util.List;
public interface UniversityService {
List<University> getAllUniversities();
List<University> getByRegion(String region);
List<University> getByLevel(String level);
}

32
project/爬虫2/src/main/java/com/example/service/UniversityServiceImpl.java

@ -0,0 +1,32 @@
/**
* 高校服务实现类
*/
package com.example.service;
import com.example.data.UniversityData;
import com.example.entity.University;
import java.util.List;
import java.util.stream.Collectors;
public class UniversityServiceImpl implements UniversityService {
@Override
public List<University> getAllUniversities() {
return UniversityData.get300Universities();
}
@Override
public List<University> getByRegion(String region) {
return getAllUniversities().stream()
.filter(u -> u.getRegion().contains(region))
.collect(Collectors.toList());
}
@Override
public List<University> getByLevel(String level) {
return getAllUniversities().stream()
.filter(u -> u.getLevel().contains(level))
.collect(Collectors.toList());
}
}

39
project/爬虫2/src/main/java/com/example/strategy/ConsoleOutputStrategy.java

@ -0,0 +1,39 @@
/**
* 控制台输出策略
*/
package com.example.strategy;
import com.example.entity.University;
import java.util.List;
public class ConsoleOutputStrategy implements OutputStrategy {
@Override
public void output(List<University> universities) {
System.out.println("╔══════════════════════════════════════════════════════════════════════════════════════════════════╗");
System.out.printf("║ %-20s %-10s %-10s %-10s %-12s %-12s %-6s %-6s %-8s ║%n",
"学校名称", "所在地区", "办学层次", "院校类型", "理科分数线", "文科分数线", "985", "211", "双一流");
System.out.println("╠══════════════════════════════════════════════════════════════════════════════════════════════════╣");
for (University university : universities) {
System.out.printf("║ %-20s %-10s %-10s %-10s %-12s %-12s %-6s %-6s %-8s ║%n",
truncate(university.getName(), 20),
truncate(university.getRegion(), 10),
truncate(university.getLevel(), 10),
truncate(university.getType(), 10),
truncate(university.getScienceScore(), 12),
truncate(university.getArtsScore(), 12),
truncate(university.getIs985(), 6),
truncate(university.getIs211(), 6),
truncate(university.getIsDoubleFirst(), 8));
}
System.out.println("╚══════════════════════════════════════════════════════════════════════════════════════════════════╝");
}
private String truncate(String str, int maxLen) {
if (str == null) return "";
if (str.length() <= maxLen) return str;
return str.substring(0, maxLen - 1) + "…";
}
}

57
project/爬虫2/src/main/java/com/example/strategy/CsvOutputStrategy.java

@ -0,0 +1,57 @@
/**
* CSV 输出策略
*/
package com.example.strategy;
import com.example.entity.University;
import com.example.exception.SpiderException.OutputException;
import java.io.*;
import java.util.List;
public class CsvOutputStrategy implements OutputStrategy {
private final String outputFile;
public CsvOutputStrategy(String outputFile) {
this.outputFile = outputFile;
}
@Override
public void output(List<University> universities) {
try (PrintWriter writer = new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream(outputFile), "UTF-8")))) {
writer.write('\ufeff');
writer.println("学校名称,所在地区,办学层次,院校类型,理科分数线,文科分数线,985,211,双一流");
for (University university : universities) {
writer.printf("%s,%s,%s,%s,%s,%s,%s,%s,%s%n",
escapeCsv(university.getName()),
escapeCsv(university.getRegion()),
escapeCsv(university.getLevel()),
escapeCsv(university.getType()),
escapeCsv(university.getScienceScore()),
escapeCsv(university.getArtsScore()),
escapeCsv(university.getIs985()),
escapeCsv(university.getIs211()),
escapeCsv(university.getIsDoubleFirst()));
}
System.out.println("CSV 文件已生成:" + outputFile);
} catch (Exception e) {
throw new OutputException("导出 CSV 失败:" + e.getMessage(), e);
}
}
private String escapeCsv(String value) {
if (value == null) return "";
if (value.contains(",") || value.contains("\"") || value.contains("\n")) {
return "\"" + value.replace("\"", "\"\"") + "\"";
}
return value;
}
}

12
project/爬虫2/src/main/java/com/example/strategy/OutputStrategy.java

@ -0,0 +1,12 @@
/**
* 输出策略接口
* Strategy 模式定义统一的输出策略接口
*/
package com.example.strategy;
import com.example.entity.University;
import java.util.List;
public interface OutputStrategy {
void output(List<University> universities);
}

BIN
project/爬虫2/target/classes/com/example/Application.class

Binary file not shown.

BIN
project/爬虫2/target/classes/com/example/Main.class

Binary file not shown.

BIN
project/爬虫2/target/classes/com/example/cli/CliArgs.class

Binary file not shown.

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save