Python3编写网络爬虫06-基本解析库Beautiful Soup的使用

二、Beautiful Soup

简介 就是python的一个HTML或XML的解析库 可以用它来很方便的从网页中提取数据

0.1 提供一些简单的 python式的函数来处理导航,搜索,修改分析树等功能,
它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用

0.2 自动将输入的文档转换为Unicode编码,输出文档转换为UTF-8编码,不需要考虑编码方式,
除非文档没有指定一个编码方式,这是你仅仅需要说明以下原始编码格式就可以了。

0.3 已成为和lxml,html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。

安装 Beautiful Soup 解析器依赖lxml库 保证lxml库安装

pip install beautifulsoup4

验证:

from bs4 import BeautifulSoup

soup = BeautifulSoup('<p>Hello</p>','lxml')
print(soup.p.string) #输出Hello

1.解析器

BeautifulSoup在解析的时候 依赖解析器 除了支持python标准库HTML解析器之外 还支持第三方解析器(lxml)

例如
解析器 优势 劣势

python标准库 python内置标准库 执行速度适中, python2.7.3及python3.2.2之前的版本 文档容错率差
文档容错能力强

lxml HTML解析器 速度快 文档容错能力强 需要安装C语言库

lxml XML解析器 速度快 唯一支持XML的解析器 需要安装C语言库

html5lib 最好的容错性,以浏览器的方式解析文档 速度慢 不依赖外部拓展
生成HTML5格式文档

对比可以看出lxml解析器有解析HTML和XML的功能 速度快 容错能力强

使用时 将第二个参数改为lxml

2.基本用法

html='''
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dormouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their name were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
'''

from bs4 import BeautifulSoup

soup = BeautifulSoup(html,'lxml')#初始化
print(soup.prettify())#把要解析的字符串以标准的缩进格式输出 
print(soup.title.string)#输出HTML中title节点的文本内容

3.节点选择器

直接调用节点的名字就可以选择节点元素,再调用string属性就可以得到节点内的文本,
这种选择方式速度非常快,如果单个节点结构层次清晰可以选用这种方式

3.1 选择元素

html='''
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dormouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their name were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
'''

from bs4 import BeautifulSoup

soup = BeautifulSoup(html,'lxml')

print(soup.title)#获取title节点

print(type(soup.title))#打印类型为bs4.element.Tag类型 经过选择器选择后 结果都是这种Tag类型

print(soup.title.string)#调用Tag里的string属性 得到节点的文本内容

print(soup.head)#查找heda节点

print(soup.p)#查找p节点 结果只有一个 后面的没有选到 这种方式只会选择到第一个匹配的节点

3.2 提取信息

获取节点名称

可以利用name属性获取节点名称 选取title节点 调用name属性

print(soup.title.name)

获取属性

每个节点可能有多个属性 比如 id class ... 选择这个节点元素后,可以调用attrs获取所有属性

print(soup.p.attrs)#返回结果是字典形式

# 获取字典里面某个键值 只需要中括号加属性名

print(soup.p.attrs['name']) 

更简单的获取方式

print(soup.p['name'])#返回结果是字符串
print(soup.p['class'])#返回结果是列表 实际处理时要注意类型 

3.3获取内容

print(soup.p.string)#返回结果是第一个p节点的文本内容

3.4 嵌套选择

html='''
<html><head><title>The Dormouse's story</title></head>
<body>
'''
from bs4 import BeautifulSoup

soup = BeautifulSoup(html,'lxml')
print(soup.head.title)#title节点元素
print(type(soup.head.title))#打印类型为Tag 在Tag基础上再次选择得到的依然是Tag
print(soup.head.title.string)#输出string属性 获取文本内容

3.5关联选择

子节点和子孙节点

html='''
<html>
<head>
<title>The Dormouse's story</title>
</head>
<body>
<p class="story">
Once upon a time there were three litile sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">
<span>Elsie</span>
</a>
<a href="http://example/lacie" class="sister" id="link2">Lacie</a>
and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
and they lived at the bottom of a well.
</p>
<p class="story">...</p>
'''

soup = BeautifulSoup(html,'lxml')
print(soup.p.contents)

 

结果是列表形式 p节点里包含文本 又包含节点
注意:列表中的每个元素都是p节点的直接子节点 例如span 相当于p节点的子孙节点 但是结果没有单独选出来

contents 返回直接子节点的列表

也可以使用children属性

print(soup.p.children)#返回结果是迭代器

for i,child in enumerate(soup.p.children):
print(i,child)#遍历

 

descendants 子孙节点

print(soup.p.descendants)#返回结果是生成器

for i,child in enumerate(soup.p.descendants):
print(i,child)#遍历 得到所有子孙节点

 

