Java爬虫-使用jsoup爬取数据入门案例(爬取豆瓣电影Top250数据)

有需要用到jsoup来获取数据,因为之前没有用过,所以就想写一个入门案例来巩固一下,这个案例的功能是爬取豆瓣电影Top250的电影数据(电影名称,简介,评分,评价等),并且将数据存到Excel表格中。
这是豆瓣电影Top250的网址,https://movie.douban.com/top250?start=0

一、前置工作

用到的技术有jsoup和apche poi
jsoup用来获取数据,apche poi用来将获取到的数据存入Excel表格

1.1 技术介绍

Jsoup

Jsoup 是一个用于解析、提取和操作 HTML 文档的 Java 库。它提供了简单且易于使用的 API,使您能够轻松地从 HTML 页面中提取数据。
Jsoup 可以加载 HTML 文档并将其解析为文档对象模型(DOM),能够轻松地遍历和操作文档中的元素和内容。

Apache POI

Apache POI 是一个用于操作 Microsoft Office 格式文件(如 Word 文档、Excel 表格和 PowerPoint 演示文稿)的 Java 库。它提供了一组丰富的 API,使得在 Java 程序中读取、写入和修改 Office 文件变得简单和方便。

1.2 依赖导入

<!--        jsoup爬取数据-->
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.14.1</version>
        </dependency>

<!--        apache.poi将数据导入到excel中-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.0.0</version>
        </dependency>
Jsoup入门demo

说这么多作用不大,我们直接来实操一下就能明白了。
下面是一个jsoup入门的demo,只需要把jsoup相关的依赖导入即可开始使用。
这个Demo的功能是,获取百度网站的标题:百度一下,你就知道

package com.example;

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import java.io.IOException;

public class Test5 {
    public static void main(String[] args) throws IOException {
        Connection connection = Jsoup.connect("https://www.baidu.com/")
                //这里可以加各种请求头,来模拟真实的请求(根据自己的真实请求来加)
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
                .method(Connection.Method.GET);
        Document doc = connection.get();
        //获取标题元素
        Element title = doc.select("head").select("title").first();
        System.out.println(title);
        //获取元素内容
        String titleName = title.text();
        System.out.println(titleName);
    }
}

Jsoup常用API了解

Jsoup 提供了许多常用的 API,使您能够解析、操作和提取 HTML 文档中的数据。以下是一些常用的 Jsoup API:

  1. 连接到 URL:使用 connect(String url) 方法可以建立与指定 URL 的连接。例如:
Connection connection = Jsoup.connect("http://www.example.com");
  1. 获取文档对象:使用 get() 方法可以获取连接的 URL 对应的文档对象。例如:
Document doc = Jsoup.connect("http://www.example.com").get();
  1. 解析 HTML 字符串:使用 parse(String html) 方法可以将 HTML 字符串解析为文档对象。例如:
String html = "<html><body><p>Hello, Jsoup!</p></body></html>";
Document doc = Jsoup.parse(html);
  1. 选择器:使用选择器可以选择和查找文档中的元素。以下是一些常用的选择器方法:
  • getElementById(String id): 通过元素的 ID 获取元素。
  • getElementsByTag(String tagName): 通过标签名获取元素。
  • getElementsByClass(String className): 通过类名获取元素。
  • select(String cssQuery): 使用 CSS 选择器选择元素。

例如:

Element element = doc.getElementById("myElement");
Elements elements = doc.getElementsByTag("a");
Elements elements = doc.getElementsByClass("myClass");
Elements elements = doc.select("div.container > p");
  1. 元素操作:通过选取元素后,可以进行各种操作,如获取元素的文本内容、属性值、链接、图像地址等。一些常用的方法包括:
  • text(): 获取元素的文本内容。
  • attr(String attributeKey): 获取元素指定属性的值。
  • absUrl(String attributeKey): 获取元素指定属性的绝对路径 URL。

例如:

String text = element.text();
String href = element.attr("href");
String imgUrl = element.absUrl("src");
  1. HTML 清理:使用 clean(String bodyHtml) 方法可以清理 HTML 内容并生成干净和规范的 HTML。例如:
