Beautiful Soup模块完整解析(上)

Beautiful Soup模块完整解析(上)

标签(空格分隔): Python Packages crawler


最近在研究python爬虫,整理了一些BeautifulSoup包的内容。
文档上篇整理了官方说明文档中的内容,简单介绍包的安装与调用、格式化后文档树的结构、遍历文档树以及搜索文档树的方式,最后会给出一个从豆瓣中抓取影单的例子。

  • 1 Beautiful Soup简介
    • 1.1 安装与基础配置
    • 1.2 基本操作
  • 2 对象的种类
    • 2.1 Tag
      • 2.1.1 Name
      • 2.1.2 Attributes
      • 2.1.3 多值属性
    • 2.2 NavigableString
    • 2.3 BeautifulSoup
    • 2.4 Comment
  • 3. 遍历文档树
    • 3.1 子节点
      • 3.1.1tag的名字
      • 3.1.2 .contents和.children
      • 3.1.3 .descendants
      • 3.1.4 .string
      • 3.1.5 .strings和stripped_strings
      • .strings
    • 3.2 父节点
      • 3.2.1 .parent
      • 3.2.2 .parents
    • 3.3 兄弟节点
      • 3.3.1 .next_silbing和.previous_sibling
    • 3.4 回退和前进
      - 3.4.1 .next_element和.previous_element
      - 3.4.2 .next_elements和.previous_elements
  • 4. 搜索文档树
    • 4.1 过滤器
      • 4.1.1 字符串
      • 4.1.2 正则表达式
      • 4.1.3 列表
      • 4.1.4 True
      • 4.1.5 方法
    • 4.2 find_all()
      • 4.2.1 Name参数
      • 4.2.2 KeyWord参数
      • 4.2.3 按CSS搜索
      • 4.2.4 text参数
      • 4.2.5 limit参数
      • 4.2.6 recursive参数
    • 4.3 像调用find_all()一样调用tag
    • 4.4 其他find系列函数
      • 4.4.1 find_parents和find_parents
      • 4.4.2 find_next_siblings和find_next_sibling
      • 4.4.4 find_all_next和find_next_previous
      • 4.4.4 find_all_previous和find_previous

1 Beautiful Soup

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库.它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的方式。

1.1 安装与基础配置

如果电脑中已经安装了pip包,那么直接在cmd窗口中输入:

pip install pip install beautifulsoup4

如果没有安装pip包,可以下载BS4的源码,然后通过setup.py来安装。即,在目录下右键打开cmd窗口,输入:

Python setup.py install

BS4的开发是在python2.7和python3.2的环境中开发的,因此本文中的命令在python2与python3中是通用的。BeautifulSoup发布时候打包成为Python2版本的代码,在Python3环境下安装时,会自动转换成Python3的代码,如果没有安装过程,那么代码就不会被转换。

如果代码抛出了 ImportError 的异常: “No module named HTMLParser”, 这是因为你在Python3版本中执行Python2版本的代码.

如果代码抛出了 ImportError 的异常: “No module named html.parser”, 这是因为你在Python2版本中执行Python3版本的代码.

解决以上问题最简单的办法就是重新安装2333

如果在ROOT_TAG_NAME = u’[document]’代码处遇到 SyntaxError “Invalid syntax”错误,需要将把BS4的Python代码版本从Python2转换到Python3. 可以重新安装BS4:

Python3 setup.py install

或者在bs4的目录中执行Python代码版本转换脚本:

2to3-3.2 -w bs4

BeautifulSoup支持Python标准库中的HTML解析器,还支持一些第三方的解析器,其中一个是lxml。lxml的同样可以使用pip进行安装。

另一个可供选择的解析其实纯Python实现的html5lib,它的结息方式与浏览器相同,安装方法与lxml相同。如果使用的是Anaconda的整体包,那么以上包应该都是包含在内的。

不同解析器之间的区别会在以后的文章中介绍。

一般情况下,推荐使用lxml解析器,其效率更高。

