Python爬虫利器 | Beautiful Soup4遍历文档
目录
Beautiful Soup介绍
Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库。它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式。BeautifulSoup会帮你节省数小时甚至数天的工作时间。
以上摘自官网
Beautiful Soup 安装
$ 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 使用
Beautiful Soup 使用来起来非常简单,将一段文档(一段字符串或一个文件句柄)传入BeautifulSoup 的构造方法,就能得到一个文档的对象,有了该对象之后,就可以对该文档做一些我们想做的操作了。而传入的文本大都是通过爬虫爬取过来的,所以 Beautiful Soup 和 requests 库结合使用体验效果更佳。
from bs4 import BeautifulSoup
soup = BeautifulSoup(open("index.html"))
soup = BeautifulSoup("<html>data</html>")
首先,文档被转换成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
Tag 对象与XML或HTML原生文档中的tag相同:
soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
tag = soup.b
type(tag)
# <class 'bs4.element.Tag'>
tag中重要的属性
tag中最重要的属性有:name和attributes
Name
每个tag都有自己的名字,通过.name来获取:
tag.name
# u'b'
如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档:
tag.name = "blockquote"
tag
# <blockquote class="boldest">Extremely bold</blockquote>
Attributes
一个tag可能有很多个属性. tag <b class="boldest">有一个“class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同:tag的属性可以被添加,删除或修改
tag['class']
# u'boldest'
# tag的属性可以被添加,删除或修改
tag['class'] = 'verybold'
tag['id'] = 1
tag
# <blockquote class="verybold" id="1">Extremely bold</blockquote>
del tag['class']
del tag['id']
tag
# <blockquote>Extremely bold</blockquote>
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)
# <class 'bs4.element.NavigableString'>
一个NavigableString 字符串与Python中的Unicode字符串相同,并且还支持包含在 遍历文档树 和 搜索文档树 中的一些特性. 通过unicode()方法可以直接将 NavigableString 对象转换成Unicode字符串:
unicode_string = unicode(tag.string)
unicode_string
# u'Extremely bold'
type(unicode_string)
# <type 'unicode'>
tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用replace_with()方法:
tag.string.replace_with("No longer bold")
tag
# <blockquote>No longer bold</blockquote>
BeautifulSoup
BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作Tag 对象,它支持 遍历文档树 和 搜索文档树 中描述的大部分的方法.
因为 BeautifulSoup 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name属性是很方便的,所以 BeautifulSoup 对象包含了一个值为“[document]”的特殊属性.name
注释及特殊字符串:
Tag,NavigableString , BeautifulSoup 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象:如文档的注释部分,这就需要Comment对象会使用特殊的格式输出注释部分:
markup = "<b><!--这是一段注释--></b>"
soup = BeautifulSoup(markup)
comment = soup.b.string
type(comment)
# <class 'bs4.element.Comment'>
# Comment 对象会使用特殊的格式输出注释部分
comment
# u'这是一段注释'
遍历文档树
先定义一串 HTML 文本,下面做解析:
html_doc = """
<html><head><title>index</title></head>
<p class="title"><b>商城</b></p>
<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="welcome">...</p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc)
子节点
一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性.
注意: Beautiful Soup中字符串节点不支持这些属性,因为字符串没有子节点
获取Tag的名字
操作文档树最简单的方法就是告诉它你想获取的tag的name.如果想获取<head> 标签,只要用 soup.head :
soup.head
# <head><title>index</title></head>
soup.title
# <title>商城</title>
可以在文档树的tag中多次调用这个方法.例如获取<body>标签中的第一个<b>标签:
soup.body.b
# <b>商城</b>
通过点取属性的方式只能获得当前名字的第一个tag:
soup.a
# <a href="http://cityShop.com/elsie" class="city" id="home">home</a>
如果想要得到所有的<a>标签,或是通过名字得到比一个tag更多的内容的时候,就需要用到 find_all():
soup.find_all('a')
# [<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>]
.contents 和 .children
tag的.contents 属性可以将tag的子节点以列表的方式输出:
head_tag = soup.head
head_tag
# <head><title>index</title></head>
head_tag.contents
[<title>index</title>]
title_tag = head_tag.contents[0]
title_tag
# <title>index</title>
title_tag.contents
# [u'index']
tag的.children 生成器,可以对tag的子节点进行循环遍历:
for child in title_tag.children:
print(child)
# index
.children只可以获取 tag 的直接节点,而获取不到子孙节点,.descendants可以满足。
.contents和 .children属性仅包含tag的直接子节点.例如,<head>标签只有一个直接子节点<title>
但是<title>标签也包含一个子节点:字符串 “index”,这种情况下字符串“index”也属于<head>标签的子孙节点. .descendants属性可以对所有tag的子孙节点进行递归循环 :
for child in head_tag.descendants:
print(child)
# <title>index</title>
# index
父节点
每个tag或字符串都有父节点:被包含在某个tag中
.parent
通过 .parent属性来获取某个元素的父节点.title的父标签是 head,html的父标签是 BeautifulSoup 对象,而 BeautifulSoup 对象的父标签是 None。
title_tag = soup.title
title_tag
# <title>index</title>
title_tag.parent
# <head><title>index</title></head>
# 文档title的字符串也有父节点:<title>标签
title_tag.string.parent
# <title>index</title>
# 文档的顶层节点比如<html>的父节点是 BeautifulSoup 对象:
html_tag = soup.html
type(html_tag.parent)
# <class 'bs4.BeautifulSoup'>
# BeautifulSoup 对象的 .parent 是None:
print(soup.parent)
# None
.parents
通过元素的 .parents属性可以递归得到元素的所有父辈节点,下面的例子使用了.parents方法遍历了<a>标签到根节点的所有节点.
link = soup.a
link
# <a href="http://cityShop.com/elsie" class="city" id="home">home</a>
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
# <a href="http://cityShop.com/tillie" class="city" id="products">products</a>
另外,同样可以通过.next_elements和 .previous_elements来迭代文档树。换行符同样会占用解析顺序,与迭代兄弟节点效果一致。
for element in last_a_tag.next_elements:
print(repr(element))
# u'Tillie'
# u';\欢迎来参观.'
# u'\n\n'
# <p class="story">...</p>
# u'...'
# u'\n'
# None
搜索文档树
Beautiful Soup定义了很多搜索方法,其中比较重要常用的是: find()和find_all().
定义一个文档实例:
html_doc = """
<html><head><title>index</title></head>
<p class="title"><b>商城首页</b></p>
<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="story">...</p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc)
使用find_all()类似的方法可以查找到想要查找的文档内容:
soup.find_all('b')
# 结果
# [<b>商城首页</b>]
如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的match() 来匹配内容.下面例子中找出所有以b开头的标签,这表示<body>和<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会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
soup.find_all(["a", "b"])
# [<b>商城首页</b>
# <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>]
更详细文档: https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#
更多推荐
所有评论(0)