import javafx.application.Application; import javafx.application.Platform; import javafx.concurrent.Task; import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Scene; import javafx.scene.control.; import javafx.scene.layout.; import javafx.stage.Stage; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; import org.openqa.selenium.*; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait;

import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors;

public class NovelDownloader extends Application { private Map<String, String> chapters = new HashMap<>(); private TextField novelNameInput; private CheckBox saveCheckBox; private ListView chapterList; private TextArea chapterText; private WebDriver driver;

public static void main(String[] args) {
    launch(args);
}

@Override
public void start(Stage primaryStage) {
    primaryStage.setTitle('小说下载器');

    novelNameInput = new TextField();
    saveCheckBox = new CheckBox('是否下载小说内容到当前目录下');
    Button searchButton = new Button('获取');
    chapterList = new ListView<>();
    chapterText = new TextArea();
    chapterText.setEditable(false); // 设置内容区域不可编辑

    VBox root = new VBox(10);
    root.setPadding(new Insets(10));
    HBox inputBox = new HBox(10);
    HBox.setHgrow(novelNameInput, Priority.ALWAYS);
    inputBox.getChildren().addAll(novelNameInput, saveCheckBox, searchButton);

    // 使用 GridPane 布局章节列表和内容区域
    GridPane gridPane = new GridPane();
    gridPane.setHgap(10);
    gridPane.setVgap(10);
    gridPane.setAlignment(Pos.CENTER);
    ColumnConstraints column1 = new ColumnConstraints();
    column1.setPercentWidth(30); // 章节列表占 30% 宽度
    ColumnConstraints column2 = new ColumnConstraints();
    column2.setPercentWidth(70); // 内容区域占 70% 宽度
    gridPane.getColumnConstraints().addAll(column1, column2);
    gridPane.add(chapterList, 0, 0);
    gridPane.add(chapterText, 1, 0);

    root.getChildren().addAll(new Label('请输入小说名称:'), inputBox, gridPane);

    searchButton.setOnAction(event -> {
        String novelName = novelNameInput.getText();
        if (!novelName.isEmpty()) {
            searchNovelAndScrape(novelName);
        } else {
            showAlert('警告', '请输入小说名称', Alert.AlertType.WARNING);
        }
    });

    chapterList.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
        if (newValue != null) {
            showChapterContent(newValue);
        }
    });

    primaryStage.setScene(new Scene(root, 800, 600));
    primaryStage.show();
}

public void searchNovelAndScrape(String novelName) {
    Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            try {
                setupChromeDriver();

                driver.get('https://www.bige3.cc/');
                WebElement searchInput = driver.findElement(By.xpath('/html/body/div[4]/div[1]/div[2]/form/input[1]'));
                searchInput.sendKeys(novelName);
                searchInput.sendKeys(Keys.ENTER);
                WebDriverWait wait = new WebDriverWait(driver, 10);
                wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath('/html/body/div[5]/div/div/div/div/div[2]/h4/a')));

                List<WebElement> elements = driver.findElements(By.xpath('/html/body/div[5]/div/div/div/div/div[2]/h4/a'));
                if (elements.isEmpty()) {
                    showAlert('提示', '没有找到相关小说', Alert.AlertType.INFORMATION);
                    return null;
                }

                for (WebElement element : elements) {
                    element.click();
                    driver.switchTo().window(driver.getWindowHandles().stream().reduce((first, second) -> second).orElse(null));
                    wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath('//*[@class='listmain']/dl/dd/a')));

                    List<WebElement> chapterElements = driver.findElements(By.xpath('//*[@class='listmain']/dl/dd/a'));
                    for (WebElement chapter : chapterElements) {
                        chapter.click();
                        while (true) {
                            wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath('//*[@id='read']/div[5]/div[3]/h1')));
                            WebElement chapterTitleElement = driver.findElement(By.xpath('//*[@id='read']/div[5]/div[3]/h1'));
                            WebElement chapterContentElement = driver.findElement(By.xpath('//*[@id='chaptercontent']'));
                            String chapterTitle = chapterTitleElement.getText().replace('、', '');
                            String chapterContent = chapterContentElement.getAttribute('innerHTML')
                                    .replaceAll('无弹窗,更新快,免费阅读!', '')
                                    .replaceAll('请收藏本站:https://www.bige3.cc。笔趣阁手机版:https://m.bige3.cc', '')
                                    .replaceAll('『点此报错』『加入书签』', '');
                            if (saveCheckBox.isSelected()) {
                                saveChapterToFile(novelName, chapterTitle, chapterContent);
                            }

                            String cleanContent = cleanChapterContent(chapterContent);
                            chapters.put(chapterTitle, cleanContent);
                            updateChapterList();

                            try {
                                wait.until(ExpectedConditions.elementToBeClickable(By.xpath('//div[@class='Readpage pagedown']/a[@id='pb_next']')));
                                WebElement nextButton = driver.findElement(By.xpath('//div[@class='Readpage pagedown']/a[@id='pb_next']'));
                                nextButton.click();
                            } catch (Exception e) {
                                break;
                            }
                        }
                        driver.navigate().back();
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
                showAlert('提示', '运行出错', Alert.AlertType.ERROR);
            } finally {
                cleanupChromeDriver();
            }

            return null;
        }
    };

    Thread thread = new Thread(task);
    thread.setDaemon(true);
    thread.start();
}

private void setupChromeDriver() {
    ChromeOptions options = new ChromeOptions();
    options.addArguments('--no-sandbox');
    options.addArguments('--disable-dev-shm-usage');
    options.addArguments('--window-size=1920,1080');
    options.addArguments('user-agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36'');
    driver = new ChromeDriver(options);
}

private void cleanupChromeDriver() {
    if (driver != null) {
        driver.close();
        driver.quit();
    }
}

private void saveChapterToFile(String novelName, String chapterTitle, String chapterContent) {
    String bookDir = System.getProperty('user.dir') + '/' + novelName;
    File bookDirectory = new File(bookDir);
    if (!bookDirectory.exists()) {
        bookDirectory.mkdirs();
    }

    String chapterPath = bookDir + '/' + chapterTitle + '.txt';
    try (BufferedWriter writer = new BufferedWriter(new FileWriter(chapterPath))) {
        writer.write(chapterContent);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

private String cleanChapterContent(String chapterContent) {
    Document doc = Jsoup.parse(chapterContent);
    Elements chapterBrElements = doc.select('br');
    return chapterBrElements.stream()
            .map(brElement -> brElement.nextSibling().toString().trim())
            .filter(text -> !text.isEmpty())
            .collect(Collectors.joining('

')); }

public void updateChapterList() {
    Platform.runLater(() -> {
        chapterList.getItems().clear();
        chapterList.getItems().addAll(chapters.keySet());
    });
}

public void showChapterContent(String chapterTitle) {
    Platform.runLater(() -> {
        String content = chapters.get(chapterTitle);
        if (content != null) {
            content = content.replaceAll('<br>', '

'); // Replace
with spaces chapterText.setText(content); } }); }

public static void showAlert(String title, String message, Alert.AlertType alertType) {
    Platform.runLater(() -> {
        Alert alert = new Alert(alertType);
        alert.setTitle(title);
        alert.setHeaderText(null);
        alert.setContentText(message);
        alert.showAndWait();
    });
}

@Override
public void stop() {
    cleanupChromeDriver();
}

原文地址: https://www.cveoy.top/t/topic/nJ1v 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录