Java爬虫jsoup工具类

一、定义

jsoup官网,jsoup是一个用于处理HTML的Java库,它提供了一个非常方便类似于使用DOM,CSS和jQuery的方法的API来提取和操作数据。

jsoup实现WHATWG HTML5规范,并将HTML解析为与现代浏览器相同的DOM。

  • 从URL,文件或字符串中提取并解析HTML
  • 查找和提取数据,使用DOM遍历或CSS选择器
  • 操纵HTML元素,属性和文本
  • 根据安全的白名单清理用户提交的内容,以防止XSS攻击
  • 输出整洁的HTML

jsoup旨在处理发现所有格式有差异的HTML,从原始和验证,到无效的标签,jsoup将创建一个明智的解析树。

二、主要作用

  • DOM:将HTML解析为与现代浏览器相同的DOM,和js中的document对象一样,用getElementById等方法获取元素
  • CSS:利用CSS选择器选择src等属性

三、开源

jsoup是一个根据自由MIT许可证分发的开源项目,源代码可在GitHub获得:http://github.com/jhy/jsoup/

四、jsoup安装

要运行任何jsoup示例,需要先安装好jsoup相关Jar包,安装jsoup主要有三种方法:

  • 通过Maven的pom.xml配置文件
  • 使用jsoup.jar文件
  • Gradle的配置文件

通过Maven的pom.xml文件配置:

<dependency>
  <!-- jsoup HTML parser library @ http://jsoup.org/ -->
  <groupId>org.jsoup</groupId>
  <artifactId>jsoup</artifactId>
  <version>1.15.3</version>
</dependency>

jsoup是完全自包含的,没有依赖关系。

五、jsoup应用的主要类

然完整的类库中有很多类,但大多数情况下,下面给出3个类是我们需要重点了解的。

5.1 org.jsoup.Jsoup

jsoup类是任何Jsoup程序的入口点,并将提供从各种来源(URL、文件、字符串)加载和解析HTML文档的方法。

5.1.1 从URL加载解析HTML文档

方式一:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;
import java.net.URL;

public class Test {
    public static void main(String[] args) throws IOException {
        Document document = Jsoup.parse(new URL("http://www.yiibai.com"), 30000);
        String title = document.title();

        //易百教程™ - 专注于IT教程和实例
        System.out.println(title);
    }
}

方式二:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        Document document = Jsoup.connect("http://www.yiibai.com").get();
        String title = document.title();

        //易百教程™ - 专注于IT教程和实例
        System.out.println(title);
    }
}
5.1.2 从文件加载解析HTML文档
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.io.File;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        Document document = Jsoup.parse( new File( "D:/yibai.html" ) , "utf-8" );
        String title = document.title();

        //易百教程™ - 专注于IT教程和实例
        System.out.println(title);
    }
}

备注:在浏览器中打开http://www.yiibai.com网站,然后ctrl+s将该网站保存到本地yibai.html。

5.1.3 从字符串加载解析HTML文档
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        String html = "<html><head><title>易百教程™ - 专注于IT教程和实例</title></head>"
                + "<body><p>Parsed HTML into a doc.</p></body></html>";
        Document document = Jsoup.parse(html);
        String title = document.title();

        //易百教程™ - 专注于IT教程和实例
        System.out.println(title);
    }
}
5.2 org.jsoup.nodes.Document

该类表示通过jsoup库加载HTML文档。可以使用此类执行适用于整个HTML文档的操作。Element类的重要方法可以参见 http://jsoup.org/apidocs/org/jsoup/nodes/Document.html

5.2.1 DOM

document对象和JavaScript里的document是一个东西,js里能用的方法这里都能调用。

Element element = document.getElementById("J_goodsList");
5.2.2 CSS选择器

下面例子中从HTML提取带a[href] 、src、 link[href]属性的元素,再用这些元素的attr函数提取更精确额内容。

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 Test {
    public static void main(String[] args) throws IOException {
        Document doc = Jsoup.connect("http://www.yiibai.com").get();
        Elements links = doc.select("a[href]");
        Elements media = doc.select("[src]");
        Elements imports = doc.select("link[href]");

        System.out.println("links size:=" + links.size());
        for (Element link : links) {
            System.out.println(link.attr("abs:href"));
        }

        System.out.println("media size:=" + media.size());
        for (Element src : media) {
            if (src.tagName().equals("img")) {
                System.out.println(src.tagName() + " " + src.attr("abs:src") + " " + src.attr("width"));
            } else {
                System.out.println(src.tagName() + " " + src.attr("abs:src") + " " + src.attr("width"));
            }
        }

        System.out.println("imports size:=" + imports.size());
        for (Element link : imports) {
            System.out.println(link.tagName() + " " + link.attr("abs:href") + " " + link.attr("rel"));
        }
    }
}
5.3 org.jsoup.nodes.Element

