整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

XML HttpRequest 最佳实践详解

ML HttpRequest(XHR) 技术是构建动态、响应式网站的关键。这项技术使得网页能在不重新加载整个页面的情况下与服务器进行数据交互,极大地优化了用户的交互体验。

  • XML HttpRequest 是什么?详细讲解其用法

定义 XML HttpRequest

XML HttpRequest 是一种浏览器与服务器进行数据交换的 API。尽管它的名字包含 “XML”,XHR 实际上支持多种数据格式,如 JSON、HTML 和纯文本等。初期,它主要用于处理 XML 格式数据,但随着时间的发展,XHR 的功能已经大为扩展。

  • 什么是 JSON?JSON 文件是干什么的

工作流程分析

使用 XML HttpRequest 的基本步骤包括:

  1. 初始化对象:创建一个新的 XML HttpRequest 对象实例。
  2. 配置请求:通过调用 .open() 方法设置请求方法(例如 GET 或 POST)和目标 URL。
  3. 执行发送:利用 .send() 方法发起请求,该方法中可以包含需要发送的数据。
  4. 监听并处理返回:通过事件监听器处理来自服务器的响应。

实际运用一例

以下 JavaScript 代码演示了如何通过 XML HttpRequest 发起一个 GET 请求:

// 初始化一个XML HttpRequest对象
var xhr = new XML HttpRequest();

// 设置请求的方法和URL
xhr.open('GET', 'https://api.example.com/data', true);

// 定义响应的处理逻辑
xhr.onreadystatechange = function() {
    if (xhr.readyState === 4 && xhr.status === 200) {
        console.log(xhr.responseText); // 输出响应数据
    }
};

// 执行请求
xhr.send();

在此示例中,我们配置了请求和如何在接收到响应后处理数据。

主要使用场景

动态内容加载

XML HttpRequest 允讼网页动态地加载和显示数据,而无需刷新页面,提高了网站的整体性能和用户体验。

异步表单提交

使用 XHR,表单数据可以异步提交,从而避免页面刷新,用户可以无缝继续他们的网站活动。

实时数据交互

对于需要频繁更新数据的应用,如实时报表或聊天应用,XHR 提供了持续与服务器通信的能力。

优点与挑战

优势

  • 用户体验提升:异步通信意味着更快的响应时间和无干扰的用户界面。
  • 效率高:只传输必需的数据,减轻了服务器负担。
  • 高度兼容性:支持多种数据格式和跨平台使用。

挑战

  • 代码复杂度:管理异步逻辑,需处理多种可能的执行路径和错误。
  • 安全考量:需警惕 XSS 等安全漏洞。
  • 对旧版本IE的支持:较老IE版本中实现 XHR 的方式有所不同。

现代 Web 开发的替代技术

  • 如何在 React 中使用 Fetch API

虽然 Fetch API 在现代 Web 开发中逐渐成为新的标准,提供了更简洁的 API 和更好的错误处理机制,Fetch 的使用示例如下:

fetch('https://api.example.com/data')
    .then(response => response.json())
    .then(data => console.log(data))
    .catch(error => console.error('Error:', error));

Fetch 基于 Promises,简化了处理异步操作的复杭性。

总结

XML HttpRequest 是 Web 开发者必须掌握的重要工具之一。尽管有更现代的技术如 Fetch API,了解 XHR 仍对处理旧项目或理解 Web 开发的历史背景非常有用。无论是更新现有的网站还是开发新的应用,XHR 都是一个宝贵的资源。

eact定义的一种类似于XML的JS扩展语法,用来简化创建虚拟DOM

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>jsx</title>
        <style>
            .title{
                background-color: orange:
                width: 200px;
            }
        </style><!--定义个样式,也可以写在CSS文件里,引入进来-->
    </head>

    <body>
        <div id="test"></div>
        <script type="text/javascript" src="../js/react.development.js"></script>
        <script type="text/javascript" src="../js/react-dom.development.js"></script>
        <script type="text/javascript" src="../js/babel.min.js"></script>
    </body>
    <script type="text/babel">
        const myid= "lOVE you"
        const mydata= "hellO jsx"
        const VDOM = ( // <!--h2标签引入样式,用className,span标签引入内联样式的时候,不是用双引号,而是双花括号,-->
        <div>
            <h2 className="title" id={myid.toLowerCase()}> 
                <span style={{color:'red',fontSize:'29px'}}>{mydata.toLowerCase()}</span>
            </h2>)// <!--标签中混入JS表达式时要用花括号{},如这里mydata取值 -->
            //jsx中只能有一个根标签,比如这里的h2,可以在h2的外面包一层div,就可以写两个h2
            <h2 className="title" id={myid.toUpperCase()}> 
                <span style={{color:'red',fontSize:'29px'}}>{mydata.toLowerCase()}</span>
            </h2>)
            //标签首字母若是小写字母,则将该标签转为html中同名元素,若html中无该标签对应的同名元素,则报错
            //标签首字母若是大写字母,react就去渲染对应的组件,若组件没有定义,则报错
        </div>

        ReactDOM.render(VDOM,document.getElementById('test'))
    </script>
