本站消息

  出租广告位,需要合作请联系站长

  今日名言-想象你自己对困难作出的反应,不是逃避或绕开它们,而是面对它们,同它们打交道,以一种进取的和明智的方式同它们奋斗 。——马克斯威尔·马尔兹

  今日名言-用谅解、宽恕的目光和心理看人、待人。人就会觉得葱笼的世界里,春意盎然,到处充满温暖。——蔡文甫


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

暂无数据

Python爬虫:通过爬取CSDN博客信息,学习lxml库与XPath语法

发布于2021-07-25 07:08     阅读(542)     评论(0)     点赞(18)     收藏(0)


在这里插入图片描述

lxml库

lxml是Python的一个解析库,专门用于解析XML与HTML,支持XPath解析方式。由于lxml库的底层是使用C语言编写的,所以其解析效率非常的高。

在我们后面讲解使用该库之前,我们需要安装该库。一般通过如下命令进行安装即可,代码如下:

pip install lxml

lxml基本用法

既然,lxml库支持解析XML以及HTML,那么肯定就需要学会这2种文档的解析方式。下面,我们来分别讲解。

解析XML文件

首先,我们需要使用lxml库解析XML文件,这里XML文件其实有很多种类,这里博主随便定义一个XML进行解析。

XML代码如下:

<?xml version="1.0" encoding="utf-8"?>
<people>
    <zhangsan class="法外狂徒">
        <sex></sex>
        <age>21</age>
    </zhangsan>
    <lisi class="法外狂徒的伙伴">
        <sex></sex>
        <age>21</age>
    </lisi>
</people>

解析示例代码如下所示:

from lxml import etree

tree = etree.parse("lxml_xml.xml")
print(str(etree.tostring(tree, encoding='utf-8'), 'utf-8'))
root = tree.getroot()
print("根节点", root.tag)
children = root.getchildren()
for child in children:
    print("sex:", child[0].text)
    print("class :", child.get('class'))

运行之后,效果如下所示:

xml解析

解析HTML文件

解析HTML比XML稍微复杂一点,它需要创建一个HTMLParser()对象传入到parser()方法中,因为其默认是解析XML的。

HTML代码如下:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>我是一个测试页面</title>
</head>
<body>
</body>
</html>

解析代码如下所示:

from lxml import etree

parser = etree.HTMLParser()
tree = etree.parse('demo.html', parser)
root = tree.getroot()
result = etree.tostring(root, encoding='UTF-8', pretty_print=True, method='html')
print(root.tag)
children = root.getchildren()
print("语言:", children[0].get('lang'))
print(root[0][1].text)

运行之后,效果如下所示:

HTML解析

XPath

估计细心的小伙伴,已经看出lxml库直接使用的弊端了。因为这是小编写的一个简单的HTML与XML,所以它的层级很低。

如果是真实的网页,那么可能层级会很多,如果还按数组这样一层一层往下查找,估计能搞出个十几维的数组。这样太复杂了。

所以,这里我们需要引入XPath进行辅助解析。

什么是XPath

XPath于1991年11月6日称为W3C标准,它被设计为可以在XSLT、XPointer以及其他XML解析软件中使用,其中文文档为:

https://www.w3school.com.cn/xpath/index.asp

XPath全称XML Path Language,中文叫XML路径语言,它是一种在XML文档中查询信息的语言。

最初虽然只支持XML文件,但是后来随着版本的迭代,已经可以支持HTML文件的解析与搜索,因为HTML与XML同源。

XPath语法

XPath语言的基本语法就是多级目录的层级结构,但比数组那种容易理解的多。下表是博主归纳总结的XPath语法规则:

语法意义
nodename选取此节点的所有子节点
/从当前节点选取直接子节点
//从当前节点选取子孙节点
.选取当前节点
选取当前节点的父亲节点
@选取属性

XPath实战

既然,我们已经了解XPath具体的语法结构,那么我们将一一实战这些语法,让读者更清晰,更快捷的掌握。

测试HTML文件如下所示:

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="utf-8">
<title>我是一个测试页面</title>
</head>
<body>
<ul>
  <li><a href="https://liyuanjinglyj.blog.csdn.net/">我的主页</a></li>
  <li><a href="https://www.csdn.net/">CSDN首页</a></li>
  <li class="li"><a href="https://www.csdn.net/nav/python" class="aaa">Python板块</a></li>
</ul>
</body>
</html>

选取某节点的所有子孙节点

假设我们需要获取上面HTML文件中<ul>标签的所有<a>子节点的链接与文本。我们需要如果去操作呢?示例代码如下:

from lxml import etree

parser = etree.HTMLParser()
html = etree.parse('demo.html', parser)
nodes = html.xpath("//ul//a")
for index in range(0, len(nodes)):
    print("网址:", nodes[index].get('href'), "  文本:", nodes[index].text)

运行之后,效果如下:

双斜杠的用法

双斜杠“//”代表获取当前节点下的子孙节点,也就是说,直接在根节点操作,就是获取根节点下面的所有该标签。

选取某节点的所有子节点

还是上面这个例子,我们如果使用单斜杠“/”获取所有的<a>标签呢?

因为<a>标签是<li>标签的子节点,所以我们需要获取<li>,再通过单斜杠"/"获取<a>标签。示例代码如下:

from lxml import etree

parser = etree.HTMLParser()
html = etree.parse('demo.html', parser)
nodes = html.xpath("//li/a")
for index in range(0, len(nodes)):
    print("网址:", nodes[index].get('href'), "  文本:", nodes[index].text)