父节点和祖先节点

获取某个节点的父节点 可以调用parent属性

print(soup.a.parent)#返回p节点及其内部内容

获取某个节点的祖先节点 可以调用parents属性

print(soup.a.parents)#返回生成器

print(list(enumerate(soup.a.parents)))#列表输出索引和内容 列表中的元素就是a标签的祖先节点

 

兄弟节点

html = '''
<html>
<body>
<p class="story">
Once
<a href="www.baidu.com">
<span>Elsie</span>
</a>
Hello
<a href="www.baidu2.com">Lacie</a>
and
<a href="www.baidu3.com">Tillie</a>
and they
</p>
'''
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)))#获取前面的兄弟节点

 

提取信息

示例:

html='''
<html>
<body>
<p class="story">
Once upon...; and their 
<a href="www.baidu.com" class="sister" id="link1">Bod</a><a href="www.baidu2.com"
class="sister" id="link2">Lacie</a>
</p>
'''

soup = BeautifulSoup(html,'lxml')

print('Next Sibling:')
print(type(soup.a.next_sibling))
print(soup.a.next_sibling)
print(soup.a.next_sibling.string)
print('Parent:')
print(type(soup.a.parents))
print(list(soup.a.parents)[0])
print(list(soup.a.parents)[0].attrs['class'])

如果返回的是单个节点,可以直接调用string attrs等属性获取文本和属性
如果返回的是多个节点,可以转为列表后取出某个元素再调用string attrs获取对应节点的文本和属性

 

4.方法选择器

find_all() #查询所有符合条件的元素

find_all(name,attrs,recursive,text,**kwargs)

4.1 name #根据节点名查询元素

html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''

soup = BeautifulSoup(html,'lxml')

print(soup.find_all(name="ul"))#查找所有ul节点
print(type(soup.find_all(name="ul")[0]))#类型为Tag类型

查询其内部的li节点

for ul in soup.find_all(name="ul"):
print(ul.find_all(name="li"))

 

遍历每个li 获取文本内容

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)

 

4.2 attrs #根据属性查询

 

html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1" name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''
soup = BeautifulSoup(html,'lxml')

print(soup.find_all(attrs={'id':'list-1'}))#结果为列表形式
print(soup.find_all(attrs={'name':'elements'}))

 


简写方式

print(soup.find_all(id="list-1"))#返回id 为list-1的节点元素
print(soup.find_all(class_="element"))#由于class在python中是关键字 所以加 _ 结果依然是Tag类型的列表

 


4.3 text #匹配节点中的文本 可以是字符串 也可以是正则表达式

 

import re

html='''
<div class="panel">
<div class="panel-body">
<a>Hello,this is a link</a>
<a>Hello,this is a link,too</a>
</div>
</div>
'''

soup = BeautifulSoup(html,'lxml')

print(soup.find_all(text=re.compile('link')))#返回正则表达式匹配的节点文本 组成的列表

 

4.4 find() #查询符合条件的第一个元素 返回单个元素

html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''

soup = BeautifulSoup(html,'lxml')

print(soup.find(name="ul"))
print(type(soup.find(name="ul")))
print(soup.find(class_="list"))

 

# 返回结果不再是列表形式 只有第一个匹配元素 类型依然是Tag类型

更多查询方法 用法相同 范围不同

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() :前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点

5.CSS选择器


使用css选择器 需要调用 select()方法 传入相应的css选择器

示例:

html='''
<div class="panel">
<div class="panel-heading">
<h4>Hello</h4>
</div>
<div class="panel-body">
<ul class="list" id="list-1" name="elements">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
<ul class="list list-small" id="list-2">
<li class="element">Foo</li>
<li class="element">Bar</li>
</ul>
</div>
</div>
'''

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]))#类型依然是Tag类型

 


5.1 嵌套选择

示例:

soup = BeautifulSoup(html,'lxml')

for ul in soup.select('ul'):
print(ul.select('li'))#返回ul下 所有li组成的列表

 


5.2 获取属性

示例:获取所有ul节点的id属性

for ul in soup.select('ul'):
print(ul['id'])
print(ul.attrs['id'])

 

5.3 获取文本

可以选择用string 或者 get_text()

for li in soup.select('li'):
print('Get Text:',li.get_text())
print('String',li.string)

 

#效果完全相同


总结: 推荐使用lxml解析库 必要时使用html.parser

节点选择筛选功能弱但是速度快

建议使用 find() 或者 find_all() 查询匹配单个结果或者多个结果

如果对CSS选择器熟悉 可以使用 select() 方法选择

posted @ 2019-01-30 10:31  贫道从来不吃素  阅读(302)  评论(0编辑  收藏  举报