使用Selenium和Jsoup框架进行Java爬虫

ops/2025/2/7 22:33:11/

Java爬虫示例:使用Selenium和Jsoup框架爬取NASA网站

在Java中使用Selenium和Jsoup框架编写爬虫,可以结合Selenium模拟浏览器行为获取动态加载的内容,然后利用Jsoup解析HTML结构并抽取所需数据。基本步骤如下:

步骤1:环境准备与依赖引入

安装Selenium WebDriver:首先需要下载对应浏览器(如Chrome或Firefox)的WebDriver,并配置到系统的PATH环境变量中。
chrome://settings/help,更新chrome浏览器浏览器最新版本,从以下页面下载对应版本的驱动。可以直接加到代码中,也可以添加到path环境变量中。

Chrome for Testing availability:https://googlechromelabs.github.io/chrome-for-testing/#stable
我这里解压后,直接把地址放项目代码里面了,如果经常用,可以放path环境变量里面

java">// chrome驱动地址
String webDriverPath = "D:\\Environments\\chromedriver-win64\\chromedriver.exe";

Maven或Gradle项目依赖:

  • 引入Selenium库(以Maven为例):
java"><dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>latest_version</version> <!-- 替换为最新稳定版 -->
</dependency>
  • 引入Jsoup库:
java"><dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>latest_version</version> <!-- 替换为最新稳定版 -->
</dependency>

本示例使用的是:

java"><!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.17.0</version>
</dependency><!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency><groupId>org.jsoup</groupId><artifactId>jsoup</artifactId><version>1.17.2</version>
</dependency>

步骤2:初始化WebDriver对象

  • 创建一个WebDriver实例来驱动浏览器,例如使用ChromeDriver:
java">import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;// 设置ChromeDriver路径(如果不在系统PATH中)
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver.exe");// 初始化WebDriver
WebDriver driver = new ChromeDriver();

步骤3:访问目标网站并等待页面加载完成

  • 使用get()方法打开指定URL:
java">driver.get("http://example.com");WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); // 使用Java 8+的时间API创建Duration对象// 等待页面加载完成的一个通用做法是等待某个代表页面内容加载完毕的元素出现或变为可见
wait.until(ExpectedConditions.presenceOfElementLocated(By.tagName("body")));// 或者等待特定元素加载完成
// WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("main-content")));

步骤4:获取页面源代码或动态生成的内容

  • 如果页面内容是通过JavaScript动态加载的,则需确保所有内容加载完毕后,再获取页面源代码:
java">String pageSource = driver.getPageSource();

只获取到HTML文档 标签内的所有内容(包括子元素及其文本),而不是整个HTML文档(即不包含部分和其他顶级元素)时,使用如下代码:

java">JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;
String pageSource = (String) jsExecutor.executeScript("return document.body.innerHTML;");

这种方式对于动态渲染且仅需抓取可视区域或body内部数据时更为精确或者高效。

步骤5:使用Jsoup解析HTML文档

  • 将从WebDriver获取的页面源代码转换为Jsoup的Document对象进行解析:
java">import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;Document doc = Jsoup.parse(pageSource);

步骤6:定位并提取数据

  • 使用Jsoup提供的API根据CSS选择器或其他方式定位元素并提取数据:
java">// 例如,提取所有文章标题
Elements titles = doc.select("h2.title");
for (Element title : titles) {String articleTitle = title.text();System.out.println(articleTitle);
}

这里就是分析目标网页的特征,看看真实的图片链接是怎么怎样的
https://www.nasa.gov/image-of-the-day/page/8/

java">// 使用Jsoup提供的API根据CSS选择器或其他方式定位元素并提取数据:
List<Element> imgElements = doc.select("img");List<Future<Void>> futures = new ArrayList<>();
for (Element imgElement : imgElements) {// 获取srcset属性值String imageUrl = imgElement.attr("src");String realImageUrl = null;System.out.println("imageUrl:  " + imageUrl);if (imageUrl.startsWith(preString) && imageUrl.endsWith(endString)) {realImageUrl = imageUrl;System.out.println("realImageUrl " + realImageUrl);}if (realImageUrl != null) {futures.add(executor.submit(new ImageDownloaderPlus(realImageUrl, outPutPath)));}
}

步骤7:处理异常和清理资源

在程序结束前,记得关闭WebDriver以释放浏览器资源:

java">driver.quit();

注意事项

  • Selenium主要用于处理JavaScript渲染的内容和交互式登录等场景,对于静态网页或者不需要模拟用户交互的情况,可以直接用Jsoup抓取。
  • 在实际应用中,应合理控制爬取频率,遵守网站robots.txt规则,避免对服务器造成过大压力。
    完整示例代码

