【Python 爬虫】HTML结构和XPath的使用

  • 用正则表达式从网页中提取数据虽然可行。但是,网页的源代码是一种结构化的数据,如果仅仅使用正则表达式,那么这种结构化的优势就没有被很好地利用起来。现在把正则表达式中举的那个例子再做一下演绎:

  • 有一个人,长得非常特别,身高3米,皮肤是绿色。如果他在你眼前,你必定可以一眼认出他。可是,现在只知道他在地球上,应该如何找到他?到全世界的每个地方都去看一下,直到遇到他为止。这种做法理论上当然没有问题,但却费时费力,而且人生苦短,可能一辈子也碰不到。但如果现在知道了他的地址“中国,北京,海淀区,xx路,xx号,第3层楼”,要找到他就易如反掌了。

HTML基础结构

  • HTML也就是前面章节提到的网页源代码,是一种结构化的标记语言。HTML可以描述一个网页的结构信息

  • HTMLCSSCascading Style Sheets,层叠样式表)、JavaScript一起构成了现代互联网的基石。

  • HTML中的标签都是成对出现的,标签的一般形式就如下图所示

<标签名>

   文本

</标签名>
  • 不加斜杠,表示标签开始;加上斜杠,表示标签结束。它们中间的部分,就是标签里面的元素。标签里面可以是另一个标签,也可以是一段文本。标签可以并列,也可以嵌套。

  • 不论谁在谁旁边,不论谁包含了谁,通过HTML 的这种表示方法,都可以轻易将不同标签的相对关系表现出来。

下面是一段真实是HTML代码的结构

<html>
    <head>
         <title>测试</title>
    </head>
    <body>
         <div class="useful">
			  <ul>
                   <li class="info">我需要的信息1</li>
                   <li class="info">我需要的信息2</li>
                   <li class="info">我需要的信息3</li>
			  </ul>
         </div>
         <div class="useless">
              <ul>
                   <li class="info">垃圾1</li>
                   <li class="info">垃圾2</li>
              </ul>
         </div>
     </body>
</html>
  • 每个标签除了标签名以外,还有“属性”。一个标签可以有0个、1个或者多个属性,所以一个真正的HTML标签应该是下面这样的:
<标签名 属性1="属性1的值" 属性2="属性2的值">
	显示在网页上的文本
</标签名>
  • 可以表示成一个倒立的树形结构,如下所示:

image-20220320164245228

  • HTML就是通过这样一种一层套一层的结构来描述一个网页各个部分的相对关系的。这里的<html></html><div></div>等都是HTML的标签。如果把HTML最外层的标签<html>当作树根,从树根上面分出了两个树枝<head><body>,<body>里面又分出了class分别为usefuluseless的两个树枝

跳转顶部


实现XPath从HTML源代码中提取有用的信息

XPath的介绍

  • XPath(XML Path)是一种查询语言,它能在XMLExtensible Markup Language,可扩展标记语言)和HTML的树状结构中寻找结点。

  • 形象一点来说,XPath就是一种根据“地址”来“找人”的语言。

  • 用正则表达式来提取信息,经常会出现不明原因的无法提取想要内容的情况。最后即便绞尽脑汁终于把想要的内容提取了出来,却发现浪费了太多的时间。

  • 需要寻找的内容越复杂,构造正则表达式所需要花费的时间也就越多。

  • XPath却不一样,熟练使用XPath以后,构造不同的XPath,所需要花费的时间几乎是一样的,所以用XPathHTML源代码中提取信息可以大大提高效率。

  • Python中,为了使用XPath,需要安装一个第三方库:lxml

跳转顶部


库的安装

Mac OS下安装lxml

  • 如果操作系统为Mac OS,可以直接使用pip:pip install lxml

Ubuntu下安装lxml

  • 如果操作系统为Ubuntu,可以使用如下命令安装:lxml:sudo apt-get install python-lxml

Windows下安装lxml

  • 如果操作系统为Windows,那么安装lxml的过程比较麻烦。如果直接使用pip install lxml命令安装,大概率会出现问题,因为lxml底层使用的是C语言来实现的,所以计算机需要安装C++的运行库
  • 首先下载对应版本的whl点击跳转,下载完成后再存放这个包的文件夹打开CMD,执行下面代码:pip install lxml‑3.7.3‑cp36‑cp36m‑win_amd64.whl
  • 若上面的方法不行,那么则可以将下载好的whl文件的后缀改成zip,然后解压缩,就会得到两个文件夹,如下所示:

image-20220320170838814

  • 把这两个文件夹复制到,Python安装文件夹下的Lib\site-packages即可
  • 打开Python交互环境,输入Import lxml,如果不报错,就说明安装成功

image-20220320171154013
跳转顶部


XPath语法讲解

<html>
  <head>
    <title>测试</title>
  </head>
  <body>
    <div class="useful">
      <ul>
        <li class="info">我需要的信息1</li>
        <li class="info">我需要的信息2</li>
        <li class="info">我需要的信息3</li>
      </ul>
    </div>

    <div class="useless">
      <ul>
        <li class="info">垃圾1</li>
        <li class="info">垃圾2</li>
      </ul>
    </div>
  </body>
</html>

如何获取以下的信息

我需要信息1

我需要信息2

我需要信息3

selector = lxml.html.fromstring(source)
info = selector.xpath('//div[@class="useful"]/ul/li/text()')
print(info)

image-20220320172402195

XPath语句的格式

XPath的使用格式如下所示,其中网页源代码可以使用requests来获取

import lxml.html

selector = lxml.fromstring('网页源代码')
info = selector.xpath('一段XPath语句')

XPath的核心就是写地址

获取文本:

//标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/..../text()

获取属性