</html>

一个jsx的小练习

 <script type="text/babel">
        const data=['A','B','C']
        const VDOM = (
            <div>   //js表达式会产生一个值。js语句(代码),有if,for,switch判断,
                <h1>jsx框架</h1>
                <ul>
                  {
                    data.map((item,index)=>{
                        return <li key={index}>{item}</li>
                    }) //item拿到data里面对应的值,map遍历的第二个值是索引值,
                  }
                </ul>
            </div>
        )
        ReactDOM.render(VDOM,document.getElementById('test'))
    </script>

对应的网页如下:

模块是向外提供特定功能的js程序,一般就是一个js文件。

组件比模块更高一级,比如实现一个网页的头部的html,字体,css,js,图像这些元素组合在一起,就形成了头部这个组件。

组件分为函数式组件和类式组件:

函数式组件:

<script type="text/babel">
function MyComponent(){
return <h2>show the function component</h2>
}
ReactDOM.render(<MyComponent/>,document.getElementById('test'))
//<MyComponent/>要写上标签,函数定义首字母需要大写
</script>

执行了ReactDOM.render(<MyComponent/>。。之后,React解析组件标签,找到MyComponent组件,发现组件是使用函数定义的,随后调用该函数,将返回的虚拟DOM转为真是DOM,随后呈现在页面中。

类式组件:

上一篇文章中:Python如何解析HTML和XML数据,我们介绍如何使用Python解析HTML和XML数据,其中提到xpath的使用。事实上,xpath的功能非常强大,熟练使用可以给数据提取带来极大的方便,所以今天打算做进一步的介绍。

准备

本文依然使用lxml进行html/xml数据解析,可以自行使用pip进行安装,如不熟悉pip的使用,可以参考另一篇文章:如何管理python软件包。

先使用urllib2去抓取一个HTML页面,比如前篇提到的中航电子2017年一季度交易记录:

>>> import urllib2

>>> url = 'http://quotes.money.163.com/trade/lsjysj_600372.html?year=2017&season=1'

>>> rsp = urllib2.urlopen(url).read()

使用lxml.html软件包进行解析:

>>> from lxml import html

>>> doc = html.document_fromstring(rsp)

>>> doc.tag

'html'

可以看到,当前doc代表的是html元素。

什么是XPath

XPath,是XML Path Language的缩写,是一种用于从XML文档中选取节点的查询语言,也可以用于对节点内容进行计算。xpath的标准是由W3C制定的。xpath虽然经历了多个版本(1999-v1.0, 2007-v2.0, 2014-v3.0, 2017-v3.1),但v1.0的使用仍然最广泛,其官方文档可以参考:

https://www.w3.org/TR/xpath/

XPath表达式

在xpath看来,XML文档就是一颗由节点构成的树,其中节点可以分为元素节点(element node)、属性节点(attribute node)和文本节点(text node)。xpath最基本的语法结构是表达式(expression),表达式运行之后可以返回一下四类结果:

  • 一组节点(不重复)

  • 布尔取值true/false

  • 数值(浮点类型)

  • 字符串

分别看下面例子:

>>> doc.xpath('child::*')

[<Element head at 0x10f478ec0>, <Element body at 0x10f478ba8>]

>>> doc.xpath('3 < 2')

False

>>> doc.xpath('3 = 3')

True

>>> doc.xpath('3 + 2')

5.0

>>> doc.xpath('2 * 3')

6.0

>>> doc.xpath('6 div 3')

2.0

>>> doc.xpath('3 mod 2')

1.0

>>> doc.xpath('/html/head/meta[@name="robots"]/@name')

['robots']

路径与步

XPath最重要的一类表达式,是用于节点选取的路径(Location Path),看一个例子:

>>> doc.xpath('/html/head/meta[@name="robots"]')

[<Element meta at 0x10f478ba8>]

可以看到,一条路径有若干步(step)组成,步之间使用斜杠"/"分隔,每一步都从上下文节点中选取满足条件的若干节点。路径可分为相对路径和绝对路径类,比如:

>>> doc.xpath('/html/head/meta[@name="robots"]') #绝对路径,从根节点开始

[<Element meta at 0x10f478ba8>]

>>> doc.xpath('head/meta[@name="robots"]') #相对路径,从当前节点开始

[<Element meta at 0x10f478ba8>]

如果想同时查询多条路径,可以使用或“|”操作符:

>>> doc.xpath('/html/head | /html/body')

[<Element head at 0x10f478c58>, <Element body at 0x10f478c00>]

轴(axis)

上面的路径采用的都是缩写的形式,其实它还有完全形式的写法,比如:

>>> doc.xpath('/child::html/child::head/child::meta[@name="robots"]')

[<Element meta at 0x10f478ba8>]

在该写法中,以child::meta[@name="robots"]为例,这一步是什么意思呢?对该步而言,上下文节点是head,该步的意思是从head的meta类型的子节点中选取name属性取值为robots的节点,其中child是axis的名字,meta是要选择的节点类型,@name="robots"是要满足的条件。事实上,每一步都由这三部分构成的,它们的作用:

  • 一个轴(axis):指明该步要选取的节点跟当前节点的关系,例如上面的child说明要选取当前节点的子节点

  • 一个节点测试(node test):指明这个路径步要选取的节点类型,比如上面child::meta要选取的是meta类型的节点

  • 零个或多个谓词(predicates)组成:起过滤作用、选取特定节点,比如上面[@name="robots"]要筛选属性值为robots的节点

除了child axis, xpath还定义了很多其他的axis,请看下表:

看下面一些例子:

1) 获取子节点:getchildren()等价于child::*

