Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库。它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式。BeautifulSoup会帮你节省数小时甚至数天的工作时间。
以上摘自官网
$ easy_install beautifulsoup4
# or
$ pip install beautifulsoup4
Beautiful Soup 不仅支持 Python 标准库中的 HTML 解析器,还支持很多第三方的解析器,比如 lxml
,html5lib
等。可以选择下列三种方法来安装lxml:
$ apt-get install Python-lxml
$ easy_install lxml
$ pip install lxml
另一个可供选择的解析器是纯Python实现的 html5lib
, html5lib
的解析方式与浏览器相同,可以选择下列方法来安装html5lib:
$ apt-get install Python-html5lib
$ easy_install html5lib
$ pip install html5lib
各解析器优缺点如下:
推荐使用lxml
作为解析器,因为效率更高. 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml
或html5lib
, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.
注意: 如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的,查看
Beautiful Soup 使用来起来非常简单,将一段文档(一段字符串或一个文件句柄)传入BeautifulSoup 的构造方法,就能得到一个文档的对象,有了该对象之后,就可以对该文档做一些我们想做的操作了。而传入的文本大都是通过爬虫爬取过来的,所以 Beautiful Soup 和 requests 库结合使用体验效果更佳。
from bs4 import BeautifulSoup
soup = BeautifulSoup(open("index.html"))
soup = BeautifulSoup("data")
首先,文档被转换成Unicode,并且HTML的实例都被转换成Unicode编码
BeautifulSoup("Sacré bleu!")
<html><head></head><body>Sacré bleu!</body></html>
然后,Beautiful Soup选择最合适的解析器来解析这段文档,如果手动指定解析器那么Beautiful Soup会选择指定的解析器来解析文档.
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:Tag
, NavigableString
, BeautifulSoup
, Comment
.
其中:
Tag 就是 HTML 的一个标签,比如 div,p ,h1~h6标签等,也是用的最多的一个对象。
NavigableString 指标签内部的文字,直译就是可遍历的字符串。
BeautifulSoup 指一个文档的全部内容,可以当成一个 Tag 来处理。
Comment 是一个特殊的 NavigableString,其输出内容不包括注释内容。
Tag 对象与XML或HTML原生文档中的tag相同:
soup = BeautifulSoup('Extremely bold')
tag = soup.b
type(tag)
#
tag中最重要的属性有:name
和attributes
每个tag都有自己的名字,通过.name
来获取:
tag.name
# u'b'
如果改变了tag的name
,那将影响所有通过当前Beautiful Soup对象生成的HTML文档:
tag.name = "blockquote"
tag
# Extremely bold
一个tag可能有很多个属性. tag
有一个“class”
的属性,值为 “boldest” . tag
的属性的操作方法与字典相同:tag
的属性可以被添加,删除或修改
tag['class']
# u'boldest'
# tag的属性可以被添加,删除或修改
tag['class'] = 'verybold'
tag['id'] = 1
tag
# Extremely bold
del tag['class']
del tag['id']
tag
# Extremely bold
tag['class']
# KeyError: 'class'
print(tag.get('class'))
# None
也可以直接”点”取属性, 比如: .attrs :
tag.attrs
# {u'class': u'boldest'}
字符串常被包含在tag内.Beautiful Soup
用NavigableString
类来包装tag
中的字符串:
tag.string
# u'Extremely bold'
type(tag.string)
#
一个NavigableString
字符串与Python中的Unicode
字符串相同,并且还支持包含在 遍历文档树 和 搜索文档树 中的一些特性. 通过unicode()
方法可以直接将 NavigableString
对象转换成Unicode
字符串:
unicode_string = unicode(tag.string)
unicode_string
# u'Extremely bold'
type(unicode_string)
#
tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用replace_with()
方法:
tag.string.replace_with("No longer bold")
tag
# No longer bold
BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作Tag 对象
,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup 对象并不是真正的HTML或XML的tag,所以它没有name
和attribute
属性.但有时查看它的 .name
属性是很方便的,所以 BeautifulSoup 对象包含了一个值为“[document]”
的特殊属性.name
注释及特殊字符串:
Tag
,NavigableString
, BeautifulSoup
几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象:如文档的注释部分,这就需要Comment
对象会使用特殊的格式输出注释部分:
markup = ""
soup = BeautifulSoup(markup)
comment = soup.b.string
type(comment)
#
# Comment 对象会使用特殊的格式输出注释部分
comment
# u'这是一段注释'
先定义一串 HTML 文本,下面做解析:
html_doc = """
index
商城
这是我的第三个商城,欢迎来参观
home
design
products
欢迎来参观.
...
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc)
一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.
注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点
操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取 标签,只要用
soup.head
:
soup.head
# index
soup.title
# 商城
可以在文档树的tag
中多次调用这个方法.例如获取标签中的第一个
标签:
soup.body.b
# 商城
通过点取属性的方式只能获得当前名字的第一个tag:
soup.a
# home
如果想要得到所有的标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到
find_all()
:
soup.find_all('a')
# [home
# design
# products]
tag的.contents
属性可以将tag
的子节点以列表的方式输出:
head_tag = soup.head
head_tag
# index
head_tag.contents
[<title>index</title>]
title_tag = head_tag.contents[0]
title_tag
# index
title_tag.contents
# [u'index']
tag的.children 生成器,
可以对tag
的子节点进行循环遍历:
for child in title_tag.children:
print(child)
# index
.children
只可以获取 tag 的直接节点,而获取不到子孙节点,.descendants
可以满足。
.contents
和 .children
属性仅包含tag的直接子节点.例如,标签只有一个直接子节点
但是
标签也包含一个子节点:字符串 “index”
,这种情况下字符串“index”
也属于标签的子孙节点.
.descendants
属性可以对所有tag的子孙节点
进行递归循环 :
for child in head_tag.descendants:
print(child)
# index
# index
每个tag或字符串都有父节点:被包含在某个tag中
通过 .parent
属性来获取某个元素的父节点.title
的父标签是 head
,html
的父标签是 BeautifulSoup 对象,而 BeautifulSoup 对象的父标签是 None。
title_tag = soup.title
title_tag
# index
title_tag.parent
# index
# 文档title的字符串也有父节点:标签
title_tag.string.parent
# index
# 文档的顶层节点比如的父节点是 BeautifulSoup 对象:
html_tag = soup.html
type(html_tag.parent)
#
# BeautifulSoup 对象的 .parent 是None:
print(soup.parent)
# None
通过元素的 .parents
属性可以递归得到元素的所有父辈节点,下面的例子使用了.parents
方法遍历了标签到根节点的所有节点.
link = soup.a
link
# home
for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
#结果:
# p
# body
# html
# [document]
# None
.next_sibling 和 .previous_sibling
兄弟节点即同一级别的兄弟元素,在文档树中,使用.next_sibling
和.previous_sibling
属性来查询兄弟节点:
soup = BeautifulSoup(html_doc, "lxml");
p_tag=soup.p
print(p_tag.next_sibling)
print(p_tag.next_sibling.next_sibling)
# 输出结果
<p class="story">这是我的第三个商城,欢迎来参观
<a href="http://cityShop.com/elsie" class="city" id="home">home</a>
<a href="http://cityShop.com/lacie" class="city" id="design">design</a>
<a href="http://cityShop.com/tillie" class="city" id="products">products</a>
欢迎来参观.</p>
p 的第一个 next_sibling 是p 和 p 之间的换行符。
.next_siblings 和 .previous_siblings
通过 .next_siblings
和.previous_siblings
属性可以对当前节点的兄弟节点迭代输出:
soup = BeautifulSoup(html_doc, "lxml");
p_tag=soup.p
for p_tag in p_tag.previous_siblings:
print( p_tag)
# 输出结果
<p class="story">这是我的第三个商城,欢迎来参观
<a href="http://cityShop.com/elsie" class="city" id="home">home</a>
<a href="http://cityShop.com/lacie" class="city" id="design">design</a>
<a href="http://cityShop.com/tillie" class="city" id="products">products</a>
欢迎来参观.</p>
# 输出结果
<p class="title"><b>商城</b></p>
通过.next_element
和.previous_element
获取指定标签的前一个或者后一个被解析的对象,注意这个和兄弟节点是有所不同的,兄弟节点是指有相同父亲节点的子节点,而这个前一个或者后一个是按照文档的解析顺序来计算的。
比如在实例的文本 html_doc
中,head
的兄弟节点是body
(不考虑换行符),因为他们具有共同的父节点 html
,但是 head
的下一个节点是 title
。即soup.head.next_sibling=title soup.head.next_element=title`。
soup = BeautifulSoup(html_doc, "lxml");
head_tag=soup.head
print(head_tag.next_element)
title_tag=soup.title
print(title_tag.next_element)
# 输出结果
<title>index</title>
index
还需要注意的是title
下一个解析的标签不是body
,而是 title
标签内的内容,因为 html
的解析顺序是打开 title
标签,然后解析内容,最后关闭title
标签。
.previous_element
属性刚好与.next_element
相反,它指向当前被解析的对象的前一个解析对象
:
last_a_tag.previous_element
# u' and\n'
last_a_tag.previous_element.next_element
# products
另外,同样可以通过.next_elements
和 .previous_elements
来迭代文档树。换行符同样会占用解析顺序,与迭代兄弟节点效果一致。
for element in last_a_tag.next_elements:
print(repr(element))
# u'Tillie'
# u';\欢迎来参观.'
# u'\n\n'
# ...
# u'...'
# u'\n'
# None
Beautiful Soup定义了很多搜索方法,其中比较重要常用的是: find()
和find_all()
.
定义一个文档实例:
html_doc = """
index
商城首页
这是我的第三个商城,欢迎来参观
home
design
products
欢迎来参观.
...
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc)
使用find_all()
类似的方法可以查找到想要查找的文档内容:
soup.find_all('b')
# 结果
# [商城首页]
如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的match()
来匹配内容.下面例子中找出所有以b
开头的标签,这表示和
标签都应该被找到:
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
# body
# b
找出所有名字中包含”t”
的标签:
for tag in soup.find_all(re.compile("t")):
print(tag.name)
# 结果
# html
# title
传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有标签和
标签:
soup.find_all(["a", "b"])
# [商城首页
# home
# design
# products]
更详细文档: https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#