//标签1[@属性1="属性值1"]/标签2[@属性2="属性值2"]/..../@属性n

注:[@属性="属性值"]不是必需的。它的作用是帮助过滤相同的标签。在不需要过滤相同标签的情况下可以省略。


标签1的选取

标签可以直接从html这个最外层的标签开始,一层一层往下找,这个时候XPath语句是这样的

/html/body/div[@class="useful"]/ul/li/text()

当以html开头的时候,它前面是单斜线。这样写虽然也可以达到目的,但是却多此一举。正如在淘宝买东西时,没有人会把收货地址的形式写为“地球,亚洲,中国,北京,海淀区,××路,××号”一样。地址前面的“地球,亚洲,中国”写了虽然也没错,但却没有必要。谁都知道全世界只有一个北京。而北京必定在中国,中国必定在亚洲,亚洲必定在地球上。所以,写收货地址的时候,直接写北京就可以了,前面的“地球,亚洲,中国”可以省略。XPath也是同样的道理。在XPath里面找到一个标志性的“地标”,然后从这个标志性的“地标”开始往下找就可以了。标志性的“地标”前面的标签都可以省略。


可以省略的属性

<div class="useful">
   
  <ul>
             
    <li class="info">我需要的信息1</li>
             
    <li class="info">我需要的信息2</li>
             
    <li class="info">我需要的信息3</li>
  </ul>
</div>

< ul >标签本身就没有属性,则写XPath的时候,其属性可以省略。
标签有属性,但是如果这个标签的所有属性值都相同,则可以省略属性,例如 < li class ="info" >,所有的 < li >标签都有一个class属性,值都为info,所以属性可以省略。


XPath的特殊情况

以相同的字符串开头,如下面的HTML代码

<!DOCTYPE html>
<html>
  <head lang="en">
    <meta charset="UTF-8" />
    <title></title>
  </head>
  <body>
    <div id="test-1">需要的内容1</div>
    <div id="test-2">需要的内容2</div>
    <div id="testfault">需要的内容3</div>
    <div id="useless">这是我不需要的内容</div>
  </body>
</html>
selector = lxml.html.fromstring(source)
content = selector.xpath('//div[starts-with(@id, "test")]/text()')
for each in content:
    print(each)

在这里插入图片描述


属性值包含相同的字符串

  • 寻找属性值包含某些字符串的元素时,XPath的写法格式和以某些字符串开头的写法格式是相同的,只不过关键字从“starts-with”变成了“contains”。例如提取所有属性值中包含“-key”的标签中的文本信息
source = '''
<!DOCTYPE html>
<html>
  <head lang="en">
    <meta charset="UTF-8" />
    <title></title>
  </head>
  <body>
    <div id="test-key">需要的内容1</div>
    <div id="test-key-2">需要的内容2</div>
    <div id="key-testfault">需要的内容3</div>
    <div id="useless">这是我不需要的内容</div>
  </body>
</html>
'''
selector = lxml.html.fromstring(source)
content = selector.xpath('//div[contains(@id, "key")]/text()')

XPath返回的对象执行XPath

<html>
  <head>
    <title>测试</title>
  </head>
  <body>
    <div class="useful">
      <ul>
        <li class="info">我需要的信息1</li>
        <li class="info">我需要的信息2</li>
        <li class="info">我需要的信息3</li>
      </ul>
    </div>

    <div class="useless">
      <ul>
        <li class="info">垃圾1</li>
        <li class="info">垃圾2</li>
      </ul>
    </div>
  </body>
</html>
useful = selector.xpath('//div[@class="useful"]')
info_list = useful[0].xpath('ul/li/text()')

注:在对XPath返回的对象再次执行XPath是不需要添加斜线,直接以标签名开始即可


不同标签下的文字

<!DOCTYPE html>
<html>
  <head lang="en">
    <meta charset="UTF-8" />
    <title></title>
  </head>
  <body>
    <div id="test3">
      aaaa,
      <span id="test4">
        bbbb,
        <ul>
          ccc,
          <li>dddd,</li>
        </ul>
        eeee,
      </span>
      ffff
    </div>
  </body>
</html>
selector = lxml.html.fromstring(source)
list_data = selector.xpath('//div[@id="test3"]/text()')
for data in list_data:
    print(data)

在这里插入图片描述
为什么只有两个字符串,是因为只有这两句是真正的属于div标签的,XPath不会自动把子标签的内容提取出来。若是要全部提取,只需要按照下面的写法

data_all = selector.xpath('string(//div[@id="test3"])')

在这里插入图片描述
跳转顶部


使用谷歌浏览器来辅助构造XPath

借助Google来协助分析网页的结构,可以大大提高分析效率

我们在需要分析的页面右击然后点击检查就可以查看网页的代码,或者可以直接使用快捷键F12查看
在这里插入图片描述
在这里插入图片描述
网页的代码如上图所示,但是我们一个个的去找我们所需要的也很麻烦,所以我们还是可以通过浏览器来快速查找
在这里插入图片描述
在这里插入图片描述
先选择红色的光标,然后点击自己想要查找的地方,就会直接跳转到所需要的代码处

在这里插入图片描述
此时点击复制即可
在这里插入图片描述
我们还可以借助浏览器来验证自己的XPath是否书写正确,Ctrl + F即可快速查询


测试:爬取豆瓣一周口碑排行榜
在这里插入图片描述

import requests
from lxml import etree

if __name__ == '__main__':
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36'
    }
    url = 'https://movie.douban.com/'
    page_text = requests.get(url=url, headers=headers).text
    # print(page_text)

    # 数据解析
    tree = etree.HTML(page_text)
    title_list = tree.xpath('//div[@class="billboard-bd"]/table/tr/td[2]/a/text()')
    print(title_list)

跳转顶部


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值