完整示例代码

对需要配置修改项目进行说明

java">// 要爬取的网页地址
String url = "https://www.nasa.gov/image-of-the-day/page/8/";
// 定义前缀,检查是否以某个开头,过滤不必要的图片
String preString = "https://www.nasa.gov/wp-content/uploads";
// 定义后缀,检查是否以某个结束,过滤不必要的图片
String endString = ".jpg";
// 输出目录,这里是项目下的images/nasa/
String outPutPath = "images/nasa/";
// chrome驱动地址
String webDriverPath = "D:\\Environments\\chromedriver-win64\\chromedriver.exe";
  • url:要爬取的图片网页地址
  • preString与endString:过滤掉不需要的图片,比如什么网站图标之类的,根据实际的情况来
  • outPutPath:输出目录,这里是项目下的images/nasa/
  • webDriverPath:前面下载解压的chrome驱动地址

最后完整的示例代码如下:

java">import java.io.IOException;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
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;public class PixabayImageScraperPlus {private static final ExecutorService executor = Executors.newFixedThreadPool(5); // 创建一个固定大小的线程池public static void main(String[] args)throws IOException, InterruptedException, URISyntaxException, ExecutionException {// 要爬取的网页地址String url = "https://www.nasa.gov/image-of-the-day/page/8/";// 定义前缀,检查是否以某个开头,过滤不必要的图片String preString = "https://www.nasa.gov/wp-content/uploads";// 定义后缀,检查是否以某个结束,过滤不必要的图片String endString = ".jpg";// 输出目录,这里是项目下的images/nasa/String outPutPath = "images/nasa/";// chrome驱动地址String webDriverPath = "D:\\Environments\\chromedriver-win64\\chromedriver.exe";System.setProperty("webdriver.chrome.driver", webDriverPath);ChromeOptions options = new ChromeOptions();// 如果要在无界面模式下运行,但有界面有时候更方便,看实际需求
//		options.addArguments("--headless");WebDriver driver = new ChromeDriver(options);try {System.out.println("获取url");driver.get(url);// 等页面关键内容加载结束后,可以手动将网页加载结束掉,而不用一直等加载结束System.out.println("休眠等待准备");WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); // 使用Java 8+的时间API创建Duration对象// 等待页面加载完成的一个通用做法是等待某个代表页面内容加载完毕的元素出现或变为可见wait.until(ExpectedConditions.presenceOfElementLocated(By.tagName("body")));//			// 休眠等待准备好,也可以直接设定一个时间
//			Thread.sleep(100000);System.out.println("休眠等待结束");// 如果页面内容是通过JavaScript动态加载的,则需确保所有内容加载完毕后,再获取页面源代码:JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;String pageSource = (String) jsExecutor.executeScript("return document.body.innerHTML;");// 将从WebDriver获取的页面源代码转换为Jsoup的Document对象进行解析:Document doc = Jsoup.parse(pageSource);// 使用Jsoup提供的API根据CSS选择器或其他方式定位元素并提取数据:List<Element> imgElements = doc.select("img");List<Future<Void>> futures = new ArrayList<>();for (Element imgElement : imgElements) {// 获取srcset属性值String imageUrl = imgElement.attr("src");String realImageUrl = null;System.out.println("imageUrl:  " + imageUrl);if (imageUrl.startsWith(preString) && imageUrl.endsWith(endString)) {realImageUrl = imageUrl;System.out.println("realImageUrl " + realImageUrl);}if (realImageUrl != null) {futures.add(executor.submit(new ImageDownloaderPlus(realImageUrl, outPutPath)));}}// 等待所有任务完成并设置超时时间for (Future<Void> future : futures) {try {future.get(3, TimeUnit.MINUTES); // 设置每个任务的最大执行时间为3分钟} catch (TimeoutException e) {System.err.println("图片下载任务超时: " + future.toString());future.cancel(true); // 超时的任务取消}}// 确保所有任务执行完毕后再关闭线程池executor.shutdown();boolean finishedInTime = executor.awaitTermination(5, TimeUnit.MINUTES); // 总等待时间5分钟if (!finishedInTime) {System.err.println("在规定时间内,有些任务未完成。");}} finally {// 在程序结束前,记得关闭WebDriver以释放浏览器资源:driver.quit();}}
}

多线程处理下载,注意通过Thread.sleep(100);减轻服务器压力

