Python爬虫:通过爬取CSDN博客信息,学习lxml库与XPath语法
目录
- lxml库
- lxml基本用法
- 解析XML文件
- 解析HTML文件
- XPath
- 什么是XPath
- XPath语法
- XPath实战
- 选取某节点的所有子孙节点
- 选取某节点的所有子节点
- 通过属性选取某节点的父节点
- 多属性匹配
- XPath运算符规则
- 实战:爬取CSDN个人博文
- XPath最简单的玩法
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 etreetree = 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'))
运行之后,效果如下所示:
解析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 etreeparser = 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)
运行之后,效果如下所示:
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 etreeparser = 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 etreeparser = 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 etreeparser = 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 etreeparser = 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运算符规则了。博主这里,也列出了一个专门的运算符规则的表格,方便读者查阅参考。
运算符 | 描述 | 示例 | 意义 |
---|---|---|---|
and | 与 | class=‘name’ and href=‘www’ | 如果class等于name并且href等于www,则返回true,否则false |
mod | 取余 | 10 mod 3 | 1 |
or | 或 | class=‘name’ or class=‘www’ | 如果class等于name或者www,则返回true,都不等于则返回false |
div | 除法 | 10 div 5 | 2 |
+ | 加法 | 10+5 | 15 |
- | 减法 | 10-5 | 5 |
* | 乘法 | 10*5 | 50 |
= | 等于 | 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 requestsurl = "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即可。如下图所示:
不过,博主不建议这么做。因为这里Copy的XPath仅仅只是针对当前的标签,而我们上面获取的标签是一个有规则的标签列表。而你不学习的XPath语法的话,这要是有100个列表标签,你难道还复制XPath语法100次不成?而学习过XPath只需要一行代码,然后遍历即可。
Python爬虫:通过爬取CSDN博客信息,学习lxml库与XPath语法相关推荐
- python爬虫爬取csdn博客专家所有博客内容
python爬虫爬取csdn博客专家所有博客内容: 全部过程采取自动识别与抓取,抓取结果是将一个博主的所有 文章存放在以其名字命名的文件内,代码如下 #coding:utf-8import urlli ...
- 【爬虫+数据可视化】Python爬取CSDN博客访问量数据并绘制成柱状图
以下内容为本人原创,欢迎大家观看学习,禁止用于商业及非法用途,谢谢合作! ·作者:@Yhen ·原文网站:CSDN ·原文链接:https://blog.csdn.net/Yhen1/article/ ...
- 大屏监控系统实战(6)-爬虫初探:爬取CSDN博客之星年度总评选投票统计数据
一.介绍 我们先来做个简单的,我们的目标是爬取CSDN博客之星年度总评选的首页信息. 首页的地址:http://m234140.nofollow.ax.mvote.cn/wxvote/43ced329 ...
- python爬虫--小白爬取csdn页面题目与链接
爬取csdn页面题目与链接 前言 随着人工智能的不断发展,爬虫这门技术越来越重要-哈哈哈,太过官方.新手小白,过程较曲折,代码较不专业,欢迎批评与指教! 进入正题:本文主要爬取csdn博客某专栏下的题 ...
- Python爬虫项目--爬取某宝男装信息
本次爬取用到的知识点有: 1. selenium 2. pymysql 3 pyquery ''' 文章:Python爬虫项目--爬取某宝男装信息 作者:Star_Zhao 源自:https://w ...
- Python爬虫小实践:使用BeautifulSoup+Request爬取CSDN博客的个人基本信息
好久都没有动Python了,自从在网上买了<Python网络数据采集>这本书之后一直没有时间写自己的小的Demo,今天再网络上无意中看见 http://www.cnblogs.com/mf ...
- python爬虫之--爬取当当网商品信息
python爬虫之--爬取当当网图商品信息 ...
- python实战系列之爬取CSDN博客之星2020年度排名情况(附源码)
前言 CSDN2020年度博客之星马上就要收尾了,你知道自己关注的博主排名实时变化吗?谁是你心目中的博客之星,让我们爬取各位博主的排名及各位博主的一些个人信息. 代码 直接上代码 # -*- codi ...
- C/C++ | Qt 实现爬虫功能,爬取CSDN博客文章
话不多说,先看程序运行截图: 注意: 本人没有看过爬虫相关的书籍,第一次写这种程序,这个程序是半屌子的,原理很简单,没有学习过爬虫的朋友,也可以写. 程序思路如下: 1.下载要爬网站的页面. 2.用正 ...
最新文章
- tomcat苹果版安装步骤_Mac系统安装和配置tomcat步骤详解
- Python3判断自身脚本是不是在运行
- VC学习笔记---ATL MFC CLR三个库的区别
- Boost Asio总结(3)异步通信
- 听说Attention与Softmax更配哦~
- python观察日志(part13)--any和all
- 动画演示 Delphi 2007 IDE 功能[3] - 修改属性
- 已管理员身份从cmd框进入mysql,及常用的简单操作!
- 数字信号处理1:卷积
- 使django与数据库保持长连接
- PADS2007教程(二)——PCB封装
- Kaggle Tabular Playground Series - Jan 2022 学习笔记2(使用时间序列的线性回归)
- codeblocks下载安装教程(完整详细)
- win10升级助手_不想使用一键重装软件?微软win10升级助手来了
- 跨专业转行数据分析真的可行吗?
- 使用proteus仿真验证基尔霍夫定律
- 树莓派机器人小车(创乐博)修炼指南(一)
- 计算机网络知识点全面总结(一篇全懂)
- 三维点云拼接 标记点拼接 SVD分解法
- mysql注入时的注释处理
热门文章
- amd ryzen7 1700 linux,AMD 锐龙 7
- [Delphi] - WebBrowser内核版本修改D7
- java 开发双十一大屏幕_2019最赞的大屏可视化,一定是这款双十一的酷屏
- 组策略自动安装证书(from gnaw0725)
- 苹果home键在哪里设置_苹果手机Home键失灵指纹无法录入,一招搞定,录入指纹更灵敏...
- STM32学习笔记 通用定时器TIM3~TIM5 13
- 失败至少不会在一棵树上吊死
- Android A4尺寸 canvas,【报Bug】canvas绘制尺寸比较大的图片时,会出现绘制不出来的问题...
- linux脚本除号,Linux Shell 脚本:基本操作符
- 金山办公的WPS AI将引入大模型能力(LLM)到表格、文字、演示和PDF四大组件