1.2 基本操作

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc)  #此处的html_doc是通过request 得到的网站响应
print(soup.prettify())  #可以得到一个整理规范的标准HTML文档

这段代码逻辑如下:首先导入BeautifulSoup包。然后通过构造方法调用一个文档对象。构造方法中可以传入一段字符串或者一个文件句柄。首先,文档被转换为Unicode格式,并且HTML的实力都被转换为Unicode编码。
然后BeautifulSoup会选择最合适的解析器来解析文档。同样,也可以手动指定解析器,格式如下:

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc,"lxml")
print(soup.prettify())

得到的是一个规范化的HTML文档。
接下来将介绍这个HTML文档包含的元素类别和属性。

2. 对象的种类

Beautiful Soup将复杂的HTMl文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为四种Tag、NavigableString、BeautifulSoup、Comment。
接下来分别介绍这四种对象。

2.1 Tag

Tag与HTML原生文档中的tag相同

soup = BeautifulSoup(' Extremely bold#

上述代码中,首先使用构造一个BeautifulSoup对象;然后通过soup.b选取第一个name为b的Tag;可以看到,tag的类型为Tag。
Tag有很多属性,在遍历文档树和搜索文档树中都有介绍
Tag的重要属性:nameattributes

2.1.1 Name

每个Tag都有自己的名字,通过.name来获取:

tag.name
# u'b'

如果改变了Tag的Name,那么将影响HTML文档中所有该Tag的属性。

tag.name = "blockquote"
print(soup)
# 
Extremely bold
class = "boldest> Extremly bold boldb>

2.1.2 Attributes

一个tag可能有多个属性,在上面给出的例子中,有一个”class”属性,值为”blodest”,对于.tag的操作类似于字典:

tag['class']
# u'blodest'

也可以直接用.attrs:

tag.attrs
# {u'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

2.1.3 多值属性

HTML4定义了一系列可以包含多个值得属性。在HTML5中溢出了一些,却增加的更多,最常见的多值属性是class(一个tag可以由多个CSS的class)。还有一些属性rel,rev,accept-charset,headers,accesskey。

css_soup = BeautifulSoup('

'
) css_soup.p['class'] # ["body", "strikeout"] css_soup.p.attrs # {'class': ['body', 'strikeout']} css_soup = BeautifulSoup('

'
) css_soup.p['class'] # ["body"]

如果某个属性看起来好像有多个值,但在任何版本的HTML中都没有被定义为为多值属性,那么BeautifulSoup会将这个属性作为字符串返回。

id_soup = BeautifulSoup('

'
) id_soup.p['id'] # 'my id'

将Tag转换为字符串时,多值属性会合并为一个值:

rel_soup = BeautifulSoup('

Back to the homepage

'
) rel_soup.a['rel'] # ['index'] rel_soup.a['rel'] = ['index', 'contents'] print(rel_soup.p) #

Back to the homepage

