数据提取概述

一. 响应内容的分类

在发送请求获取响应之后,可能存在多种不同类型的响应内容;而且很多时候,我们只需要响应内容中的一部分数据

1. 结构化的响应内容

(1). json字符串

可以使用re,json等模块来提取特定数据

(2). xml字符串

可以使用re,lxml等模块来提取特定数据

2. 非结构化的响应内容

(1). html字符串

可以使用re,lxml等模块来提取特定数据

二. 认识xml以及和html的区别

1. 认识xml

xml是一种可扩展标记语言,样子和html很像,功能更专注于对传输和存储数据

 <bookstore>
 <book category="COOKING">
   <title lang="en">Everyday Italian</title>
   <author>Giada De Laurentiis</author>
   <year>2005</year>
   <price>30.00</price>
 </book>
 <book category="CHILDREN">
   <title lang="en">Harry Potter</title>
   <author>J K. Rowling</author>
   <year>2005</year>
   <price>29.99</price>
 </book>
 <book category="WEB">
   <title lang="en">Learning XML</title>
   <author>Erik T. Ray</author>
   <year>2003</year>
   <price>39.95</price>
 </book>
 </bookstore>

上面的xml内容可以表示为下面的树结构:

https://picsur.cloud.fairies.ltd/i/bd2575d0-4df3-429a-9c05-13cc4b3b7660.webp

2. xml和html的区别

https://picsur.cloud.fairies.ltd/i/1604c5e8-6936-42c1-986b-194ac0f01954.webp

  • html

    • 超文本标记语言

    • 为了更好的显示数据,侧重点是为了显示

  • xml

    • 可扩展标记语言

    • 为了传输和存储数据,侧重点是在于数据内容本身

3. 常用数据解析方法

https://picsur.cloud.fairies.ltd/i/c2746eff-a975-4db2-ab3f-e2d6ad9cc503.webp

数据提取: jsonpath模块

一. jsonpath模块的使用场景

如果有一个多层嵌套的复杂字典,想要根据key和下标来批量提取value,这是比较困难的.jsonpath模块就能解决这个痛点,接下来我们就来学习jsonpath模块

jsonpath可以按照key对python字典进行批量数据提取

二. jsonpath模块的使用方法

1. jsonpath模块的安装

jsonpath是第三方模块,需要额外安装

 pip install jsonpath

2. jsonpath模块提取数据的方法

 from jsonpath import jsonpath
 ret = jsonpath(a, 'jsonpath语法规则字符串')

3. jsonpath语法规则

https://picsur.cloud.fairies.ltd/i/f8f55b1f-9bce-43f4-8969-3e133ffffa8e.webp

4. jsonpath使用示例

 book_dict = {
   "store": {
     "book": [
       { "category": "reference",
         "author": "Nigel Rees",
         "title": "Sayings of the Century",
         "price": 8.95
       },
       { "category": "fiction",
         "author": "Evelyn Waugh",
         "title": "Sword of Honour",
         "price": 12.99
       },
       { "category": "fiction",
         "author": "Herman Melville",
         "title": "Moby Dick",
         "isbn": "0-553-21311-3",
         "price": 8.99
       },
       { "category": "fiction",
         "author": "J. R. R. Tolkien",
         "title": "The Lord of the Rings",
         "isbn": "0-395-19395-8",
         "price": 22.99
       }
     ],
     "bicycle": {
       "color": "red",
       "price": 19.95
     }
   }
 }
 ​
 from jsonpath import jsonpath
 ​
 print(jsonpath(book_dict, '$..author')) # 如果取不到将返回False # 返回列表,如果取不到将返回False

https://picsur.cloud.fairies.ltd/i/f8f55b1f-9bce-43f4-8969-3e133ffffa8e.webp

三. jsonpath练习

我们以拉勾网城市JSON文件 http://www.lagou.com/lbs/getAllCitySearchLabels.json 为例,获取所有城市的名字的列表,并写入文件。