>>> doc.xpath('child::*')

[<Element head at 0x10e8bad60>, <Element body at 0x10e8e5d60>]

>>> doc.getchildren()

[<Element head at 0x10e8bad60>, <Element body at 0x10e8e5d60>]

2) 获取当前节点:"."等价于 self::node()

>>> doc.xpath(".")

[<Element html at 0x10dc1d4c8>]

>>> doc.xpath("self::node()")

[<Element html at 0x10dc1d4c8>]

3) 获取父节点:".."等价于parent::node()

>>> doc.head.xpath("..")

[<Element html at 0x10dc1d4c8>]

>>> doc.head.xpath("parent::node()")

[<Element html at 0x10dc1d4c8>]

4)ancestor轴和descendant轴

分别代表当前元素所有祖先元素、所有后代元素,比如:

>>> meta.xpath('ancestor::*')

[<Element html at 0x10dc1d4c8>, <Element head at 0x10e8bad60>]

>>> meta.xpath('ancestor::head')

[<Element head at 0x10e8bad60>]

>>> doc.xpath('descendant::table')

>>> doc.xpath('descendant::table[@id="tcdatafields"]')

>>> doc.xpath('//table[@id="tcdatafields"]')

5)ancestor-or-self 和 descendant-or-self轴

分别表示当前元素或其所有祖先元素、当前元素或其所有后代元素,比如:

>>> meta.xpath('ancestor-or-self::*')

[<Element html at 0x10dc1d4c8>, <Element head at 0x10e8bad60>, <Element meta at 0x10e8bae68>]

6)child和parent轴

分别表示当前元素所有子元素、父元素:

>>> doc.xpath('child::*')

[<Element head at 0x10e8bad60>, <Element body at 0x10e8baf18>]

>>> doc.xpath('child::head')

[<Element head at 0x10e8bad60>]

>>> head.xpath('child::meta[1]')

[<Element meta at 0x10e8baf18>]

>>> head.xpath('child::meta[position()<3]')

[<Element meta at 0x10e8baf18>, <Element meta at 0x10e8bad08>]

7)attribute轴

表示当前元素的所有属性,例如下面是meta元素的name和content两个属性以及取值:

>>> meta.items()

[('name', 'googlebot'), ('content', 'index, follow')]

获取所有属性取值:

>>> meta.xpath('attribute::*')

['googlebot', 'index, follow']

获取name属性的取值:

>>> meta.xpath('attribute::name')

['googlebot']

8)following和preceding

分别表示当前元素的所有后继元素、前置元素,比如:

>>> meta.xpath('following::*')

>>> meta.xpath('preceding::*')

9)following-sibling和preceding-sibling轴

分别表示当前元素的所有平级后继元素、平级前置元素,比如:

>>> meta.xpath('preceding-sibling::*')

>>> meta.xpath('following-sibling::*')

10)self轴

