Java爬虫Jsoup的使用

Jsoup的使用

    • 0. Jsoup介绍
    • 1. Get请求
    • 2. Post请求
    • 3. 通过document获取元素
    • 4. 练习Demo
      • 练习1
      • 练习2:获取所有图片
      • 练习3获取商品
      • 练习4 下载图片
    • 5. @PostConstruct

0. Jsoup介绍

jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。

org.jsoup.Jsoup类

方法 描述
static Connection connect(String url) 创建并返回URL的连接。
static Document parse(File in, String charsetName) 将指定的字符集文件解析成文档。
static Document parse(String html) 将给定的html代码解析成文档。
static String clean(String bodyHtml, Whitelist whitelist) 从输入HTML返回安全的HTML,通过解析输入HTML并通过允许的标签和属性的白名单进行过滤。

org.jsoup.nodes.Document类

修饰符和类型 方法 描述
Connection connection() 返回用于获取此文档的连接(请求/响应)对象(如果有);否则,一个新的默认 Connection 对象。
Document connection(Connection connection) 设置用于获取此文档的连接。

org.jsoup.nodes.Element类

修饰符和类型 方法 描述
Element child(int index) 通过从 0 开始的索引号获取此元素的子元素。
int childNodeSize() 获取该节点拥有的子节点数。
Elements children() 获取此元素的子元素。
int childrenSize() 获取此元素的子节点的数量。
String className() 获取此元素的“类”属性的字面值,其中可能包含多个类名,以空格分隔。
Set classNames() 获取所有元素的类名。
String cssSelector() 获取将唯一选择此元素的 CSS 选择器。
String data() 获取该元素的组合数据。
Elements getAllElements() 查找此元素下的所有元素(包括 self 和 children 的 children)。
Element getElementById(String id) 按 ID 查找元素,包括或在此元素下。
Elements getElementsByAttribute(String key) 查找具有命名属性集的元素。
Elements getElementsByAttributeStarting(String keyPrefix) 查找属性名称以提供的前缀开头的元素。
Elements getElementsByAttributeValue(String key, String value) 查找具有特定值的属性的元素。
Elements getElementsByAttributeValueContaining(String key, String match) 查找具有其值包含匹配字符串的属性的元素。
Elements getElementsByAttributeValueEnding(String key, String valueSuffix) 查找具有以值后缀结尾的属性的元素。
Elements getElementsByAttributeValueMatching(String key, String regex) 查找具有其值与提供的正则表达式匹配的属性的元素。
Elements getElementsByAttributeValueMatching(String key, Pattern pattern) 查找具有其值与提供的正则表达式匹配的属性的元素。
Elements getElementsByAttributeValueNot(String key, String value) 查找不具有此属性或具有不同值的元素。
Elements getElementsByAttributeValueStarting(String key, String valuePrefix) 查找具有以值前缀开头的属性的元素。
Elements getElementsByClass(String className) 查找具有此类的元素,包括或在此元素下。
Elements getElementsByIndexEquals(int index) 查找兄弟索引等于提供的索引的元素。
Elements getElementsByIndexGreaterThan(int index) 查找兄弟索引大于提供的索引的元素。
Elements getElementsByIndexLessThan(int index) 查找同级索引小于提供的索引的元素。
Elements getElementsByTag(String tagName) 查找具有指定标记名称的元素,包括并递归地在此元素下。
Elements getElementsContainingOwnText(String searchText) 查找直接包含指定字符串的元素。
Elements getElementsContainingText(String searchText) 查找包含指定字符串的元素。
Elements getElementsMatchingOwnText(String regex) 查找其自身文本与提供的正则表达式匹配的元素。
Elements getElementsMatchingOwnText(Pattern pattern) 查找其自身文本与提供的正则表达式匹配的元素。
Elements getElementsMatchingText(String regex) 查找其文本与提供的正则表达式匹配的元素。
Elements getElementsMatchingText(Pattern pattern) 查找其文本与提供的正则表达式匹配的元素。
Elements select(String cssQuery) 查找与SelectorCSS 查询匹配的元素,并以该元素作为起始上下文。

1. Get请求


// 方式一
Document document = Jsoup.connect("http://www.baidu.com/").get();
// 方式二
Document parse = Jsoup.parse(new URL("http://www.baidu.com/"), 3000);//3秒