java">import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.concurrent.Callable;public class ImageDownloaderPlus implements Callable<Void> {private final String imageUrl;private final String savePath;ImageDownloaderPlus(String imageUrl, String savePath) {this.imageUrl = imageUrl;this.savePath = savePath;}@Overridepublic Void call() throws Exception {if (!new File(savePath).exists()) {Files.createDirectories(Paths.get(savePath));}Path localFile = Paths.get(savePath, imageUrl.substring(imageUrl.lastIndexOf("/") + 1));if (Files.exists(localFile)) { // 检查文件是否已存在System.out.println("Image already exists: " + localFile);InputStream in = null; // 增加InputStream变量用于显式关闭try {URI uri = new URI(imageUrl);in = uri.toURL().openStream(); // 打开输入流} catch (IOException e) {System.err.println("打开输入流失败: " + imageUrl);return null;} finally {if (in != null) {in.close(); // 当文件已存在时,显式关闭输入流}}return null;}try (InputStream in = new URI(imageUrl).toURL().openStream()) {Files.copy(in, localFile, StandardCopyOption.REPLACE_EXISTING);System.out.println("Downloaded image: " + imageUrl);} catch (IOException e) {System.err.println("下载图片失败: " + imageUrl);}// 减轻服务器压力Thread.sleep(100);return null;}
}

参考链接:https://www.yuque.com/ican/machine/xgufmiahx1xi3llr?#


http://www.ppmy.cn/ops/156560.html

相关文章

什么是图神经网络?

一、概念 图神经网络&#xff08;Graph Neural Network, GNN&#xff09;是一类专门用于处理图结构数据的神经网络。图结构数据广泛存在于各种实际应用中&#xff0c;如社交网络、分子结构、知识图谱等。GNN通过在图的节点和边上进行信息传递和聚合&#xff0c;能够有效地捕捉图…

MySQL初学之旅(5)详解查询

目录 1.前言 2.正文 2.1聚合查询 2.1.1count() 2.1.2sum() 2.1.3avg() 2.1.4max() 2.1.5min() 2.1.6总结 2.2分组查询 2.2.1group by字句 2.2.2having字句 2.2.3group by与having的关系 2.3联合查询 2.3.1笛卡尔积 2.3.2内连接 2.3.3外连接 2.3.4自连接 2.3…

OpenGL学习笔记(十):初级光照:材质 Materials

文章目录 材质属性设置材质属性光的属性设置光照属性 在现实世界里&#xff0c;每个物体会对光产生不同的反应。比如&#xff0c;钢制物体看起来通常会比陶土花瓶更闪闪发光&#xff0c;一个木头箱子也不会与一个钢制箱子反射同样程度的光。有些物体反射光的时候不会有太多的散…

使用scikit-learn中的K均值包进行聚类分析

聚类是无监督学习中的一种重要技术&#xff0c;用于在没有标签信息的情况下对数据进行分析和组织。K均值算法是聚类中最常用的方法之一&#xff0c;其目标是将数据点划分为K个簇&#xff0c;使得每个簇内的数据点更加相似&#xff0c;而不同簇之间的数据点差异较大。 准备自定…

C# 添加、替换、提取、或删除Excel中的图片

在Excel中插入与数据相关的图片&#xff0c;能将关键数据或信息以更直观的方式呈现出来&#xff0c;使文档更加美观。此外&#xff0c;对于已有图片&#xff0c;你有事可能需要更新图片以确保信息的准确性&#xff0c;或者将Excel 中的图片单独保存&#xff0c;用于资料归档、备…

双系统共用一个蓝牙鼠标

前言 由于蓝牙鼠标每次只能配置一个系统&#xff0c;每次切换系统后都需要重新配对&#xff0c;很麻烦&#xff0c;双系统共用一个鼠标原理就是通过windows注册表中找到鼠标每次生成的mac地址以及配置&#xff0c;将其转移到linux上。 解决 1. 首先进入linux系统 进行蓝牙鼠…

PostgreSql 函数异常处理

BEGIN 逻辑块 EXCEPTION WHEN 错误码&#xff08;如&#xff1a;unique_violation&#xff09; or others THEN 异常逻辑块 END; 在PL/pgSQL函数中&#xff0c;如果没有异常捕获&#xff0c;函数会在发生错误时直接退出&#xff0c;与其相关的事物也会随之回滚。我们可以通过使…

最大矩阵的和

最大矩阵的和 真题目录: 点击去查看 E 卷 100分题型 题目描述 给定一个二维整数矩阵&#xff0c;要在这个矩阵中选出一个子矩阵&#xff0c;使得这个子矩阵内所有的数字和尽量大&#xff0c;我们把这个子矩阵称为和最大子矩阵&#xff0c;子矩阵的选取原则是原矩阵中一块相互…