在之前的文章中我们介绍了Python爬虫的基本原理、怎么通过Python的第三方模块发起基本的HTTP请求,今天咱们接着爬虫网络请求之后剩余的流程进行讲解。咱们已经清楚爬虫的流程:发起请求、返回响应、响应解析、数据存储。以上是基本爬虫的标准流程,接下来咱们详细介绍一下在Python爬虫中响应解析库的使用。
对前端稍有了解的朋友就能知道,对于网页的节点来说,它可以定义id、class等属性,而且节点标签之前有很强的层次关系,在解析网页时,我们可以通过xpath、css选择器来实现对一个或多个节点的定位。最主要的是,正向咱们前面说的那样,Python中针对这些常用的功能,都有成熟的第三方模块,我们可直接引入使用,例如lxml、BeautifulSoup、pyquery等。
1、Xpath用法
Xpath全称XML Path Language(xml路径语言),它是一门在XML文档中查找信息的语言,原始用法是用来搜索XML文档的,但是在HTML文档中同样适用。在爬虫中,我们就可以通过Xpath来解析HTML网页获取我们想要得到的数据。
1.1 XPath常用规则
表达式 | 描述说明 |
Nodename | 获取此节点的所有子节点 |
/ | 获取当前节点的直接子节点 |
// | 获取当前节点的子孙节点 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 设置属性选取 |
以上就是XPath比较常用的几条规则,一般的选取规则都可以通过以上几条规则转换而来。例如:
//title[@lang=’eng’] 说明:获取当前节点下面属性lang为eng的所有title子节点。
1.2 环境搭建
Pip install lxml
1.3 示例说明
1.3.1 XPath用法举例说明
from lxml import etree
stext = """
以上是简单说明,首先导入lxml支持模块,将手动定义的html源代码通过初始化构建成一个XPath解析对象。Etree的HTML用法会自动补全代码中的标签缺失。Tostring显示修正后的html源代码,但格式部位字符串,而是bytes。
1.3.2 读取文件进行解析
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
htmlBytes = etree.tostring(hmtlContent)
1.3.3 获取所有节点
获取所有节点指获取html代码中的所有子节点,通常使用“//”规则开头来获取我们想提取的节点,同样以上述html源代码为例:
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
allLabels = hmtlContent.xpath("//*")
print(allLabels)
这里使用“*”代表匹配html代码中的所有节点,返回的allLabels为list类型,list中包含上述文件中的所有ul、li、a标签。
也可以通过指定节点标签名来获取指定所有标签:
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
allLabels = hmtlContent.xpath("//li")
print(allLabels)
上述代码即获取html文件代码中的所有“li”标签。即“//”代表获取所有当前节点。
1.3.4 获取子节点
我们可通过“/”、“//”来查找元素的子节点或者子孙节点。例如获取li节点xia的所有直接a节点:
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
allLabels = hmtlContent.xpath("//li/a")
print(allLabels)
如果想获取节点下的所有子孙节点,则直接通过“//”即可实现,例如获取“ul”节点下的所有“a”节点:
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
allLabels = hmtlContent.xpath("//ul//a")
print(allLabels)
在此主要区别“/”和“//”的用法,“/”用于获取直接子节点,“//”用于获取节点下的所有子孙节点。
1.3.4 获取父节点
通过上述举例我们了解到XPath可通过连续的“/”、“//”获取直接子节点或子孙节点,同样我们可通过子节点来获取其父节点。例如:
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
result = hmtlContent.xpath('//a[@href="d4.html"]/../@class')
print(result)
上述代码含义:先获取href属性为“d4.html”的a节点,然后通过“..”获取到其父节点,赞通过“@”用法获取到其父节点的class属性值。
也可通过“parent::”来达到上述目的:
hmtlContent = etree.parse("test.html",etree.HTMLParser())
result = hmtlContent.xpath('//a[@href="d4.html"]/parent::*/@class')
print(result)
1.3.5 属性匹配
当我们在选取节点标签的时候,我们可以通过“@”来进行属性的过滤。比如现在我们要实现获取class为t3的li节点:
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
result = hmtlContent.xpath('//li[@class="t3"]')
print(result)
1.3.6 获取节点文本
获取节点的本质是想获取节点标签间的数据,所以此实现方法是比较重要的。接下来实现1.3.5中获取到的li节点的文本:
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
result = hmtlContent.xpath('//li[@class="t3"]//text()')
print(result)
1.3.7 节点属性获取
很多时候页面中有一部分数据比较隐蔽,并不是存在于节点标签文本域中的,儿事存在于节点属性中的,怎么获取?
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
result = hmtlContent.xpath('//li/a/@href')
print(result)
上述代码实现内容,获取所有li节点的直接子节点a的href属性。
1.3.8 属性多值匹配
有的时候节点的某个属性可能有多个值,例如:
stext = """
这个时候我们就不能用上述的@方式来获取属性值了,这样只会获取到“”,此时我们可通过“contains()”函数来实现获取。“contains”,顾名思义就是包含的意思,写法为:contains(@class, ‘t0’)
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
result = hmtlContent.xpath('//li[contains(class,"t2")]/a/text()')
print(result)
含义:获取class属性包含有t2的li节点的直接a节点的文本。
还可进行多属性值匹配:
from lxml import etree
hmtlContent = etree.parse("test.html",etree.HTMLParser())
result = hmtlContent.xpath('//li[contains(class,"t2") and @name="l2"]/a/text()')
print(result)
2、BeautifulSoup用法
BeautifulSoup是python的一个HTML和XML的解析库,依赖于第三方模块lxml,通过它同样可以实现XPath的功能,很方便的从网页中提取到相关的节点并获取其数据。但是其在解析时实际还依赖与其他解析器,如下表。
解析器 | 使用说明 |
Python标准解析 | BeautifulSoup(markup,”html.parser”) |
Lxml html解析器 | BeautifulSoup(markup,”lxml”) |
Lxml xml解析器 | BeautifulSoup(markup,”xml”) |
Html5li | BeautifulSoup(markup,”html5lib”) |
此处讲解我们使用以下html示例代码:
2.1 节点选择器基本用法:
from bs4 import BeautifulSoup
soup = BeautifulSoup(stext,"lxml")
Print(soup.div.id)
以上就是获取html代码中的div标签的id属性。
还可以获取节点的所有属性:
Print(soup.div.attrs)
Print(soup.div.attrs[‘id’])
获取节点标签的文本:
Print(soup.p.string)
通过节点关系进行嵌套获取:
Print(soup.div.ul.attrs[‘name’])
2.2 方法选择器基本用法
2.2.1 find_all()
通过方法名可直译,获取满足某个条件的所有节点:
例如,获取上述代码中的所有li节点:
Lis = soup.find_all(“li”)
可通过名称查询:
Print(soup.find_all(name=”uld”))
属性名查找:
Print(soup.find__all(attrs={“name”:”uld”}))
2.2.2 find()
和上述find_all()方法对应,只是find_all()返回的是结果列表,而find()返回的是单个结果。
3、pyquery用法
获取web页面节点获取数据出了上述两种解析方法之外还有一种比较常见的方法:pyquery()。
安装:pip install pyquery
3.1基本用法:
from pyquery import PyQuery as pq
doc = pq(stext)
print(doc('ul'))
3.2 通过链接进行初始化
除了直接解析本地HTML代码段之外,还可直接同构链接进行网页内容解析:
from pyquery import PyQuery as pq
doc = pq(url="https://afcentry.cn")
print(doc('title'))
结果:
3.3 通过本地文件进行初始化
from pyquery import PyQuery as pq
doc = pq(url="de.html")
print(doc('title'))
3.4 选择器说明
Pyquery选择器是基于css选择器来进行节点筛选的,通过‘#’和‘.’来分别指定id和类名进行主区域选择。
例如:
from pyquery import PyQuery as pq
doc = pq(stext)
print(doc('.t0 a'))
含义:获取类包含‘t0’下的a标签节点。
3.5 子节点查找
进行子节点查找时同样使用find()方法,
from pyquery import PyQuery as pq
doc = pq(stext)
print(doc.find(‘li’))
3.6 父节点查询
可通过已知节点.parent()方法获取其父节点:
from pyquery import PyQuery as pq
doc = pq(stext)
item = doc.find(‘li’)
Print(item.parent())
3.7 属性获取
通过attr()方法获取属性
from pyquery import PyQuery as pq
doc = pq(stext)
item = doc.find(‘li’)
3.8 文本获取
通过text()方法获取节点文本
From pyquery import PyQuery as pq
Doc = pq(stext)
A = doc.find(“li” “a”)
Print(A.text())