5 changed files with 248 additions and 0 deletions
@ -0,0 +1,11 @@ |
|||
package com.example; |
|||
|
|||
/** |
|||
* 命令接口 - 命令模式 |
|||
* 课堂知识点:命令模式、接口 |
|||
*/ |
|||
public interface Command { |
|||
void execute() throws CrawlerException; |
|||
void undo(); |
|||
String getDescription(); |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
package com.example; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 命令调用器 - 命令模式 |
|||
* 课堂知识点:命令模式、集合框架 |
|||
*/ |
|||
public class CommandInvoker { |
|||
private final List<Command> history = new ArrayList<>(); |
|||
private int currentIndex = -1; |
|||
|
|||
public void execute(Command cmd) throws CrawlerException { |
|||
cmd.execute(); |
|||
if (currentIndex < history.size() - 1) { |
|||
history.subList(currentIndex + 1, history.size()).clear(); |
|||
} |
|||
history.add(cmd); |
|||
currentIndex++; |
|||
} |
|||
|
|||
public void undo() { |
|||
if (currentIndex < 0) { |
|||
System.out.println("没有可撤销的命令"); |
|||
return; |
|||
} |
|||
history.get(currentIndex).undo(); |
|||
currentIndex--; |
|||
} |
|||
|
|||
public void showHistory() { |
|||
System.out.println("\n========== 命令历史 =========="); |
|||
if (history.isEmpty()) { |
|||
System.out.println("暂无命令历史"); |
|||
} else { |
|||
for (int i = 0; i < history.size(); i++) { |
|||
String marker = (i == currentIndex) ? "→" : " "; |
|||
System.out.printf("%s %2d. %s\n", marker, i + 1, history.get(i).getDescription()); |
|||
} |
|||
} |
|||
System.out.println("=============================="); |
|||
} |
|||
} |
|||
@ -0,0 +1,106 @@ |
|||
package com.example; |
|||
|
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* 爬虫基类 - 定义爬虫的通用行为和属性 |
|||
* 课堂知识点:继承、抽象类、封装 |
|||
*/ |
|||
public abstract class Crawler { |
|||
|
|||
/** |
|||
* 请求间隔时间(毫秒)- 避免被网站封禁 |
|||
*/ |
|||
protected static final int DELAY_MS = 1000; |
|||
|
|||
/** |
|||
* 爬取状态:是否成功 |
|||
*/ |
|||
private boolean success; |
|||
|
|||
/** |
|||
* 爬取的数据数量 |
|||
*/ |
|||
private int dataCount; |
|||
|
|||
/** |
|||
* 爬取数据 - 抽象方法,子类必须实现 |
|||
* 课堂知识点:抽象方法 |
|||
*/ |
|||
public abstract void crawl(); |
|||
|
|||
/** |
|||
* 打印结果 - 抽象方法,子类必须实现 |
|||
*/ |
|||
public abstract void printResults(); |
|||
|
|||
/** |
|||
* 保存数据到文件 - 抽象方法,子类必须实现 |
|||
*/ |
|||
public abstract void saveToFile(String filename) throws IOException; |
|||
|
|||
/** |
|||
* 保存数据到数据库 - 抽象方法,子类必须实现 |
|||
* 课堂知识点:JDBC、数据库持久化 |
|||
*/ |
|||
public abstract void saveToDatabase(); |
|||
|
|||
/** |
|||
* 检查网络状态 |
|||
* @return 网络是否可用 |
|||
*/ |
|||
protected boolean isNetworkAvailable() { |
|||
return HttpCrawler.isNetworkAvailable(); |
|||
} |
|||
|
|||
/** |
|||
* 添加请求延迟,避免被封禁 |
|||
*/ |
|||
protected void delay() { |
|||
try { |
|||
Thread.sleep(DELAY_MS); |
|||
} catch (InterruptedException e) { |
|||
Thread.currentThread().interrupt(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 获取爬取状态 |
|||
* @return 是否成功 |
|||
*/ |
|||
public boolean isSuccess() { |
|||
return success; |
|||
} |
|||
|
|||
/** |
|||
* 设置爬取状态 |
|||
* @param success 是否成功 |
|||
*/ |
|||
protected void setSuccess(boolean success) { |
|||
this.success = success; |
|||
} |
|||
|
|||
/** |
|||
* 获取数据数量 |
|||
* @return 数据数量 |
|||
*/ |
|||
public int getDataCount() { |
|||
return dataCount; |
|||
} |
|||
|
|||
/** |
|||
* 设置数据数量 |
|||
* @param dataCount 数据数量 |
|||
*/ |
|||
protected void setDataCount(int dataCount) { |
|||
this.dataCount = dataCount; |
|||
} |
|||
|
|||
/** |
|||
* 获取爬虫名称(默认实现,子类可重写) |
|||
* @return 爬虫名称 |
|||
*/ |
|||
public String getCrawlerName() { |
|||
return this.getClass().getSimpleName(); |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
package com.example; |
|||
|
|||
/** |
|||
* 爬虫命令 - 命令模式实现 |
|||
*/ |
|||
public class CrawlerCommand implements Command { |
|||
private final Crawler crawler; |
|||
private final String filename; |
|||
private boolean executed; |
|||
|
|||
public CrawlerCommand(Crawler crawler, String filename) { |
|||
this.crawler = crawler; |
|||
this.filename = filename; |
|||
this.executed = false; |
|||
} |
|||
|
|||
@Override |
|||
public void execute() throws CrawlerException { |
|||
if (executed) { |
|||
throw new CrawlerException("命令已经执行过"); |
|||
} |
|||
try { |
|||
System.out.println("执行: " + getDescription()); |
|||
crawler.crawl(); |
|||
crawler.printResults(); |
|||
crawler.saveToFile(filename); |
|||
executed = true; |
|||
} catch (Exception e) { |
|||
throw new CrawlerException("执行失败: " + e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public void undo() { |
|||
if (!executed) { |
|||
System.out.println("命令未执行,无法撤销"); |
|||
return; |
|||
} |
|||
System.out.println("撤销: " + getDescription()); |
|||
executed = false; |
|||
} |
|||
|
|||
@Override |
|||
public String getDescription() { |
|||
return crawler.getCrawlerName() + " -> " + filename; |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
package com.example; |
|||
|
|||
import java.io.IOException; |
|||
|
|||
/** |
|||
* 爬虫异常基类 - 异常体系 |
|||
* 课堂知识点:自定义异常、异常继承体系 |
|||
*/ |
|||
public class CrawlerException extends Exception { |
|||
public CrawlerException(String message) { |
|||
super(message); |
|||
} |
|||
public CrawlerException(String message, Throwable cause) { |
|||
super(message, cause); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 网络异常 - 网络连接失败 |
|||
*/ |
|||
class NetworkException extends CrawlerException { |
|||
public NetworkException(String message) { super(message); } |
|||
public NetworkException(String message, Throwable cause) { super(message, cause); } |
|||
} |
|||
|
|||
/** |
|||
* 数据解析异常 - HTML/JSON解析失败 |
|||
*/ |
|||
class DataParseException extends CrawlerException { |
|||
public DataParseException(String message) { super(message); } |
|||
public DataParseException(String message, Throwable cause) { super(message, cause); } |
|||
} |
|||
|
|||
/** |
|||
* 文件保存异常 - 文件写入失败 |
|||
*/ |
|||
class FileSaveException extends CrawlerException { |
|||
public FileSaveException(String message) { super(message); } |
|||
public FileSaveException(String message, IOException cause) { super(message, cause); } |
|||
} |
|||
Loading…
Reference in new issue