java爬虫--jsoup的使用

简介:

jsoup 是一款基于 Java 的HTML解析器,它提供了一套非常省力的API,不但能直接解析某个URL地址、HTML文本内容,而且还能通过类似于DOM、CSS或者jQuery的方法来操作数据,所以 jsoup 也可以被当做爬虫工具使用。

 

Document :文档对象。每份HTML页面都是一个文档对象,Document 是 jsoup 体系中最顶层的结构。
Element:元素对象。一个 Document 中可以着包含着多个 Element 对象,可以使用 Element 对象来遍历节点提取数据或者直接操作HTML。
Elements:元素对象集合,类似于List<Element>。
Node:节点对象。标签名称、属性等都是节点对象,节点对象用来存储数据。
一般执行流程:先获取 Document 对象,然后获取 Element 对象,最后再通过 Node 对象获取数据。

 

获取文档(Document)

获得文档对象 Document 一共有4种方法,分别对应不同的获取方式:

正式开始之前,我们需要导入有关 jar 包。

<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.15.1</version>
</dependency>

方式一:从URL中加载文档对象(常用)

Document document = Jsoup.parse(new URL(path), 30000);

方式二:从字符串文本中加载文档对象(就是将已知的html页面转换成string字符串,然后加载成document对象然后进行操作)

使用静态的Jsoup.parse(String html) 方法可以从字符串文本中获得文档对象 Document ,示例代码:

String html = "<html><head><title>First parse</title></head>"
+ "<body><p>Parsed HTML into a doc.</p></body></html>";

Document doc = Jsoup.parse(html);
System.out.println(doc);

方式三:从本地文件中加载文档对象(也就是从已经获得的本地的html页面加载document对象)

Jsoup.parse(File in, String charsetName)其中in表示路径,charsetName表示编码方式,示例代码:

File input = new File("/tmp/input.html");
Document doc = Jsoup.parse(input, "UTF-8");
System.out.println(doc);

方式四:从<body>片断中获取文档对象

Jsoup.parseBodyFragment(String html)方法.示例代码:

String html = "<p>Lorem ipsum.</p>";

// parseBodyFragment 方法创建一个新的文档,并插入解析过的HTML到body元素中
Document doc = Jsoup.parseBodyFragment(html);
// doc 此时为:<body> <p>Lorem ipsum.</p></body>

Element body = doc.body();
System.out.println(body);

parseBodyFragment 方法创建一个新的文档,并插入解析过的HTML到body元素中。假如你使用正常的 Jsoup.parse(String html) 方法,通常也能得到相同的结果,但是明确将用户输入作为 body 片段处理是个更好的方式。

Document.body() 方法能够取得文档body元素的所有子元素,与 doc.getElementsByTag("body")相同。

选择元素(Element)

解析文档对象并获取数据一共有 2 种方式,分别为 DOM方式、CSS选择器方式,我们可以选择一种自己喜欢的方式去获取数据,效果一样。

DOM方式

查找元素

getElementById(String id):通过id来查找元素
getElementsByTag(String tag):通过标签来查找元素
getElementsByClass(String className):通过类选择器来查找元素
getElementsByAttribute(String key) :通过属性名称来查找元素,例如查找带有href元素的标签。
siblingElements():获取兄弟元素。如果元素没有兄弟元素,则返回一个空列表。
firstElementSibling():获取第一个兄弟元素。
lastElementSibling():获取最后一个兄弟元素。
nextElementSibling():获取下一个兄弟元素。
previousElementSibling():获取上一个兄弟元素。
parent():获取此节点的父节点。
children():获取此节点的所有子节点。
child(int index):获取此节点的指定子节点。

获取元素数据

attr(String key):获取单个属性值
attributes():获取所有属性值
attr(String key, String value):设置属性值
text():获取文本内容
text(String value):设置文本内容
html():获取元素内的HTML内容
html(String value):设置元素内的HTML内容
outerHtml():获取元素外HTML内容
data():获取数据内容(例如:script和style标签)
id():获得id值(例:<p id="goods">衣服</p>)
className():获得第一个类选择器值
classNames():获得所有的类选择器值
tag():获取元素标签
tagName():获取元素标签名(如:<p>、<div>等)

操作HTML文本:

  • append(String html):在末尾追加HTML文本
  • prepend(String html):在开头追加HTML文本
  • html(String value):在匹配元素内部添加HTML文本。

CSS选择器方式

可以使用类似于CSS选择器的语法来查找和操作元素,常用的方法为select(String selector)如下:

 

Document doc = Jsoup.connect("http://csdn.com").get();

// 获取带有 href 属性的 a 元素
Elements elements = doc.select("a[href]");

for (Element content : elements) {
String linkHref = content.attr("href");
String linkText = content.text();
System.out.print(linkText + "\t");
System.out.println(linkHref);
}

select()方法在DocumentElementElements对象中都可以使用,而且是上下文相关的,因此可实现指定元素的过滤,或者采用链式访问。

select() 方法将返回一个Elements集合,并提供一组方法来抽取和处理结果。