参考代码:

 import requests
 import jsonpath
 import json
 ​
 # 获取拉勾网城市json字符串
 url = '<http://www.lagou.com/lbs/getAllCitySearchLabels.json>'
 headers = {"User-Agent": "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)"}
 response =requests.get(url, headers=headers)
 html_str = response.content.decode()
 ​
 # 把json格式字符串转换成python对象
 jsonobj = json.loads(html_str)
 ​
 # 从根节点开始,获取所有key为name的值
 citylist = jsonpath.jsonpath(jsonobj,'$..name')
 ​
 # 写入文件
 with open('city_name.txt','w') as f:
     content = json.dumps(citylist, ensure_ascii=False)
     f.write(content)

数据提取: lxml模块

一. 了解 lxml模块和xpath语法

对html或xml形式的文本提取特定的内容

  • lxml模块可以利用XPath规则语法,来快速的定位HTML\XML 文档中特定元素以及获取节点信息(文本内容、属性值)

  • XPath (XML Path Language) 是一门在 HTML\XML 文档中查找信息的语言,可用来在 HTML\XML 文档中对元素和属性进行遍历

  • 提取xml、html中的数据需要lxml模块和xpath语法配合使用

二. 谷歌浏览器xpath helper插件的安装和使用

1. 谷歌浏览器xpath helper插件的作用

在谷歌浏览器中对当前页面测试xpath语法规则

2. 谷歌浏览器xpath helper插件的安装和使用

(1). xpath helper插件的安装

https://picsur.cloud.fairies.ltd/i/7d34f154-29e3-4680-a666-e2c8eb2d953f.webp

https://picsur.cloud.fairies.ltd/i/ad594923-13bd-42aa-a3a3-d145575f83dd.webp

https://picsur.cloud.fairies.ltd/i/27b71cae-4f27-496f-929c-515d5cde783f.webp

https://picsur.cloud.fairies.ltd/i/bdad393a-b5a6-42ae-9591-f13c7553ae7d.webp

三. xpath的节点关系

1. xpath中的节点是什么

每个html、xml的标签我们都称之为节点,其中最顶层的节点称为根节点。我们以xml为例,html也是一样的

https://picsur.cloud.fairies.ltd/i/a57a0bef-faf2-4921-bd96-3261313e0520.webp

2. xpath中节点的关系

https://picsur.cloud.fairies.ltd/i/c8155caf-bc8b-47af-94d9-a412135cf8c7.webp

authortitle的第一个兄弟节点

四. xpath语法-基础节点选择语法

XPath 使用路径表达式来选取 XML 文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。使用chrome插件选择标签时候,选中时,选中的标签会添加属性class="xh-highlight"

1. xpath定位节点以及提取属性或文本内容的语法

表达式

描述

nodename

选中该元素。

/

从根节点选取、或者是元素和元素间的过渡。

//

从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。

.

选取当前节点。

..

选取当前节点的父节点。

@

选取属性。

text()

选取文本。

五. xpath语法-节点修饰语法

可以根据标签的属性值、下标等来获取特定的节点

1. 节点修饰语法

路径表达式

结果

//title[@lang="eng"]

选择lang属性值为eng的所有title元素

/bookstore/book[1]

选取属于 bookstore 子元素的第一个 book 元素。

/bookstore/book[last()]

选取属于 bookstore 子元素的最后一个 book 元素。

/bookstore/book[last()-1]

选取属于 bookstore 子元素的倒数第二个 book 元素。

/bookstore/book[position()>1]

选择bookstore下面的book元素,从第二个开始选择

//book/title[text()='Harry Potter']

选择所有book下的title元素,仅仅选择文本为Harry Potter的title元素

/bookstore/book[price>35.00]/title

选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

2. 关于xpath的下标

  • 在xpath中,第一个元素的位置是1

  • 最后一个元素的位置是last()

  • 倒数第二个是last()-1

六. xpath语法-其他常用节点选择语法

可以通过通配符来选取未知的html、xml的元素

1. 选取未知节点的语法

通配符

描述

*

匹配任何元素节点。

node()

匹配任何类型的节点。

七. lxml模块的安装与使用示例

lxml模块是一个第三方模块,安装之后使用

对发送请求获取的xml或html形式的响应内容进行提取

1. lxml模块的安装

 pip install lxml

2. 爬虫对html提取的内容

  • 提取标签中的文本内容

  • 提取标签中的属性的值

    • 比如,提取a标签中href属性的值,获取url,进而继续发起请求

