jsoup是一个操纵HTML的Java库。它提供了很多便利的API,我们可以用HTML5 DOM方法和CSS选择器来获取URL,提取和操作数据。
先看一个简单的例子,新建一个Maven项目:
![74571fdf5bf5dcacb4b851c5d2aa36e0.png](https://i-blog.csdnimg.cn/blog_migrate/146ec0069841e2f479edab1e0d871855.jpeg)
在项目的pom.xml文件中添加如下依赖:
<!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.13.1</version>
</dependency>
确认项目下的External Libraries中包含如下jar包
![e85e0739184fee5e0d2c406a951cbeaa.png](https://i-blog.csdnimg.cn/blog_migrate/19f49b9f0aec3218fbd3a31a03662f8c.png)
我们的例子是提取到百度首页的标题,如下图所示:
![fea049d1ef38aa27ae4478a9c1d4f64c.png](https://i-blog.csdnimg.cn/blog_migrate/413347aca8b6b0b49d151862fe609205.png)
使用jsoup库可以很容易做到这事儿,
package com.andy;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
public class JsoupDemo {
public static void main(String[] args) throws IOException {
Document document = Jsoup.connect("http://www.baidu.com").get();
System.out.println(document.title());
}
}
结果如下:
![e85344a9acc426fca6e4e372c837fd47.png](https://i-blog.csdnimg.cn/blog_migrate/a63d665f2fd7daef82dad80dc45e487f.png)
通过Jsoup.connect("http://www.baidu.com").get()
我们获取到了百度首页的HTML文档并返回一个Document
对象(它就代表了HTML文档),使用Document
对象的title()
方法获取到了HTML文档的标题。
获取Document对象
有多个方法获取到Document
对象。
Jsoup.parse(String html)
该方法从字符串解析出一个Document
对象,
Document document = Jsoup.parse("<style>p {font-size:10px}</style>");
System.out.println(document);
![d6e0ecde24137193c5416431e5fd8b7b.png](https://i-blog.csdnimg.cn/blog_migrate/836f868ecabe17587f660633c42e7b45.png)
可以看到对于缺失的标签,jsoup库帮我们补全。
Jsoup.parseBodyFragment(String html)
该方法从字符串解析出一个Document
对象,与Jsoup.parse(String html)
方法不同的是,此方法会将html
插入到body
标签中,
Document document = Jsoup.parseBodyFragment("<style>p {font-size:10px}</style>");
System.out.println(document);
![c6bfa005aa692b0787b2c6e93d9c38ba.png](https://i-blog.csdnimg.cn/blog_migrate/71b613b5ac229468a542c2841b5363e1.png)
Jsoup.connect(String url).get()
该方法从目标URL解析出一个Document
对象,正如样例所示。
Jsoup.parse(File in, String charset, String baseUri)
该方法从文件中解析出一个Document
对象,我们将百度首页的html文档保存到本地,然后用此方法得到一个Document
对象:
File file = new File("./index.html");
Document document = Jsoup.parse(file, "UTF-8");
System.out.println(document);
![2df6bc269e771e819de20c1a3b621d6d.png](https://i-blog.csdnimg.cn/blog_migrate/bd0eae8e83266d823c071deb8af4447f.png)
提取数据
获取到了Document
对象有什么用呢?我们可以通过它的很方便的方法提取数据。
DOM方法
在Document
对象上我们可以使用一些类DOM方法,比如:
getElementById(String id)
getElementsByTag(String tag)
getElementsByClass(String className)
getElementsByAttribute(String key)
还是用百度首页来举例:
File file = new File("./index.html");
Document document = Jsoup.parse(file, "UTF-8");
Elements elements = document.getElementsByTag("a");
for (Element element : elements) {
System.out.println(element);
}
![acab88d0814aa343336edb398f32f707.png](https://i-blog.csdnimg.cn/blog_migrate/296ea8ebb03858578269101268553da8.jpeg)
我们getElementsByTag()
方法获取到了整个页面的a
元素,然后打印输出每个元素。
可以用下列方法提取元素的数据:
attr(String key)
获取元素key
属性的值attributes()
获取元素所有属性id()
获取元素id属性的值className
和classNames
获取元素class属性的值text()
获取元素内容
例如:
File file = new File("./index.html");
Document document = Jsoup.parse(file, "UTF-8");
Elements elements = document.getElementsByTag("a");
for (Element element : elements) {
System.out.println(element.text() + " : " + element.attr("href"));
}
![dbdcd1257b0ef1c175337c92f7dc66bb.png](https://i-blog.csdnimg.cn/blog_migrate/ca784dc4b5cd536ceca6401f730c10c4.png)
选择器方法
除了类DOM方法外,还可以使用CSS选择器语法对元素进行筛选,主要是用Element.select(String selector)
方法。
File file = new File("./index.html");
Document document = Jsoup.parse(file, "UTF-8");
Elements elements = document.select("a[href]");
for (Element element : elements) {
System.out.println(element.text() + " : " + element.attr("href"));
}
![81cc0b41d183edf365dae70fdfbdf4ed.png](https://i-blog.csdnimg.cn/blog_migrate/66b362dc05fae53500cbd70f0b6afdd0.png)
如上例所示,通过select()
方法找到了所有带有href
属性的a
标签。值得注意的是,select()
方法可以在Document
,Element
或Elements
对象上使用。
更多选择器语法请看Use selector-syntax to find elements。
获取绝对路径
有时我们需要将资源的相对路径转换为绝对路径,我们可以用如下两种方法:
Document doc = Jsoup.connect("http://jsoup.org").get();
Element link = doc.select("a").first();
String relHref = link.attr("href"); // == "/"
String absHref = link.attr("abs:href"); // "http://jsoup.org/"
// 等价于
String absLink = link.absUrl("href") // "http://jsoup.org/"
爬取豆瓣电影TOP250
了解了上述内容后,我们现在可以尝试爬取豆瓣电影TOP250啦!
![1196ca4fdad030ae6246edf3d305a85d.png](https://i-blog.csdnimg.cn/blog_migrate/598c6800ffab523e4afed541319126d5.jpeg)
观察其URLhttps://movie.douban.com/top250?start=0&filter=
,可以发现页数和URL是一一对应的,start
的值会等于当前页i
减1乘以25,即start=(i-1)*25
,因此通过改变start
的值就可以请求到不同的HTML页面。我们对页面上的每部电影信息做进一步分析:
![0346d8a6899794f27cdbea4f28b3a276.png](https://i-blog.csdnimg.cn/blog_migrate/daca00245500aa634f2ede397848784d.jpeg)
可以看到每部电影都包含在一个class名为item
的div
标签中,作为示例我们这里只提取每部电影的标题和URL。完整代码如下所示:
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 baseUrl = "https://movie.douban.com/top250?start=%d&filter=";
for (int i = 0; i < 10; i++) {
String url = String.format(baseUrl, i * 25);
try {
parsePage(url);
} catch (Exception e) {
System.out.println("Error !!");
}
}
}
public static void parsePage(String src) throws IOException {
Document document = Jsoup.connect(src).get();
Elements elements = document.select("div.item");
for (Element element : elements) {
String title = element.select("span.title").first().text();
String url = element.select("div.hd > a").first().attr("href");
System.out.println("title : " + title + " url : " + url);
}
}
}
抓取结果如下:
![0003f19359a178b81e7a4784daf66237.png](https://i-blog.csdnimg.cn/blog_migrate/2d1cbf1ce904dff3eddc17b41949e798.png)