JSoup 简介
网络爬虫可以被视为一种数字寻宝活动。你浏览一个网站,从中挖掘出所需的所有信息。这种技术被广泛应用于各种领域,例如查找最低价格、分析客户情绪,或是为研究收集数据。
Java 被视为一种非常适合网络爬虫的编程语言,因为它拥有种类繁多的库和框架,能够辅助完成这一过程。JSoup 是 Java 中最著名的网络爬虫库之一。JSoup 允许您浏览和搜索网站的 HTML 内容,并提取所需的所有数据。
通过将 Java 与 JSoup 结合使用,您可以创建出色的网页抓取应用程序,从而快速、轻松地从网站中提取数据。在本文中,我将带您了解使用 JSoup 进行网页抓取的基础知识。
创建一个 JSoup 项目
在本节中,我们将使用 Maven 创建一个新的 Java 项目,并配置它以便通过 exec-maven-plugin 从命令行运行。这样,您就可以轻松地将项目打包并在服务器上运行,从而实现数据提取过程的自动化和可扩展性。之后,我们将安装 JSoup 库。
创建一个 Maven 项目
Maven 是一款用于 Java 项目的构建自动化工具。它能够管理依赖项、构建过程和文档,从而简化复杂 Java 项目的管理。借助 Maven,您可以轻松管理和组织项目的构建流程、依赖项及文档。此外,它还能与各类工具和框架轻松集成。
安装 Maven 是一个简单的过程,只需几个步骤即可完成。
首先,从官方网站(https://maven.apache.org/download.cgi)下载最新版本的 Maven。
下载完成后,请将压缩包中的文件解压到您指定的目录中。
接下来,您需要设置环境变量。
在 Windows 系统上,请将 JAVA_HOME 变量设置为 JDK 的安装路径,并将 Maven 安装目录下的 bin 文件夹添加到 PATH 变量中。
在 Linux/macOS 系统上,您需要在 ~/.bashrc 或 ~/.bash_profile 文件中添加以下内容:
export JAVA_HOME=jdk的路径
export PATH=$PATH:maven的路径/bin
在终端中运行 `mvn --version` 来确认 Maven 是否已安装。
安装好 Maven 后,您现在可以创建一个新的 Java Maven 项目:
mvn archetype:generate -DgroupId=com.project.scraper
-DartifactId=jsoup-scraper-project
-DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
这将创建一个名为“jsoup-scraper-project”的新文件夹,其中包含该项目的所有内容。
该应用程序的入口点(主类)将位于“com.project.scraper”包中。
从命令行运行项目
为了从命令行运行一个 Maven Java 项目,我们将使用 exec-maven-plugin。
To install the plugin, you need to add it to the project's pom.xml file. This can be done by adding the following code snippet to the <build><plugins> section of the pom.xml file:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.project.scraper.App</mainClass>
</configuration>
</plugin>
</plugins>
</build>
请确保为项目的主类选择正确的路径。
在终端(位于项目目录下)中输入 `mvn package exec:java` 来运行该项目。
安装 JSoup 库
要安装 JSoup 库,请在项目的 pom.xml 文件中添加以下依赖项:
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.3</version>
</dependency>
请访问https://mvnrepository.com/artifact/org.jsoup/jsoup查看最新版本。
使用 JSoup 在 Java 中解析 HTML
在本节中,我们将探索https://www.scrapethissite.com/pages/forms/网站,并了解如何从中提取冰球队的相关信息。通过分析这个真实的网站,您将掌握使用 JSoup 进行网页抓取的概念和技巧,并了解如何将其应用到自己的项目中。
获取 HTML
要获取网站上的 HTML 内容,你需要向该网站发送一个 HTTP 请求。在 JSoup 中,使用 connect() 方法可以与指定的 URL 建立连接。该方法会返回一个 Connection 对象,可用于配置请求并从服务器获取响应。
让我们来看看如何使用 connect() 方法从指定 URL 获取 HTML 内容,然后将其写入本地 HTML 文件(hockey.html):
package com.project.scraper;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.*;
import java.io.IOException;
public class App
{
public static void main( String[] args )
{
String RAW_HTML;
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")
.get();
RAW_HTML = document.html();
FileWriter writer = new FileWriter("hockey.html");
writer.write(RAW_HTML);
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
现在我们可以打开该文件,并使用开发者工具查看 HTML 的结构:

我们所需的数据位于页面上的一个 HTML 表格中。现在我们已经访问了该页面,可以继续使用选择器从表格中提取内容。
编写选择器
JSoup 中的选择器与 JavaScript 中的选择器有相似之处。两者的语法相似,都允许您根据元素的标签名、类名、ID 以及 CSS 属性从 HTML 文档中选择元素。
以下是 JSoup 中常用的主要选择器:
- getElementsByTag(): 根据标签名选择元素。
- getElementsByClass():根据元素的类名选择元素。
- getElementById():根据元素的 ID 选择该元素。
- select():根据 CSS 选择器选择元素(类似于 querySelectorAll)
现在,让我们利用其中一些方法提取所有球队名称:
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")
.get();
Elements rows = document.getElementsByTag("tr");
for(Element row : rows) {
Elements teamName = row.getElementsByClass("name");
if(teamName.text().compareTo("") != 0)
System.out.println(teamName.text());
}
} catch (IOException e) {
e.printStackTrace();
}
// Prints the team names:
Boston Bruins
Buffalo Sabres
Calgary Flames
Chicago Blackhawks
Detroit Red Wings
Edmonton Oilers
Hartford Whalers
...
我们遍历了每一行,并对每一行都使用类选择器“name”输出球队名称。
最后一个示例突显了该方法的灵活性,以及能够对已提取的元素多次应用选择器的方法。这在处理复杂且庞大的 HTML 文档时尤为有用。
以下是另一个版本,它使用 Java 流和 select() 方法来打印所有球队名称:
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")
.get();
Elements teamNamesElements = document.select("table .team .name");
String[] teamNames = teamNamesElements.stream()
.map(element -> element.text())
.toArray(String[]::new);
for (String teamName : teamNames) {
System.out.println(teamName);
}
} catch (IOException e) {
e.printStackTrace();
}
// Also prints the team names:
Boston Bruins
Buffalo Sabres
Calgary Flames
...
现在,让我们打印出表中的所有表头和行:
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/")
.get();
Elements tableHeadersElements = document.select("table th");
Elements tableRowsElements = document.select("table .team");
String[] tableHeaders =
tableHeadersElements.stream()
.map(element -> element.text())
.toArray(String[]::new);
String[][] tableRows =
tableRowsElements.stream()
.map(
table_row -> table_row
.select("td")
.stream()
.map(row_element -> row_element.text())
.toArray(String[]::new)
)
.toArray(String[][]::new);
for (int i = 0; i < tableHeaders.length; i++) {
System.out.print(tableHeaders[i] + " ");
}
for (int i = 0; i < tableRows.length; i++) {
for (int j = 0; j < tableRows[i].length; j++) {
System.out.print(tableRows[i][j] + " ");
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
// Prints
Team Name Year Wins Losses OT Losses Win ...
Boston Bruins 1990 44 24 0.55 299 264 35
Buffalo Sabres 1990 31 30 0.388 292 278 14
Calgary Flames 1990 46 26 0.575 344 263 81
Chicago Blackhawks 1990 49 23 0.613 284 211 73
Detroit Red Wings 1990 34 38 0.425 273 298 -25
...
请注意,我们使用了流来存储 rowS。这里有一种更简单的方法,使用 for 循环:
String[][] tableRows = new String[tableRowsElements.size()][];
for (int i = 0; i < tableRowsElements.size(); i++) {
Element table_row = tableRowsElements.get(i);
Elements tableDataElements = table_row.select("td");
String[] rowData = new String[tableDataElements.size()];
for (int j = 0; j < tableDataElements.size(); j++) {
Element row_element = tableDataElements.get(j);
String text = row_element.text();
rowData[j] = text;
}
tableRows[i] = rowData;
}分页处理
从网站提取数据时,信息通常分散在多个页面上。为了抓取所有相关数据,必须向网站的每个页面发送请求,并从每个页面中提取信息。我们可以轻松地将这一功能集成到我们的项目中。

我们只需修改 URL 中的 page_num 查询参数,然后使用 connect() 方法发起另一个 HTTP 请求即可。
int pageLimit = 25;
String [] tableHeaders = new String[0];
Vector<String[][]> rowsGroups = new Vector<String [][]>();
for (int currentPage=1; currentPage<pageLimit; currentPage++) {
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/?page_num=" + currentPage)
.get();
if(currentPage == 1) {
Elements tableHeadersElements = document.select("table th");
tableHeaders = tableHeadersElements.stream()
.map(element -> element.text())
.toArray(String[]::new);
}
Elements tableRowsElements = document.select("table .team");
String[][] tableRows = new String[tableRowsElements.size()][];
for (int i = 0; i < tableRowsElements.size(); i++) {
Element table_row = tableRowsElements.get(i);
Elements tableDataElements = table_row.select("td");
String[] rowData = new String[tableDataElements.size()];
for (int j = 0; j < tableDataElements.size(); j++) {
Element row_element = tableDataElements.get(j);
String text = row_element.text();
rowData[j] = text;
}
tableRows[i] = rowData;
}
rowsGroups.add(tableRows);
} catch (IOException e) {
e.printStackTrace();
}
// do something with the headers and the the table rows groups
}
由于每页的表格标题都相同,请确保不要重复抓取它们。
完整代码
以下是从https://www.scrapethissite.com/pages/forms/网站提取所有表格的完整代码。我还添加了一个将数据保存为 .CSV 格式的函数:
package com.project.scraper;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.*;
import java.io.IOException;
import java.util.Vector;
public class App
{
public static void main( String[] args )
{
int pageLimit = 25;
String [] tableHeaders = new String[0];
Vector<String[][]> rowsGroups = new Vector<String [][]>();
for (int currentPage=1; currentPage<pageLimit; currentPage++) {
try {
Document document = Jsoup.connect("https://www.scrapethissite.com/pages/forms/?page_num=" + currentPage)
.get();
if(currentPage == 1) {
Elements tableHeadersElements = document.select("table th");
tableHeaders = tableHeadersElements.stream()
.map(element -> element.text())
.toArray(String[]::new);
}
Elements tableRowsElements = document.select("table .team");
String[][] tableRows = new String[tableRowsElements.size()][];
for (int i = 0; i < tableRowsElements.size(); i++) {
Element table_row = tableRowsElements.get(i);
Elements tableDataElements = table_row.select("td");
String[] rowData = new String[tableDataElements.size()];
for (int j = 0; j < tableDataElements.size(); j++) {
Element row_element = tableDataElements.get(j);
String text = row_element.text();
rowData[j] = text;
}
tableRows[i] = rowData;
}
rowsGroups.add(tableRows);
} catch (IOException e) {
e.printStackTrace();
}
}
writeFullTableToCSV(rowsGroups, tableHeaders, "full_table.csv");
}
public static void writeFullTableToCSV(Vector<String[][]> rowsGroups, String[] headers, String fileName) {
File file = new File(fileName);
try {
FileWriter writer = new FileWriter(file);
// write the headers first
for (int i = 0; i < headers.length; i++) {
writer.append(headers[i]);
if (i != headers.length - 1) {
writer.append(",");
}
}
writer.append("\n");
// write all the rows groups
for (String [][] rowsGroup : rowsGroups) {
for (String[] row : rowsGroup) {
for (int i = 0; i < row.length; i++) {
writer.append(row[i]);
if (i != row.length - 1) {
writer.append(",");
}
}
writer.append("\n");
}
}
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}总结
在本文中,我们介绍了如何安装 Maven 并创建一个新的 Java Maven 项目,以及如何通过命令行运行该项目。此外,我们还探讨了如何通过在项目的 pom.xml 文件中添加依赖项来安装 JSoup 库。 最后,我们通过一个示例演示了如何使用 JSoup 解析 HTML 并从网站中提取数据。按照本文所述的步骤操作,您应该已经为搭建 JSoup 项目并开始从网站中提取数据打下了坚实的基础。JSoup 为网络爬虫提供了丰富的选项和可能性,我鼓励您去探索并将其应用到自己的项目中。
正如您所见,数据通常会在多个网页之间共享。如果向同一个域名频繁发送请求,可能会导致您的 IP 地址被封禁。使用我们的产品WebScrapingAPI,您将再也不用担心此类问题。我们的 API 确保您可以根据需要发送任意数量的请求。最棒的是,您可以免费试用。