HTML元素是由标签名称,属性和子节点组成。 使用Element类,您可以提取数据,遍历节点和操作HTML。Element类的重要方法可参见 http://jsoup.org/apidocs/org/jsoup/nodes/Element.html

六、常用功能

6.1 获取URL的链接

Elements类中可以使用for-each循环遍历元素。Element类提供了attr()和text()方法来返回链接的链接和对应的文本。

Document doc = Jsoup.connect("http://www.yiibai.com").get();  
Elements links = doc.select("a[href]");  
for (Element link : links) {  
    System.out.println("\nlink : " + link.attr("href"));  
    System.out.println("text : " + link.text());  
}
6.2 获取URL的元信息
Document doc = Jsoup.connect("http://www.yiibai.com").get();  
String keywords = doc.select("meta[name=keywords]").first().attr("content");  
System.out.println("Meta keyword : " + keywords);  
String description = doc.select("meta[name=description]").get(0).attr("content");  
System.out.println("Meta description : " + description);
6.3 获取URL的图片
Document doc = Jsoup.connect("http://www.yiibai.com").get();  
Elements images = doc.select("img[src~=.+(.JPEG|.jpeg|.JPG|.jpg|.PNG|.png|.GIF|.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"));  
}
6.4 获取表单参数
Document doc = Jsoup.parse(new File("D:/yibai.html"),"utf-8");  
Element loginform = doc.getElementById("registerform");  

Elements inputElements = loginform.getElementsByTag("input");  
for (Element inputElement : inputElements) {  
    String key = inputElement.attr("name");  
    String value = inputElement.attr("value");  
    System.out.println("Param name: "+key+" \nParam value: "+value);  
}

七、jsoup上传图片到OSS

7.1 OssComponent
@Slf4j
@Component
public class OssComponent {

    @Resource
    private OssConfig ossConfig;

    @Value("${spring.profiles.active}")
    private String active;

    public String uploadByInputStream(InputStream inputStream,
                                      Long memberId, String fileName) {
        String bucketName = getBucketName();

        // /content/{memberId}/{datetime.now.date}/xxxxxxx.jpg
        String date = DateFormatUtils.format(new Date(), "yyyy-MM-dd");
        String objectName = StrUtil.format("content/{}/{}/{}", memberId, date, fileName);

        OSS ossClient = new OSSClientBuilder().build(ossConfig.getEndpoint(), ossConfig.getAccessKeyId(), ossConfig.getAccessKeySecret());
        try {
            PutObjectResult result = ossClient.putObject(bucketName, objectName, inputStream);
            if (StringUtil.isNotEmpty(result.getETag())) {
                String newUrl = StrUtil.format("https://{}.{}/{}",bucketName,ossConfig.getEndpoint(),objectName);
                return newUrl;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return null;
    }

    private String getBucketName() {
        if ("prod".equals(active)) {
            return "prod-public";
        } else {
            return "test-public";
        }
    }
}
7.2 OssConfig
@Data
@Component
@ConfigurationProperties(prefix="aliyun-oss")
public class OssConfig {
    private String endpoint;
    private String accessKeyId;
    private String accessKeySecret;
    private String roleArn;
    private Long durationSeconds;
    private String bucketName;
}
7.3 loadImage
/**
     * 上传图片到OSS
     *
     * @param originUrl:可访问的待上传图片的全地址
     * @param memberId
     * @return 上传到OSS的新地址
     */
    public String loadImage(String originUrl, Long memberId) {
        try {
            String[] fileNameArray = originUrl.split("/");
            String fileName = fileNameArray[fileNameArray.length - 1];
            if (fileName.contains("?")) {
                fileName = fileName.split("\\?")[0];
            }

            byte[] byteArray = Jsoup.connect(originUrl).validateTLSCertificates(false).ignoreContentType(true).maxBodySize(Integer.MAX_VALUE).execute().bodyAsBytes();

            InputStream inputStream = new ByteArrayInputStream(byteArray);
            return ossComponent.uploadByInputStream(inputStream, memberId, fileName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,137评论 6 511
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,824评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,465评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,131评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,140评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,895评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,535评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,435评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,952评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,081评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,210评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,896评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,552评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,089评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,198评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,531评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,209评论 2 357

推荐阅读更多精彩内容