一、解析XML
1、简介
在日常开发中常见的XML解析方式有如下两种:
DOM解析
DOM解析要求解析器将整个XML文件全部加载到内存中,生成一个Document对象。
1.优点:元素和元素之间保留结构,关系,可以针对元素进行增删改查操作。
2.缺点:如果XML文件过大,可能会导致内存溢出。
SAX解析
SAX解析是一种更加高效的解析方式。它是逐行扫描,边扫描边解析,并且以时间驱动的方式进行具体的解析,每解析一行都会触发一个事件。
1.优点:不会出现内存溢出的问题,可以处理大文件。
2.缺点:只能读,不能写。
2、常见的解析XML类库
解析器就是根据不同的解析方式提供具体的实现,为了方便开发人员来解析XML,有一些方便操作的类库。具体如下所示:
1.dom4j:比较简单的XML解析类库;
2.Jsoup:功能强大的DOM方式解析的类库,尤其对HTML的解析更加方便,所以可以使用Jsoup来爬取网页的数据。
3、命名空间
XML 命名空间提供避免元素命名冲突的方法。
(1)命名冲突
在 XML 中,元素名称是由开发者定义的,当两个不同的文档使用相同的元素名时,就会发生命名冲突。
这个 XML 携带 HTML 表格的信息:
<table>
<tr>
<td>Apples</td>
<td>Bananas</td>
</tr>
</table>
这个 XML 文档携带有关桌子的信息(一件家具):
<table>
<name>African Coffee Table</name>
<width>80</width>
<length>120</length>
</table>
假如这两个 XML 文档被一起使用,由于两个文档都包含带有不同内容和定义的 <table>
元素,就会发生命名冲突。
XML 解析器无法确定如何处理这类冲突。
(2)使用前缀来避免命名冲突
在 XML 中的命名冲突可以通过使用名称前缀从而容易地避免。
该 XML 携带某个 HTML 表格和某件家具的信息:
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
在上面的实例中,不会有冲突,因为两个 <table>
元素有不同的名称。
(3)XML 命名空间 - xmlns 属性
当在 XML 中使用前缀时,一个所谓的用于前缀的命名空间必须被定义。
命名空间是在元素的开始标签的 xmlns 属性中定义的。
命名空间声明的语法如下。xmlns:前缀=”URI“。
<root>
<h:table xmlns:h="http://www.w3.org/TR/html4/">
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table xmlns:f="http://www.w3cschool.cc/furniture">
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
在上面的实例中,<table>
标签的 xmlns 属性定义了 h: 和 f: 前缀的合格命名空间。
当命名空间被定义在元素的开始标签中时,所有带有相同前缀的子元素都会与同一个命名空间相关联。
命名空间,可以在他们被使用的元素中或者在 XML 根元素中声明:
<root xmlns:h="http://www.w3.org/TR/html4/"
xmlns:f="http://www.w3cschool.cc/furniture">
<h:table>
<h:tr>
<h:td>Apples</h:td>
<h:td>Bananas</h:td>
</h:tr>
</h:table>
<f:table>
<f:name>African Coffee Table</f:name>
<f:width>80</f:width>
<f:length>120</f:length>
</f:table>
</root>
注释:命名空间 URI 不会被解析器用于查找信息。
其目的是赋予命名空间一个惟一的名称。不过,很多公司常常会作为指针来使用命名空间指向实际存在的网页,这个网页包含关于命名空间的信息。
二、dom4j
1、使用步骤
(1)依赖
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
</dependency>
(2)XML文件
在项目的 resource
目录下创建 user.xml
文件;
在下面 user.xml
文件中,users
是根标签,根标签是全局唯一的;
在根标签下有两个 user
子标签,每一个 user
子标签都有两个属性,一个是 country
,另一个是 source
;在 user
标签下同样有三个子标签,分别是id,name以及password标签,具体如下所示:
<?xml version="1.0" encoding="UTF-8" ?>
<!--文档声明
XML的文档声明是可选的,也就是可以不写,但是日常生活开发中大家都会写
XML文档声明如果写了,它必须放在XML文档的第一行第一列,必须以<?xml开头 以?>结尾,而且必须包含两个属性
一个是version,表示XML的版本
一个是encoding,表示XML的编码
-->
<!--
元素是XML的重要组成部分,元素也被称为标签
每个XML文件必须要有一个根标签
标签有开始标签和结束标签组成,开始标签和结束标签可以写标签,也可以是文本字符串
标签可以嵌套使用,但是不能随便嵌套
标签名必须准守命名规则和命名规范
-->
<!--
属性是标签的组成部分,属性只能定义在开始标签中,不能定义在结束标签中
属性定义的格式:属性名=属性值,属性值需要使用""包含起来
开始标签中可以定义多个属性,但是多个属性的属性名不能相同
属性名必须准守命名规则和命名规范
-->
<users>
<user id="10001" country="Chinese" source="Android">
<id>10001</id>
<name>admin</name>
<password>111111</password>
</user>
<user id="10002" country="Chinese" source="ios">
<id>10002</id>
<name>tony</name>
<password>666666</password>
</user>
</users>
(3)获取document对象
//创建解析器对象
SAXReader saxReader = new SAXReader();
//DOMReader domReader = new DOMReader();
//根据user.xml文档生成Document对象
Document document = saxReader.read(DomTest.class.getClassLoader().getResource("xml/user.xml"));
read()
方法有下面几种重载方式
public Document read(File file){}
public Document read(URL url){}
public Document read(String systemId){}
public Document read(InputStream in){}
public Document read(Reader reader){}
public Document read(InputStream in, String systemId){}
public Document read(Reader reader, String systemId){}
public Document read(InputSource in){}
systemId:是一个 URL 或文件名,如果systemId包含一个’:’字符,那么它被认为是一个URL,否则它被认为是一个文件名。如果你想要对这个机制进行更细粒度的控制,那么请显式地传递一个URL或者一个文件对象而不是一个String来表示文档的源。
(4)Document API
Dom4j的常用API说明:
方法 | 操作 |
---|---|
Element getRootElement(); |
获取XML文件的根节点 |
String getName(); |
返回标签的名称 |
List<Element> elements(); |
获取标签所有的子标签 |
String arrtributeVallue(String name); |
获取指定属性名称的属性值 |
String getText(); |
获取标签的文本 |
String elementText(String name); |
获取指定名称的子标签的文本,返回子标签文本的值 |
示例代码
//创建解析器对象
SAXReader saxReader = new SAXReader();
//DOMReader domReader = new DOMReader();
//根据user.xml文档生成Document对象
Document document = saxReader.read(DomTest.class.getClassLoader().getResource("xml/user.xml"));
// 获取根标签
Element rootElement = document.getRootElement();
// 获取根标签的所有子标签
List<Element> elements = rootElement.elements();
for (Element element : elements) {
System.out.println("标签名称: " + element.getName());
System.out.println("标签内容: " + element.getText());
System.out.println("标签id属性值: " + element.attributeValue("id"));
}
// 获取根标签下的第一个user标签
Element userElement = rootElement.element("user");
System.out.println(userElement);
2、结合 XPath
XPath 可以使用 路径表达式 来选取 XML 文档中的元素或者属性节点,节点是沿着路径来选取的
(1)依赖
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.2.0</version>
</dependency>
(2)Dom4j提供基于XPath的API
方法 | 操作 |
---|---|
Node selectSingleNode(String xpathExpression); |
根据XPath表达式获取单个标签(元素/节点) |
List<Node> selectNodes(String xpathExpression) |
根据XPath表达式获取多个标签(元素/节点) |
(3)示例代码
// 获取根标签下的第一个user标签
Element userElement = rootElement.element("user");
System.out.println(userElement);
// 绝对路径获取标签,如果有多个,默认返回第一个
Node node = rootElement.selectSingleNode("/users/user/name");
System.out.println(node.getText());
// 相对径获取标签
Node node2 = rootElement.selectSingleNode("./user/name");
System.out.println(node2.getText());
// 全文搜索获取标签
Node node3 = rootElement.selectSingleNode("/users//name");
System.out.println(node3.getText());
// 条件搜索获取标签
Node node4 = rootElement.selectSingleNode("//user[@id=10002]");
Node nameNode = node4.selectSingleNode("./name");
System.out.println(nameNode.getText());
(4)路径表达式
表达式 | 示例 | 描述 |
---|---|---|
nodename | users | 选取users节点的所有子节点 |
/ | /users/user | 从根节点开始选取(绝对路径)每层只取子节点 |
// | /users//user | 选取users下的所有user节点,包括孙子节点 |
. | 选取当前节点 | |
.. | 选取当前节点的父节点 | |
@ | //@lang | 选取属性,选取所有拥有 lang 属性的元素 |
(5)谓语
谓语用来查找某个特定的节点或者包含某个指定的值的节点。
谓语被嵌在方括号中。
路径表达式 | 结果 |
---|---|
/bookstore/book[1] | 选取属于 bookstore 子元素的第一个 book 元素。 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后一个 book 元素。 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第二个 book 元素。 |
/bookstore/book[position()<3] | 选取最前面的两个属于 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 选取所有拥有名为 lang 的属性的 title 元素。 |
//title[@lang=’eng’] | 选取所有 title 元素,要求这些元素拥有值为 eng 的 lang 属性。 |
//title[@lang!=’eng’] | 选取所有 title 元素,要求这些元素拥有值为 eng 不为 lang 属性。 |
//title[not(@id)] | 选取id属性不存在的title |
/bookstore/book[price>35.00] | 选取所有 bookstore 元素的 book 元素,要求book元素的子元素 price 元素的值须大于 35.00。 |
/bookstore/book[price>35.00]/title | 选取所有 bookstore 元素中的 book 元素的 title 元素,要求book元素的子元素 price 元素的值须大于 35.00 |
(6)选取未知节点(通配符)
XPath 通配符可用来选取未知的 XML 元素。
通配符 | 描述 |
---|---|
* | 匹配任何元素节点 |
@* | 匹配任何属性节点 |
node() | 匹配任何类型的节点 |
实例
路径表达式 | 结果 |
---|---|
/bookstore/* | 选取 bookstore 元素的所有子节点 |
//* | 选取文档中的所有元素 |
//title[@*] | 选取所有带有属性的 title 元素。 |
(7)选取若干路径(或)
通过在路径表达式中使用 ‘|’ 运算符,您可以选取若干个路径。
实例
路径表达式 | 结果 |
---|---|
//book/title | //book/price | 选取所有 book 元素的 title 和 price 元素。 |
//title | //price | 选取所有文档中的 title 和 price 元素。 |
/bookstore/book/title|//price | 选取所有属于 bookstore 元素的 book 元素的title 元素,以及文档中所有的 price 元素。 |
(8)XPath 轴
轴可定义某个相对于当前节点的节点集。
轴名称 | 结果 |
---|---|
ancestor | 选取当前节点的所有先辈(父、祖父等) |
ancestor-or-self | 选取当前节点的所有先辈(父、祖父等)以及当前节点本身 |
attribute | 选取当前节点的所有属性 |
child | 选取当前节点的所有子元素。 |
descendant | 选取当前节点的所有后代元素(子、孙等)。 |
descendant-or-self | 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。 |
following | 选取文档中当前节点的结束标签之后的所有节点。 |
namespace | 选取当前节点的所有命名空间节点 |
parent | 选取当前节点的父节点。 |
preceding | 选取文档中当前节点的开始标签之前的所有节点。 |
preceding-sibling | 选取当前节点之前的所有同级节点。 |
self | 选取当前节点。 |
(9)路径
位置路径可以是绝对的,也可以是相对的。
绝对路径起始于正斜杠( / ),而相对路径不会这样。在两种情况中,位置路径均包括一个或多个步,每个步均被斜杠分割:
/step/step/...
step/step/...
每个步均根据当前节点集之中的节点来进行计算。
轴(axis):定义所选节点与当前节点之间的树关系
节点测试(node-test):识别某个轴内部的节点
零个或者更多谓语(predicate):更深入地提炼所选的节点集
步的语法:轴名称::节点测试[谓语]
实例
例子 | 结果 |
---|---|
child::book | 选取所有属于当前节点的子元素的 book 节点 |
attribute::lang | 选取当前节点的 lang 属性 |
child::* | 选取当前节点的所有子元素 |
attribute::* | 选取当前节点的所有属性 |
child::text() | 选取当前节点的所有文本子节点 |
child::node() | 选取当前节点的所有子节点 |
descendant::book | 选取当前节点的所有 book 后代 |
ancestor::book | 选择当前节点的所有 book 先辈 |
ancestor-or-self::book | 选取当前节点的所有book先辈以及当前节点(假如此节点是book节点的话) |
child::*/child::price | 选取当前节点的所有 price 孙。 |
(10)XPath 运算符
运算符 | 描述 | 实例 | 返回值 |
---|---|---|---|
| | 计算两个节点集 | //book | //cd | 返回所有带有 book 和 ck 元素的节点集 |
+ | 加法 | 6 + 4 | 10 |
- | 减法 | 6 - 4 | 2 |
* | 乘法 | 6 * 4 | 24 |
div | 除法 | 8 div 4 | 2 |
= | 等于 | price=9.80 | 如果 price 是 9.80,则返回 true。如果 price 是 9.90,则返回 fasle。 |
!= | 不等于 | price!=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 fasle。 |
< | 小于 | price<9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 fasle。 |
<= | 小于或等于 | price<=9.80 | 如果 price 是 9.00,则返回 true。如果 price 是 9.90,则返回 fasle。 |
> | 大于 | price>9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.80,则返回 fasle。 |
>= | 大于或等于 | price>=9.80 | 如果 price 是 9.90,则返回 true。如果 price 是 9.70,则返回 fasle。 |
or | 或 | price=9.80 or price=9.70 | 如果 price 是 9.80,则返回 true。如果 price 是 9.50,则返回 fasle。 |
and | 与 | price>9.00 and price<9.90 | 如果 price 是 9.80,则返回 true。如果 price 是 8.50,则返回 fasle。 |
mod | 计算除法的余数 | 5 mod 2 | 1 |
3、生成 XML 文件
DocumentHelper
:是用来生成生成 XML 文档的工厂类
// 新建 document 对象,来操作和生成 xml
Document document = DocumentHelper.createDocument();
// 创建 users 节点
Element userElement = document.addElement("user");
// 添加注释
userElement.addComment("users节点注释");
// 添加属性
userElement.addAttribute("id", "1");
// 添加子节点
Element nameElement = userElement.addElement("name");
// 设置节点内容
nameElement.setText("张三");
String fileName = "c:/user2.xml";
XMLWriter xmlWriter = null;
try {
xmlWriter = new XMLWriter(new FileWriter(fileName));
xmlWriter.write(document);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (xmlWriter != null){
try {
xmlWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件内容
<?xml version="1.0" encoding="UTF-8"?>
<user id="1"><!--users节点注释--><name>张三</name></user>
4、修改节点属性
Attribute::setValue
设置属性值Element::setText
设置节点内容Element::remove
删除节点
public static void modifyXMLFile() {
String oldStr = "c:/text.xml";
String newStr = "c:/text1.xml";
Document document = null;
//修改节点的属性
try {
// 用来读取xml文档
SAXReader saxReader = new SAXReader();
// 读取xml文档
document = saxReader.read(new File(oldStr));
// 用xpath查找节点book的属性
List list = document.selectNodes("/books/book/@show");
Iterator iter = list.iterator();
while (iter.hasNext()) {
Attribute attribute = (Attribute) iter.next();
if (attribute.getValue().equals("yes"))
attribute.setValue("no");
}
} catch (Exception e) {
e.printStackTrace();
}
//修改节点的内容
try {
// 用来读取xml文档
SAXReader saxReader = new SAXReader();
// 读取xml文档
document = saxReader.read(new File(oldStr));
// 用xpath查找节点book的内容
List list = document.selectNodes("/books/book/title");
Iterator iter = list.iterator();
while (iter.hasNext()) {
Element element = (Element) iter.next();
element.setText("xxx");// 设置相应的内容
}
} catch (Exception e) {
e.printStackTrace();
}
try {
XMLWriter writer = new XMLWriter(new FileWriter(new File(newStr)));
writer.write(document);
writer.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
5、编码转换
- 文档中全为英文,不设置编码,直接写入
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"));
writer.write(document);
writer.close();
- 文档中含有中文,设置编码格式再写入
OutputFormat format = OutputFormat.createPrettyPrint();
format.setEncoding("GBK"); // 指定XML编码
XMLWriter writer = new XMLWriter(new FileWriter("output.xml"),format);
writer.write(document);
writer.close();
6、字符串与XML的转换
将字符串转化为XML
String text = "<members><member>sitinspring</member></members>";
Document document = DocumentHelper.parseText(text);
将XML转化为字符串.
SAXReader reader = new SAXReader();
Document document = reader.read(new File("input.xml"));
Element root = document.getRootElement();
String docXmlText = document.asXML();
String rootXmlText = root.asXML();
Element memberElm = root.element("member");
String memberXmlText = memberElm.asXML();
7、事件模型
(1)SAXReader类
只有 SAXReader
才有事件驱动,是逐行扫描,边扫描边解析,并且以时间驱动的方式进行具体的解析,每解析一行都会触发一个事件。
当解析到 path
指定的路径时,将调用参数handler指定的处理器。针对不同的节点可以添加多个handler实例。或者调用默认的 Handler setDefaultHandler(ElementHandler handler);
public class SAXReader {
// 为指定路径的标签添加处理器
public void addHandler(String path, ElementHandler handler){}
// 删除指定路径标签的处理器
public void removeHandler(String path){}
// 清空所有处理器
public void resetHandlers(){}
}
示例代码
//创建解析器对象
SAXReader saxReader = new SAXReader();
saxReader.removeHandler("/users/user/name");
saxReader.resetHandlers();
(2)ElementHandler接口
onStart()
该方法在解析到元素的开始标签时被调用。onEnd()
该方法在解析到元素的结束标签时被调用
//创建解析器对象
SAXReader saxReader = new SAXReader();
saxReader.addHandler("/users/user/name", new ElementHandler() {
/**
* 解析到元素的开始标签时被调用
* 注意:开始标签中无法读取到标签内容
*/
@Override
public void onStart(ElementPath elementPath) {
System.out.println("start ...");
// 输出为空
System.out.println(elementPath.getCurrent().getText());
}
/**
* 解析到元素的结束标签时被调用
*/
@Override
public void onEnd(ElementPath elementPath) {
System.out.println("end ...");
System.out.println(elementPath.getCurrent().getText());
// 获取当前元素
Element current = elementPath.getCurrent();
// 指定深度索引处的元素,0=根元素,users
Element rootElement = elementPath.getElement(0);
// 获取当前节点的路径 /users/user/name
String path = elementPath.getPath();
// 同 SAXReader
//elementPath.removeHandler();
//elementPath.addHandler();
}
});
//根据user.xml文档生成Document对象
Document document = saxReader.read(Test1.class.getClassLoader().getResource("xml/users.xml"));
(3)Element API
方法 | 说明 |
---|---|
getQName() |
元素的QName对象 |
getNamespace() |
元素所属的Namespace对象 |
getNamespacePrefix() |
元素所属的Namespace对象的prefix |
getNamespaceURI() |
元素所属的Namespace对象的URI |
getName() |
元素的local name |
getQualifiedName() |
元素的qualified name |
getText() |
元素所含有的text内容,如果内容为空则返回一个空字符串而不是null |
getTextTrim() |
元素所含有的text内容,其中连续的空格被转化为单个空格,该方法不会返回null |
attributeIterator() |
元素属性的iterator,其中每个元素都是Attribute对象 |
attributeValue() |
元素的某个指定属性所含的值 |
elementIterator() |
元素的子元素的iterator,其中每个元素都是Element对象 |
element() |
元素的某个指定(qualified name或者local name)的子元素 |
elementText() |
元素的某个指定(qualified name或者local name)的子元素中的text信息 |
getParent() |
元素的父元素 |
getPath() |
元素的XPath表达式,其中父元素的qualified name和子元素的qualified name之间使用”/“分隔 |
isTextOnly() |
是否该元素只含有text或是空元素 |
isRootElement() |
是否该元素是XML树的根节点 |
三、jsoup
1、使用步骤
(1)依赖
<!--解析网页-->
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.10.2</version>
</dependency>
(2)获取Document对象
// 参数1: html 文本
// 参数2: 文档基础 uri,用于组合解析html文本中的相对路径
public static Document parse(String html, String baseUri){}
public static Document parse(String html){}
public static Document parse(File in, String charsetName, String baseUri){}
public static Document parse(File in, String charsetName){}
public static Document parse(InputStream in, String charsetName, String baseUri){}
(3)API
/**
* 1. 根据id查询元素getElementById
* 2. 根据标签获取元素getElementsByTag
* 3. 根据class获取元素getElementsByClass
* 4. 根据属性获取元素getElementsByAttribute
*
* Elements skuEles = spuEle.select("li.ps-item");
* String picUrl ="https:"+ skuEle.select("img[data-sku]").first().attr("data-lazy-img");
*/
String path = ItCastSpidder.class.getClassLoader().getResource("index.html").getPath();
Document document = Jsoup.parse(new File(path), "UTF-8");
//1. 获取title的内容
Element element = document.getElementById("city_bj");
//2. 根据标签获取元素getElementsByTag
element = document.getElementsByTag("title").first();
//3. 根据class获取元素getElementsByClass
element = document.getElementsByClass("s_name").last();
//4. 根据属性获取元素getElementsByAttribute
element = document.getElementsByAttribute("abc").first();
element = document.getElementsByAttributeValue("class", "city_con").first();
//tagname: 通过标签查找元素,比如:span
Elements span = document.select("span");
for (Element element1 : span) {
System.out.println(element1.text());
}
//#id: 通过ID查找元素,比如:#city_bjj
String str = document.select("#city_bj").text();
//.class: 通过class名称查找元素,比如:.class_a
str = document.select(".class_a").text();
System.out.println(".class_a" + str);
//[attribute]: 利用属性查找元素,比如:[abc]
str = document.select("[abc]").text();
System.out.println("[abc]" + str);
//[attr=value]: 利用属性值来查找元素,比如:[class=s_name]
str = document.select("[class=s_name]").text();
System.out.println("[class=s_name]" + str);
//el#id: 元素+ID,比如: h3#city_bj
str = document.select("h3#city_bj").text();
System.out.println("[h3#city_bj]" + str);
//el.class: 元素+class,比如: li.class_a
str = document.select("li.class_a").text();
System.out.println("[li.class_a]" + str);
//el[attr]: 元素+属性名,比如: span[abc]
str = document.select("span[abc]").text();
System.out.println("[span[abc]]" + str);
//任意组合,比如:span[abc].s_name
str = document.select("span[abc].s_name").text();
System.out.println("[span[abc].s_name]" + str);
//ancestor child: 查找某个元素下子元素,比如:.city_con li 查找"city_con"下的所有li
str = document.select(".city_con li").text();
System.out.println("[.city_con li]" + str);
//parent > child: 查找某个父元素下的直接子元素,
//比如:.city_con > ul > li 查找city_con第一级(直接子元素)的ul,再找所有ul下的第一级li
str = document.select(".city_con > ul > li").text();
System.out.println("[.city_con > ul > li]" + str);
//parent > * 查找某个父元素下所有直接子元素.city_con > *
str = document.select(".city_con > *").text();
System.out.println(".city_con > *" + str);
(4)示例代码
public List<Content> goods(String keyword) throws IOException {
//获取请求
String url= "https://search.jd.com/Search?keyword="+keyword+"&enc=utf-8";
//解析网页
Document document = Jsoup.parse(new URL(url), 30000);
Element element = document.getElementById("J_goodsList");
//获取所有的li元素
Elements li = element.getElementsByTag("li");
ArrayList<Content> goodsList = new ArrayList<>();
for (Element el : li) {
//获取每一个li标签下所有的img标签下的第一个元素,属性为src
String img = el.getElementsByTag("img").eq(0).attr("data-lazy-img");
//获取每一个li标签下所有的p-price标签下的第一个元素,并转换为文档
String price = el.getElementsByClass("p-price").eq(0).text();
String title = el.getElementsByClass("p-name").eq(0).text();
// 封装获取的数据
Content content = new Content();
content.setTitle(title);
content.setImg(img);
content.setPrice(price);
//将封装的好的对象放入list集合
goodsList.add(content);
}
return goodsList;
}
2、结合XPath
(1)依赖
<dependency>
<groupId>cn.wanghaomiao</groupId>
<artifactId>JsoupXpath</artifactId>
<version>2.2</version>
</dependency>
(2)示例代码
String path = JsoupDemo1.class.getClassLoader().getResource("student.xml").getPath();
Document document = Jsoup.parse(new File(path),"utf-8");
// 旧版 package cn.wanghaomiao.xpath.model;
// JXDocument jxDocument = new JXDocument(document);
// 新版 package org.seimicrawler.xpath;
JXDocument jxDocument = JXDocument.create(document);
//获取dom树种的student标签的内容
List<JXNode> jxNodes = jxDocument.selN("//student");
for (JXNode jxNode : jxNodes) {
System.out.println(jxNode);
}
System.out.println("-----------------");
//获取number属性的值 @获取属性值
List<JXNode> nodes = jxDocument.selN("//@number");
for (JXNode node : nodes) {
System.out.println(node);
}