总结27 XML的应用

所要用到的jar包下载地址:
链接: https://pan.baidu.com/s/1E6562c2c7RVkSQvSBl9BbA 提取码: igfh

概念

Extensible Markup Language 可扩展标记语言
可扩展:标签都是自定义的。 意思就是说,无论是自闭标签还是围堵标签,名字可以自定义.

XML的应用

一. 当作配置文件
虽然JSON也同样有配置文件的功能,但是还不能取代XML,因此XML目前的主要作用是存储配置文件.
二.在网络中传输
JSON已经可以替代XML的网络传输功能,在使用传输时,优先使用JSON替代.
何谓传输网络功能?就是能兼容不同语言间的文件传输,比如把C语言的数据传输到JAVA语言.

在这里插入图片描述

xml与html的区别

一. xml标签都是任意自定义的,html标签是必须按照对应标签去定义。
二. xml的语法严格,html语法松散
XML的语法非常严谨,一个空格一个字符都不能搞错,因此要注意格式
三. xml是存储数据的,html是展示数据

XML的DOM

在这里插入图片描述

创建XML文件和其语法

创建XML文件

右键单击src模块目录,选择’new’ → 选择’XML Configuration File’ → 选择’JSP Tag Library Descriptor’ → 自定义XML文件名并点确定创建 → 将其格式由’tid’格式更改为’xm’格式.
在这里插入图片描述

XML文件的基本格式

1.xml第一行必须定义为文档声明
推荐的固定格式:<?xml version="1.0" encoding="UTF-8"?>
在这里插入图片描述

2. xml文档中有且仅有一个根标签
也就是说,所有标签都必须呆在一个大标签里头,且XML文档中不能有第二个大标签.
在这里插入图片描述

3. 标签属性值必须使用引号(单双都可)引起来

4. 标签必须正确关闭 即:如果是围堵标签,前后的标签名必须一致,且不能少前标签后或后标签
5. xml文档中,标签名称有大小写区分 推荐全部标签使用小写

6.标签名称可以包含字母、数字以及其他的字符

7.标签名称不能以数字或者标点符号为开头

8.标签名称不能以字母 xml(或者 XML、Xml 等等)开头

9.标签名称不能包含空格

XML文档头的详细定义格式

xml的文档头此前提到必须定义在第一行,且推荐的固定格式为**:<?xml version="1.0" encoding="UTF-8"?>**
但是,在一些其它需求下,XML文档头的属性列表格式还是会有所不同

一.version:版本号
这是必须的格式,虽然XML出了1.1版本,但是不能够向下兼容,因此主流的推荐版本为1.0
二.encoding:编码方式
告知解析引擎当前文档使用的字符集,默认值:ISO-8859-1 推荐为:UTF-8
三.standalone:是否为独立的XML文档
取值为yes和no,yes表示不依赖其他文件,no表示依赖其他文件
作用有两个
第一个作用:用于引入CSS文件,为XML文档美化
第二个作用(常用):引入约束文档,以对XML文件的标签格其属性进行特定的规则和格式约束.

标签与标签元素的相关注意

一.标签属性’ID’的注意事项
每个标签的ID属性值必须是唯一的,不能和其它标签的ID属性值相同.
二.CDATA区的作用
在该区域中的数据会被原样展示 即:用于解析展示一些代码,但是却不想这个代码被执行,要原汁原味地以文本形式展示.
格式<![CDATA[ 数据 ]]>

XML中’约束’作用与应用

用于规定XML内容,如标签的格式,标签的顺序等等.
出于个人的定位,我们只需要看的懂约束语句就可以了,以方便在日后更改XML文档时,能够看得懂约束内容,依照其约束的规则修改XML文档即可
XML分为DTD约束和Schema约束

XML约束之DTD约束

详细参考:https://www.w3school.com.cn/dtd/index.asp
DTD是一个相对简单约束技术,目前用的不多.
文件后缀为’.dtd’

XML文档内部的DTD约束

DTD约束可以写在XML文档内部
格式:<!DOCTYPE 自定义目标根标签名 [元素声明体]>
列如:

<!DOCTYPE dtdtest [
        <!ELEMENT dtdtest (userinfo,userinfo,address)>
        <!ELEMENT userinfo (name,age,sex)>
        <!ELEMENT name      (#PCDATA)>
        <!ELEMENT age    (#PCDATA)>
        <!ELEMENT sex (#PCDATA)>
        <!ELEMENT address (nametwo,guojia)>
        <!ELEMENT nametwo      (#PCDATA)>
        <!ELEMENT guojia      (#PCDATA)>
                            ]>

XML文档外部的DTD约束

DTD约束可以引用网络上的DTD格式文件,也可以引用本地硬盘中的DTD格式文件.
一.DTD约束文档的定义格式
当你要自己写一个dtd约束文件时,要省略<!DOCTYPE 自定义目标根标签名 []>这样的声明格式
如:

<?xml version="1.0" encoding="UTF-8"?>

<!ELEMENT student (name,age,sex,info)>
        <!ELEMENT name      (#PCDATA)>
        <!ELEMENT age    (#PCDATA)>
        <!ELEMENT sex (#PCDATA)>
<!ELEMENT info (id,time)>
        <!ELEMENT id      (#PCDATA)>
        <!ELEMENT time      (#PCDATA)>


二.本地dtd引用格式<!DOCTYPE 自定义目标根标签名 SYSTEM "dtd文件的位置">
列如:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE student SYSTEM "dtdfile.dtd">
<student>
    <name>刘玉飞</name>
    <age>15</age>
    <sex>女</sex>

    <info>
        <id>10001</id>
        <time>2020-1-2</time>

    </info>

</student>

三.网络dtd引用格式:<!DOCTYPE 根标签名 PUBLIC "自定义dtd文件名字" "dtd文件的位置URL">

DTD的常用约束格式和语法

更多参考:https://www.w3school.com.cn/dtd/dtd_elements.asp
在这里插入图片描述
在该语法文档中规定了,dtdtest根标签内必须有address和userinfo子标签.
且userinfo子标签内必须要右name,age,sex三个标签.
address子标签内必须要右nametwo,guojia两个标签,并且的上下顺序也不能搞混.

XML约束之Schema约束

详细参考:https://www.w3school.com.cn/schema/index.asp
Schema是一个相对复杂的约束技术,因为它的语法比较难.
文件后缀为’.xsd’

引入.xsd约束文件

参考:https://www.w3school.com.cn/schema/schema_howto.asp
引入步骤一 在XML中填写约束目标的根标签名
格式:<约束目标的根标签名> </约束目标的根标签名)

引入步骤二 引入xsi前缀文件
格式:xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
注意:将其写入到步骤一的标签内
引入步骤三 引入xsd文件命名空间
格式:xsi:schemaLocation="自定义前缀名 xsd文件名.xsd"
列如:

xsi:schemaLocation="http://www.itcast.cn/xml  student.xsd"

注意:将其写入到步骤一的标签内
注意:此处的’自定义前缀’,是为了以后对某个标签引用约束时所用的’前缀’,目的是为了省力气.

引入步骤四 为每一个xsd约束声明一个前缀,作为标识
格式:xmlns="自定义前缀名"
列如:

xmlns="http://www.itcast.cn/xml" 

注意:将其写入到步骤一的标签内
在这里插入图片描述

Schema约束的格式和语法

参考:https://www.w3school.com.cn/schema/schema_simple.asp

解析的概念

所谓解析,就是获取html文件(也可以是代码,或者URL网页)或者xml文档的指定元素.
当需要使用爬虫,或者读取配置信息时,就需要用到解析.

解析的方式

1. DOM:将标记语言文档一次性加载进内存,在内存中形成一颗dom树
* 优点:操作方便,可以对文档进行CRUD的增删改查操作
* 缺点:占内存
2. SAX:逐行读取,基于事件驱动的。
* 优点:不占内存。
* 缺点只能读取,不能增删改
这里,我们都用DOM的方式进行解析!

常见的解析器:

是的,解析时,还有解析器的区分,有的解析器只能以SAX方式解析,有的解析器只能以DOM的方式解析.
1.JAXP:sun公司提供的解析器,支持dom和sax两种思想
2.DOM4J:一款非常优秀的解析器

3. Jsoup(推荐):jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址,也就是网页的HTML文本内容,这意味着可以爬虫。它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据。
而我们用的就是这一种,Jsoup通过DOM方式解析

  1. PULL:Android操作系统内置的解析器,sax方式的。

Jsoup解析器的应用

在使用jsoup解析器时,我们需要先将其XML文档/HTML文件实例化为一个Document对象.
因此,我们需要一个Jsoup的jay包,包名为’jsoup-1.11.2.jar’.
只有用以下几种方式实例化Document对象后,才可以进行很多增删改查的操作.

将文档实例化为DOM对象的几种方式

在这里插入图片描述
方式一:通过Document对象解析xml文档 并将其xml文档实例化为对象返回
分为两个步骤
注意:该方式也可以应用到HTML文档

步骤一:通过反射形式,获取xml文档的file路径.
格式:String 自定义String路径名 = 调用类的类名.class.getClassLoader().getResource("XML文档名.xml").getPath();
列如:

String fileStr = JsoupAnalyze.class.getClassLoader().getResource("XMLFile.xml").getPath();

注意:1.file文档名不能含有中文,不然会报错

步骤二: 将获取到的XML文档File对象实例化为Document对象
格式:Document 自定义xml文档实例化对象名 = Jsoup.parse(new File(自定义String路径名),"xml文档所使用的编码");
列如:

   Document documentObj = Jsoup.parse(new File(fileStr),"utf-8" );

方式二:通过Documen解析字符串类型的网页代码,并将其代码实例化为对象返回
格式:Document 自定义代码实例化对象名 = Jsoup.parse(string字符串类类型的代码);

方式三:通过Document对象解析网页,并将其网页文件实例化为对象返回
格式:Document 自定义网页实例化对象名 Jsoup.connect("url链接").get();
列如:

Document documentUrlObj = Jsoup.connect("https://www.kuaidaili.com/free/inha/3/").get();

创建模拟浏览器的爬虫式DOM对象

在爬虫时,一些网站有防爬虫机制,此时我们在通过url链接获取dom对象的同时,为其设置协议头了
格式:Document 自定义代码实例化对象名 = Jsoup.connect("URL链接").timeout(自定义最大连接等待延时).header("自定义请求头的协议名", "自定义其请求头协议名对应的参数")get();
列如:

在这里插入代码片

三种选择器的应用

意义

选择器的目的是为了能够对Document对象所获取到的标签元素进行更方便,更细致的增删改查工作
选择器分为三种:
一.通过传统Element的DOM查询(仅用于简单查询时才推荐)
二.通过selector选择器查询(语法较Xpath稍微复杂一点,但可以直接进行格式调用)
三.通过Xpath选择器查询(语法简单,格式调用时需要转换为Element对象)
接下来详细介绍

传统Element的DOM查询

使用该选择器前,需要将 XML/HTML/页面代码 实例化为Document对象

传统的Element的DOM能够在解析器jay包’jsoup-1.11.2.jar’的基础上直接进行增删改查工作,但是不能通过一些语法进行更细致地查询

在这里插入图片描述

创建

创建格式一 根据id属性值获取唯一的element对象
格式:Element 自定义BOM实例化标签结果对象名 = 自定义xml/网页/代码实例化对象名.getElementById("id值");
列如:

Element idObj = documentObj.getElementById("two");

创建格式二 根据标签名称获取获取标签对象,以集合形式返回
格式:Elements 自定义BOM实例化标签结果对象名 = 自定义xml/网页/代码实例化对象名.getElementsByTag("目标标签名");
列如:

Elements byTag = documentObj.getElementsByTag("name");

创建格式三 根据属性名称获取标签对象,以集合形式返回
格式:Elements 自定义BOM实例化标签结果对象名 = 自定义xml/网页/代码实例化对象名.getElementsByAttribute("属性名称");
列如:

Elements attributeObj = documentObj.getElementsByAttribute("id");

创建格式四 根据对应的属性名,和其属性对应的值获取标签对象,以集合形式返回
格式:Elements 自定义BOM实例化标签结果对象名 = documentObj.getElementsByAttributeValue("属性名","属性名对应的值");
列如:

Elements attributeValue = documentObj.getElementsByAttributeValue("id","add" );

方法

方法一 String attr(String key):根据属性名称获取其对应的属性值
非集合格式:String 自定义属性值结果变量 = 自定义BOM实例化标签结果对象名.attr("属性名");

集合格式:String 自定义属性值结果变量 = 自定义BOM实例化标签结果对象名.get(索引值).attr("属性名");

方法二 String text():获取标签体的不包括代码在内的内容 以字符串形式返回
非集合格式:String 自定义文本结果变量 = 自定义BOM实例化标签结果对象名.text();

集合格式:String 自定义文本结果变量 = 自定义BOM实例化标签结果对象名.get(索引值).text();

方法三 String html():获取标签体包括代码在内的文本 并以字符串返回
非集合格式:String 自定代码体文本结果变量 = 自定义BOM实例化标签结果对象名.html();
集合格式:String 自定代码体文本结果变量 = 自定义BOM实例化标签结果对象名.get(索引值).html();

selector选择器查询

在这里插入图片描述
使用该选择器前,需要将 XML/HTML/页面代码 实例化为Document对象

selector选择器同样能够在jay包’jsoup-1.11.2.jar’的基础上直接工作,它可以使用一些语法进行更细致的增删改查,但语法对我来将稍微有点难懂,跟CSS的选择器语法一样.

官方语法参考:https://www.open-open.com/jsoup/selector-syntax.htm
在这里插入图片描述

创建

格式:Elements 自定义selector实例化标签结果对象名 = 自定义xml/网页/代码实例化对象名.select​("选择器语句的表达式");
列如:

Elements select = documentObj.select("#liuyufeiage");//语法:根据id值获取对应元素
Elements select = documentObj.select("#liuyufeiage");//语法:根据id值获取对应元素

方法

同DOM查询的方法一致

Xpath选择器查询

在这里插入图片描述
使用该选择器前,需要将 XML/HTML/页面代码 实例化为Document对象

Xpath选择器需要在jay包’JsoupXpath-0.3.2.jar’的基础上进行工作,同样能细致地进行增删改查,语法也相对简单.
但是如果要进行’格式调用’(如text,html方法)则需要转换为Element对象才行.
Xpath往往多用于爬虫,反正我自己就是用这个爬免费代理的

语法参考:https://www.w3school.com.cn/xpath/xpath_syntax.asp

创建

步骤一 将Document对象实例化为JXDocument对象
格式:JXDocument 自定义Xpath选择器对象名 = new JXDocument(自定义网页/代码/XML文档的实例化对象名);
列如:

JXDocument jxdObj = new JXDocument(documentUrlObj);

步骤二 通过将其Xpath选择器对象实例化为结果对象,并填入语法参数,来获得结果
格式:List<JXNode> 自定义结果接收对象名 = 自定义Xpath选择器对象名.selN("Xpath选择器的语法");
列如:

    List<JXNode> resultOfIp = jxdObj.selN("//td[@data-title='IP']");//获取端口
        List<JXNode> resultOfPort = jxdObj.selN("//td[@data-title='PORT']");//获取IP地址

方法(转换为Element)

getElement(); 转为Element对象
如果你想要使用text,html等方法,则需要将结果接收对象转换为Element对象才行.
以下就是该方法的格式
Xpath没有类似于Element的’text()‘这种方法,因此不能仅仅取标签中的’文本元素’.
解决这种办法的途径就是将其转换为Element,再通过element的方法’text()‘获取’文本元素’

格式:Element 自定义Element转换结果接收对象名 = 自定义结果接收对象名.get(索引值).getElement();
列如:

Element elementOfIp = resultOfIp.get(i).getElement();

爬虫IP代理的示例

02点45分 今天暂且先搁置注册功能,先实现登录和代理抓取功能.
注册功能需要用到sql中表数据的’增’,目测用JdbcTemplate实现
03点38分 当使用for循环爬虫时,i数值为1的第一遍可以,第二遍则出现了HttpStatusException: HTTP error fetching URL. Status=503这个错误.
百度了下,应该是网站设置了防止爬虫的原因. 参考这个网站的解决方法:http://www.it1352.com/871098.html
目前先不鼓捣了 该网站内解决方法的内容如下:
//这将使您退出Http 503
Document document = Jsoup.connect(https://kissanime.to/ AnimeList /)
.userAgent(”Mozilla / 5.0(Macintosh; Intel Mac OS X 10.11; rv:49.0)Gecko / 20100101 Firefox / 49.0)。ignoreHttpErrors(true).followRedirects(true).timeout(100000 ).ignoreContentType(真)获得();
System.out.println(document.body());
}
16点37分 找到解决方案 首先,方案一就是设置协议头,在定义目标url链接的同时,通过Jsoup的’header’方法设置各项协议头和其参数,然后再用’get’方法返回一个dom对象.
参考网址:http://www.mamicode.com/info-detail-2300171.html
自己的代码如下:domObj = Jsoup.connect(url).timeout(5000)
.header(“Accept”, “text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3”)
.header(“Accept-Encoding”, “gzip, deflate, br”)
.header(“Accept-Language”, “zh-CN,zh;q=0.9”)
.header(“User-Agent”, “Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36”).get();
然后发现还是不行,自己尝试刷新代理页面,发现如果小于一秒内刷新将会报错,所以想到需要给用于爬虫的方法设置延时.百度了下延时的方法,需要用到"Thread.sleep();",
此前在学习多线程中就用到过该方法,实际上他不仅仅是用于多线程的延时,也可以用于单一的某个方法的延时.因为’Thread.sleep’方法需要处理异常,所以我的代码如下:
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}

19点36分 完工,但是注册有两小Bug,
1.当你要注册用户名是已经存在了的话,将会直接跳转到系统给出的页面,而不是我所指定的虚拟路径.
2.另外一点,在做约束时,对于密码(同为varchar类型)的非空约束不起效,空密码注册的话没有问题,而对用户名的非空约束则起效,不知道是为何.

package cn.proxyweb.web;

import cn.wanghaomiao.xpath.exception.XpathSyntaxErrorException;
import cn.wanghaomiao.xpath.model.JXDocument;
import cn.wanghaomiao.xpath.model.JXNode;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

@WebServlet(name = "ServletProxy",value = "/proxy")
public class ServletProxy extends HttpServlet {
    // 代理网址:http://www.kuaidaili.com/free/inha/1/
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("utf-8");
        String nameResult = (String) request.getAttribute("nameInfo");
        String proxyResult = (String) request.getAttribute("proxyInfo");
        response.setContentType("text/html;charset=utf-8");//设置编码
        PrintWriter stringOutObj = response.getWriter();
        stringOutObj.write("登陆成功!欢迎您!"+nameResult+",您要抓取的代理最大页数为"+proxyResult+"页");
        stringOutObj.write("<br>");//换行

        //定义一个字符串对象用于接收结果
        String strForResult = new String();
        for (int i = 1; i <= Integer.parseInt(proxyResult); i++) {
            try {
                Thread.sleep(1000);//设置调用'爬虫方法'的延时,以免触发代理网页的反爬虫机制.
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String url = "http://www.kuaidaili.com/free/inha/"+i+"/";

            strForResult += getProxyInfo(url);

        }
        System.out.println("======以下为循环完毕的所有结果=========");
        System.out.println("未进行换行符号处理的内容"+strForResult);
        strForResult+="以上为所有代理的获取结果";

        // 将其中文本内容中的逗号','全部替换成'<br>'以实现换行效果.
        //  String replaceAll​(String regex, String replacement) 指定一个字符,将其字符串中的该字符全部替换为另外一个字符
        // 进行替换,并返回一个新的字符串
        String replaceResult = strForResult.replaceAll(",", "<br>");

        System.out.println("进行了换行符号处理的内容"+replaceResult);
        //将处理后的结果输出到网页
        stringOutObj.write("已经进行了文本格式的处理,这将能让个个代理后面都有一个换行符号,达到了美观和方便使用的目的.<br>以下为您所获取到的所有代理<br>"+replaceResult);



    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        //该方法用于通过jsoup抓取网页代理
    }
    public String getProxyInfo(String url){
        Document domObj = null;
        JXDocument jxdObj = null;
        Element eleObjForIp = null;
        Element eleObjForPort = null;
        List<JXNode> htmlResultForIp = null;
        List<JXNode> htmlResultForPort = null;
        String str = null;
        try {
          //  domObj = Jsoup.connect(url).get();
             domObj = Jsoup.connect(url).timeout(5000)
                    .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3")
                    .header("Accept-Encoding", "gzip, deflate, br")
                    .header("Accept-Language", "zh-CN,zh;q=0.9")
                    .header("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36").get();


            jxdObj = new JXDocument(domObj);
            System.out.println("你好"+"当前页面网址为:"+url);
            //通过xpath语法抓取指定的内容 列如://title[@lang='eng']	选取所有 title 元素(标签),且这些元素拥有值为 eng 的 lang 属性。
            htmlResultForIp = jxdObj.selN("//td[@data-title='IP']"); // 抓取IP data-title="IP"
            htmlResultForPort = jxdObj.selN("//td[@data-title='PORT']"); // 抓取端口 data-title="PORT"
            System.out.println(htmlResultForIp);
            System.out.println(htmlResultForPort);
            System.out.println("-----------------");
            //将其存放抓取结果的集合元素转换为Element对象,以便获取纯文本内容
            for (int i = 0; i < htmlResultForIp.size(); i++) {
            eleObjForIp = htmlResultForIp.get(i).getElement();
            eleObjForPort = htmlResultForPort.get(i).getElement();

        /*    //通过text方法将其转为纯文本内容,并添加到字符串
            proxyParam.add(eleObjForIp.text()+":"+eleObjForPort.text());*/
        if (i==0){
            str = eleObjForIp.text()+":"+eleObjForPort.text()+",";
        }else {
            str += eleObjForIp.text()+":"+eleObjForPort.text()+",";

        }
            }
            System.out.println(str);
        return str;

        } catch (Exception e) {
            e.printStackTrace();
            return str+"出现异常";
        }


    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值