如果转换文档是XML格式(即使用XML解析markup,那么tag中不包含多值属性:

xml_soup = BeautifulSoup('

'
, 'xml') xml_soup.p['class'] # u'body strikeout'

2.2 NavigableString

在解析后的HTML文档中,字符串通常被包含在Tag内,BeautifulSoup通常用NavigableString类来包装Tag中的字符串:

tag.string
# u'Extremely bold'
type(tag.string)
# 

这段代码中,在提取了Tag之后,调用tag的string属性获得该Tag下的字符串。
注意,如果提取的Tag并非叶节点,则无法使用.string方法获取该节点下的字符串:

soup = BeautifulSoup("""

Once upon a time there were three little sisters; and their names were Elsie, Lacie and Tillie; and they lived at the bottom of a well.

"""
, "lxml") tag = soup.p print(tag.string) # None

一个NavigableString字符串与Python中的Unicode字符串相同,且支持包含在遍历文档树和搜索文档树中的一些特性。通过Unicode()方法可以直接将NavigableString对象转换为Unicode字符串

soup = BeautifulSoup("">Extremely bold")
tag = soup.b
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

NavigableString对象支持遍历文档树和搜索文档树中定义的大部分属性,但需要注意,一个字符串不能包含其他内容(tag能包含字符串或者是其他tag),字符串不支持.contents或者.string属性或find()方法。

如果想在BeautifulSoup之外使用NavigableString对象,需要调用unicode()方法,将该对象转换为普通的Unicode字符串,否则就算BeautifulSoup方法已经执行结束,该对象的输出也会带有对象的引用地址,会造成内存的浪费。

2.3 BeautifulSoup

BS对象表示的是一个文档的全部内容,大部分时候,可以把它当作Tag对象,支持遍历文档树和搜索文档树中描述的大部分方法。
因为BeautifulSoup对象并不是真正的HTML或者XML的tag,所以他没有name和attribute属性。所以BeautifulSoup对象一般包含值为”[document]”的特殊属性.name。

soup.name
# u'[document]'

2.4 Comment

通常情况下,Tag,NavigableString,BeautifulSoup几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象。如文档的注释部分:

markup = ""
soup = BeautifulSoup(markup)
comment = soup.b.string
type(comment)
# 
print(soup.b.string)
# Hey, buddy. Want to buy a used parser?

comment对象是一个特殊类型的NavigableString对象,但当期出现在HTML文档中时,Comment对象会使用特殊的格式输出:

print(soup.b.prettify())
# 
#  
# 

BS重定义的其他类型都可能出现在XNL文档中:CDataProcessingInstructionDeclarationDoctype。与Comment对象类似,这些类都是NavigableString的子类,只是添加了一些额外的方法的字符串独享。

from bs4 import CData
cdata = CData("A CDATA block")
comment.replace_with(cdata)

print(soup.b.prettify())
# 
#  
# 

3. 遍历文档树

以下文为例子:

html_doc = """
The Dormouse's story

The Dormouse's story

Once upon a time there were three little sisters; and their names were Elsie, Lacie and Tillie; and they lived at the bottom of a well.

...

"""
from bs4 import BeautifulSoup soup = BeautifulSoup(html_doc)

3.1 子节点

一个Tag可能包含多个字符串或者其他的Tag,这些都是这个Tag的子节点。BeautifulSoup提供了许多操作和遍历子节点的属性。
需要注意的是,BeautifulSoup中的字符串节点不支持这些属性,因为字符串没有子节点。

3.1.1 tag的名字

操作文档树最简单的方法就是告诉它你想获取的tag的name。如果想获取标签,只需要使用soup.name:

soup.head
# The Dormouse's story

soup.title
# The Dormouse's story

这是个获取tag的小窍门,可以在文档树的tag中多次调用此方法。如下所示:

soup.body.b
# The Dormouse's story

注意,该方法只能获得节点中第一个该子标签的节点。同样的如果对这类子标签的name属性进行修改,修改的也只是这一个子节点的name。
如果希望获得所有包含的标签,或是通过名字得到一个比tag更多的内容的时候,就需要Searching the tree中描述的方法,比如:find_all(),将在下一节具体介绍。

soup.find_all('a')
# [Elsie,
#  Lacie,
#  Tillie]

3.1.2 .contents和.children

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

head_tag = soup.head
head_tag
# The Dormouse's story

head_tag.contents
[The Dormouse<span class="hljs-string">'s story]

title_tag = head_tag.contents[0]
title_tag
# The Dormouse'</span>s story
title_tag.contents
# [u'The Dormouse's story']

如果tag内同时包含字符串和多个子节点,将得到以下形式的输出:

soup = BeautifulSoup("""

Once upon a time there were three little sisters; and their names were Elsie, Lacie and Tillie; and they lived at the bottom of a well.

"""
, "lxml") tag = soup.p tag.contents #[u'Once upon a time there were three little sisters; and their names were\n', # Elsie, # u',\n', # Lacie, # u' and\n', # Tillie, # u';\nand they lived at the bottom of a well.']

BeautifulSoup对象本身一定会包含子节点,也就是说标签也是BeautifulSoup对象的子节点:

len(soup.contents)
# 1
soup.contents[0].name
# u'html'

字符串没有.contents属性,因为字符串没有子节点:

text = title_tag.contents[0]
text.contents
# AttributeError: 'NavigableString' object has no attribute 'contents'

通过tag的.children生成器,可以对tag的子节点进行循环:

for child in title_tag.children:
    print(child)
    # The Dormouse's story

3.1.3 .descendants

.contents和.children属性仅包含tag的直接子节点。例如,标签只有一个直接子节点

head_tag.contents
# [The Dormouse's story]

但是标签也包含一个字符串子节点。这种情况下字符串属于标签的子孙节点,.descendats属性可以对所有tag的子孙节点进行递归:

for child in head_tag.descendants:
    print(child)
    # The Dormouse's story
    # The Dormouse's story

上面的例子中,标签只有一个子节点,但是有2个子孙节点:
节点和的子节点,BeautifulSoup有一个直接子节点(节点),却有很多子孙节点:

len(list(soup.children))
# 1
len(list(soup.descendants))
# 25

3.1.4 .string

如果tag只有一个NavigableString类型子节点,那么这个tag可以使用.string得到子节点:

title_tag.string
# u'The Dormouse's story'

如果一个tag仅有一个子节点,那么这个tag也可以使用.string方法,输出结果与当前唯一子节点.string结果相同:

head_tag.contents
# [The Dormouse's story]

head_tag.string
# u'The Dormouse's story'

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

print(soup.html.string)
# None

3.1.5 .strings和stripped_strings

.strings

当tag包含很多子节点时,无法用string直接输出string的内容,此时可以使用strings的迭代器。如之前的例子中,如果使用strings迭代输出:

soup = BeautifulSoup("""

Once upon a time there were three little sisters; and their names were Elsie, Lacie and Tillie; and they lived at the bottom of a well.

"""
, "lxml") strs = [] for string in soup.strings: strs.append(string) print(strs) #[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.']

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

strs = []
for string in soup.stripped_strings:
    strs.append(string)
print(strs)
[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.']

全部是空格的行会被忽略掉,段首和段末的空白会被删除。

3.2 父节点

继续分析文档树,每个tag或字符串都有父节点:被包含在某个tag中(在文档的树形结构中,根节点是BeautifulSoup对象)

3.2.1 .parent

通过.parent属性来获取某个元素的父节点。

title_tag = soup.title
title_tag
# The Dormouse's story
title_tag.parent
# The Dormouse's story

文档title的字符串也有父节点:标签

title_tag.string.parent
# The Dormouse's story

文档的顶曾节点比如的父节点是BeautifulSoup对象:

html_tag = soup.html
type(html_tag.parent)
# 

BeautifulSoup对象的.parent是None:

print(soup.parent)
# None

3.2.2 .parents

通过元素的.parents属性可以递归得到元素的所有父辈节点,下面的例子使用了.parents方法遍历了标签到根节点的所有节点:

link = soup.a
link
# Elsie
for parent in link.parents:
    if parent is None:
        print(parent)
    else:
        print(parent.name)
# p
# body
# html
# [document]
# None

.parent和.parents的关系与.children和.descendants的关系类似。一个是获取所有获取一级关系下的全部节点,另一个是向该方向进行的迭代器。

3.3 兄弟节点

看一段简单地例子:

sibling_soup = BeautifulSoup("text1text2")
print(sibling_soup.prettify())
# 
#  
#   
#    
#     text1
#    
#    
#     text2
#    
#   
#  
# 

因为标签和标签是同一层:他们是同一个元素的子节点,所以和可以被成为兄弟节点。一段文档以标准格式输出时,兄弟节点有相同的缩进级别,在代码中也可以使用这种关系。

3.3.1 .next_silbing和.previous_sibling

在文档书中,使用上述两个方法可以查询兄弟节点:

sibling_soup.b.next_sibling
# text2

sibling_soup.c.previous_sibling
# text1

同级节点中的第一个没有.previous_sibling属性,同理,同级节点中最后一个没有.next_sibling属性。
考虑使用如下的文档

soup = 
"""
Elsie
Lacie
Tillie
"""
link = soup.a
link.next_silbing
# # u',\n'

得到的结果并不是第二个标签,而是第一个标签和第二个标签之间的顿号和换行符号。

同级标签之中同样存在相同的迭代输出方法:
可以通过 .next_silbings和 .previous_silbings对其进行迭代输出:

for sibling in soup.a.next_siblings:
    print(repr(sibling))
    # u',\n'
    # Lacie
    # u' and\n'
    # Tillie
    # u'; and they lived at the bottom of a well.'
    # None

for sibling in soup.find(id="link3").previous_siblings:
    print(repr(sibling))
    # ' and\n'
    # Lacie
    # u',\n'
    # Elsie
    # u'Once upon a time there were three little sisters; and their names were\n'
    # None

3.4 回退和前进

首先参考一下文档:

<html><head><title>The Dormouse's storytitle>head>
<p class="title"><b>The Dormouse's storyb>p>

HTML解析器把这段字符串转换成一连串的事件: “打开标签”,”打开一个标签”,”打开一个标签”,”添加一段字符串”,”关闭标签”,”打开

标签”,等等。Beautiful Soup提供了重现解析器初始化过程的方法。

3.4.1 .next_element和.previous_element

这两个属性分别只想解析过程中下一个和上一个被解析的对象(字符串或者tag),结果可能与.next_siblings相同,但通常是不一样的。
在本文的例子中,.next_sibling结果是一个字符串(或者说下一个同级标签中包含的内容)。
而标签的.next_element属性结果是在标签被解析之后的解析内容,不是标签后的句子部分。
.previous_element属性刚好与.next_silbing相反,它指向当前被解析的对象的前一个解析对象。

last_a_tag.previous_element
# u' and\n'
last_a_tag.previous_element.next_element
# Tillie

3.4.2 .next_elements和.previous_elements

这两个方法是对应的关于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'
# 

...

# u'...' # u'\n' # None

4. 搜索文档树

BeautifulSoup定义了很多搜索方法,这里着重介绍2个:
find()和find_all()。其他方法的参数和用法类似。

html_doc = """
The Dormouse's story

The Dormouse's story

Once upon a time there were three little sisters; and their names were Elsie, Lacie and Tillie; and they lived at the bottom of a well.

...

"""
from bs4 import BeautifulSoup soup = BeautifulSoup(html_doc)

使用find_all()类似方法可以查找到想要查找的文档内容

4.1 过滤器

介绍find_all方法前,首先介绍一下过滤器的类型,这些过滤器贯穿整个关于搜索的API。过滤器可以用在tag的name中,节点的属性中,字符串中或者他们的混合中。

4.1.1 字符串

最简单的过滤器是字符串。在搜索方法中传入一个字符串参数,BeautifulSoup会查找与字符串完整匹配的内容,下面的例子用于查找文档中所有的标签:

soup.find_all('b')
# [The Dormouse's story]

如果传入字节码参数,BeautifulSoup会当做UTF-8编码,可以传入一段Unicode编码来避免BeautifulSoup解析编码出错

4.1.2 正则表达式

如果传入正则表达式作为参数,BeautifulSoup会通过正则表达式的matcfh()来匹配内容。下面的例子中找出所有以b为开头的标签,这表明和标签都应该被找到:

import re
for tag in soup.find_all(re.compile("^b")):
    print(tag.name)
# body
# b

关于正则表达式,接下来将重新有一篇文章整体介绍。

4.1.3 列表

如果传入的参数是列表,BeautifulSoup将会与列表中任一元素匹配的内容返回。下面代码找到文档中所有标签和标签:

soup.find_all(["a", "b"])
# [The Dormouse's story,
#  Elsie,
#  Lacie,
#  Tillie]

4.1.4 True

True可以匹配任何值。

4.1.5 方法

如果没有合适的过滤器,那么还可以定义一个方法,方法只接受一个元素参数,如果这个方法返回True表示当前元素匹配并且被找到,如果不是则返回False。

def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

soup.find_all(has_class_but_no_id)
# [

The Dormouse's story

,
#

Once upon a time there were...

,
#

...

]

这里需要注意的是,上面提到了,可以把tag的属性当做一个字典,可以使用has_attr方法来判断是否具有属性。

4.2 find_all()

find_all(name, attrs, recursive, text, **kwargs)
find_all()方法搜索当前tag的所有tag子节点。在这部分继续使用一下文本段,并先给出几个例子:

soup.find_all("title")
# [The Dormouse's story]

soup.find_all("p", "title")
# [

The Dormouse's story

]
soup.find_all("a") # [Elsie, # Lacie, # Tillie] soup.find_all(id="link2") # [Lacie] import re soup.find(text=re.compile("sisters")) # u'Once upon a time there were three little sisters; and their names were\n'

接下来,详细介绍一下各个参数的含义

4.2.1 Name参数

name参数可以查找所有名字为name的tag,字符串对象会被自动忽略掉
简单的用法如下:

soup.find_all("title")
# [The Dormouse's story]

4.2.2 KeyWord参数

如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当做置顶名字的tag属性来搜索,如果包含一个名字为”id”的参数,BeautifulSoup会搜索每个tag的”id”属性。

soup.find_all(id='link2')
# [Lacie]

如果传入href参数,BeautifulSoup会搜索每个tag的”href”属性:

soup.find_all(href=re.compile("elsie"))
# [Elsie]

搜索指定名字的属性时,可以使用参数值包括字符串、正则表达式、列表、True:
下面的例子在文档树中查找所有包含id属性的tag,无论id的值是什么:

soup.find_all(id=True)
# [Elsie,
#  Lacie,
#  Tillie]

使用多个指定名字的参数可以同时过滤tag的多个属性:

soup.find_all(href=re.compile("elsie"), id='link1')
# [three]

有些tag属性在搜索中不能使用,比如HTML5中的date-*属性:

data_soup = BeautifulSoup('
foo!
'
) data_soup.find_all(data-foo="value") # SyntaxError: keyword can't be an expression

当使用find_all()对全部整理文本进行搜索的时候,也可以使用字典定义多个参数:

data_soup.find_all(attrs={"data-foo": "value"})
# [
foo!
]

4.2.3 按CSS搜索

按照CSS类名搜索tag功能非常实用,但表示CSS类名搜索tag的功能非常实用,但识别CSS类名的关键字class在python中是保留字,因此BeautifulSoup的4.1.1版本开始,可以通过class_参数搜索指定CSS类名的tag:

soup.find_all("a", class_="sister")
# [Elsie,
#  Lacie,
#  Tillie]

class_参数同样接受不同类型的过滤器,字符串、正则表达式、方法或者True都可以在该参数下使用:

soup.find_all(class_=re.compile("itl"))
# [

The Dormouse's story

]
def has_six_characters(css_class): return css_class is not None and len(css_class) == 6 soup.find_all(class_=has_six_characters) # [Elsie, # Lacie, # Tillie]

