怎么用jsoup实现抓取图片爬虫

发布时间:2021-10-19 09:22:46 作者:iii
来源:亿速云 阅读:372
# 怎么用jsoup实现抓取图片爬虫

## 前言

在当今互联网时代,网络爬虫技术已经成为获取网络数据的重要手段之一。其中,图片爬虫作为专门用于抓取网络图片的工具,在数据采集、内容分析等领域有着广泛的应用。本文将详细介绍如何使用Java的jsoup库来实现一个高效的图片爬虫。

## 一、jsoup简介

### 1.1 什么是jsoup

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

主要功能包括:
- 从URL、文件或字符串中抓取和解析HTML
- 使用DOM遍历或CSS选择器查找和提取数据
- 操作HTML元素、属性和文本
- 清除用户提交的内容以防止XSS攻击

### 1.2 jsoup的优势

相比于其他HTML解析库,jsoup具有以下优势:
1. 简单易用的API
2. 支持CSS选择器语法
3. 能够处理不规范的HTML(类似浏览器)
4. 内置HTML清理功能
5. 轻量级且性能良好

## 二、环境准备

### 2.1 安装jsoup

要在项目中使用jsoup,可以通过以下方式添加依赖:

Maven项目:
```xml
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.16.1</version>
</dependency>

Gradle项目:

implementation 'org.jsoup:jsoup:1.16.1'

或者直接下载jar包:jsoup官网下载

2.2 基本使用示例

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

public class BasicExample {
    public static void main(String[] args) throws Exception {
        // 从URL获取HTML文档
        Document doc = Jsoup.connect("https://example.com").get();
        
        // 获取页面标题
        String title = doc.title();
        System.out.println("Title: " + title);
        
        // 使用CSS选择器获取元素
        String firstParagraph = doc.select("p").first().text();
        System.out.println("First paragraph: " + firstParagraph);
    }
}

三、图片爬虫实现原理

3.1 爬虫工作流程

一个基本的图片爬虫通常包含以下步骤: 1. 发送HTTP请求获取网页内容 2. 解析HTML文档 3. 提取图片URL 4. 下载图片到本地 5. 处理分页或链接跳转

3.2 图片URL识别

网页中的图片通常以以下几种形式存在: - <img>标签的src属性 - <img>标签的data-src属性(懒加载) - CSS背景图片 - JavaScript动态加载的图片

我们的爬虫主要关注<img>标签的src和data-src属性。

四、基础图片爬虫实现

4.1 单页面图片抓取

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.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;

public class BasicImageCrawler {
    
    // 图片保存目录
    private static final String SAVE_DIR = "downloaded_images";
    
    public static void main(String[] args) {
        String url = "https://example.com";
        
        try {
            // 创建保存目录
            Files.createDirectories(Paths.get(SAVE_DIR));
            
            // 获取HTML文档
            Document doc = Jsoup.connect(url).get();
            
            // 选择所有img标签
            Elements imgElements = doc.select("img");
            
            for (Element img : imgElements) {
                // 获取图片URL
                String imgUrl = img.absUrl("src");
                
                // 处理data-src(懒加载情况)
                if (imgUrl.isEmpty()) {
                    imgUrl = img.absUrl("data-src");
                }
                
                if (!imgUrl.isEmpty()) {
                    System.out.println("Found image: " + imgUrl);
                    downloadImage(imgUrl);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void downloadImage(String imgUrl) throws IOException {
        // 从URL中提取文件名
        String fileName = imgUrl.substring(imgUrl.lastIndexOf('/') + 1);
        
        // 防止文件名无效
        if (fileName.isEmpty() || !fileName.contains(".")) {
            fileName = "image_" + System.currentTimeMillis() + ".jpg";
        }
        
        // 下载图片
        try (InputStream in = new URL(imgUrl).openStream()) {
            Files.copy(in, Paths.get(SAVE_DIR, fileName));
            System.out.println("Downloaded: " + fileName);
        } catch (Exception e) {
            System.err.println("Failed to download: " + imgUrl);
        }
    }
}

4.2 代码解析

  1. Jsoup.connect(url).get() - 建立连接并获取HTML文档
  2. doc.select("img") - 使用CSS选择器选取所有img标签
  3. img.absUrl("src") - 获取绝对URL(相对于当前页面)
  4. 下载逻辑使用Java标准库的URL和Files类

五、高级功能实现

5.1 处理懒加载图片

许多现代网站使用懒加载技术,初始时图片并不在src属性中,而是在data-src等自定义属性中。我们需要同时检查这些属性:

// 扩展的图片URL获取方法
private static String getImageUrl(Element img) {
    String[] attrNames = {"src", "data-src", "data-original", "data-srcset"};
    
    for (String attr : attrNames) {
        String url = img.absUrl(attr);
        if (!url.isEmpty()) {
            return url;
        }
    }
    return "";
}

5.2 支持多页面爬取

要实现网站的多页面爬取,我们需要发现并跟踪链接:

// 递归爬取页面
private static void crawlPage(String url, Set<String> visitedUrls, int depth) throws IOException {
    if (depth <= 0 || visitedUrls.contains(url)) {
        return;
    }
    
    visitedUrls.add(url);
    System.out.println("Crawling: " + url);
    
    try {
        Document doc = Jsoup.connect(url)
                           .timeout(10000)
                           .get();
        
        // 下载图片
        downloadImagesFromDoc(doc);
        
        // 查找并跟踪其他链接
        Elements links = doc.select("a[href]");
        for (Element link : links) {
            String nextUrl = link.absUrl("href");
            if (isValidUrl(nextUrl)) {
                crawlPage(nextUrl, visitedUrls, depth - 1);
            }
        }
    } catch (IOException e) {
        System.err.println("Failed to crawl: " + url);
    }
}

// 简单的URL验证
private static boolean isValidUrl(String url) {
    return url.startsWith("http") && !url.contains("logout") && !url.contains("signout");
}

5.3 添加用户代理和超时

许多网站会阻止默认的Java用户代理,我们需要设置合理的请求头:

Document doc = Jsoup.connect(url)
                   .userAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
                   .timeout(10000)
                   .referrer("https://www.google.com")
                   .get();

5.4 图片过滤和分类

我们可以根据图片属性进行过滤和分类:

// 只下载大于指定尺寸的图片
private static boolean shouldDownload(Element img) {
    String widthStr = img.attr("width");
    String heightStr = img.attr("height");
    
    try {
        int width = widthStr.isEmpty() ? 0 : Integer.parseInt(widthStr);
        int height = heightStr.isEmpty() ? 0 : Integer.parseInt(heightStr);
        
        // 只下载宽度大于100像素的图片
        return width > 100 || height > 100;
    } catch (NumberFormatException e) {
        return true;
    }
}

六、性能优化

6.1 多线程下载

使用线程池提高下载速度:

private static final ExecutorService executor = Executors.newFixedThreadPool(10);

private static void downloadImageAsync(String imgUrl) {
    executor.submit(() -> {
        try {
            downloadImage(imgUrl);
        } catch (IOException e) {
            System.err.println("Failed to download: " + imgUrl);
        }
    });
}

6.2 连接池管理

jsoup本身没有内置连接池,但我们可以实现简单的重用:

private static Connection getConnection(String url) {
    return Jsoup.connect(url)
               .userAgent("Mozilla/5.0...")
               .timeout(10000);
}

6.3 去重处理

使用Set存储已访问URL和已下载图片的hash:

private static Set<String> downloadedImages = new HashSet<>();

private static void downloadImage(String imgUrl) throws IOException {
    // 检查是否已下载
    String hash = Integer.toHexString(imgUrl.hashCode());
    if (downloadedImages.contains(hash)) {
        return;
    }
    
    // 下载逻辑...
    
    // 添加到已下载集合
    downloadedImages.add(hash);
}

七、异常处理和日志

7.1 健壮的异常处理

try {
    Document doc = Jsoup.connect(url)
                       .timeout(10000)
                       .get();
    // 处理文档
} catch (SocketTimeoutException e) {
    System.err.println("Timeout while fetching: " + url);
} catch (HttpStatusException e) {
    System.err.println("HTTP error: " + e.getStatusCode() + " for URL: " + url);
} catch (IOException e) {
    System.err.println("IO error while fetching: " + url);
}

7.2 日志记录

使用SLF4J或Log4j记录爬取过程:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

private static final Logger logger = LoggerFactory.getLogger(ImageCrawler.class);

// 使用示例
logger.info("Starting to crawl: {}", url);
logger.warn("Failed to download image: {}", imgUrl, e);

八、完整示例代码

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.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AdvancedImageCrawler {
    
    private static final String SAVE_DIR = "downloaded_images";
    private static final ExecutorService executor = Executors.newFixedThreadPool(10);
    private static final Set<String> visitedUrls = new HashSet<>();
    private static final Set<String> downloadedImages = new HashSet<>();
    
    public static void main(String[] args) {
        String startUrl = "https://example.com";
        int maxDepth = 2;
        
        try {
            Files.createDirectories(Paths.get(SAVE_DIR));
            crawlPage(startUrl, maxDepth);
            executor.shutdown();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private static void crawlPage(String url, int depth) throws IOException {
        if (depth <= 0 || visitedUrls.contains(url)) {
            return;
        }
        
        visitedUrls.add(url);
        System.out.println("Crawling: " + url + " at depth: " + depth);
        
        try {
            Document doc = Jsoup.connect(url)
                               .userAgent("Mozilla/5.0...")
                               .timeout(10000)
                               .get();
            
            // 下载图片
            Elements imgs = doc.select("img");
            for (Element img : imgs) {
                String imgUrl = getImageUrl(img);
                if (!imgUrl.isEmpty() && shouldDownload(img)) {
                    downloadImageAsync(imgUrl);
                }
            }
            
            // 递归爬取链接
            Elements links = doc.select("a[href]");
            for (Element link : links) {
                String nextUrl = link.absUrl("href");
                if (isValidUrl(nextUrl)) {
                    executor.submit(() -> {
                        try {
                            crawlPage(nextUrl, depth - 1);
                        } catch (IOException e) {
                            System.err.println("Error crawling: " + nextUrl);
                        }
                    });
                }
            }
        } catch (IOException e) {
            System.err.println("Failed to crawl: " + url);
        }
    }
    
    // 其他辅助方法...
    // getImageUrl(), shouldDownload(), isValidUrl(), downloadImageAsync()
    // 参考前面章节的实现
}

九、法律和道德考虑

在开发和使用网络爬虫时,必须注意以下事项:

  1. 遵守robots.txt:检查目标网站的robots.txt文件,尊重网站的爬取规则
  2. 限制请求频率:避免对目标服务器造成过大负担
    
    // 在请求间添加延迟
    Thread.sleep(1000); // 1秒延迟
    
  3. 版权问题:未经许可不得下载和使用受版权保护的图片
  4. 隐私保护:不得爬取和存储个人隐私信息
  5. 服务条款:遵守目标网站的服务条款

十、扩展思路

  1. 图片内容识别:结合OpenCV或TensorFlow实现图片内容分析
  2. 分布式爬虫:使用Redis管理任务队列,实现分布式爬取
  3. 增量爬取:记录ETag或Last-Modified头,实现增量更新
  4. 浏览器渲染:对于JavaScript渲染的页面,可结合Selenium或Playwright
  5. 反爬虫绕过:处理验证码、IP封锁等反爬机制

结语

本文详细介绍了如何使用jsoup实现一个功能完善的图片爬虫,从基础实现到高级功能,再到性能优化和异常处理。jsoup虽然是一个轻量级库,但配合Java强大的网络和IO能力,完全可以构建出强大的网络爬虫应用。

在实际应用中,请根据具体需求调整代码,并始终遵守网络道德和相关法律法规。希望本文能够帮助你快速掌握jsoup爬虫开发技术。

推荐阅读:
  1. 使用Jsoup如何实现爬虫技术
  2. Python爬虫实现抓取京东店铺信息及下载图片功能示例

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

jsoup

上一篇:VS Code中快捷键操作的方法教程

下一篇:final关键字的小细节有哪些

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》