爬虫基础:Beautiful Soup
作者:快盘下载 人气:参考文档:beautiful soup 4.2.0 文档
Beautiful Soup 是一个可以从HTML和XML文件中提取数据的Python。它可以实现文档的增删改查操作,我们侧重点是它的查询操作。
安装 Beautiful Soup你可以根据自己的系统选择下面的安装代码进行安装操作:
$ apt-get install Python-bs4 $ easy_install beautifulsoup4 $ pip install beautifulsoup4
Beautiful Soup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是 lxml。根据操作系统不同,你可以选择下面方法来安装 lxml:
$ apt-get install Python-lxml $ easy_install lxml $ pip install lxml
另外一个可供选择的解析器是纯Python实现的 html5lib,解析方式和浏览器相同,你可以选择下面的方法来安装 html5lib:
$ apt-get install Python-html5lib $ easy_install html5lib $ pip install html5lib
几种解析器的优缺点
推荐使用 lxml 作为解析器,因为效率更高。在Python2.7.3之前的版本和Python3.2.3之前的版本,必须安装 lxml 或 html5lib,因为Python版本的标准库中的内置的 HTML 解析方法不够稳定。
如何使用将一段文档传入 Beautiful Soup 的构造方法,就能得到一个文档的对象,可以传入一段文字或一个文件句柄。
from bs4 import BeautifulSoup soup = BeautifulSoup(open("index.html")) // 文件句柄 soup = BeautifulSoup("<html>data</html>") // 文档
文档被转换成 Unicode,并且HTML的实例都被转换成 Unicode 编码,然后,Beautiful Soup选择最合适的解析器来解析这段文档,如果手动指定解析器,那么 Beautiful Soup 会选择指定的解析器来解析文档。
对象的种类Beautiful Soup 将复杂 HTML 文档转换成一个复杂的树形结构,每隔节点都是Python对象,多有对象可以归纳为4种:Tag
,NavigableString
,BeautifulSoup
,Comment
。
Tag对象
Tag 对象与 XML 或 HTML 原生文档中的 tag 相同。Tag 有很多方法和属性,其中最重要的属性:name 和 attributes。
from bs4 import BeautifulSoup soup = BeautifulSoup('<b class="boldest">Extremely bold</b>','lxml') tag = soup.b // 一个 Tag 对象 print(type(tag)) // <class 'bs4.element.Tag'> print(tag.name) // tag都有自己的名字:b print(tag.attrs) // tag的属性字典:{'class': ['boldest']} print(tag['class']) // 属性字典中的'class'值:['boldest'] // tag 的属性可以被添加,删除或修改,属性操作和字典一样 tag['class'] = 'myClsss' print(tag) // <b class="myClsss">Extremely bold</b> tag['id'] = 'custemId' print(tag) // <b class="myClsss" id="custemId">Extremely bold</b>
NavigableString对象
字符串常被包含在 tag 内。Beautiful Soup 用 NavigableString 类来包装 tag 中的字符串。
print(type(tag.string)) // <class 'bs4.element.NavigableString'> print(tag.string) // Extremely bold
字符串不支持 .contents 或 .string 属性或 find() 方法。
BeautifulSoup对象
该对象表示的是一个文档的全部内容,大部分的适合可以把它当作 Tag 对象。因为 BeautifulSoup 对象并不是真正的 HTML 或 XML 的tag,所以它没有name和attributes属性。但有时查看它时,.name 属性还是可以的。
print(type(soup)) // <class 'bs4.BeautifulSoup'> print(soup.name) // [document]
Comment对象
Tag , NavigableString , BeautifulSoup 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.容易让人担心的内容是文档的注释部分。
markup = "<b><!--Hey, buddy. Want to buy a used parser?--></b>" soup = BeautifulSoup(markup,'lxml') comment = soup.b.string print(type(comment)) // <class 'bs4.element.Comment'>
Comment 对象是一个特殊类型的 NavigableString 对象:
print(comment) // Hey, buddy. Want to buy a used parser?
但是当它出现在HTML文档中时, Comment 对象会使用特殊的格式输出:
print(soup.b.prettify()) <b> <!--Hey, buddy. Want to buy a used parser?--> </b>
说完 Beautiful Soup 中的四种对象,接下来介绍一下如何搜索内容。 Beautiful Soup 提供了很多搜索方法,这里着重介绍其中的两个:find()
和 find_all()
,其他的方法和参数以及用法都类似。
过滤器
过滤器作为搜索文档的参数,贯穿整个搜索的 API。过滤器可以被用在 tag 中的 name 中,节点的属性中,字符串中或者他们的混合中。过滤器可以是字符串、正则表达式、列表、True值甚至是方法。
字符串soup.find_all('b') // 查找文档中所有的<b>标签
import re // 查找 b 开头的标签 for tag in soup.find_all(re.compile("^b")): print(tag.name) // body // b
markup = ''' <b>Hey, buddy. Want to buy a used parser?</b> <p class="title">The DorMouse+'s story</p> <a>Once upon a time there were three little sisters</a> ''' soup = BeautifulSoup(markup,'lxml') for tag in soup.find_all(['a','p']): print(tag) // <p class="title">The Dormouse's story</p> // <a>Once upon a time there were three little sisters</a>
True 可以匹配任何值
for tag in soup.find_all(True): print(tag.name) // html // body // b // p // a
如果没有合适的过滤器,那么还可以定一个方法,方法只接收一个参数,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则返回 False
def has_class(tag): return tag.has_attr('class') for tag in soup.find_all(has_class): print(tag) # <p class="title">The Dormouse's story</p>
find_all()
find_all(self, name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
name 参数可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉。其中,name 参数的值可以是任一类型的过滤器:字符串、正则表达式、列表、Ture或是方法。
markup = ''' <b>Hey, buddy. Want to buy a used parser?</b> <p class="title">The Dormouse's story</p> <a>Once upon a time there were three little sisters</a> ''' soup = BeautifulSoup(markup,'lxml') print(soup.find_all('p')) # [<p class="title">The Dormouse's story</p>]
如果一个指定名字的参数不是搜索内置的参数名称,搜索时会把该参数当作指定名字tag的属性来搜索。
markup = ''' <b id='link'>Hey, buddy. Want to buy a used parser?</b> <p class="title">The Dormouse's story</p> <a>Once upon a time there were three little sisters</a> <a class="sister" href="http://example.com/elsie" id="link1">three</a> <div data-foo="value">foo!</div> ''' soup = BeautifulSoup(markup,'lxml') print(soup.find_all(id='link')) # [<b id="link">Hey, buddy. Want to buy a used parser?</b>] # class是Python的保留关键字,这里是 class_ print(soup.find_all(class_='title')) # [<p class="title">The Dormouse's story</p>] # 可以使用多个指定名字的参数来过滤多个tag的属性 import re print(soup.find_all(id='link1', href=re.compile('example'))) # [<a class="sister" href="http://example.com/elsie" id="link1">three</a>]
有些tag属性在搜索中不能使用,如HTML5中的 data-* 属性,我们可以使用 attrs 参数定一个字典参数来搜索包含特殊属性的tag。
print(soup.find_all(attrs={'data-foo':'value'})) # [<div data-foo="value">foo!</div>]
通过 text 参数可以搜索文档中的字符串内容,和 name 参数一样,接受参数有:字符串、正则表达式、列表、True。
print(soup.find_all(text='three')) # ['three'] import re print(soup.find_all(text=re.compile('^H.*?$'))) # ['Hey, buddy. Want to buy a used parser?']
Beautiful Soup 会检索当前tag的所有子孙节点,如果你只想搜索tag的直接子节点,可以使用参数 recursive = False。
markup = ''' <html> <head> <title> The Dormouse's story </title> </head> </html> ''' soup = BeautifulSoup(markup, 'lxml') print(soup.html.find_all('title')) # [<title> # The Dormouse's story # </title>] print(soup.html.find_all('title', recursive=False)) # []
markup = ''' <b id='link'>Hey, buddy. Want to buy a used parser?</b> <p class="title">The Dormouse's story</p> <a>Once upon a time there were three little sisters</a> <a class="sister" href="http://example.com/elsie" id="link1">three</a> <div data-foo="value">foo!</div> ''' soup = BeautifulSoup(markup,'lxml') import re for str in soup.find_all(text=re.compile('o')): print(str) # Hey, buddy. Want to buy a used parser? # The Dormouse's story # Once upon a time there were three little sisters # foo! for str in soup.find_all(text=re.compile('o'), limit=1): print(str) # Hey, buddy. Want to buy a used parser?
find_all()
是 Beautiful Soup中最常用的搜索方法,该库还提供了它的简写方法。BeautifulSoup 对象和 tag 对象都可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面代码是等价的:
soup.find_all("a") soup("a") soup.title.find_all(text=True) soup.title(text=True)
find()
find_all()
方法将返回文档中符合条件的所有tag,尽管有时候我们只想得到一个结果。比如文档中只有一个<body>
标签,那么使用 find_all()
方法来查找显然不太合适,如果使用 limit=1 参数不如使用 find()
方法。
soup.find_all('title', limit=1) # [<title>The Dormouse's story</title>] soup.find('title') # <title>The Dormouse's story</title>
唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果。
find_all() 方法没有找到目标是返回空列表,find() 方法找不到目标时,返回 None 。
soup.head.title 是 tag的名字 方法的简写。这个简写的原理就是多次调用当前tag的 find() 方法:
soup.head.title # <title>The Dormouse's story</title> soup.find("head").find("title") # <title>The Dormouse's story</title>
prettify()
方法将Beautiful Soup的文档树格式化后以Unicode编码输出,每个XML/HTML标签都独占一行。
markup = '<a href="http://example.com/">I linked to <i>example.com</i></a>' soup = BeautifulSoup(markup, 'lxml') print(soup.prettify()) # <html> # <body> # <a href="http://example.com/"> # I linked to # <i> # example.com # </i> # </a> # </body> # </html>
Beautiful Soup 功能强大,除了上面提到的 搜索文档树 功能,更有遍历文档树,修改文档树等等功能。更多详细介绍请查阅官方文档 4.2.0。
加载全部内容