搜索class属性时多值属性,按照CSS类名搜索tag时,可以分别搜索tag中的每个CSS类名,也可以通过CSS值完全匹配:

css_soup = BeautifulSoup('

'
) css_soup.find_all("p", class_="strikeout") # [

]
css_soup.find_all("p", class_="body") # [

]
css_soup.find_all("p", class_="body strikeout") # [

]

搜索class属性时,也可以通过CSS值完全匹配:

css_soup.find_all("p", class_="body strikeout")
# [

]

完全匹配class的值时,如果CSS类名的顺序与实际比附,将搜索不到结果:

soup.find_all("a", attrs={"class": "sister"})
# [Elsie,
#  Lacie,
#  Tillie]

4.2.4 text参数

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

soup.find_all(text="Elsie")
# [u'Elsie']

soup.find_all(text=["Tillie", "Elsie", "Lacie"])
# [u'Elsie', u'Lacie', u'Tillie']

soup.find_all(text=re.compile("Dormouse"))
[u"The Dormouse's story", u"The Dormouse's story"]

def is_the_only_string_within_a_tag(s):
    ""Return True if this string is the only child of its parent tag.""
    return (s == s.parent.string)

soup.find_all(text=is_the_only_string_within_a_tag)
# [u"The Dormouse's story", u"The Dormouse's story", u'Elsie', u'Lacie', u'Tillie', u'...']

