使用jsoup抓取和解析网页数据

时间:2021-10-16 00:55:08

如果您觉得本博客的内容对您有所帮助或启发,请关注我的博客,以便第一时间获取最新技术文章和教程。同时,也欢迎您在评论区留言,分享想法和建议。谢谢支持!

一、jsoup是什么,它的作用和优势

Jsoup是一款基于Java的HTML解析器,它可以方便地从网页中抓取和解析数据。它的主要作用是帮助开发者处理HTML文档,提取所需的数据或信息。

Jsoup的优势主要有以下几点:

  1. 简单易用:Jsoup提供了类似于jQuery的API,使得处理HTML文档变得非常简单和易于理解。
  2. 兼容性好:Jsoup能够解析HTML、XML等各种类型的文档,并且支持标准的CSS选择器,使得选择元素变得更加简单。
  3. 支持修改:Jsoup不仅可以解析HTML文档,还可以对其进行修改和操作。例如,可以用Jsoup来修改网页中的元素、属性和文本等。
  4. 性能高效:Jsoup的代码简洁,使用了高效的算法和数据结构,因此具有良好的性能和较低的内存消耗。
  5. 开源免费:Jsoup是一款完全开源的软件,可以免费使用和修改,而且社区支持较好,遇到问题可以得到帮助。

Jsoup是一款功能强大、易于使用的HTML解析器,适用于各种场景,例如数据爬取、数据挖掘、数据分析等。

二、如何使用jsoup抓取HTML页面

  1. 引用jsoup依赖:首先需要在项目中引用jsoup的依赖,可以在项目的pom.xml文件中添加以下代码:

<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.3</version>
</dependency>

或者下载jsoup的jar包,并将其添加到项目的classpath中。

  1. 创建连接:使用Jsoup.connect(url)方法创建一个连接对象。
  2. 获取HTML文档:使用连接对象的get()或post()方法获取HTML文档的字符串形式。
  3. 解析HTML文档:使用Jsoup.parse(html)方法将HTML文档字符串解析为一个Document对象,然后就可以使用Jsoup提供的API进行元素的选择、遍历、修改等操作了。

下面是一个简单的Java代码示例,演示如何使用jsoup抓取一个网页并打印其中的标题和链接:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;

