Quantcast
Channel: CodeSection,代码区,Python开发技术文章_教程 - CodeSec
Viewing all articles
Browse latest Browse all 9596

Python数据解析工具:Beautiful Soup

$
0
0
Beautiful Soup简介

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的python库,简单来说,它能将HTML的标签文件解析成树形结构,然后方便地获取到指定标签的对应属性。这个特性lxml差不多。

Beautiful Soup的安装

Beautiful Soup 3 目前已经停止开发,推荐在现在的项目中使用Beautiful Soup 4,安装方法:

pip install beautifulsoup4

如果仅是想要解析HTML文档,只要用文档创建 BeautifulSoup 对象就可以了。Beautiful Soup会自动选择一个解析器来解析文档.但是还可以通过参数指定使用那种解析器来解析当前文档。BeautifulSoup 第一个参数应该是要被解析的文档字符串或是文件句柄,第二个参数用来标识怎样解析文档.如果第二个参数为空,那么Beautiful Soup根据当前系统安装的库自动选择解析器,解析器的优先数序: lxml, html5lib, Python标准库.在下面两种条件下解析器优先顺序会变化:

要解析的文档是什么类型: 目前支持, “html”, “xml”, 和 “html5” 指定使用哪种解析器: 目前支持, “lxml”, “html5lib”, 和 “parser”

如果指定的解析器没有安装,Beautiful Soup会自动选择其它方案.目前只有 lxml 解析器支持XML文档的解析,在没有安装lxml库的情况下,创建 beautifulsoup 对象时无论是否指定使用lxml,都无法得到解析后的对象。

安装解析器:

lxml,具体请看:lxml的安装 html5lib pip install html5lib

下表列出了主要的解析器,以及它们的优缺点:

解析器 使用方法 优势 劣势 Python标准库 BeautifulSoup(markup,“html.parser”)

执行速度适中

文档容错能力强

Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差 lxml HTML 解析器 BeautifulSoup(markup,“lxml”)

文档容错能力强

需要安装C语言库 lxml XML 解析器

BeautifulSoup(markup,“xml”)

唯一支持XML的解析器

需要安装C语言库 html5lib BeautifulSoup(markup,“html5lib”)

以浏览器的方式解析文档

生成HTML5格式的文档

不依赖外部扩展

推荐使用lxml作为解析器,因为效率更高. 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.

Beautiful Soup的使用 创建Beautiful Soup 对象 # -*- coding: utf-8 -*- frombs4importBeautifulSoup importrequests url = 'https://www.biaodianfu.com' r = requests.get(url, timeout=20) soup = BeautifulSoup(r.content, 'html.parser') printtype(soup) printsoup

BeautifulSoup 构造方法的第二个参数为文档解析器,若不传入该参数,BeautifulSoup会自行选择最合适的解析器来解析文档,不过会有警告提示。也可以通过文件句柄来初始化,可先将HTML的源码保存到本地同级目录 reo.html,然后将文件名作为参数:

soup = BeautifulSoup(open('test.html'))

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

Tag NavigableString BeautifulSoup Comment Tag

Tag 是什么?通俗点讲就是 HTML 中的一个个标签,下面我们来感受一下怎样用 Beautiful Soup 来方便地获取 Tags:

printsoup.title printsoup.h1

我们可以利用 soup加标签名轻松地获取这些标签的内容,不过有一点是,它查找的是在所有内容中的第一个符合要求的标签,如果要查询所有的标签,我们在后面进行介绍。

对于 Tag,它有两个重要的属性,是 name 和 attrs:

name:soup 对象本身比较特殊,它的 name 即为 [document],对于其他内部标签,输出的值便为标签本身的名称。 attrs:一个Tag对象可以有多个属性,操作方法和字典相同,属性包含key和value,同样可以获取value的信息。 printsoup.p.attrs printsoup.p['class'] printsoup.p.get('class') #等价print soup.p['class'] tag的属性可添加、删除(del soup.b[‘class’])、修改,和字典方法相同。如果一个属性key对应多个value,则返回一个value的list。 NavigableString

既然我们已经得到了标签的内容,那么问题来了,我们要想获取标签内部的文字怎么办呢?很简单,用 .string 即可,Tag中的字符串即为NavigableString对象。

printsoup.p.string

在BeautifulSoup之外使用该类型,推荐转换为Unicode:unicode(Tag.string)。tag能够包含其他tag或字符串,而NavigableString则不能包含其他对象。不支持.content,.string,find(),只支持部分遍历文档树和搜索文档树中的属性。