String html = "<p>Hello <b>Jsoup</b>!</p>";
String cleanHtml = Jsoup.clean(html, Whitelist.basic());

重点关注一下选择器的内容
选择器是 Jsoup 中非常强大和灵活的功能,它允许您使用类似于 CSS 选择器的语法来选择和查找 HTML 文档中的元素。通过使用选择器,您可以根据元素的标签名、类名、ID、属性等进行定位和筛选。

以下是一些常用的选择器示例:

  1. 标签选择器:
    使用标签选择器可以选择指定标签名的元素。例如,选择所有的 <a> 元素:
Elements elements = doc.select("a");
  1. 类选择器:
    使用类选择器可以选择具有指定类名的元素。例如,选择所有具有 “myClass” 类的元素:
Elements elements = doc.select(".myClass");
  1. ID 选择器:
    使用 ID 选择器可以选择具有指定 ID 的元素。例如,选择具有 “myElement” ID 的元素:
Element element = doc.select("#myElement").first();
  1. 属性选择器:
    使用属性选择器可以根据元素的属性值来选择元素。例如,选择具有 “href” 属性的 <a> 元素:
Elements elements = doc.select("a[href]");
  1. 属性值选择器:
    使用属性值选择器可以根据元素的属性值的匹配规则选择元素。例如,选择所有具有 “example.com” 域名的链接:
Elements elements = doc.select("a[href^=http://www.example.com]");
  1. 子元素选择器:
    使用子元素选择器可以选择某个元素的直接子元素。例如,选择 <div> 元素下的所有 <p> 元素:
Elements elements = doc.select("div > p");
  1. 后代元素选择器:
    使用后代元素选择器可以选择某个元素的所有后代元素。例如,选择 <div> 元素内的所有 <p> 元素:
Elements elements = doc.select("div p");
  1. 过滤器方法:
    除了基本的选择器语法外,Jsoup 还提供了一些过滤器方法,可以根据特定的条件对元素进行进一步筛选和过滤。例如,根据元素的索引值选择第一个 <a> 元素:
Element element = doc.select("a").first();

二、代码逻辑分析

我们知道,像页面中是有很多标签的,我们需要从这些标签中分析并取到自己想要的内容。
jsoup中是提供了api来调用取这些元素跟内容的,我们只需要分析页面标签的层级,一层一层去取自己想要的内容就可以了(关于这个案例,可以直接去代码中调试,很容易就能看明白了,使用文字反而不好讲清楚),这个过程类似于dom文档解析。
这里我们可以看到直接取到了25个电影的详情标签元素了,接下来就分析拿数据就可以了。
在这里插入图片描述

代码是比较简单的,重要的是分析过程,过程会分析了,就能爬取任何想要的内容了。
我以豆瓣top250电影为例来进行分析:

https://movie.douban.com/top250

我们可以看到这里是标题,下面就是内容列表了
在这里插入图片描述
分析方法:
1、直接按F12打开开发者模式,点击这个元素选择,直接在页面上选取你想获取到的内容,就可以看到他在标签中的位置了。
然后就是通过一层一层的标签选择,取到某一个element,然后获取他里面的内容。
在这里插入图片描述
2、我们看到的只是当前页面的东西,当前是第1页,那么我们该如何获取所有页,比如10页,100页的全部内容呢?
这时候就需要分析跳转下一页时,当前网站的url变化了
我们可以看到第一页start=0,第二页start=25,因为一页有25条内容,所以每次多25
到这里我们就知道了,获取完当前页内容后,让这个start这个参数的值+25,放到下一次请求的url中就可以了。
在这里插入图片描述

在这里插入图片描述

三、代码的编写

爬虫部分代码:

public class FinalMovie {


        public static void main(String[] args) throws IOException, ParseException, InterruptedException {
        int num=0;
        String url="https://movie.douban.com/top250?start=";
        //循环获取10页的全部内容
        for (int i = 0; i < 10; i++) {
            getMovie(url+num);
            //睡眠两秒
            Thread.sleep(2000);
            num=num+25;

        }
    }