2. Post请求

Document doc = Jsoup.connect("http://example.com")
  .data("query", "Java")
  .userAgent("Mozilla")
  .cookie("auth", "token")
  .timeout(3000)
  .post();

3. 通过document获取元素

	getElementById(String id):通过id来获取
  getElementsByTag(String tagName):通过标签名字来获取
  getElementsByClass(String className):通过类名来获取
  getElementsByAttribute(String key):通过属性名字来获取
  getElementsByAttributeValue(String key, String value):通过指定的属性名字,属性值来获取
  getAllElements():获取所有元素

4. 练习Demo

练习1

File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8", "http://example.com/");

Elements links = doc.select("a[href]"); //带有href属性的a元素
Elements pngs = doc.select("img[src$=.png]");
  //扩展名为.png的图片

Element masthead = doc.select("div.masthead").first();
  //class等于masthead的div标签

Elements resultLinks = doc.select("h3.r > a"); //在h3元素之后的a元素

练习2:获取所有图片

		Document documents = Jsoup.parse(new URL(url),3000);
        Elements images = document.select("img[src~=(?i)\\.(png|jpe?g|gif)]");
        for (Element image : images)
        {
            System.out.println("src : " + image.attr("src"));
            System.out.println("height : " + image.attr("height"));
            System.out.println("width : " + image.attr("width"));
            System.out.println("alt : " + image.attr("alt"));
        }

练习3获取商品

package test;

import entity.Commodity;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

/**
 * 获取商品信息
 * @author 有多勉为其难
 * @version 1.0.0
 * @date 2022/9/6
 */
public class HtmlParseUtils {
    public static void main(String[] args) throws Exception {
        new HtmlParseUtils().getCommodity("鞋子", 10000).forEach(System.out::println);
    }

    private List<Commodity> getCommodity(String keyword, int timeout) throws IOException {
        //设置url编码
        String keywords = URLEncoder.encode(keyword,"utf-8");
        String url = "https://search.jd.com/Search?keyword="+keywords;

        Document document = Jsoup.parse(new URL(url), timeout);

        Element element = document.getElementById("J_goodsList");
        Elements liElements = element.getElementsByTag("li");
        List<Commodity> commodityList = new ArrayList<>();

        for (Element el : liElements) {
            String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img").trim();
            String price = el.getElementsByClass("p-price").eq(0).text().trim();
            String name = el.getElementsByClass("p-name").eq(0).text().trim();
            Commodity commodity = new Commodity();
            commodity.setName(name);
            commodity.setImg(img);
            commodity.setPrice(price);
            commodityList.add(commodity);
        }
        return commodityList;
    }
}
package entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 商品实体类
 * @author 有多勉为其难
 * @version 1.0.0
 * @date 2022/9/6
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Commodity {
    private String name;
    private String img;
    private String price;
}

练习4 下载图片

package test;

import entity.Image;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import utils.DownloadUtils;

import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

/**
 * 找到并下载图片
 *
 * @author 有多勉为其难
 * @date 2022/9/6
 */
public class DownloadImagesUtils {
    public static void main(String[] args) throws IOException {
        String url = "https://www.quanjing.com/creative/topic/1";
        downloadImage(url, "a");
    }

    private static void downloadImage(String url, String keyword) throws IOException {
        //设置url编码
        String keywords = URLEncoder.encode(keyword, "utf-8");
        Document doc = Jsoup.parse(new URL(url + keywords), 3000);
        Element element = doc.getElementById("gallery-list");
        Elements elements = element.getElementsByTag("li");
        List<Image> imageList = new ArrayList<>();
        List<String> urlList = new ArrayList<>();
        for (Element imgElement : elements) {
            String src = imgElement.getElementsByTag("img").eq(0).attr("src");
            String alt = imgElement.getElementsByTag("alt").eq(0).attr("alt");
            Image image = new Image();
            image.setAlt(alt);
            image.setSrc(src);
            imageList.add(image);
            urlList.add(src);
        }
        System.out.println(imageList);
        System.out.println(urlList);

        DownloadUtils.processSync(urlList, keyword);
    }
}

package utils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 图片下载工具类
 *
 * @author 有多勉为其难
 * @version 1.0.0
 * @date 2022/9/6
 */
public class DownloadUtils {
    private String extension = ".jpg";
    private String path;