虽然text参数用于搜索字符串,但依旧可以同其他参数混合使用来过滤tag。

soup.find_all("a", text="Elsie")
# [Elsie]

4.2.5 limit参数

find_all()方法返回全部的搜索结构,如果文档树很大那么搜索的记说会很慢,如果我们不需要全部结果,可以使用limit参数限制返回结果的数量。效果与SQL中的limit关键字类似,当搜索到的结果数量达到limit的限制时,就会停止搜索并返回结果

如下所示:

soup.find_all("a", limit=2)
# [Elsie,
#  Lacie]

4.2.6 recursive参数

调用tag的find_all()方法时,BeautifulSoup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数recursive = False

像调用find_all()一样调用tag、

find_all()几乎是BeautifulSoup中最常用的搜索方法,所以我们定了它的简写方法。BeautifulSoup对象和tag对象可以被当做一个方法来使用,这个方法的执行结果与调用这个对象的find_all方法相同,下面两行代码是等价的:

soup.find_all("a")
soup("a")

soup.title.find_all(text=True)
soup.title(text=True)

find()

find(name, attrs, recursive, text, **kwargs)
find_all()方法将返回文档中所有符合条件的tag,尽管有时候我们只想得到一个结果,比如文档中只有一个标签,那么使用find_all()方法来查找标签就不太合适,使用find_all方法并设置limit=1参数不如直接使用find()方法。下面两行代码是等价的:

