随着Web的发展,人们越来越需要从互联网上获取数据并进行分析。为此,Java提供了一个功能强大的库——Jsoup,它可以帮助我们轻松地从Web页面中提取信息。在这篇文章中,我们将探讨如何使用Java和Jsoup来实现一个简单的网络爬虫。
一、Jsoup简介
Jsoup是一款基于Java的HTML解析器,可以将HTML文档转换为可操作的Java对象。Jsoup提供了一些简单而方便的API来解析HTML,例如选择器API、DOM操作API、属性处理API等。这使得我们可以轻松地从Web页面中提取所需的数据。由于Jsoup的高度灵活性和易用性,它被广泛应用于Web抓取和数据挖掘领域。
二、开始编写爬虫
在编写我们的网络爬虫之前,我们需要做一些准备工作。首先,我们需要安装Java和Jsoup,并设置Java环境变量。然后,我们需要确定我们要从哪个网站获取数据,并了解该网站的结构。在这个例子中,我们将使用豆瓣电影作为我们的目标网站,并从该网站获取电影的基本信息。
在Java中使用Jsoup非常简单。我们只需要在Java项目中导入Jsoup库,并使用以下代码创建一个Document对象
Document doc = Jsoup.connect("https://movie.douban.com/top250").get();
这个代码将会从豆瓣电影的Top250页面中获取数据,并创建一个Document对象来存储这些数据。我们可以使用Document对象的方法来解析HTML文档,并提取我们需要的信息。例如,我们可以使用以下代码来获取页面的标题:
String title = doc.title();
在这个例子中,我们将获取页面的标题作为一个字符串。我们可以在控制台上输出这个字符串,以验证我们的代码是否有效:
System.out.println("页面标题是:" + title);
如果一切正常,我们应该能够在控制台上看到页面的标题。
三、提取数据
现在我们已经成功地从豆瓣电影的Top250页面中获取了HTML文档,并将其存储在了一个Document对象中。接下来,我们需要从这个HTML文档中提取我们所需的信息。在这个例子中,我们将提取电影的基本信息,例如电影名称、导演、演员、评分和评论数等。
我们可以使用Jsoup提供的选择器API来轻松地选择HTML文档中的元素。例如,如果我们想选择页面上所有的电影条目,我们可以使用以下代码:
Elements movieElements = doc.select("div.item");
这个代码将会从HTML文档中选择所有class为item的div元素,并将它们存储在一个Elements对象中。接下来,我们可以遍历这些元素,并提取我们所需的信息。例如,我们可以使用以下代码获取每个电影的名称和评分:
for (Element movie : movieElements) {
String title = movie.select("div.hd a span").text();
String rating = movie.select("span.rating_num").text();
System.out.println("电影名称:" + title + ",评分:" + rating);
}
在这个例子中,我们使用选择器API来选择每个电影条目中的电影名称和评分元素。然后,我们使用text()方法来获取这些元素的文本内容,并将它们打印到控制台上。
我们还可以使用选择器API来选择其他元素,例如导演和演员。例如,我们可以使用以下代码获取每个电影的导演和演员:
for (Element movie : movieElements) {
String directors = movie.select("div.bd p").first().ownText();
String actors = movie.select("div.bd p").get(1).ownText();
System.out.println("导演:" + directors + ",演员:" + actors);
}
在这个例子中,我们使用选择器API来选择每个电影条目中的导演和演员元素。然后,我们使用ownText()方法来获取这些元素的文本内容,并将它们打印到控制台上。
四、分页爬取数据
到目前为止,我们已经成功地从豆瓣电影的Top250页面中提取了电影的基本信息。然而,Top250页面只包含前250部电影的信息,如果我们想获取更多的电影信息,我们需要使用分页来获取。
在豆瓣电影中,每个页面最多显示25部电影。因此,如果我们想获取前1000部电影的信息,我们需要遍历40个页面。为了实现这个功能,我们需要使用循环来遍历每个页面,并提取每个页面中的电影信息。例如,我们可以使用以下代码获取前1000部电影的信息:
for (int i = 0; i < 40; i++) {
String url = "https://movie.douban.com/top250?start=" + i * 25;
Document doc = Jsoup.connect(url).get();
Elements movieElements = doc.select("div.item");
for (Element movie : movieElements) {
String title = movie.select("div.hd a span").text();
String rating = movie.select("span.rating_num").text();
String directors = movie.select("div.bd p").first().ownText();
String actors = movie.select("div.bd p").get(1).ownText();
System.out.println("电影名称:" + title + ",评分:" + rating + ",导演:" + directors + ",演员:" + actors);
}
}
在这个例子中,我们使用循环来遍历前40个页面。
五、注意问题
问题1:使用Jsoup抓取数据时,如果网站是https开头,可能存在证书SSL证书安全的问题,无法直接爬虫。请求会报错:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException:
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
解决方案有很多,这里推荐一种封装一个工具类,在爬虫之前调用一下这个方法即可。
import javax.net.ssl.*;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class JsoupUtil {
public static void trustEveryone() {
try {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, new X509TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}}, new SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
} catch (Exception e) {
e.printStackTrace();
}
}
}
以下是一个完整的示例:
import com.ruoyi.aqosc.util.JsoupUtil;
import org.jsoup.Connection;
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 PaChongDemo {
public static void main(String[] args) throws IOException {
JsoupUtil.trustEveryone();
//要爬取的网站
String url = "https://jxj.anqing.gov.cn/xxfb/tzgg/index.html";
//获得一个和网站的链接,注意是Jsoup的connect
Connection connect = Jsoup.connect(url);
//获得该网站的Document对象
Document document = connect.get();
//我们可以通过对Document对象的select方法获得具体的文本内容
Elements rootselect = document.select(".navjz ul li");
for(Element ele : rootselect){
Elements dateElements = ele.select(".right.date");
//发布时间
String date = dateElements.text();
System.out.println("发布时间:"+date);
//然后获得a标签里面具体的内容
Elements titleElements = ele.select("a");
//文章名称
String title = titleElements.attr("title");
System.out.println("文章名称:"+title);
//文章链接
String sourceUrl = titleElements.attr("href");
System.out.println("文章链接:"+sourceUrl);
}
}
}
运行之后控制台打印结果:
有时候需要进入详情页面获取更多详细信息,比如作者,正文,附件之类的,跟上面类似,前面已经拿到了文章链接,拿文章链接跟上面一样再去执行一次 Connection connect = Jsoup.connect(文章链接);获取页面元素即可。
问题2:如果正文内的图片、附件之类的使用的是相对路径,因为抓取之后到新的服务器,前缀变了,新的服务器对应的路径并没有相应的文件,在前端显示的时候就会提示找不到,如何处理呢?处理方法也比较简单,就是进行地址转化,使用绝对地址。
Elements links = element.select("a");
for(Element link: links){
link.attr("href",link.attr("abs:href"));
}
Elements imgs = element.select("img");
for(Element img: imgs){
img.attr("src",img.attr("abs:src"));
}
查找"a"和"img",将其中的href元素值修改为绝对路径。link.attr("abs:href")将会得到对应链接的绝对路径,其中属性为abs:href。同理,img.attr("abs:src")可以得到图片绝对路径abs:src。
以下是一个详细的demo:
import com.ruoyi.aqosc.util.JsoupUtil;
import org.jsoup.Connection;
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 PaChongDemo {
public static void main(String[] args) throws IOException {
JsoupUtil.trustEveryone();
//要爬取的网站
String url = "https://jxj.anqing.gov.cn/xxfb/tzgg/2002525881.html";
//获得一个和网站的链接,注意是Jsoup的connect
Connection connect = Jsoup.connect(url);
Document document = connect.get();
//获取内容
Element element = document.selectFirst(".wzcon.j-fontContent");
System.out.println("转化前正文:"+element.html());
//正文,注意正文中的 img 和 a 标签
Elements links = element.select("a");
for(Element link: links){
link.attr("href",link.attr("abs:href"));
}
Elements imgs = element.select("img");
for(Element img: imgs){
img.attr("src",img.attr("abs:src"));
}
System.out.println("转化后正文:"+element.html());
}
}
本文暂时没有评论,来添加一个吧(●'◡'●)