如上面代码所示,我们把xpath语法改成“//li/a”即可。运行之后,效果与上面一模一样。

通过属性选取某节点的父节点

对于当前节点来说,我们只需要通过其标签与属性确认,自然就可以获取当前节点。所以.这里就不赘述了。

我们直接介绍后两种语法,通过"@“查找属性,然后通过”.."查找其父节点。

实战,通过class等于aaa的节点获取父亲节点,然后获取其属性class的值。示例代码如下所示:

from lxml import etree

parser = etree.HTMLParser()
html = etree.parse('demo.html', parser)
nodes = html.xpath("//a[@class='aaa']/../@class")
print(nodes)

运行之后,效果如下:

属性与父节点

除了通过/…获取父节点之外,我们还可以通过parent::*获取父节点。那么同样的转换语法也可以得到如上图所示的结果。(把…替换成即可)

多属性匹配

我们还是来获取那个有class的<a>标签,这里使用多属性匹配原则。

也就是,我们匹配其父节点class等于li以及class等于aaa的标签<a>。那么如何首先呢?示例如下:

from lxml import etree

parser = etree.HTMLParser()
html = etree.parse('demo.html', parser)
nodes = html.xpath("//a[contains(@class,'aaa') and ../@class='li']")
print(nodes[0].text)

运行之后,效果如下:

多属性匹配

看看上面的输出图,是不是最后一个<a>标签的文本内容?这里通过and进行多属性条件判断。

XPath运算符规则

不过,这里就涉及XPath运算符规则了。博主这里,也列出了一个专门的运算符规则的表格,方便读者查阅参考。

运算符描述示例意义
andclass=‘name’ and href=‘www’如果class等于name并且href等于www,则返回true,否则false
mod取余10 mod 31
orclass=‘name’ or class=‘www’如果class等于name或者www,则返回true,都不等于则返回false
div除法10 div 52
+加法10+515
-减法10-55
*乘法10*550
=等于value=520如果value等于520,返回true,否则返回false
!=不等于value!=520如果value不等于520,返回true,否则返回false
<小于value<520如果value小于520,返回true,否则返回false
>大于value>520如果value大于520,返回true,否则返回false
<=小于等于value<=520如果value小于等于520,返回true,否则返回fals
>=大于等于value>=520如果value大于等于520,返回true,否则返回false

实战:爬取CSDN个人博文

我们先通过chrome,或者任意浏览器按F12打开查看CSDN个人主页的元素,可以看到,这里的div是整个主页内容的div。

爬虫
同时,其下边的所有子div都是一篇篇作者的博文内容。那么我们就可以先通过class="article-list"找到主页博文列表。

然后,在一条一条的遍历子div获取里面的每篇博文信息即可。不过,我们首先需要获取网页的HTML文本,通过requests进行获取。

然后,我们再来看看其标题与链接到底在哪里?如下图所示:

标签代码
可以看到,标题与链接都在每个div的<h4>标签中,而描述信息在class='content’的<p>标签中,知道了这些,我们来获取主页的所有博文。

示例代码如下:

from lxml import etree
import requests

url = "https://blog.csdn.net/liyuanjinglyj"
session = requests.session()
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'zh-CN,zh;q=0.9',
    'cache-control': 'max-age=0',
    'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9'
}
result = requests.get(url, headers=headers)
result.encoding = result.apparent_encoding
html = etree.HTML(result.text)
urlStr = html.xpath("//div[@class='article-list']//div/h4/a/@href")
titleStr = html.xpath("//div[@class='article-list']//div/h4/a/text()")
titleStr = [i for i in titleStr if i.strip() != '']
contentStr = html.xpath("//div[@class='article-list']//div/p[@class='content']/text()")
for url, title, content in zip(urlStr, titleStr, contentStr):
    print("博文链接:", url)
    print("博文标题:", title.strip())
    print("博文描述:", content.strip())

运行之后,效果如下:
最终效果
这里有一个很奇怪的问题,相信大家也发现了,我们titleStr遍历了2遍,其他的只遍历的一遍这是为什么呢?我们先来看一张图:
文本
这里获取<a>标签文本的时候,默认是获取了2个,一个是空,一个才是下面的标题。所以,这里每次获取<a>标签文本标题时,都是一个空白,一个标题。

所以,我们在后续遍历的时候,应该去除掉空白字符串。只要标题。

XPath最简单的玩法

如果你是安装的Chrome,那么XPath语法,你可以不必学。因为这个浏览器可以直接生成XPath。

比如,我们获取上面的<a>标签,那么如何获取呢?只要选中<a>标签,然后按住右键选择Copy-Copy XPath即可。如下图所示:
复制Xpath

不过,博主不建议这么做。因为这里Copy的XPath仅仅只是针对当前的标签,而我们上面获取的标签是一个有规则的标签列表。而你不学习的XPath语法的话,这要是有100个列表标签,你难道还复制XPath语法100次不成?而学习过XPath只需要一行代码,然后遍历即可。

原文链接:https://blog.csdn.net/liyuanjinglyj/article/details/118860417



所属网站分类: 技术文章 > 博客

作者:想要飞翔的天使

链接:http://www.pythonpdf.com/blog/article/422/bd4fc83d0f9c5abbcfb0/

来源:编程知识网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

18 0
收藏该文
已收藏

评论内容:(最多支持255个字符)