表示当前元素自身

>>> doc.xpath("self::*")

[<Element html at 0x10dc1d4c8>]

使用谓词(predicates)

谓词就是step中使用中括号[...]定义的那部分,使用谓词能实现精确查找,看下面的例子:

>>> doc.xpath('/html/head/meta')

[<Element meta at 0x10e8baf70>, <Element meta at 0x10e8bae10>, <Element meta at 0x10e8bacb0>, <Element meta at 0x10e8baf18>, <Element meta at 0x10e8bad08>, <Element meta at 0x10e8bafc8>, <Element meta at 0x10e8bae68>]

1) 位置谓词

>>> doc.xpath('/html/head/meta[1]')

[<Element meta at 0x10e8baf70>]

>>> doc.xpath('/html/head/meta[2]')

[<Element meta at 0x10e8bae10>]

>>> doc.xpath('/html/head/meta[last()]')

[<Element meta at 0x10e8bae68>]

>>> doc.xpath('/html/head/meta[last()-1]')

[<Element meta at 0x10e8bae10>]

>>> doc.xpath('/html/head/meta[position()<3]')

[<Element meta at 0x10e8baf70>, <Element meta at 0x10e8bae10>]

注:这里使用了last()和position()两个函数,xpath还支持更多的函数,结合这些函数可以获得非常强大的处理能力。

2) 属性谓词

含有属性name的meta元素:

>>> doc.xpath('/html/head/meta[@name]')

[<Element meta at 0x10e8bacb0>, <Element meta at 0x10e8baf70>, <Element meta at 0x10e8bae10>, <Element meta at 0x10e8bae68>]

含有属性name而且其取值为robots的meta元素:

>>> doc.xpath('/html/head/meta[@name="robots"]')

[<Element meta at 0x10e8bacb0>]

含有任意属性的meta元素:

>>> doc.xpath('/html/head/meta[@*]')

3) 函数谓词

xpath内置很多函数,灵活使用这些函数,可以极大提升查找效率,比如:

-使用 text()函数

>>> doc.xpath('//td[text()="2017-03-21"]')

[<Element td at 0x10e8bacb0>]

- 使用contains函数

>>> [ td.text for td in doc.xpath('//td[contains(text(), "2017-03-2")]')]

['2017-03-29', '2017-03-28', '2017-03-27', '2017-03-24', '2017-03-23', '2017-03-22', '2017-03-21', '2017-03-20']

- 使用starts-with函数

>>> [ td.text for td in doc.xpath('//td[starts-with(text(),"2017-02-2")]')]

['2017-02-28', '2017-02-27', '2017-02-24', '2017-02-23', '2017-02-22', '2017-02-21', '2017-02-20']

>>> [ td.text for td in doc.xpath('//td[text()>21.0 and text()<23.0]')]

['21.02']

>>> [ td.text for td in doc.xpath('//td[text()< -2.5 or text()>21.0]')]

['21.02', '-2.64']

通配符

xpath也支持通配符"*",其中'*"可以匹配任何标签元素,"@*"可以匹配任何元素属性,node()可以匹配任何节点:

>>> head.xpath('./*')

[<Element title at 0x10e8baf18>, <Element meta at 0x10e8bacb0>, <Element meta at 0x10e8baf70>, <Element meta at 0x10e8bad08>, <Element meta at 0x10e8bafc8>, <Element meta at 0x10e8bae10>, <Element meta at 0x10e8c6050>, <Element meta at 0x10e8bae68>, <Element link at 0x10e8c60a8>, <Element link at 0x10e8c6100>]

>>> head.xpath('./meta[@*]')

[<Element meta at 0x10e8baf18>, <Element meta at 0x10e8bacb0>, <Element meta at 0x10e8baf70>, <Element meta at 0x10e8bad08>, <Element meta at 0x10e8bafc8>, <Element meta at 0x10e8bae10>, <Element meta at 0x10e8bae68>]

>>> head.xpath('./node()')

['\r\n', <Element title at 0x10e8badb8>, '\r\n', <Element meta at 0x10e8baf18>, '\r\n', <Element meta at 0x10e8bacb0>, '\r\n', <Element meta at 0x10e8baf70>, '\r\n', <Element meta at 0x10e8bad08>, '\r\n', <Element meta at 0x10e8bafc8>, '\r\n', <Element meta at 0x10e8bae10>, '\r\n', <Element meta at 0x10e8bae68>, '\r\n', <Element link at 0x10e8c6050>, '\n', <Element link at 0x10e8c60a8>, '\r\n']

今天就写这么多。