diff --git a/project/view/README.md b/project/view/README.md new file mode 100644 index 0000000..f5ecc7f --- /dev/null +++ b/project/view/README.md @@ -0,0 +1,358 @@ +# Java爬虫框架 + +基于MVC架构的Java爬虫框架,支持多态扩展,可轻松添加新的网站爬虫。 + +## 功能特性 + +- **MVC架构**:清晰的分层设计,职责分明 +- **多态扩展**:通过继承BaseCrawler实现新爬虫 +- **命令行界面**:支持交互式命令操作 +- **自动识别**:根据URL自动选择合适的爬虫 +- **日期提取**:支持从URL中提取发布日期 + +## 支持的网站 + +| 网站 | 域名 | 爬虫名称 | +|------|------|----------| +| 湖南大学官网 | `*.hnu.edu.cn` | HunanUniversityCrawler | +| 湖南大学新闻网 | `news.hnu.edu.cn` | HunanUniversityNewsCrawler | +| 中国天气网 | `*.weather.com.cn` | ChinaWeatherCrawler | +| 骑砍中文站 | `*.mountblade.com.cn` | MountBladeCrawler | + +## 快速开始 + +### 编译项目 + +```bash +javac -d target/classes src/main/java/com/crawler/**/*.java +``` + +### 运行程序 + +```bash +java -cp target/classes com.crawler.Main +``` + +### 命令行使用 + +``` +======================================== +Java爬虫框架 +======================================== + +======================================== +Java爬虫框架 - 命令行模式 +======================================== +输入 'help' 查看可用指令 +======================================== +> help +可用指令: +--------- +help : 显示所有可用指令 +list : 查看使用过的指令历史 +crawl : 运行爬虫,输入URL自动选择爬虫 +exit : 退出程序 + +> crawl +请输入要爬取的URL: https://www.mountblade.com.cn +使用爬虫: MountBladeCrawler +... +``` + +## 项目结构 + +``` +src/main/java/com/crawler/ +├── Main.java # 主入口 +├── model/ +│ ├── CrawlerData.java # 爬取数据模型(标题、链接、来源、发布日期) +│ └── CrawlerConfig.java # 爬虫配置(超时时间、User-Agent) +├── view/ +│ └── CrawlerView.java # 视图层(输出结果展示) +├── controller/ +│ └── CrawlerController.java # 爬虫控制器 +├── crawler/ +│ ├── Crawler.java # 爬虫接口 +│ ├── BaseCrawler.java # 爬虫抽象基类 +│ ├── CrawlerFactory.java # 爬虫工厂(自动选择爬虫) +│ └── impl/ +│ ├── ExampleCrawler.java # 通用爬虫 +│ ├── TestCrawler.java # 测试爬虫 +│ ├── HunanUniversityCrawler.java +│ ├── HunanUniversityNewsCrawler.java +│ ├── ChinaWeatherCrawler.java +│ └── MountBladeCrawler.java +└── command/ + ├── Command.java # 命令接口 + ├── BaseCommand.java # 命令抽象基类 + ├── CommandHistory.java # 命令历史记录 + ├── HelpCommand.java # 帮助命令 + ├── ListCommand.java # 历史记录命令 + ├── CrawlCommand.java # 爬虫命令 + ├── ExitCommand.java # 退出命令 + └── CommandController.java # 命令控制器 +``` + +## 扩展新爬虫 + +只需继承 `BaseCrawler` 并重写两个方法: + +```java +package com.crawler.crawler.impl; + +import com.crawler.crawler.BaseCrawler; +import com.crawler.model.CrawlerData; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MyWebsiteCrawler extends BaseCrawler { + private static final String BASE_URL = "https://www.mywebsite.com"; + + @Override + public String getCrawlerName() { + return "MyWebsiteCrawler"; + } + + @Override + protected List parseHtml(String html) { + List results = new ArrayList<>(); + + // 使用正则表达式解析HTML + Pattern pattern = Pattern.compile("([^<]+)"); + Matcher matcher = pattern.matcher(html); + + while (matcher.find()) { + CrawlerData data = new CrawlerData(); + data.setTitle(matcher.group(2)); + data.setUrl(normalizeUrl(matcher.group(1))); + data.setSource(getCrawlerName()); + data.setPublishDate(extractDateFromUrl(matcher.group(1))); + results.add(data); + } + + return results; + } + + private String normalizeUrl(String url) { + if (url.startsWith("/")) { + return BASE_URL + url; + } + return url; + } + + private String extractDateFromUrl(String url) { + Pattern datePattern = Pattern.compile("/(\\d{4}-\\d{2}-\\d{2})/"); + Matcher matcher = datePattern.matcher(url); + return matcher.find() ? matcher.group(1) : null; + } +} +``` + +然后在 `CrawlerFactory.java` 中添加识别规则: + +```java +crawlerPatterns.put("MyWebsiteCrawler", + Pattern.compile(".*mywebsite\\.com.*", Pattern.CASE_INSENSITIVE)); +``` + +并在 `createCrawlerByName` 方法中添加: + +```java +case "MyWebsiteCrawler": + return new MyWebsiteCrawler(); +``` + +## 架构设计 + +### MVC模式 + +- **Model**:`CrawlerData`(数据模型)、`CrawlerConfig`(配置) +- **View**:`CrawlerView`(结果展示) +- **Controller**:`CrawlerController`(爬虫控制)、`CommandController`(命令控制) + +### 多态设计 + +- `Crawler` 接口定义标准方法 +- `BaseCrawler` 提供通用HTTP请求能力 +- 各爬虫实现类继承 `BaseCrawler` 并重写 `parseHtml` 方法 + +### 工厂模式 + +`CrawlerFactory` 根据URL模式自动选择合适的爬虫实现。 + +## 配置说明 + +`CrawlerConfig` 支持以下配置: + +- `timeout`:HTTP请求超时时间(默认30000毫秒) +- `userAgent`:User-Agent(默认模拟Chrome浏览器) + +## 命令列表 + +| 命令 | 功能 | +|------|------| +| `help` | 显示所有可用指令 | +| `list` | 查看使用过的指令历史 | +| `crawl` | 运行爬虫,输入目标URL,爬取后可保存结果 | +| `cache` | 缓存操作:save/load/list/delete | +| `exit` | 退出程序 | + +### cache 命令子操作 + +| 子操作 | 功能 | +|--------|------| +| `save` | 保存当前爬取数据到数据文件 | +| `load` | 从数据文件读取数据 | +| `list` | 列出 `data/` 目录中的所有文件 | +| `delete` | 删除指定的数据文件或所有文件 | + +### 数据目录 + +程序会自动创建 `data/` 目录用于保存爬取的数据文件。 + +### 爬取后自动保存 + +使用 `crawl` 命令爬取完成后,系统会自动询问是否保存结果: + +``` +爬虫运行完成,共获取 10 条数据 +======================================== + +是否保存爬取结果? (y/n): y +请输入保存路径 (默认: data/crawler_data.json): +数据已保存到: data/crawler_data.json +``` + +### 删除缓存文件示例 + +``` +> cache +请输入缓存操作 (save/load/list/delete): delete +======================================== +可选删除的文件: +======================================== +[1] crawler_data.json (1024 bytes) +[2] mountblade_data.json (2048 bytes) +[all] 删除所有文件 +======================================== +请输入要删除的文件序号或 'all': 1 +确定要删除 'crawler_data.json' 吗? (y/n): y +已删除: crawler_data.json +``` + +## 输出示例 + +``` +[12] +标题: 骑砍2《战帆》v1.2.4与本体v1.4.4测试版更新日志 +链接: https://www.mountblade.com.cn/news/Bannerlord/2026-05-13/3175.html +来源: MountBladeCrawler +发布日期: 2026-05-13 +---------------------------------------- +``` + +## 异常处理 + +项目采用分层异常体系设计,区分受检异常和非受检异常: + +### 异常分类 + +| 异常类型 | 说明 | 示例 | +|---------|------|------| +| **受检异常** | 可恢复异常,强制调用者处理 | `HttpRequestException`, `TimeoutException`, `HtmlParseException`, `DataExtractException` | +| **非受检异常** | 编程错误,不可恢复 | `InvalidUrlException`, `UnsupportedCrawlerException` | + +### 异常继承树 + +``` +CrawlerException (爬虫框架根异常 - 受检) +├── NetworkException (网络异常父类) +│ ├── HttpRequestException (HTTP请求失败) +│ └── TimeoutException (连接超时) +└── ParseException (解析异常父类) + ├── HtmlParseException (HTML解析失败) + └── DataExtractException (数据提取失败) + +ConfigurationException (配置异常父类 - 非受检) +├── InvalidUrlException (无效URL) +└── UnsupportedCrawlerException (不支持的爬虫) +``` + +### 异常处理示例 + +```java +try { + List data = crawler.crawl(); + view.showData(data); +} catch (HttpRequestException e) { + view.showErrorMessage("HTTP请求失败: " + e.getStatusCode()); +} catch (TimeoutException e) { + view.showErrorMessage("连接超时,请稍后重试"); +} catch (HtmlParseException e) { + view.showErrorMessage("HTML解析失败: " + e.getSourceUrl()); +} catch (CrawlerException e) { + view.showErrorMessage("爬虫执行失败: " + e.getMessage()); +} +``` + +完整的异常设计文档请参考 [EXCEPTIONS.md](file:///C:/Users/黄志楷/Documents/ocix/学校相关/jwork/w12/EXCEPTIONS.md) + +## 数据序列化 + +项目提供基于Jackson的JSON序列化工具类,支持将爬取数据保存到文件和从文件读取。 + +### 使用示例 + +```java +import com.crawler.util.JsonSerializer; +import com.crawler.model.CrawlerData; +import java.util.List; + +List dataList = crawler.crawl(); + +JsonSerializer.serializeToFile(dataList, "output/crawler_data.json"); + +List loadedData = JsonSerializer.deserializeFromFile("output/crawler_data.json"); +``` + +### JsonSerializer 类方法 + +| 方法 | 功能 | +|------|------| +| `serializeToFile(List, String)` | 将数据列表序列化到指定文件 | +| `deserializeFromFile(String)` | 从文件反序列化数据列表 | +| `toJsonString(List)` | 将数据列表转换为JSON字符串 | +| `toJsonString(CrawlerData)` | 将单条数据转换为JSON字符串 | +| `fromJsonString(String)` | 从JSON字符串反序列化数据列表 | +| `fromJsonStringToSingle(String)` | 从JSON字符串反序列化单条数据 | + +### 输出格式示例 + +```json +[ + { + "title": "新闻标题", + "content": "新闻内容", + "url": "https://example.com/news/1", + "source": "ExampleCrawler", + "publishDate": "2026-05-21" + } +] +``` + +## 技术栈 + +- Java 21+ +- Java HttpClient(内置HTTP客户端) +- Jackson(JSON序列化) +- 正则表达式(HTML解析) + +## 注意事项 + +1. 请遵守目标网站的robots.txt规则 +2. 不要频繁请求,避免给目标服务器造成压力 +3. 某些网站可能有反爬机制,可能需要添加额外的请求头 +4. 建议在爬取前获取网站的爬取授权 \ No newline at end of file diff --git a/project/view/pom.xml b/project/view/pom.xml new file mode 100644 index 0000000..f731ef3 --- /dev/null +++ b/project/view/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + com.crawler + crawler-framework + 1.0.0 + jar + + crawler-framework + Java MVC Crawler Framework + + + 11 + 11 + UTF-8 + + + + + junit + junit + 4.13.2 + test + + + com.fasterxml.jackson.core + jackson-databind + 2.15.2 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.15.2 + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 11 + 11 + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.3.0 + + + jar-with-dependencies + + + + com.crawler.Main + + + + + + make-assembly + package + + single + + + + + + + \ No newline at end of file diff --git a/project/view/temp_doc.zip b/project/view/temp_doc.zip new file mode 100644 index 0000000..cf4a5a1 Binary files /dev/null and b/project/view/temp_doc.zip differ diff --git a/project/view/test.html b/project/view/test.html new file mode 100644 index 0000000..6121e0b --- /dev/null +++ b/project/view/test.html @@ -0,0 +1,23 @@ + + + + Test Page + + +

测试页面

+
+ + + +
+ + \ No newline at end of file