3. lxml模块的使用

  1. 导入lxml 的 etree 库

     from lxml import etree
  2. 利用etree.HTML,将html字符串(bytes类型或str类型)转化为Element对象,Element对象具有xpath的方法,返回结果的列表

     html = etree.HTML(text)
     ret_list = html.xpath("xpath语法规则字符串")
  3. xpath方法返回列表的三种情况

    1. 返回空列表:根据xpath语法规则字符串,没有定位到任何元素

    2. 返回由字符串构成的列表:xpath字符串规则匹配的一定是文本内容或某属性的值

    3. 返回由Element对象构成的列表:xpath规则字符串匹配的是标签,列表中的Element对象可以继续进行xpath

4. lxml模块使用示例

运行下面的代码,查看打印的结果

from lxml import etree
text = '''
<div>
  <ul>
    <li class="item-1">
      <a href="link1.html">first item</a>
    </li>
    <li class="item-1">
      <a href="link2.html">second item</a>
    </li>
    <li class="item-inactive">
      <a href="link3.html">third item</a>
    </li>
    <li class="item-1">
      <a href="link4.html">fourth item</a>
    </li>
    <li class="item-0">
      a href="link5.html">fifth item</a>
  </ul>
</div>
'''

html = etree.HTML(text)

#获取href的列表和title的列表
href_list = html.xpath("//li[@class='item-1']/a/@href")
title_list = html.xpath("//li[@class='item-1']/a/text()")

#组装成字典
for href in href_list:
    item = {}
    item["href"] = href
    item["title"] = title_list[href_list.index(href)]
    print(item)

八. 练习

将下面的html文档字符串中,将每个class为item-1的li标签作为1条新闻数据。提取a标签的文本内容以及链接,组装成一个字典。

text = ''' <div> <ul>
        <li class="item-1"><a>first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive"><a href="link3.html">third item</a></li>
        <li class="item-1"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a>
        </ul> </div> '''
  • 注意

    • 先分组,再提取数据,可以避免数据的错乱

    • 对于空值要进行判断

  • 参考代码

from lxml import etree
text = '''
<div>
<ul>
  <li class="item-1">
    <a href="link1.html">first item</a>
  </li>
  <li class="item-1">
    <a href="link2.html">second item</a>
  </li>
  <li class="item-inactive">
    <a href="link3.html">third item</a>
  </li>
  <li class="item-1">
    <a href="link4.html">fourth item</a>
  </li>
  <li class="item-0">
    a href="link5.html">fifth item</a>
</ul>
</div>'''

#根据li标签进行分组
html = etree.HTML(text)
li_list = html.xpath("//li[@class='item-1']")

#在每一组中继续进行数据的提取
for li in li_list:
    item = {}
    item["href"] = li.xpath("./a/@href")[0] if len(li.xpath("./a/@href"))>0 else None
    item["title"] = li.xpath("./a/text()")[0] if len(li.xpath("./a/text()"))>0 else None
    print(item)

九. lxml模块中etree.tostring函数的使用

运行下边的代码,观察对比html的原字符串和打印输出的结果

from lxml import etree
html_str = ''' <div> <ul>
        <li class="item-1"><a href="link1.html">first item</a></li>
        <li class="item-1"><a href="link2.html">second item</a></li>
        <li class="item-inactive"><a href="link3.html">third item</a></li>
        <li class="item-1"><a href="link4.html">fourth item</a></li>
        <li class="item-0"><a href="link5.html">fifth item</a>
        </ul> </div> '''

html = etree.HTML(html_str)

handeled_html_str = etree.tostring(html).decode()
print(handeled_html_str)

1. 现象和结论

打印结果和原来相比:

  1. 自动补全原本缺失的li标签

  2. 自动补全html等标签

<html><body><div> <ul>
<li class="item-1"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</li></ul> </div> </body></html>
  • 结论

  • lxml.etree.HTML(html_str)可以自动补全标签

  • lxml.etree.tostring函数可以将转换为Element对象再转换回html字符串

  • 爬虫如果使用lxml来提取数据,应该以lxml.etree.tostring的返回结果作为提取数据的依据