0x01 Bs4解析器

1.BeautifulSoup(markup, “html.parser”)

  • Python 的内置标准库
  • 执行速度适中
  • 文档容错能力强
  • Python 2.7.3 or 3.2.2) 前 的版本中文档容错能力差

lxml HTML 解析器

2.BeautifulSoup(markup, “lxml”)

  • 速度快
  • 文档容错能力强
  • 需要安装 C 语言库

lxml XML 解析器

3.BeautifulSoup(markup, [“lxml”, “xml”])BeautifulSoup(markup, “xml”)

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

4.html5lib

BeautifulSoup(markup, “html5lib”)

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

0x02 四大对象种类

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

  • Tag
  • NavigableString
  • BeautifulSoup
  • Comment

1.Tag

html里每个标签都可以看成一个tag对象

soup.head
soup.title
print(type(soup.head) # <class 'bs4.element.Tag'>)
print(type(soup.title) # <class 'bs4.element.Tag'>)

2.NavigableString

类似每个标签里的string他的类型为NavigableString,翻译就是可遍历的字符串

print(type(soup.head.string)) # <class 'bs4.element.NavigableString'>

3.BeautifulSoup、Comment

这两个比较特殊 前者是tag的一种特殊对象 即soup对象本身,后者则为特殊的NavigableString对象,其实输出的内容仍然不包括注释符号

print(type(soup)) 	# <class 'bs.BeautifulSoup'>
print(soup.a) 	# <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
print(soup.a.string) #  Elsie
print(type(soup.a.string) # <class 'bs4.element.Comment'>

所以在为了不然注释干扰到我们爬出的内容最好加一个判断类型的操作,从而达到我们想要的结果:

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

0x03 遍历文档树

1.直接子节点

要点:.contents .children 属性

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

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

输出方式为列表,我们可以用列表索引来获取它的某一个元素

print soup.head.contents[0]
#<title>The Dormouse's story</title>

.children 它返回的不是一个 list,不过我们可以通过遍历获取所有子节点。 我们打印输出 .children 看一下,可以发现它是一个 list 生成器对象

print soup.head.children
#<listiterator object at 0x7f71457f5710>

我们怎样获得里面的内容呢?很简单,遍历一下就好了,代码及结果如下

for child in  soup.body.children:
    print child
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>


<p class="story">...</p>

2. 所有子孙节点

知识点:.descendants 属性

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

for child in soup.descendants:
    print child

运行结果如下,可以发现,所有的节点都被打印出来了,先生最外层的 HTML 标签,其次从 head 标签一个个剥离,以此类推。

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


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


<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<b>The Dormouse's story</b>
The Dormouse's story


<p class="story">Once upon a time there were three little sisters; and their names were
<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
Once upon a time there were three little sisters; and their names were

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

<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
Lacie
 and

<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
Tillie
;
and they lived at the bottom of a well.


<p class="story">...</p>
...

3. 节点内容

知识点:.string 属性

如果 tag 只有一个 NavigableString 类型子节点,那么这个 tag 可以使用 .string 得到子节点。如果一个 tag 仅有一个子节点,那么这个 tag 也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同。 通俗点说就是:如果一个标签里面没有标签了,那么 .string 就会返回标签里面的内容。如果标签里面只有唯一的一个标签了,那么 .string 也会返回最里面的内容。例如

print soup.head.string
#The Dormouse's story
print soup.title.string
#The Dormouse's story

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

print soup.html.string
# None

4. 多个内容

知识点: .strings .stripped_strings 属性

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

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'...'

5. 父节点

知识点: .parent 属性

p = soup.p
print p.parent.name
#body
content = soup.head.title.string
print content.parent.name
#title

6. 全部父节点

知识点:.parents 属性

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

content = soup.head.title.string
for parent in  content.parents:
    print parent.name
title
head
html
[document]

7. 兄弟节点

知识点:.next_sibling .previous_sibling 属性

兄弟节点可以理解为和本节点处在统一级的节点,.next_sibling 属性获取了该节点的下一个兄弟节点,.previous_sibling 则与之相反,如果节点不存在,则返回 None 注意:实际文档中的 tag 的 .next_sibling 和 .previous_sibling 属性通常是字符串或空白,因为空白或者换行也可以被视作一个节点,所以得到的结果可能是空白或者换行

print soup.p.next_sibling
#       实际该处为空白
print soup.p.prev_sibling
#None   没有前一个兄弟节点,返回 None
print soup.p.next_sibling.next_sibling
#<p class="story">Once upon a time there were three little sisters; and their names were
#<a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>,
#<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> and
#<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>;
#and they lived at the bottom of a well.</p>
#下一个节点的下一个兄弟节点是我们可以看到的节点

8. 全部兄弟节点

知识点:.next_siblings .previous_siblings 属性

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

for sibling in soup.a.next_siblings:
    print(repr(sibling))
    # u',\n'
    # <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
    # u' and\n'
    # <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
    # u'; and they lived at the bottom of a well.'
    # None

9. 前后节点

知识点:.next_element .previous_element 属性

与 .next_sibling .previous_sibling 不同,它并不是针对于兄弟节点,而是在所有节点,不分层次 比如 head 节点为

<head><title>The Dormouse's story</title></head>

那么它的下一个节点便是 title,它是不分层次关系的

print soup.head.next_element
#<title>The Dormouse's story</title>

10. 所有前后节点

知识点:.next_elements .previous_elements 属性

通过 .next_elements 和 .previous_elements 的迭代器就可以向前或向后访问文档的解析内容,就好像文档正在被解析一样

for element in last_a_tag.next_elements:
    print(repr(element))
# u'Tillie'
# u';\nand they lived at the bottom of a well.'
# u'\n\n'
# <p class="story">...</p>
# u'...'
# u'\n'
# None

0x04 搜索文档树

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

  • name参数

    字符串过滤

    list = soup.find_all("a")

    正则匹配

    list = soup.find_all(re.compile("a"))

    列表

    list = soup.find_all(["link","title"])

    方法

    list = soup.find_all(name_method) # 传入方法名,匹配方法的返回值

  • kwargs参数

# 查询id=link1

list = soup.find_all(id="linke1")

# 查询所有包含class的Tag

list = soup.find_all(class_=True) # 因为class是python关键字所以需要'_'

# 查询href特定的值(爬取链接一般都可以用)

list = soup.find_all(href=re.compile("example"))

# id,class href 后面都可以直接跟正则表达式

  • attrs参数

定义一个字典参数来搜索包含特殊属性的 tag

list = soup.find_all(attrs={"class":"value"})

  • text参数

通过 text 参数可以搜搜文档中的字符串内容。与 name 参数的可选值一样,text 参数接受 字符串,正则表达式,列表,True

list = soup.find_all(text="Elsie")

  • limit参数

当文本过大时我们可以对find_all()等下面这些方法的搜索值进行限制,与sql里面的limit类似

list = soup.find_all("a",limit=2)

  • recursive参数

find_all会检索当前tag所有的子孙节点,如果只对子节点搜索可执行

recursive=False

list =soup.find_all("a",recuresive=False)

(2)find( name , attrs , recursive , text , **kwargs )

它与 find_all () 方法唯一的区别是 find_all () 方法的返回结果是值包含一个元素的列表,而 find () 方法直接返回第一次匹配的结果

(3)find_parents()

find_all () 和 find () 只搜索当前节点的所有子节点,孙子节点等. find_parents () 和 find_parent () 用来搜索当前节点的父辈节点,搜索方法与普通 tag 的搜索方法相同,搜索文档搜索文档包含的内容

(4)find_next_siblings() find_next_sibling()

这 2 个方法通过 .next_siblings 属性对当 tag 的所有后面解析的兄弟 tag 节点进行迭代,find_next_siblings () 方法返回所有符合条件的后面的兄弟节点,find_next_sibling () 只返回符合条件的后面的第一个 tag 节点

(5)find_previous_siblings()find_previous_sibling()

这 2 个方法通过 .previous_siblings 属性对当前 tag 的前面解析的兄弟 tag 节点进行迭代,find_previous_siblings () 方法返回所有符合条件的前面的兄弟节点,find_previous_sibling () 方法返回第一个符合条件的前面的兄弟节点

(6)find_all_next() find_next()

这 2 个方法通过 .next_elements 属性对当前 tag 的之后的 tag 和字符串进行迭代,find_all_next () 方法返回所有符合条件的节点,find_next () 方法返回第一个符合条件的节点

(7)find_all_previous () 和 find_previous ()

这 2 个方法通过 .previous_elements 属性对当前节点前面的 tag 和字符串进行迭代,find_all_previous () 方法返回所有符合条件的节点,find_previous () 方法返回第一个符合条件的节点

注:以上(2)(3)(4)(5)(6)(7)方法参数用法与 find_all () 完全相同,原理均类似,在此不再赘述。

0x05 css选择器

除了对标签搜索意外也可根据css语法进行搜索

# 标签名查询 list = soup.select("titile") # 类名 list = soup.select(".mnav") # id查找 list = soup.select("#u1") # 组合查找 list = soup.select(".mnav #u1") # 属性查找 list = soup.select(a[class="mnav"]) # 获取内容 list = soup.select(a[class="mnav"])[0]get_text()

0x06 参考文献

Python 爬虫利器二之 Beautiful Soup 的用法 | 静觅 (cuiqingcai.com)