5.HTML内容解析-XPath

目录

一、HTML基础结构

1.什么是HTML?

2.HTML标签

3.网页的结构化特点

二、XPath

1.XPath介绍

2.lxml介绍

3.lxml的安装

4.lxml.etree的使用

(1)将字符串格式的网页代码转换为Element对象

(2)使用etree.xpath表达式获取标签中的文本

(3)XPath语句格式

(4)标签构造技巧

(5)文本提取技巧

 三、练习:爬取豆瓣Top250数据


通过前面的学习,我们已经掌握了使用正则表达式从网页的源代码中提取数据的方法。这一章,我们将利用网页的结构化特点,学习使用更加高效的方法提取页面信息。

学习重点:

(1)HTML基础结构

(2)使用XPath从HTML源代码中提取有用信息

一、HTML基础结构

1.什么是HTML?

HTML指的是超文本标记语言 (Hyper Text Markup Language),它不是一种编程语言,而是一种标记语言。HTML可以描述一个网页的结构信息。

HTML与CSS、JavaScript分别代表了网页中的结构、样式与行为,他们构成了丰富多彩的互联网世界。

2.HTML标签

  • HTML 标签是由尖括号包围的关键词,比如 <html>
  • HTML 标签通常是成对出现的,比如 <b> 和 </b>
  • 标签对中的第一个标签是开始标签,第二个标签是结束标签
  • 开始和结束标签也被称为开放标签和闭合标签
<标签名>
    正文
</标签名>

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

示例:

<html>
<head>
    <title>My First Page.</title> # 这是网页的名字
<body>
    <h1>My First Heading.</h1> # 这是一个标题
    <p>My First Paragraph.</p> # 这是一个段落
</body>
</html>

 在元素的起始标签中, 还可以包含“属性”来设置元素的其他特性。 一个标签可以有多个属性,每个属性之间用空格隔开。

<div 属性名="属性值">显示在网页上的文本</div> # 拥有单个属性的标签

<div 属性名1="属性1的值" 属性名2="属性2的值">显示在网页上的文本</div> # 拥有多个属性的标签

拥有多个属性的标签

<div id="wrapper" class="wrapper_new wrapper_s">显示在网页上的文本</div>

3.网页的结构化特点

HTML标签的层级关系就像一棵树,html是树根,head和body是树枝,title是从head分出来的树枝,div是从body分出来的树枝。按照特定的标签,我们就可以一步步的找到想要的信息。

图1 HTML的树状结构

二、XPath

1.XPath介绍

XPath(XML Path) 是一门在 XML (Extensible Markup Language,可拓展标记语言)文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。

XPath 使用路径表达式在 XML 文档和HTML的树状结构中追踪目标节点或节点集。

XPath的详细语法可参考w3school

在Python中,为了使用XPath,需要使用lxml库。

2.lxml介绍

lxml XML工具箱是C库libxml2libxslt的Python绑定 。它的独特之处在于它将这些库的速度和XML功能的完整性与本机Python API的简单性结合在一起,该Python API大多数都兼容,但优于著名的 ElementTree API。最新版本适用于2.7到3.9的所有CPython版本。

lxml是功能最丰富且易于使用的库,用于处理Python语言中的XML和HTML。想要阅读lxml的完整文档可访问lxml官网

在python中我们主要使用lxml下的子库etree进行网页内容的解析。

3.lxml的安装

windows下安装

pip install lxml

进入python的交互环境,输入import lxml,如没有错误提示则lxml安装成功。

4.lxml.etree的使用

使用下面代码导入etree库

>>from lxml import etree

etree解析文本的四种方式:

  • fromstring() 解析字符串
  • HTML()解析HTML对象
  • XML()解析XML对象
  • parse()解析文件类型的对象 

(1)将字符串格式的网页代码转换为Element对象

使用etree.HTML将字符串格式的网页代码转换为_Element对象,调用HTML方法将自动把缺省的主要节点补齐。

from lxml import etree
html = '''
<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
'''
ele = etree.HTML(html) # 将字符串格式的网页代码转换为_Element对象
print(etree.tostring(ele)) # .tostring方法将_Element对象转换为字符串

# 输出
'<html><head>\n    <meta charset="UTF-8"/>\n    <title>Title</title>\n</head>\n</html>'

(2)使用etree.xpath表达式获取标签中的文本

content = ele.xpath('//title/text()') # 括号内的字符串是我们下面要重点了解的xpath语句
print(content)

# 输出
['Title']

(3)XPath语句格式

上面括号内的字符串就是一段XPath语句。XPath语句的核心就是写地址。

获取文本:

//标签1[@属性1=“属性值1”]/标签2[@属性2=“属性值2”]/……/text()

获取属性:

//标签1[@属性1=“属性值1”]/标签2[@属性2=“属性值2”]/……/@属性n

其中[@属性=“属性值”]不是必需的,它的主要作用是帮助过滤相同的标签。在不需要过滤相同标签的时候可以省略。

实例:从下面的网页源码中提取html、css、javascript三段信息,我们该怎么做?

<!DOCTYPE html>
<html lang="zh-en">
<head>
    <meta charset="UTF-8"/>
    <title>网络爬虫</title>
</head>
<body>
    <div id='useful'>
        <ul>有用的信息
            <li>html</li>
            <li>css</li>
            <li>javascript</li>
        </ul>
    </div>
    <div id='useless'>
        <ul>无用的信息
            <li>python</li>
            <li>java</li>
            <li>c#</li>
        </ul>
    </div>

</body>
</html>

XPath语句可以写成下面这样。

'//div[@id="useful"]/ul/li/text()'

如果是要获得charset的属性值,则可以写成:

'//meta/@charset'

(4)标签构造技巧