Comment

Comment 对象是一个特殊类型的 NavigableString 对象,其实输出的内容仍然不包括注释符号,但是如果不好好处理它,可能会对我们的文本处理造成意想不到的麻烦。

我们找一个带注释的标签

printsoup.a printsoup.a.string printtype(soup.a.string)

运行结果如下:

<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a> Elsie <class 'bs4.element.Comment'>

a 标签里的内容实际上是注释,但是如果我们利用 .string 来输出它的内容,我们发现它已经把注释符号去掉了,所以这可能会给我们带来不必要的麻烦。

另外我们打印输出下它的类型,发现它是一个 Comment 类型,所以,我们在使用前最好做一下判断,判断代码如下

if type(soup.a.string)==bs4.element.Comment: printsoup.a.string

上面的代码中,我们首先判断了它的类型,是否为 Comment 类型,然后再进行其他操作,如打印输出。

遍历文档树

BeautifulSoup对象作为一棵树,有多个节点。对于一个节点,相对于它所在的位置,有子节点、父节点、兄弟节点。

子节点

一个Tag可包含多个Tag以及字符串,这些都是这个Tag的子节点。而NavigableString不会有子节点。

直接子节点

.contents tag 的 .content 属性可以将tag的子节点以列表的方式输出

printsoup.head.contents #[<title>The Dormouse's story</title>]

.children 它返回的不是一个 list,不过我们可以通过遍历获取所有子节点。

printsoup.head.children #<listiterator object at 0x7f71457f5710> for child in soup.body.children: printchild

如果想要获得某个Tag,上述已提到方法:

soup.tag_name

通过点取属性,只能获得当前名字的第一个tag,若要获取所有,需要使用搜索文档树中的方法:

soup.find_all('tag_name')

tag的.contents属性可将所有子节点以列表的方式输出。可通过tag的.children生成器,对所有子节点进行遍历。.contents和.children只对获取Tag的直接子节点,.descendants可用于对Tag的所有子孙节点进行遍历。

如果tag只有一个NavigableString类型子节点,则可用.string获取。如果包含多个,使用.strings遍历。若输出的字符串中包含空格或空行,使用.stripped_strings去除。

所有子孙节点

.contents 和 .children 属性仅包含tag的直接子节点,.descendants 属性可以对所有tag的子孙节点进行递归循环,和 children类似,我们也需要遍历获取其中的内容。

for child in soup.descendants: printchild

如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点。如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同。

通俗点说就是:如果一个标签里面没有标签了,那么 .string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容。例如

printsoup.head.string #The Dormouse's story printsoup.title.string #The Dormouse's story

如果tag包含了多个子节点,tag就无法确定,string 方法应该调用哪个子节点的内容, .string 的输出结果是 None

获取多个内容,不过需要遍历获取,比如下面的例子

for string in soup.strings: print(repr(string)) # u"The Dormouse's story" # u'\n\n' # u"The Dormouse's story" # u'\n\n' # u'Once upon a time there were three little sisters; and their names were\n' # u'Elsie' # u',\n' # u'Lacie' # u' and\n' # u'Tillie' # u';\nand they lived at the bottom of a well.' # u'\n\n' # u'...' # u'\n'

.stripped_strings ,输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容

for string in soup.stripped_strings: print(repr(string)) # u"The Dormouse's story" # u"The Dormouse's story" # u'Once upon a time there were three little sisters; and their names were' # u'Elsie' # u',' # u'Lacie' # u'and' # u'Tillie' # u';\nand they lived at the bottom of a well.' # u'...' 父节点 p = soup.p print p.parent.name #body content = soup.head.title.string printcontent.parent.name #title 全部父节点

通过元素的 .parents 属性可以递归得到元素的所有父辈节点,例如

content = soup.head.title.string for parent in content.parents: printparent.name

返回数据:

title head html [document] 兄弟节点

兄弟节点可以理解为和本节点处在统一级的节点,.next_sibling 属性获取了该节点的下一个兄弟节点,.previous_sibling 则与之相反,如果节点不存在,则返回 None

注意:实际文档中的tag的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行

printsoup.p.next_sibling printsoup.p.prev_sibling printsoup.p.next_sibling.next_sibling 全部兄弟节点

通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出

for siblingin soup.a.next_siblings: print(repr(sibling)) # u',

Viewing all articles
Browse latest Browse all 9596

Trending Articles