Python3网络爬虫(五) -- 爬虫解析库LXML、BeautifulSoup、PyQuery

解析库会按照层次来提取,所以效率会大大的提升

BeautifulSoup

BeautifulSoup 是 Python 的一个 HTML 或 XML 的解析库,我们可以用它来方便地从网页中提取数据。 BeautifulSoup 在解析的时候实际上是依赖于解析器的,它除了支持 Python 标准库中的 HTML 解析器,还支持一些第三方的解析器比如 LXML,推荐使用LXML解析库,因为它能解析HTML和XML:

BeautifulSoup(markup, "lxml")  # 解析HTML
BeautifulSoup(markup, "xml")  # 解析XML

例子:

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')  # 会自动更正HTML里面未闭合的错误
print(soup.prettify())  # 将要解析的字符串以标准的缩进格式输出
print(soup.title.string)  # 选中节点然后输出文本
# 这样选择title只会选择第一个匹配到的节点

1.节点选择器 X不推荐使用X:

获取节点名称:
print(soup.title.name)

获取节点属性:

print(soup.p.attrs)  # 返回字典结构,所有属性
print(soup.p.attrs['name'])
print(soup.p['name'])  # 简易写法
print(soup.p['class'])  # 如果想获取Class,会返回一个列表,因为一个节点有可能有多个class

获取节点内容:
print(soup.p.string)

嵌套选择:
print(soup.head.title.string)
可以在一个节点的基础上再选择节点

关联选择:
子节点:
使用contents属性可以得到一个列表形式的直接子节点,如果里面既包含文本,也包含节点,返回结果都将以列表的形式统一返回,

使用children属性也可以得到结果

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.children)
for i, child in enumerate(soup.p.children):
    print(i, child)

返回的结果一样,只不过该属性返回的是生成器类型,而contents返回的是列表类型

子孙节点:

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.descendants)
for i, child in enumerate(soup.p.descendants):
    print(i, child)

父节点:
使用parent属性可以获取节点的直接父节点:
print(soup.a.parent)

祖父节点:
使用parents属性可以获取所有的祖先节点:
list(enumerate(soup.a.parents))
返回结果是一个生成器类型,上面代码用列表输出了它的索引和内容

兄弟节点:

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print('Next Sibling', soup.a.next_sibling)
print('Prev Sibling', soup.a.previous_sibling)
print('Next Siblings', list(enumerate(soup.a.next_siblings)))  # 返回生成器
print('Prev Siblings', list(enumerate(soup.a.previous_siblings)))  # 返回生成器

2. 方法选择器!!!推荐使用

最常用的查询方法莫过于 find_all() 和 find() 了
find_all():
find_all(name , attrs , recursive , text , **kwargs)

  • 根据名字查询:
    print(soup.find_all(name='ul'))
    可以继续遍历:
for ul in soup.find_all(name='ul'):
    print(ul.find_all(name='li'))
    for li in ul.find_all(name='li'):
        print(li.string)
  • 根据属性查询:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(attrs={'id': 'list-1'}))
print(soup.find_all(attrs={'name': 'elements'}))

对于特殊的id和class,我们有更方便的方法查询:

print(soup.find_all(id='list-1'))
print(soup.find_all(class_='element'))

这里注意class在python里面是一个关键字,所以需要在后面加一个下划线

  • 根据文本关键字查询
    匹配的可以是字符串也可以是正则表达式对象:
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(text=re.compile('link')))

结果会返回所有匹配正则表达式的节点文本组成的列表

find():
该方法返回单个元素,也就是第一个匹配的元素

还有一些其他方法:
find_parents() 返回所有祖先节点
find_parent() 返回直接父节点

find_next_siblings() 返回后面所有兄弟节点
find_next_sibling() 返回后面第一个兄弟节点

find_previous_siblings() 返回前面所有兄弟节点
find_previous_sibling() 返回前面第一个兄弟节点

find_all_next() 返回节点后所有符合条件的节点
find_next() 返回第一个符合条件的节点

find_all_previous() 返回节点后所有符合条件的节点
find_previous() 返回第一个符合条件的节点


3. CSS选择器

使用 CSS 选择器,只需要调用 select() 方法,传入相应的 CSS 选择器即可

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
print(type(soup.select('ul')[0]))


XPath(LXML):

首先先安装LXML库

常用规则:

表达式 用法
nodename 选取此节点的所有子节点
/ 从当前节点选取直接子节点
// 从当前节点选取子孙节点
. 选取当前节点
选取当前节点的父节点
@ 选取属性

例子://title[@lang=’eng’] 表示所有标签为title,并且lang属性为eng的节点
例子:

from lxml import etree
html = etree.HTML(text)  # 要么将文本用HTML类初始化
html = etree.parse('./test.html', etree.HTMLParser())  # 要么直接对文本文件进行解析
result = etree.tostring(html)
print(result.decode('utf-8'))

result = html.xpath('//*')  # 返回了所有节点列表

result = html.xpath('//li/a')  # 选择 li 节点下的所有直接 a 子节点

result = html.xpath('//a[@href="link4.html"]/../@class')  # 首先选中href为特定东西的a节点,然后选取它的父节点,在得到它的class属性。

result = html.xpath('//li[@class="item-0"]/text()')  # text()方法可以获取节点的文本

result = html.xpath('//li/a/@href')  # 获取li节点下所有a节点的href属性

属性多值匹配:
result = html.xpath('//li[contains(@class, "li")]/a/text()')
通过contains()方法,第一个参数为属性名称,第二个为属性值,只要属性包含这个传入的属性值就可以匹配

**多属性匹配:**from bs4 import BeautifulSoup
soup = BeautifulSoup(html, ‘lxml’)
print(soup.select(’.panel .panel-heading’))
print(soup.select(‘ul li’))
print(soup.select(’#list-2 .element’))
print(type(soup.select(‘ul’)[0]))
result = html.xpath('//li[contains(@class, "li") and @name="item"]/a/text()')
使用运算符来连接

按序选择节点:

result = html.xpath('//li[1]/a/text()')  # 选择第一个li节点,这里是从1开始计数!!!

result = html.xpath('//li[last()]/a/text()')  # 选取最后一个节点

result = html.xpath('//li[position()<3]/a/text()')  # 选择位置小于三的节点,也就是1和2

result = html.xpath('//li[last()-2]/a/text()')  # 选取倒数第三个节点

使用节点轴选择节点:

result = html.xpath('//li[1]/ancestor::*')
# 使用了ancestor轴,获取所有的祖先节点,后面需要跟着两个冒号,后面才是节点选择器,使用了*代表匹配所有节点

result = html.xpath('//li[1]/ancestor::div')
# 加上限定条件,只返回div这个祖先节点

result = html.xpath('//li[1]/attribute::*')
# 使用attribute轴,获取所有属性,两个冒号后面是*即获取所有属性,所以最后返回第一个li的所有属性

result = html.xpath('//li[1]/child::a[@href="link1.html"]')
# 使用child轴,获取所有子节点

result = html.xpath('//li[1]/descendant::span')
#  使用descendant轴,获取所有的子孙节点

result = html.xpath('//li[1]/following::*[2]')
#  使用了following轴,可以获取所有的后续节点,虽然使用了*匹配所有,但是后面紧跟

result = html.xpath('//li[1]/following-sibling::*')
# 使用了following-sibling轴,可以获取当前节点之后的所有同级节点,加上*匹配,即所有后续同级节点

猜你喜欢

转载自blog.csdn.net/qq_43355223/article/details/86005026