    private static volatile AtomicInteger suc;
    private static volatile AtomicInteger fails;

    public DownloadUtils() {
        setPath("E:/pipeline/");
        suc = new AtomicInteger();
        fails = new AtomicInteger();
    }

    public void setPath(String path) {
        this.path = path;
    }

    /**
     * 下载
     *
     * @param url
     * @param cate
     * @throws Exception
     */
    private void downloadImg(String url, String cate, String name) throws Exception {
        String path = this.path + "/" + cate + "/";
        File dir = new File(path);
        if (!dir.exists()) {    // 目录不存在则创建目录
            dir.mkdirs();
        }
        // 获取扩展名
        String realExt = url.substring(url.lastIndexOf("."));
        String fileName = name + realExt;
        fileName = fileName.replace("-", "");
        String filePath = path + fileName;
        File img = new File(filePath);
        // 若文件之前已经下载过,则跳过
        if (img.exists()) {
            System.out.println(String.format("文件%s已存在本地目录", fileName));
            return;
        }

        URLConnection con = new URL(url).openConnection();
        con.setConnectTimeout(5000);
        con.setReadTimeout(5000);
        InputStream inputStream = con.getInputStream();
        byte[] bs = new byte[1024];

        File file = new File(filePath);
        FileOutputStream os = new FileOutputStream(file, true);
        // 开始读取 写入
        int len;
        while ((len = inputStream.read(bs)) != -1) {
            os.write(bs, 0, len);
        }
        System.out.println("picUrl: " + url);
        System.out.println(String.format("正在下载第%s张图片", suc.getAndIncrement()));
    }

    /**
     * 多线程处理
     *
     * @param data
     * @param word
     */
    public static void processSync(List<String> data, String word) {
        long start = System.currentTimeMillis();
        int count = 0;
        DownloadUtils downloadUtils = new DownloadUtils();
        // 创建缓存线程池
        /*
        优点:线程池会根据任务数量创建线程池,并且在一定时间内可以重复使用这些线程,产生相应的线程池。
        缺点:适用于短时间有大量任务的场景,它的缺点是可能会占用很多的资源。
        */
        ExecutorService executorService = Executors.newCachedThreadPool();
//        ThreadPoolExecutor executorService = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));
        for (int i = 0; i < data.size(); i++) {
            String picUrl = data.get(i);
            if (picUrl == null) {
                continue;
            }
            String name = "";
            if (i < 10) {
                name = "000" + i;
            } else if (i < 100) {
                name = "00" + i;
            } else if (i < 1000) {
                name = "0" + i;
            }
            String finalName = name;

            executorService.execute(() -> {
                try {
                    downloadUtils.downloadImg(picUrl, word, finalName);
                } catch (Exception e) {
//                    e.printStackTrace();
                    DownloadUtils.fails.incrementAndGet();
                }
            });
            count++;
        }
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
                // 超时的时候向线程池中所有的线程发出中断(interrupted)。
                //                executorService.shutdownNow();
            }
            System.out.println("AwaitTermination Finished");
            System.out.println("共有URL: " + data.size());
            System.out.println("下载成功: " + suc);
            System.out.println("下载失败: " + fails);

            File dir = new File(downloadUtils.path + "/" + word + "/");
            int len = Objects.requireNonNull(dir.list()).length;
            System.out.println("当前共有文件: " + len);

            long end = System.currentTimeMillis();
            System.out.println("耗时:" + (end - start) / 1000.0 + "秒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

5. @PostConstruct

@PostConstruct是Java自带的注解,在方法上加该注解会在项目启动的时候执行该方法,也可以理解为在spring容器初始化的时候执行该方法。

@SpringBootApplication
@ComponentScan(basePackages = {"com.my.spider"})
@MapperScan("com.my.spider.mapper")
public class MySpiderApplication {
    @Autowired
    private SpiderHandle spiderHandle;
    @Autowired
    private ContentNoticeHandle contentNoticeHandle;

    public static void main(String[] args) {
        SpringApplication.run(MySpiderApplication.class, args);
    }

    @PostConstruct
    public void task(){
        contentNoticeHandle.spiderData();
    }/*
    @PostConstruct
    public void task(){
        spiderHandle.spiderData();
    }
*/
}

你可能感兴趣的:(java,springboot,java,爬虫)