    /**
     * 获取豆瓣top250电影信息
     * @param url 目标网站网址
     * @throws IOException
     * @throws ParseException
     */
    private static void getMovie(String url) throws IOException, ParseException {
        Connection connection = Jsoup.connect(url)
                //真实的User-Agent
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
                .method(Connection.Method.GET);
        Document doc = connection.get();
        //取id为content下面的h1
        Elements select = doc.select("#content > h1");
        String title = select.text();
        //System.out.println(title);
        Elements ol = doc.select("div.article > ol");
        //System.out.println(ol);
        //遍历 ol 中的每个 <li> 标签
        List<Movie> movieList=new ArrayList<Movie>();
        for (Element li : ol) {

            //取li标签下的所有<div class="info">
            Elements infos = li.select("div.item").select("div.info");
            //System.out.println(info);
            for (Element info : infos) {
                Movie movie=new Movie();
                //ArrayList<String> nameList=new ArrayList<String>();
                //取所有的div class=hd
                Elements hds = info.select("div.hd");
                //往里进一层,取hd中的a
                Elements as = hds.select("a[href]");
                for (Element a : as) {
                    Element element = a.selectFirst("span.title");
                    String name = element.text();
                    //System.out.println(name);
                    movie.setMovieName(name);
                    //nameList.add(name);
                }
                //取所有的div class=bd
                Elements bds = info.select("div.bd");
                for (Element bd : bds) {
                    Element p = bd.selectFirst("p");
                    String text = p.text();
                    movie.setMovieIntroduce(text);
                    //System.out.println(p);
                    Elements stars = bd.select("div.star");
                    for (Element star : stars) {
                        Element child = star.child(1);
                        //System.out.println(child);
                        String text1 = child.text();
                        movie.setMovieStar(text1);
                    }
                    Element p1 = bd.select("p").last();
                    //System.out.println(p1);
                    String text1 = p1.text();
                    movie.setMovieEvaluate(text1);
                }
                //System.out.println(movie.toString());
                movieList.add(movie);
            }

        }
        for (Movie movie : movieList) {
            System.out.println(movie);
        }
        //将数据写入到excel表中
        writeDataToExcel(movieList,"moviesData.xlsx",true);
        System.out.println("15条数据写入excel成功");
    }
}

将集合插入excel表格代码:

/**
     * 将集合数据写入到Excel中
     * @param dataList 数据集合
     * @param outputPath 输出路径
     * @param append 是否追加数据,true为追加,false为覆盖
     * @throws IOException
     */
    public static void writeDataToExcel(List<?> dataList, String outputPath, boolean append) throws IOException {
        Workbook workbook;
        if (append && new File(outputPath).exists()) {
            workbook = WorkbookFactory.create(new FileInputStream(outputPath));
        } else {
            workbook = new XSSFWorkbook();
        }

        Sheet sheet = workbook.getSheet("Sheet1");
        if (sheet == null) {
            sheet = workbook.createSheet("Sheet1");
        }

        // 获取当前已有数据的最后一行索引
        int lastRow = sheet.getLastRowNum();

        // 创建表头(仅当需要创建新的表格时)
        if (lastRow == 0) {
            Row headerRow = sheet.createRow(0);
            Object firstData = dataList.get(0);
            List<String> propertiesList = getObjectPropertiesList(firstData);
            for (int i = 0; i < propertiesList.size(); i++) {
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(propertiesList.get(i));
            }
        }

        int rowCount = lastRow + 1;

        // 写入数据
        for (Object data : dataList) {
            Row dataRow = sheet.createRow(rowCount++);

            int colCount = 0;
            List<String> propertiesList = getObjectPropertiesList(data);
            for (String property : propertiesList) {
                Cell cell = dataRow.createCell(colCount++);
                Object value = getObjectPropertyValue(data, property);

                if (value != null) {
                    if (value instanceof String) {
                        cell.setCellValue((String) value);
                    } else if (value instanceof Number) {
                        cell.setCellValue(((Number) value).doubleValue());
                    } else if (value instanceof Date) {
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        cell.setCellValue(dateFormat.format((Date) value));
                    }
                    // 根据需要添加其他数据类型的处理逻辑
                }
            }
        }

        FileOutputStream outputStream = new FileOutputStream(outputPath);
        workbook.write(outputStream);
        workbook.close();
        outputStream.close();
    }

    /**
     * 获取对象的属性列表
     * @param object 对象
     * @return 属性列表
     */
    private static List<String> getObjectPropertiesList(Object object) {
        List<String> propertiesList = new ArrayList<>();
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            propertiesList.add(field.getName());
        }
        return propertiesList;
    }

    /**
     * 获取对象的属性值
     * @param object 对象
     * @param property 属性名
     * @return 属性值
     */
    private static Object getObjectPropertyValue(Object object, String property) {
        try {
            Field field = object.getClass().getDeclaredField(property);
            field.setAccessible(true);
            return field.get(object);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

电影实体类:

(这里我为了方便就简单的都设置成了string类型)

package com.example.domain;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

public class Movie {
    //电影名称
    private String movieName;
    //电影介绍
    private String movieIntroduce;
    //电影评分
    private String movieStar;
    //电影评价
    private String movieEvaluate;

    public Movie() {
    }

    public Movie(String movieName, String movieIntroduce, String movieStar, String movieEvaluate) {
        this.movieName = movieName;
        this.movieIntroduce = movieIntroduce;
        this.movieStar = movieStar;
        this.movieEvaluate = movieEvaluate;
    }

    @Override
    public String toString() {
        return "Movie{" +
                "movieName='" + movieName + '\'' +
                ", movieIntroduce='" + movieIntroduce + '\'' +
                ", movieStar='" + movieStar + '\'' +
                ", movieEvaluate='" + movieEvaluate + '\'' +
                '}';
    }

    public String getMovieName() {
        return movieName;
    }

    public void setMovieName(String movieName) {
        this.movieName = movieName;
    }

    public String getMovieIntroduce() {
        return movieIntroduce;
    }

    public void setMovieIntroduce(String movieIntroduce) {
        this.movieIntroduce = movieIntroduce;
    }

    public String getMovieStar() {
        return movieStar;
    }

    public void setMovieStar(String movieStar) {
        this.movieStar = movieStar;
    }

    public String getMovieEvaluate() {
        return movieEvaluate;
    }

    public void setMovieEvaluate(String movieEvaluate) {
        this.movieEvaluate = movieEvaluate;
    }


}

错误写法:
String text = hd.select(“a[href]”).select(“span.title”).text();
System.out.println(text);
这样就会把所有电影名称放到一块进行输出,就像这样:
在这里插入图片描述

四、附录(完整的可以直接运行的代码)

把实体类粘进去即可。

package com.example;

import com.example.domain.Movie;
import com.example.domain.Product;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
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.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class FinalMovie {


    public static void main(String[] args) throws IOException, ParseException, InterruptedException {
        int num=0;
        String url="https://movie.douban.com/top250?start=";
        //循环获取10页的全部内容
        for (int i = 0; i < 10; i++) {
            getMovie(url+num);
            //睡眠两秒
            Thread.sleep(2000);
            num=num+25;

        }
    }

    /**
     * 获取豆瓣top250电影信息
     * @param url 目标网站网址
     * @throws IOException
     * @throws ParseException
     */
    private static void getMovie(String url) throws IOException, ParseException {
        Connection connection = Jsoup.connect(url)
                //真实的User-Agent
                .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36")
                .method(Connection.Method.GET);
        Document doc = connection.get();
        //取id为content下面的h1
        Elements select = doc.select("#content > h1");
        String title = select.text();
        //System.out.println(title);
        Elements ol = doc.select("div.article > ol");
        //System.out.println(ol);
        //遍历 ol 中的每个 <li> 标签
        List<Movie> movieList=new ArrayList<Movie>();
        for (Element li : ol) {

            //取li标签下的所有<div class="info">
            Elements infos = li.select("div.item").select("div.info");
            //System.out.println(info);
            for (Element info : infos) {
                Movie movie=new Movie();
                //ArrayList<String> nameList=new ArrayList<String>();
                //取所有的div class=hd
                Elements hds = info.select("div.hd");
                //往里进一层,取hd中的a
                Elements as = hds.select("a[href]");
                for (Element a : as) {
                    Element element = a.selectFirst("span.title");
                    String name = element.text();
                    //System.out.println(name);
                    movie.setMovieName(name);
                    //nameList.add(name);
                }
                //取所有的div class=bd
                Elements bds = info.select("div.bd");
                for (Element bd : bds) {
                    Element p = bd.selectFirst("p");
                    String text = p.text();
                    movie.setMovieIntroduce(text);
                    //System.out.println(p);
                    Elements stars = bd.select("div.star");
                    for (Element star : stars) {
                        Element child = star.child(1);
                        //System.out.println(child);
                        String text1 = child.text();
                        movie.setMovieStar(text1);
                    }
                    Element p1 = bd.select("p").last();
                    //System.out.println(p1);
                    String text1 = p1.text();
                    movie.setMovieEvaluate(text1);
                }
                //System.out.println(movie.toString());
                movieList.add(movie);
            }

        }
        for (Movie movie : movieList) {
            System.out.println(movie);
        }
        //将数据写入到excel表中
        writeDataToExcel(movieList,"moviesData2.xlsx",true);
        System.out.println("15条数据写入excel成功");
    }

    /**
     * 将集合数据写入到Excel中
     * @param dataList 数据集合
     * @param outputPath 输出路径
     * @param append 是否追加数据,true为追加,false为覆盖
     * @throws IOException
     */
    public static void writeDataToExcel(List<?> dataList, String outputPath, boolean append) throws IOException {
        Workbook workbook;
        if (append && new File(outputPath).exists()) {
            workbook = WorkbookFactory.create(new FileInputStream(outputPath));
        } else {
            workbook = new XSSFWorkbook();
        }

        Sheet sheet = workbook.getSheet("Sheet1");
        if (sheet == null) {
            sheet = workbook.createSheet("Sheet1");
        }

        // 获取当前已有数据的最后一行索引
        int lastRow = sheet.getLastRowNum();

        // 创建表头(仅当需要创建新的表格时)
        if (lastRow == 0) {
            Row headerRow = sheet.createRow(0);
            Object firstData = dataList.get(0);
            List<String> propertiesList = getObjectPropertiesList(firstData);
            for (int i = 0; i < propertiesList.size(); i++) {
                Cell cell = headerRow.createCell(i);
                cell.setCellValue(propertiesList.get(i));
            }
        }

        int rowCount = lastRow + 1;

        // 写入数据
        for (Object data : dataList) {
            Row dataRow = sheet.createRow(rowCount++);

            int colCount = 0;
            List<String> propertiesList = getObjectPropertiesList(data);
            for (String property : propertiesList) {
                Cell cell = dataRow.createCell(colCount++);
                Object value = getObjectPropertyValue(data, property);

                if (value != null) {
                    if (value instanceof String) {
                        cell.setCellValue((String) value);
                    } else if (value instanceof Number) {
                        cell.setCellValue(((Number) value).doubleValue());
                    } else if (value instanceof Date) {
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        cell.setCellValue(dateFormat.format((Date) value));
                    }
                    // 根据需要添加其他数据类型的处理逻辑
                }
            }
        }

        FileOutputStream outputStream = new FileOutputStream(outputPath);
        workbook.write(outputStream);
        workbook.close();
        outputStream.close();
    }

    /**
     * 获取对象的属性列表
     * @param object 对象
     * @return 属性列表
     */
    private static List<String> getObjectPropertiesList(Object object) {
        List<String> propertiesList = new ArrayList<>();
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            propertiesList.add(field.getName());
        }
        return propertiesList;
    }

    /**
     * 获取对象的属性值
     * @param object 对象
     * @param property 属性名
     * @return 属性值
     */
    private static Object getObjectPropertyValue(Object object, String property) {
        try {
            Field field = object.getClass().getDeclaredField(property);
            field.setAccessible(true);
            return field.get(object);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丿BAIKAL巛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值