XML与Jsoup

1. XML概述

1.1 XML的概念

  • HTML:Hyper Text Markup Language 超文本标记语言,由各种标签组成。
  • XML: eXtensible Markup Language 可扩展标记语言

名词解释:

  • 可拓展: 标签名可以自定义。
  • 标记语言: 这门语言完全由标签构成的。

1.2 XML的作用

  1. 描述数据之间的关系。
  2. 用于不同系统之间的数据传输

1.3 XML 与HTML 的主要差异

区别 HTML XML
功能 制作网页,主要用于表示层 用于配置文件,传递数据
大小写 不区分大小写

区分大小写,是不同的
语法严谨 不严谨,如果一个标签有开头,没有结尾。
浏览器也可以解析。
严谨,标签开头和结尾必须严格配对
可扩展性 没有可扩展性,所有的标签都是固定好,
每个标签的功能固定。
所有的标签都是人为创造的,可以扩展。

2. XML文件组成

2.1 XML文件的组成元素

  1. 文档声明
  2. 标签元素Element
  3. 属性Attribute
  4. 注释Comment
  5. 转义字符(实体字符)
  6. 字符数据区(用于显示大量的特殊字符的时候)
  7. 处理指令(不常用)

2.2 文档声明

声明:
格式: 以开头,以?>结尾
位置:必须出现在XML 文件的第1 行

2.2.1 文档声明的三个属性

  • version: 用于指定XML 使用哪个版本,固定的写法1.0
  • enconding :指定当前XML 编码
  • standalone: yes/no 默认是yes,这个XML 文件是否是一个单独的文档

2.3 标签元素Element

  • 语法:<开头,>结尾, 中间是标签名;
  • 主体部分:分为有主体和无主体标签,主体部分可以包含文本或其它的子元素;
  • 空元素:无主体标签也必须要关闭;
  • 命名:不能有空格,不能有冒号,数字不能开头
  • 根元素:每个XML 文档必须有且只有一个根元素

2.4 属性Attribute

  • 属性位置:必须放在开始标签中;
  • 属性的值:必须使用单引号或双引号引起来
  • 在同一个标签中不能同时出现多个同名的属性
  • 命名中不能出现空格和冒号

2.5 注释Comment

  • 注意:不可嵌套

2.6 转义字符(实体字符)

特殊字符需要转义才能显示,常用的有

特殊字符 转义字符
< <
> >
"
'
& &
空格  

2.7 字符数据区

  • 字符区的内容xml 文件的解释器在解释的时候,全部的内容都会当成普通的文本处理,即使遇到了特殊的符号也会只当成普通的文本去处理。

  • 格式:

  • 定义:不由XML 解析器进行解析的纯文本数据
  • 不可嵌套