soup.find_all('title', limit=1)
# [The Dormouse's story]

soup.find('title')
# The Dormouse's story

而区别在于,find_all()方法返回的结果列表,而find()方法将直接返回结果。

4.3 其他find系列函数

与大部分搜索文档树类型的函数与find、find_all()的性质类似。BeautifulSoup提供了与遍历文档树类似的方法对文档树进行搜索。主要包含以下几类:

4.4.1 find_parents和find_parents

find_parents(name, attrs, recursive, text, **kwargs)
find_parent(name, attrs, recursive, text, **kwargs)
对所有parent节点进行迭代。注意,find()、find_all()是对所有当前节点的子孙节点进行搜索,而这两个函数是对当前节点的父辈节点。

a_string = soup.find(text="Lacie")
a_string
# u'Lacie'

a_string.find_parents("a")
# [Lacie]

a_string.find_parent("p")
# 

Once upon a time there were three little sisters; and their names were # Elsie, # Lacie and # Tillie; # and they lived at the bottom of a well.

a_string.find_parents("p", class="title") # []

4.4.2 find_next_siblings和find_next_sibling

find_next_siblings(name, attrs, recursive, text, **kwargs)
find_next_sibling(name, attrs, recursive, text, **kwargs)
这两个方法对后面所有的兄弟节点进行搜索,搜索范围即为与next_silbings的迭代范围。

first_link = soup.a
first_link
# Elsie

first_link.find_next_siblings("a")
# [Lacie,
#  Tillie]

first_story_paragraph = soup.find("p", "story")
first_story_paragraph.find_next_sibling("p")
# 

...

4.4.3 find_all_next和find_next_previous

find_all_next(name, attrs, recursive, text, **kwargs)
find_next(name, attrs, recursive, text, **kwargs)

这两个方法对后面的所有元素进行搜索,搜索方式即为next_elements()迭代得到的所有元素,同时包括节点和字符串,将所有满足条件的结果返回。

first_link = soup.a
first_link
# Elsie

first_link.find_all_next(text=True)
# [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']

first_link.find_next("p")
# 

...

4.4.4 find_all_previous和find_previous

find_all_previous(name, attrs, recursive, text, **kwargs)
find_previous(name, attrs, recursive, text, **kwargs)

你可能感兴趣的:(Python,Crawler,Package)