select(String selector)方法参数简介(下面的为此方法中的参数类型):

tagname: 通过标签查找元素,例如通过"a"来查找<a>标签,类似getElementsByTag("标签名")。
#id: 通过ID查找元素,比如通过#logo查找<p id="logo">,类似getElementById(String id)。
.class: 通过class名称查找元素,比如通过.titile查找<p class="titile">,类似getElementsByClass(String className)。
ns|tag: 通过标签在命名空间查找元素,比如使用 fb|name 来查找 <fb:name> 。
[attribute]: 利用属性查找元素,比如通过[href]查找<a href="...">。
[^attribute]: 利用属性名前缀来查找元素,比如:可以用[^data-] 来查找带有HTML5 dataset属性的元素。
[attribute=value]: 利用属性值来查找元素,比如:[width=500]。
[attribute^=value], [attribute$=value], [attribute*=value]: 利用匹配属性值开头、结尾或包含属性值来查找元素,比如通过[href*=/path/]来查找<a href="a/path/c.html">。
[attribute~=regex]: 利用属性值匹配正则表达式来查找元素,比如通过 img[src~=(?i)\.(png|jpe?g)]来匹配所有的png或者jpg、jpeg格式的图片。
*: 通配符,匹配所有元素。

获取数据(Node)

在获得文档对象并且指定查找元素后,我们就可以获取元素中的数据。
这些访问器方法都有相应的setter方法来更改数据。

.attr(String key) :获得属性的值。
.text():获得元素中的文本。
.html():获得元素或属性内部的HTML内容(不包括本身)。
.outerHtml():获得元素或属性完整的HTML内容。
.id():获得元素id属性值。
className():获得元素类选择器值。
.tagName():获得元素标签命名。
.hasClass(String className):检查这个元素是否含有一个类选择器(不区分大小写)。

其他操作:

消除不受信任的HTML (防止XSS攻击)
问题描述:在某些网站中经常会提供用户评论的功能,但是有些不怀好意的用户,会搞一些脚本到评论内容中,而这些脚本可能会破坏整个页面的行为,更严重的是获取一些机要信息,此时需要清理该HTML,以避免跨站脚本攻击(XSS)。

解决方式:

使用clean()方法清除恶意代码,但需要指定一个配置的 Safelist(旧版本中是Whitelist),通常使用Safelist.basic()即可。Safelist的工作原理是将输入的 HTML 内容单独隔离解析,然后遍历解析树,只允许已知的安全标签和属性输出。

String unsafe =
"<p><a href='http://csdn.com/' οnclick='attack()'>Link</a></p>";
// 输出: <p><a href="http://csdn.com/" >Link</a></p>
String safe = Jsoup.clean(unsafe, Safelist.basic());
System.out.println(safe);

说明:jsoup的Safelist不仅能够在服务器端对用户输入的HTML进行过滤,只输出一些安全的标签和属性,还可以限制用户可以输入的标签范围。

 

具体示例如下:

package com.demo;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.util.ObjectUtils;

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

/**
* @Author sun
* @Date 2021/6/28 11:14
*/
public class HtmlParsesUtil {
//从京东爬虫----有可能从数据库中。
public static List<Product> parseJd(String keyword) throws Exception {
String path = "https://search.jd.com/Search?keyword=" + keyword;
//Document整个网页对象
Document document = Jsoup.parse(new URL(path), 30000);
Element j_goodsList = document.getElementById("J_goodsList");
Elements li = j_goodsList.getElementsByTag("li");
List<Product> list = new ArrayList<Product>();
for (Element element : li) {
//爬取商品价格
String pprice = element.getElementsByClass("p-price").eq(0).text();
//爬取商品信息
String pname = element.getElementsByClass("p-name").eq(0).text();
//爬取商品图片地址
String img = element.getElementsByTag("img").eq(0).attr("data-lazy-img");
list.add(new Product(pname, pprice, img));
}
return list;
}

// 从网上爬取区域码
public static List<CodePo> parseGj(String keyword) throws Exception {
String path = "http://www.stats.gov.cn/sj/tjbz/tjyqhdmhcxhfdm/2016/" + keyword;
//Document整个网页对象
Document document = null;
try {
document = Jsoup.parse(new URL(path), 30000);
} catch (IOException e) {
return new ArrayList<>();
}
List<CodePo> list = new ArrayList<CodePo>();
if (keyword.contains("/")) {
Elements trs = document.getElementsByClass("countytr");
for (Element tr : trs) {
String code = "";
String name = "";
Elements tds = tr.getElementsByTag("td");
code = tds.get(0).text();
name = tds.get(1).text();
list.add(new CodePo(code, name));
}
} else {
Elements trs = document.getElementsByClass("citytr");
for (Element tr : trs) {
String code = "";
String name = "";
Elements tds = tr.getElementsByTag("td");
code = tds.get(0).text();
name = tds.get(1).text();
list.add(new CodePo(code, name));
}
}
return list;
}
}
posted @ 2023-06-29 18:12  一剑一叶一花  阅读(335)  评论(0编辑  收藏  举报