2.8 处理指令

  • 处理指令,简称PI(Processing instruction)用来指挥解析引擎如何解析XML 文档内容。

  • 格式: 开头,?>结尾

    eg:`

3. XML文件的约束

  • 作用:用于限制xml文件出现的元素范围
  • xml约束文件分类:

    • DTD 约束
    • Schema 约束
  • 一般不需要编写约束,直接引入已编写完

3.1 DTD约束

3.1.1 概念

  • 定义:Document Type Definition 文档类型定义
  • 作用:用来约束XML 文件的,它本身是一个文本文件

3.1.2 导入格式

导入DTD文件方式 描述
系统的DTD 文件,使用范围比较小,一般用于公司,不对外开放的DTD 文件
公共的DTD 文件,用于互联网上,使用广泛的用途
xml 文件与dtd 文件混合在一起。

3.1.3 DTD文件写法(理解)

基本写法如下

<!ELEMENT 书架 (书+)>  
        <!ELEMENT 书 (书名,作者,售价)>  
        <!ELEMENT 书名 (#PCDATA)> 
        <!ELEMENT 作者 (#PCDATA)>
        <!ELEMENT 售价 (#PCDATA)>
导入方法1


<书架>
    <>
        <书名>哈利波特书名>
        <作者>JK罗琳作者>
        <售价>49.9售价>
    >
书架>
导入方法2


        
        
        
        ]>
<书架>
    <>
        <书名>哈利波特书名>
        <作者>JK罗琳作者>
        <售价>49.9售价>
    >
书架>

3.6 Schema约束

  • xml不能判断较多的数据类型,功能比较简单

  • 而Schema功能更加强大,数据类型约束更完善,可取代DTD

3.6.1 Schema约束的使用

  1. 新建schema 约束文件(固定格式)

    
    <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
              targetNamespace="名称空间"
              elementFormDefault="qualified">
       <xs:element name='根元素' >
           <xs:complexType>
               <xs:sequence maxOccurs='unbounded' >
                   <xs:element name='子元素' >
                       <xs:complexType>
                           <xs:sequence>
                               <xs:element name='子元素下的子元素1' type='xs:string' />
                               <xs:element name='子元素下的子元素2' type='xs:string' />
                           xs:sequence>
                       xs:complexType>
                   xs:element>
               xs:sequence>
           xs:complexType>
       xs:element>
    xs:schema>
  2. 使用schema文件创建xml文件:

    <根元素 xmlns="名称空间" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="名称空间 xsd约束文件名">
    根元素>
注意事项
  1. 名称空间(targetNamespace): 任何schema 文件都有一个名称空间,schema 名称空间是一个xsd 文件的唯一标记。相当于一个id 号,引入的时候必须要使用名称空间进行引入。
  2. 一般工程不需要自行编写schema文件,只需要根据相关的约束文件编写所需的xml文件即可

3.6.2 示例代码

xsd文件:


<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.baidu.com" --名称空间-->
           elementFormDefault="qualified">
    <xs:element name='书架' >
        <xs:complexType>
            <xs:sequence maxOccurs='unbounded' >
                <xs:element name='书' >
                    <xs:complexType>
                        <xs:sequence>
                            <xs:element name='书名' type='xs:string' />
                            <xs:element name='作者' type='xs:string' />
                            <xs:element name='售价' type='xs:double' />
                        xs:sequence>
                    xs:complexType>
                xs:element>
            xs:sequence>
        xs:complexType>
    xs:element>
xs:schema>

xml文件:


<书架 xmlns="http://www.baidu.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.baidu.com books.xsd">
<>
    <书名>哈利波特书名>
    <作者>JK罗琳作者>
    <售价>49.9售价>
>
书架>

4. XML解析技术概述(重要)

4.1 XML解析的概述

  • 对xml文件解析,读取其中的数据

4.2 解析方式和解析器

4.2.1 三种解析方式

4.2.1.1 DOM
  • DOM:要求解析器把整个XML文档装载到内存,并解析成一个Document对象
  • 优点:元素与元素之间保留了结构关系,可以进行增删改查操作。
  • 缺点:因为整个XML文档被加载到内存中,如果XML过大,可能出现内存溢出
4.2.1.2 SAX
  • SAX:一种速度更快,更有效的方法。逐行扫描文档,一边扫描一边解析。并以事件驱动的方式进行具体解析,每执行一行,都触发对应的事件
  • 优点:处理速度快,可以处理大文件。
  • 缺点:只能读,逐行后将释放资源,解析操作繁琐。
4.2.1.3 PULL
  • Android内置的XML解析方式,原理与SAX相同

4.3 DOM常见的解析开发包

  • JAXP:Oracle公司提供支持DOM和SAX开发包

  • Dom4j:比较简单的的解析开发包,将整个XML加载到内存中做为一棵DOM树。

  • JDom:与Dom4j类似

  • Jsoup:功能强大DOM方式的XML解析开发包,同时对HTML解析也很方便。

5. Jsoup的基本使用(重要)

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

5.1 DOM解析原理

  • 与HTML的DOM编程相同。XML的DOM会将整个XML文档加载到内存,生成一个DOM树,并获得一个Document对象,通过Document对象就可以对DOM进行操作。
  • 与HTML相同,每个元素、属性和文本都是节点,类型为Node。而元素具有独有的类型:Element。注意,Element是Node的子类

5.2 获取Document文档对象三种方式

由于所有节点都封装到Document对象中,只要获得Document对象,就可以获取xml的所有节点

5.2.1 利用html代码获得(不常用)

- static Document parse(String html);
    ->将该html内容转换为一个document对象。
    ->参数String html是整个html文件的内容

☞如果html内容太多,使用该方式将非常不方便,一般很少使用

5.2.2 利用file文件获得

- static Document parse(File file,"码表");
    根据本地的资源文件生成document对象
5.2.2.1 步骤及注意事项
  1. 资源文件要求必须置于本模块的src目录下;
  2. 通过Class对象获得类路径,Class对象可以任意;
  3. 使用Class对象的getResource()方法获得类路径,类路径必须以/开头(可理解为当前src目录);
  4. 使用getPath()方法获得类路径对应字符串,并据此创建File对象;
  5. 根据File对象获得Document对象
5.2.2.2相关方法
- URL getResource("/xml文件名");
    根据文件名路径获得文件资源URL对象;
- String getPath();
    获得URL对象的真实路径
5.2.2.3示例代码
public class Demo02 {
    public static void main(String[] args) throws IOException {
        //获得类路径
        String path = Demo02.class.getResource("/person.xml").getPath();
        //创建File对象
        File file = new File(path);
        //生成document对象。
        Document document = Jsoup.parse(file, "UTF-8");
        System.out.println(document);
    }
}

5.2.3 利用url获取Document对象

- static org.jsoup.Connection connect(String url);
    通过URL字符串创建连接对象
- Document get();   通过连接对象返回一个Document对象
5.2.3.1 步骤
  1. 定义一个具体html网页的url;
  2. 调用connect()方法,创建一个连接对象;
  3. 调用连接对象的get()方法,获得Document对象
5.2.3.2 示例代码
public class Demo03 {
    public static void main(String[] args) throws IOException {
        //根据指定url获得Connection对象
        Connection connection = Jsoup.connect("http://baidu.com");
        //根据连接对象获得Document对象
        Document document = connection.get();
        System.out.println(document);
    }
}

5.3 根据标签属性获得节点

返回类型 Document对象的方法 说明
Element getElementById(String id) 通过id得到唯一元素对象
Element*s* getElementsByTag(String tagName) 通过标签名得到一组元素
Element*s* getElementsByClass(String className) 通过类名得到一组元素对象

5.3.1 示例代码

public class Demo01 {
    public static void main(String[] args) throws IOException {
        //获得Document对象
        Document document = Jsoup.parse(new File(Demo01.class.getResource("/index.html").getPath()),"utf-8" );
        //1.获取index.html中元素属性id="header"的一个元素并打印输出
        Element element = document.getElementById("header");
        System.out.println(element);
        System.out.println("---------");
        //2.获取index.html中所有h2标签名称的元素列表并打印输出
        Elements elements = document.getElementsByTag("h2");
        elements.forEach(System.out::print);
        System.out.println("---------");
        //3.获取index.html中所有元素含有class属性值为fl并打印输出
        elements = document.getElementsByClass("fl");
        elements.forEach(System.out::print);
    }
}

5.4 根据css选择器获得节点(元素)

5.4.1 css选择器相关方法

返回类型 方法 说明
Elements select(String cssQuery) 作用:通过选择器得到多个元素
Element selectFirst(String cssQuery) 作用:通过选择器得到第一个元素

其中,String cssQuery指css选择器:

选择器类型 选择器语法
ID选择器 #id
class选择器 .类名
标签选择器 标签名
属性选择器(属性名) [属性名]
属性选择器(属性值) [属性名=属性值]

5.4.2 示例代码

public class Demo02 {
    public static void main(String[] args) throws IOException {

    //获得Document对象
        Document document = Jsoup.parse(new File(Demo02.class.getResource("/index.html").getPath()), "utf-8");

        //1.获取id="footer"元素并输出元素名称
        Elements elements = document.select("#footer");
        print(elements);
        System.out.println("---------");
        //2.获取index.html中所有元素含有class属性值为item并打印输出元素体内容
        elements = document.select(".item");
        print(elements);
        System.out.println("---------");

        //3.获取index.html中所有h3标签名称的元素列表并打印输出元素名称
        elements = document.select(".item");
        print(elements);
        System.out.println("---------");

        //4.获取index.html中含有属性data-toggle所有元素并打印输出元素名称和元素体数据
        elements = document.select("[data-toggle]");
        print(elements);
        System.out.println("---------");

        //5.获取index.html中属性role值为"tablist"的所有元素列表并打印输出元素名称
        elements = document.select("[role=tablist]");
        print(elements);

    }
    private static void print(Elements elements) {
        elements.forEach(System.out::print);
    }
}

5.5 组合选择器

选择器代码 说明
标签名.类名 交集选择器,同时指定标签名和类名的选择器
标签名[属性名] 得到某标签,包含指定属性的标签。
父元素 子元素 层级选择器,找某个元素下的所有子元素,选择器之间使用空格隔开
兄弟A+兄弟B 查找在A元素后面第一个同级元素B

5.5.1 示例代码

public class Demo03 {
    public static void main(String[] args) throws IOException {
        //获得Document对象
        Document document = Jsoup.parse(new File(Demo03.class.getResource("/index.html").getPath()), "utf-8");

        //1.获取index.html中div元素类名为item的元素,并打印元素数据
        Elements elements = document.select("div.item");
        elements.forEach(System.out::print);
        System.out.println();
        System.out.println("==============================");

        //2.获取index.html中a元素含有属性data-toggle的所有元素列表并打印元素数据
        elements = document.select("a[data-toggle]");
        elements.forEach(System.out::print);
        System.out.println();
        System.out.println("==============================");

        //3.获取index.html中属性id值为"banner"的所有div子元素并打印元素数据
        elements = document.select("#banner div");
        elements.forEach(System.out::print);
        System.out.println();
        System.out.println("==============================");

        //4.获取index.html中class="navitem"元素后面同级第一个兄弟section元素并打印元素数据
        elements = document.select(".navitem+section");
        elements.forEach(System.out::print);
    }
}

5.6 补充Element元素相关方法

Element对象的方法 说明
String attr(“属性名”) 得到元素指定属性的值
Elements children() 得到当前元素所有的子元素,返回集合
String tagName() 得到元素的标签名字
String text() 得到元素主体内容中的文本

5.6.1 示例代码

需求:已有xml保存书的信息,已有一个对应的类Book(String category, String title, String author, int year, double price),使用DOM解析xml,封装成List,并且输出封装好的集合

准备xml文件:


<bookstore>
    <book category="CHILDREN">
        <title>Harry Pottertitle>
        <author>J K. Rowlingauthor>
        <year>2005year>
        <price>29.99price>
    book>
    <book category="WEB">
        <title>Learning XMLtitle>
        <author>Erik T. Rayauthor>
        <year>2003year>
        <price>39.95price>
    book>
bookstore>

Book类代码省略

main代码如下:

public class Demo03 {
    public static void main(String[] args) throws IOException {
        //获得Document对象
        Document document = Jsoup.parse(new File(Demo03.class.getResource("/books.xml").getPath()), "utf-8");
        //使用dom解析
        Elements books = document.select("book");
        //创建List存放book对象
        List bookList = new ArrayList<>();
        //遍历books获得每个元素
        for (Element element : books) {
            //创建对象
            Book book = new Book();
            //获得属性值
            String category = element.attr("category");
            book.setCategory(category);
            //获得element的所有子元素
            Elements children = element.children();
            //遍历children
            for (Element child : children) {
                //获得每个子元素的tagName和text,然后进行判断赋值
                String tagName = child.tagName();
                String text = child.text();
                if ("title".equalsIgnoreCase(tagName)) {
                    book.setTitle(text);
                } else if ("author".equalsIgnoreCase(tagName)) {
                    book.setAuthor(text);
                } else if ("year".equalsIgnoreCase(tagName)) {
                    book.setYear(Integer.parseInt(text));
                } else if ("price".equalsIgnoreCase(tagName)) {
                    book.setPrice(Double.parseDouble(text));
                }
            }
            //每循环一次就给bookList添加元素
            bookList.add(book);
        }
        //输出bookList
        for (Book book : bookList) {
            System.out.println(book);
        }
    }
}

6. xPath表达式

6.1 概念

  • JsoupXpath 是一款纯Java开发的使用xpath解析HTML的解析器,JsoupXpath不是jsoup的一部分,是在jsoup基础上进行的扩展。
  • XPath 使用路径表达式来选取HTML或XML文档中的元素节点或属性节点。

6.1.1使用步骤

导包—>写xPath表达式

6.1.2四种XPath语法方式:

1)绝对路径
2)相对路径
3)全文搜索
4)条件筛选

6.2 核心API方法

6.2.1 获得核心类JXDocument

方法 说明
public JXDocument(Document doc) 通过构造方法创建JXDocument对象
参数:org.jsoup.nodes.Document对象
List selN(String xpath) 通过xpath表达式得到指定的节点对象JXNode
返回一个节点集合
JXNode selNOne(String xpath) 通过xpath表达式得到符合条件的节点对象,返回一个节点
节点:包含属性Attribute或元素Element

selN()相当于css选择器获得元素的select(),selNOne相当于css选择器获得元素的selectFirst()

6.2.2 节点JXNode的API

方法 说明
Element getElement() 通过节点得到它的元素
List sel(String xpath) 用在相对路径上,从当前节点开始向下查询其它的子节点

6.3 绝对路径

6.3.1 语法

  • 绝对路径必须从根元素开始写路径,所以必须以/开头,/代表了根标签;
  • 绝对路径找节点绝对不能跳级 去寻找(即不能跳过路径下所有父元素);
  • 路径中不能出现标签

6.2.2 示例代码

public class Demo01 {
    public static void main(String[] args) throws XpathSyntaxErrorException, IOException {
        Document document = Jsoup.parse(new File(Demo01.class.getResource("/index.html").getPath()),"utf-8" );
        //使用Document对象获得对应的额JXDocument对象
        JXDocument jxDocument = new JXDocument(document);
        //需求1: 采用绝对路径获取从根节点开始逐层的/body/div/ul/li节点列表并打印信息
        List jxNodes = jxDocument.selN("/body/div/ul/li");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
        //需求2: 找到head里面link标签
        jxNodes = jxDocument.selN("/head/link");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
    }
}

6.4 相对路径

6.4.1 语法

  • 相对路径指的是相对于当前的路径,相对路径找节点就是相对于当前的路径找到需要的节点
  • 所以需要判断执行sel()查找节点的是哪个对象
格式 说明
子元素/孙元素 相对当前路径元素里面的子元素的选取
./子元素/孙元素 功能与上面的写法一样 . 表示当前路径
/子元素/孙元素 相对当前节点元素位置继续查找节点,需要使用JXNode.sel(xpath)的方法执行相对路径表达式。

其实上面三种是等价的,最常用的是第一种

注意事项
  1. 使用相对路径前需要用绝对路径获得当前的对象,此时xpath的/不可省略!!
  2. 当前对象使用相对路径sel()时,xpath才可以省略.以及/

6.4.2 特殊的方法

  • 相对路径对比其余三种方式有特有的方法获取元素:sel()

6.4.3 示例代码

public class Demo02 {
    public static void main(String[] args) throws XpathSyntaxErrorException, IOException {
        //需求:先采用绝对路径获取body节点,再采用相对路径获取下一级div节点列表并打印信息

        //获得JXDocument对象
        JXDocument jxDocument = new JXDocument(Jsoup.parse(new File(
                Demo02.class.getResource("/index.html").getPath()), "utf-8"));

        //使用绝对路径获得当前的元素(body)
        JXNode body = jxDocument.selNOne("/body");  //中间的“/”不可省略
        //根据相对路径获得下一级的div节点
        List jxNodes = body.sel("div");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
    }
}

6.5 全局搜索

6.5.1 全文搜索获得不同节点语法

获取类型 语法代码
获取元素节点 元素名
获取属性节点 @属性名

6.5.2 全文搜索语法

格式 说明
//子元素//元素或@属性(基本格式) “//”符号,不用逐级写路径,可以直接选取到对应的节点,全文搜索匹配不需要按照逐层级写。

例子:

示例 含义
//li 全文搜索所有的li元素列表,不论li在哪一级元素
//div/a/img 全文搜索所有的div,再逐层级搜索下面的a标签下的img元素
//link/@href 全文搜索link元素,得到它的href属性,属性名前加@符号(注意在@前不要漏了/

6.5.3 示例代码

public class Demo03 {
    public static void main(String[] args) throws XpathSyntaxErrorException, IOException {
        Document document = Jsoup.parse(new File(Demo03.class.getResource("/index.html").getPath()),"utf-8" );
        //使用Document对象获得对应的额JXDocument对象
        JXDocument jxDocument = new JXDocument(document);

        //需求1: 直接全文搜索所有的 li 元素列表并打印
        List jxNodes = jxDocument.selN("//li");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
        //需求2: 直接全文搜索所有的 div,再逐层级搜索下面的 a 元素下的 img 元素列表并打印
        jxNodes = jxDocument.selN("//div/a/img");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
        //需求3: 接获取 link 元素里面 href 属性的值,注意属性要用@符号
        jxNodes = jxDocument.selN("//link/@href");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
    }
}

6.6 条件筛选

  • 根据条件过滤选取节点

6.6.1 语法

格式 说明
//元素[@属性=value] 获取元素属性=value的元素
//元素[@属性>value]/@属性 获取元素属性>value的元素的所有属性的值
//元素[@属性=value]/text() 获取符合条件元素的纯文本数据
//元素[@属性=value]/html() 获取符合条件元素的html数据(包括标签)
注意事项
  1. 如果value是一个包含“-”的字符串,那么需要用单引号括住

6.6.2 示例代码

public class Demo04 {
    public static void main(String[] args) throws XpathSyntaxErrorException, IOException {
        Document document = Jsoup.parse(new File(Demo04.class.getResource("/index.html").getPath()),"utf-8" );
        //使用Document对象获得对应的额JXDocument对象
        JXDocument jxDocument = new JXDocument(document);

        //需求1: 搜索li,属性为class="nav-active"的元素并打印
        List jxNodes = jxDocument.selN("//li[@class='nav-active']");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
        //需求2: 属性为data-slide-to大于0的元素,再查询data-slide-to的属性值
        jxNodes = jxDocument.selN("//li[@data-slide-to>0]/@data-slide-to");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
        //需求3: 搜索a标签,属性为href="login.html"的元素,得到它的文本。
        jxNodes = jxDocument.selN("//a[@href='login.html']/text()");
        for (JXNode jxNode : jxNodes) {
            System.out.println(jxNode);
        }
    }
}

你可能感兴趣的:(xml编程)