public class JsoupDemo {
public static void main(String[] args) {
String url = "https://www.baidu.com/";
try {
Document doc = Jsoup.connect(url).get();
String title = doc.title();
System.out.println("网页标题为:" + title);
Elements links = doc.select("a[href]");
for (Element link : links) {
String linkHref = link.attr("href");
String linkText = link.text();
System.out.println("链接地址:" + linkHref);
System.out.println("链接文字:" + linkText);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

三、jsoup解析HTML的常用API介绍

jsoup是一个强大的Java库,可以用于解析HTML文档。它提供了许多常用的API,用于选择、遍历和修改HTML文档中的元素和属性。下面介绍几个常用的API:

  1. 选择器(Selector)API:用于根据CSS选择器语法选择HTML元素。
  2. 属性(Attribute)API:用于获取、设置和移除HTML元素的属性。
  3. 遍历(Traversal)API:用于遍历HTML文档中的元素。
  4. 操作(Manipulation)API:用于修改HTML文档中的元素和属性。

接下来,我们将逐一介绍这些API,并给出相应的代码示例。

选择器(Selector)API

选择器API允许您使用CSS选择器语法选择HTML元素。它提供了一些方法,如​​Document.select()​​​和​​Elements.select()​​,可用于选择HTML元素。

// 根据CSS选择器选择所有p元素
Elements elements = doc.select("p");

// 根据CSS选择器选择带有class为"example"的所有元素
Elements elements = doc.select(".example");

// 根据CSS选择器选择id为"example"的元素
Element element = doc.select("#example").first();

属性(Attribute)API

属性API用于获取、设置和移除HTML元素的属性。可以使用​​Element.attr()​​​方法获取或设置单个属性的值,或使用​​Element.attributes()​​方法获取所有属性。

// 获取元素的属性值
String href = element.attr("href");

// 设置元素的属性值
element.attr("href", "http://example.com");

// 移除元素的属性
element.removeAttr("href");

// 获取元素的所有属性
Attributes attributes = element.attributes();

遍历(Traversal)API

遍历API用于遍历HTML文档中的元素。可以使用​​Element.parent()​​​、​​Element.children()​​​、​​Element.nextElementSibling()​​等方法遍历HTML文档中的元素。

// 获取元素的父元素
Element parentElement = element.parent();

// 获取元素的子元素
Elements childrenElements = element.children();

// 获取元素的下一个兄弟元素
Element nextSiblingElement = element.nextElementSibling();

操作(Manipulation)API

操作API用于修改HTML文档中的元素和属性。可以使用​​Element.html()​​​、​​Element.text()​​​、​​Element.append()​​等方法操作HTML文档中的元素和属性。

// 获取元素的HTML内容
String html = element.html();

// 获取元素的文本内容
String text = element.text();

// 向元素中追加HTML内容
element.append("<p>这是一个新段落</p>");

// 向元素中追加文本内容
element.appendText("这是一段新文本");

四、jsoup常用的选择器及使用示例

元素选择器

元素选择器是指选择某一种或某些元素,常用的元素选择器有​​tagname​​​、​​tagname.class​​​、​​tagname#id​​等。例如:

// 选择所有的a元素
Elements links = doc.select("a");

// 选择所有class属性为news的div元素
Elements divs = doc.select("div.news");

// 选择id属性为header的div元素
Element header = doc.select("div#header").first();

属性选择器

属性选择器是指选择具有某一属性的元素,常用的属性选择器有​​[attr]​​​、​​[attr=value]​​​、​​[attr~=value]​​等。例如:

// 选择所有href属性以"http"开头的a元素
Elements links = doc.select("a[href^=http]");

// 选择所有class属性包含news的div元素
Elements divs = doc.select("div[class~=news]");

组合选择器

组合选择器是指将多个选择器组合在一起,常用的组合选择器有空格、大于号、加号等。例如:

// 选择所有div元素内的p元素
Elements paragraphs = doc.select("div p");

// 选择所有直接子元素是div的p元素
Elements directParagraphs = doc.select("div > p");

// 选择紧接在div元素后面的p元素
Elements adjacentParagraphs = doc.select("div + p");

伪类选择器

伪类选择器是指选择不符合常规CSS选择器语法的元素,常用的伪类选择器有​​:contains(text)​​​、​​:empty​​​、​​:not(selector)​​等。例如:

// 选择所有包含"example"文本的a元素
Elements links = doc.select("a:contains(example)");

// 选择所有不包含子元素的div元素
Elements divs = doc.select("div:empty");

// 选择所有不是div元素的元素
Elements notDivs = doc.select(":not(div)");

五、jsoup处理HTML页面中的特殊字符和编码问题

在处理HTML页面时,特殊字符和编码问题是常见的难点。特殊字符指的是一些在HTML文档中具有特殊意义的字符,如<、>、&等,这些字符需要进行转义才能正确地显示。而编码问题则是由于不同的编码格式导致的,如果不进行正确的编码处理,就会出现乱码等问题。下面介绍如何使用jsoup处理HTML页面中的特殊字符和编码问题。

转义特殊字符

在jsoup中,可以使用​​text()​​方法来获取元素的文本内容,这个方法会自动转义HTML页面中的特殊字符。例如:

Element element = doc.select("p").first();
String text = element.text();

在这个例子中,如果​​<p>​​​标签中包含特殊字符,如​​<​​​、​​>​​​、​​&​​​等,​​text()​​​方法会将它们自动转义成​​&lt;​​​、​​&gt;​​​、​​&amp;​​等。

另外,如果我们需要将特殊字符手动转义,可以使用​​Entities​​类中的静态方法。例如:

String escaped = Entities.escape("<p>Hello, world!</p>");
System.out.println(escaped); // 输出:<p>Hello, world!</p>

在这个例子中,​​Entities.escape()​​​方法将​​<p>​​​标签转义成了​​&lt;p&gt;​​。

处理编码问题

在使用jsoup解析HTML页面时,如果页面的编码格式不是UTF-8,需要指定正确的编码格式,否则会出现乱码等问题。可以通过​​Connection​​​对象的​​charset()​​​方法或​​Document​​​对象的​​outputSettings()​​方法来设置编码格式。例如:

Document doc = Jsoup.connect(url)
.timeout(5000)
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.get();
doc.outputSettings().charset("GBK");

在这个例子中,​​outputSettings().charset()​​​方法将编码格式设置为GBK。如果HTML页面的编码格式是ISO-8859-1或其他编码格式,可以使用​​Charset.forName()​​方法来指定编码格式。例如:

Document doc = Jsoup.connect(url)
.timeout(5000)
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.get();
doc.outputSettings().charset(Charset.forName("ISO-8859-1"));

需要注意的是,如果页面中使用了<meta>标签来指定编码格式,jsoup会自动识别并使用这个编码格式。如果页面中没有指定编码格式,jsoup会默认使用UTF-8编码。

六、jsoup爬虫实战:以抓取豆瓣电影信息为例

1、分析目标网站

首先,我们需要对目标网站进行分析,确定要抓取的内容和URL。

在本例中,我们要抓取豆瓣电影Top250页面中每部电影的名称、评分、导演、主演、年份和海报图片URL。

URL:​​https://movie.douban.com/top250​

2、发送请求并解析HTML页面

使用jsoup发送请求并获取HTML页面。这里我们使用​​Jsoup.connect()​​​方法发送GET请求,并使用​​Document​​类解析HTML页面。

String url = "https://movie.douban.com/top250";
Document doc = Jsoup.connect(url)
.timeout(5000)
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.get();

在这个例子中,我们使用了​​timeout()​​​方法设置请求超时时间,​​userAgent()​​方法设置User-Agent信息,以便让服务器识别为正常浏览器访问。

3、解析HTML页面

通过分析目标网站的HTML页面,我们可以使用jsoup提供的选择器和API获取需要的信息。

Elements movieList = doc.select("ol.grid_view li");
for (Element movie : movieList) {
String title = movie.select("div.hd a span.title").text();
String rate = movie.select("div.star span.rating_num").text();
String year = movie.select("div.bd p span.year").text();
String directors = movie.select("div.bd p:first-child").text();
String actors = movie.select("div.bd p:nth-child(2)").text();
String imgUrl = movie.select("div.pic img").attr("src");

System.out.println(title + " / " + rate + " / " + year + " / " + directors + " / " + actors + " / " + imgUrl);
}

在这个例子中,我们使用​​select()​​​方法获取每部电影的HTML元素,并使用选择器选择需要的信息。使用​​text()​​​方法获取元素的文本内容,使用​​attr()​​方法获取元素的属性值。

4、存储数据

抓取到需要的数据后,我们可以选择将数据存储到数据库、文件或其他存储介质中。在本例中,我们将数据简单地输出到控制台中。

5、完整代码

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.io.IOException;

public class DoubanSpider {
public static void main(String[] args) throws IOException {
String url = "https://movie.douban.com/top250";
Document doc = Jsoup.connect(url)
.timeout(5000)
.userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)")
.get();
Elements movieList = doc.select("ol.grid_view li");
for (Element movie : movieList) {
String title = movie.select("div.hd a span.title").text();
String rate = movie.select("div.star span.rating_num").text();
String year = movie.select("div.bd p span.year").text();
String directors = movie.select("div.bd p:first-child").text();
String actors = movie.select("div.bd p:nth-child(2)").text();
String imgUrl = movie.select("div.pic img").attr("src");
System.out.println(title + " / " + rate + " / " + year + " / " + directors + " / " + actors + " / " + imgUrl);
}
}
}

我们以抓取豆瓣电影Top250页面为例,介绍了使用jsoup实现爬虫的基本流程,包括发送请求、解析HTML页面、处理数据等步骤。使用jsoup,我们可以快速、简单地实现爬虫,并获取需要的数据。但是需要注意的是,爬虫行为需要遵守相关法律法规和网站的规定,不得用于商业用途和非法用途。

七、jsoup实现网站内容自动抓取与更新

假设我们需要定期从一个网站上抓取最新的文章并更新到我们的网站上。我们可以使用jsoup实现以下步骤:

  1. 发送请求

使用jsoup发送请求获取最新文章的页面。例如:

Document doc = Jsoup.connect("https://www.example.com/latest-articles").get();
  1. 解析HTML页面

解析HTML页面,获取文章列表。例如:

Elements articles = doc.select("div.article-list ul li");
  1. 处理数据

遍历文章列表,获取文章标题、链接、发布时间等信息,并进行处理。例如:

for (Element article : articles) {
String title = article.select("h3.title").text();
String url = article.select("a.link").attr("href");
String date = article.select("span.date").text();

// 进行数据处理,如去除HTML标签、日期格式化等
// ...
}
  1. 存储数据

将处理后的数据存储到我们的数据库或文件系统中。例如:

// 将处理后的数据存储到数据库中
db.save(title, url, date);
  1. 定时任务

使用定时任务,定期执行上述步骤,实现网站内容的自动抓取和更新。例如:

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
scheduler.scheduleAtFixedRate(() -> {
// 发送请求、解析HTML、处理数据、存储数据
// ...
}, 0, 1, TimeUnit.HOURS);

通过以上步骤,我们就可以实现网站内容的自动抓取和更新。需要注意的是,爬虫行为需要遵守相关法律法规和网站的规定,不得用于商业用途和非法用途。同时,为了避免对目标网站造成过大的负担,我们需要控制爬虫的频率和抓取量,避免对目标网站的正常运营造成影响。

八、jsoup自定义扩展

1、创建自定义元素

在这个示例中,我们将创建一个自定义元素​​<custom>​​​,它将有一个名为​​attribute1​​的属性,并且可以包含文本内容。

import org.jsoup.nodes.Element;
import org.jsoup.parser.Tag;

public class CustomElement extends Element {
private static final String TAG_NAME = "custom";
private static final String ATTRIBUTE_NAME = "attribute1";

public CustomElement(String baseUri, String text) {
super(Tag.valueOf(TAG_NAME), baseUri);
this.text(text);
}

public String getAttribute1() {
return this.attr(ATTRIBUTE_NAME);
}

public void setAttribute1(String value) {
this.attr(ATTRIBUTE_NAME, value);
}
}

在上面的示例中,我们重载了​​org.jsoup.nodes.Element​​​的构造函数,并传递了标签名称和基础URI。此外,我们还调用了​​text()​​方法,将传递的文本设置为元素的文本内容。

我们还添加了​​getAttribute1()​​​和​​setAttribute1()​​​方法,以便读取和设置​​attribute1​​属性的值。

现在,我们可以使用以下代码创建一个自定义元素:

CustomElement custom = new CustomElement("http://example.com", "This is custom text");
custom.setAttribute1("custom attribute value");

我们可以将自定义元素添加到文档中,如下所示:

Document doc = Jsoup.parse("<html><body><div></div></body></html>");
Element div = doc.select("div").first();
div.appendChild(custom);

在上面的示例中,我们首先使用jsoup解析了一个简单的HTML文档,然后选择了文档中的一个​​<div>​​元素,并将自定义元素添加为它的子元素。

现在,我们可以将文档输出为字符串,并看到我们的自定义元素已经被正确地序列化了:

System.out.println(doc.outerHtml());
// 输出结果: <html>
<head></head>
<body>
<div>
<custom attribute1="custom attribute value">
This is custom text
</custom>
</div>
</body>
</html>

2、创建自定义节点访问器

我们将创建一个自定义节点访问器,它将在遍历HTML文档时打印每个元素的标签名称和深度。

import org.jsoup.nodes.*;
import org.jsoup.select.NodeVisitor;

public class CustomNodeVisitor implements NodeVisitor {

@Override
public void head(Node node, int depth) {
if (node instanceof Element) {
Element element = (Element) node;
System.out.println("Start element: " + element.tagName() + ", depth: " + depth);
}
}

@Override
public void tail(Node node, int depth) {
if (node instanceof Element) {
Element element = (Element) node;
System.out.println("End element: " + element.tagName() + ", depth: " + depth);
}
}
}

在上面的示例中,我们实现了​​org.jsoup.nodes.NodeVisitor​​​接口,并重载了​​head()​​​和​​tail()​​​方法。在​​head()​​​方法中,我们打印了开始元素的标签名称和深度,而在​​tail()​​方法中,我们打印了结束元素的标签名称和深度。

现在,我们可以将自定义节点访问器应用于HTML文档:

Document doc = Jsoup.parse("<html><body><div><p>Paragraph 1</p><p>Paragraph 2</p></div></body></html>");
doc.traverse(new CustomNodeVisitor());

在上面的示例中,我们首先使用jsoup解析了一个简单的HTML文档,然后将自定义节点访问器传递给​​traverse()​​方法,以便遍历文档中的每个节点。

当我们运行这段代码时,会输出每个元素的标签名称和深度,如下所示:

使用jsoup抓取和解析网页数据

在上面的输出中,我们可以看到每个元素的标签名称和深度,以及元素的开始和结束位置。这可以帮助我们更好地理解HTML文档的结构。

九、jsoup的性能优化

jsoup已经做了很多的性能优化和错误处理技巧,但在实际应用中,还是需要开发人员按照一定的规范来使用jsoup,以达到最优的性能和最好的错误处理效果。以下是一些jsoup的性能优化和错误处理技巧:

  1. 使用连接池:连接池是一种重复利用连接的技术,可以减少连接的创建和销毁,提高性能。在使用jsoup时,建议使用连接池来管理连接。
  2. 避免过多的选择器:选择器是jsoup的重要特性之一,但过多的选择器会导致性能下降。建议在选择器中尽量使用标签名和类名,而不是属性和伪类。
  3. 缓存解析结果:在需要多次解析同一个页面时,可以缓存解析结果,避免重复解析,提高性能。
  4. 错误处理:jsoup提供了许多错误处理机制,例如默认的错误处理器、HTML5的错误处理模式、容错模式等。在使用jsoup时,需要根据实际情况选择合适的错误处理机制,以避免程序出现不可预料的错误。
  5. 避免内存泄漏:在使用jsoup时,需要注意及时释放资源,避免内存泄漏。例如,在解析大量HTML页面时,可以使用垃圾回收机制,及时释放无用的对象。
  6. 使用异步IO:在处理大量HTML页面时,可以使用异步IO技术,将IO操作与业务逻辑分离,提高性能。


如果您觉得本博客的内容对您有所帮助或启发,请关注我的博客,以便第一时间获取最新技术文章和教程。同时,也欢迎您在评论区留言,分享想法和建议。谢谢支持!