从目标开始倒着找

从需要提取的内容往上找标签,直到找到一个拥有“标志性属性值”的标签为止。在上面的例子中,我们要找的是html、css、javascript三段信息,往上找我们找到了li标签,但是我们知道无效的信息也是在另外3个li标签下;我们接着往上找到了ul标签,但这依然无法将有效信息和无效信息区别开,再接着往上,我们找到了<div id = 'useful'>,注意到无效的信息是在<div id = 'useless'>下,明显不同。<div id = 'useful'>就是拥有标志性属性值的标签,我们可以开始从这个标签开始定位。

使用Google Chrome浏览器辅助构造XPath

在Chrome的检查页面,右键点击想要追踪的内容,

图2 使用Chrome开发者工具复制追踪对象的XPath

 

复制出来的结果:

//*[@id="maincontent"]/h1

由于我们需要查找的是<h1>HTML 实例</h1>中的内容,我们改造一下上面的XPath就可以了。

//*[@id="maincontent"]/h1/text()

刚刚我们找到的是1级标题,接下来我们试试找到所有二级标题。

复制出来的xpath

//*[@id="maincontent"]/div[3]/h2

我们稍微解释下这一段:该目标对象从id为"maincontent"的标签开始,位于其后第3个div子标签下的h2标签下。另外值得一提的是div[3]方括号中的3代表的是id为"maincontent"标签下面的第3个<div>

标签,这里的编号从1开始,与编程语言中从0开始不一样。

按照以下写法,我们只能得到这一个标签下的内容。

url = 'https://www.w3school.com.cn/tags/tag_meta.asp'
response = requests.get(url).content
page = etree.HTML(response)
content = page.xpath('//*[@id="maincontent"]/div[3]/h2/text()')

# 输出
['浏览器支持']

为了获得所有二级标题的内容,我们将[3]删除即可。

url = 'https://www.w3school.com.cn/tags/tag_meta.asp'
response = requests.get(url).content
page = etree.HTML(response)
content = page.xpath('//*[@id="maincontent"]/div/h2/text()')
print(content)

# 输出
['定义和用法', '浏览器支持', 'HTML 与 XHTML 之间的差异', '提示和注释:', '必需的属性', '可选的属性', '全局属性', 'TIY 实例', '相关页面']

(5)文本提取技巧

  • 提取某个节点下的所有文本

实例:将<div id = 'useful'>/ul下的所有文本提取出来。

我们期望的结果是把“有用的信息”“html”"css""javascript"四个字符串找出来,我们尝试使用之前的方法匹配内容。

page = etree.parse(url)
content = page.xpath('//div[@id = "useful"]/ul/text()')
print(content)

# 输出
['有用的信息\n            ', '\n            ', '\n            ', '\n        ']

结果发现它只匹配到“有用的信息”和另外3个换行符‘\n’,这是因为xpath不会自动把自标签的文字提取出来。试试下面的方法将更有效。

page = etree.parse(url)
content = page.xpath('//div[@id = "useful"]/ul')
print(content[0].xpath('(string(.))'))

# 输出
有用的信息
            html
            css
            javascript

说明:此处采用了正则表达式中提到的先抓大后抓小原则。page.xpath('//div[@id = "useful"]/ul')将ul节点下的内容作为一个整体提取出来,接着对这个节点再使用一次xpath,string(.)关键字的作用是将当前整个节点中的所有字符串提取出来。

排除以相同字符串开头的无效文本

<html>
    <body>
        <div id="data-jobid">6575906</div>
        <div id="data-positionid">6575906</div>
        <div id="data-salary">20k-30k·16薪</div>
        <div id="data-company">耀天游戏</div>
        <div id="data-positionname">用户产品经理</div>
        <div id="data-companyid">10723</div>
        <div id="useless">无效的信息<div>
    </body>
</html>

我们需要从以上信息中将id属性值以“data”开头的所有标签下的内容提取出来,如果只是指定div标签 ,则势必包含了“无效信息”。

此种情况我们可以使用starts-with指定属性值的开头内容。

text = '''
<html>
    <body>
        <div id="data-jobid">6575906</div>
        <div id="data-positionid">6575906</div>
        <div id="data-salary">20k-30k·16薪</div>
        <div id="data-company">耀天游戏</div>
        <div id="data-positionname">用户产品经理</div>
        <div id="data-companyid">10723</div>
        <div id="useless">无效的信息<div>
    </body>
</html>
'''
page = etree.HTML(text)
content = page.xpath('//div[starts-with(@id,"data")]/text()')
print(content)

# 输出
['6575906', '6575906', '20k-30k·16薪', '耀天游戏', '用户产品经理', '10723']
  • 包含或不包含某些信息

如果想提取id属性值只包含“position”标签下的内容,可以使用contains

page = etree.HTML(text)
content = page.xpath('//div[contains(@id,"position")]/text()')
print(content)

# 输出
['6575906', '用户产品经理']

如果是不包含,则可增加not关键词

content = page.xpath('//div[not(contains(@id,"position"))]/text()')

 三、练习:爬取豆瓣Top250数据

爬取目标网站:豆瓣Top250:https://movie.douban.com/top250

爬取内容:电影名称、电影排名、电影评分、导演姓名、主演1姓名、上映时间、制片国家、电影类型

变量名称:movie_title、movie_rank、rating_score、director_name、actor_1_name、released_year、country、genres

爬取技术:requests、XPath

提交资料:代码文件、数据文件csv

技术提示:1.需要装饰请求头;2.先抓大后抓小,即先抓到每部电影代码段,再从中分别抓取需要的信息;3.有些数据是合并在一起的,需要分割,导演姓名和主演1姓名在一个text中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胡老师11452

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值