Beautiful Soup提供了许多方法用于浏览一个剖析树,收集你指定的Tag和NavigableString。
有几种方法去定义用于Beautiful Soup的匹配项。
我们先用深入解释最基本的一种搜索方法findAll。
和前面一样,我们使用下面这个文档说明:
from BeautifulSoup import BeautifulSoup
doc = ['<html><head><title>Page title</title></head>',
'<body><p id="firstpara" align="center">This is paragraph <b>one</b>.',
'<p id="secondpara" align="blah">This is paragraph <b>two</b>.',
'</html>']
soup = BeautifulSoup(''.join(doc))
print soup.prettify()
# <html>
# <head>
# <title>
# Page title
# </title>
# </head>
# <body>
# <p id="firstpara" align="center">
# This is paragraph
# <b>
# one
# </b>
# .
# </p>
# <p id="secondpara" align="blah">
# This is paragraph
# <b>
# two
# </b>
# .
# </p>
# </body>
# </html>
还有, 这里的两个方法(findAll和 find)仅对Tag对象以及
顶层剖析对象有效,但 NavigableString不可用。
这两个方法在Searching 剖析树内部同样可用。
findAll(name, attrs, recursive, text, limit, **kwargs)方法findAll 从给定的点开始遍历整个树,并找到满足给定条件所有Tag以及NavigableString。 findall函数原型定义如下:
findAll(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)
这些参数会反复的在这个文档中出现。其中最重要的是name参数
和keywords参数(译注:就是**kwargs参数)。
参数name 匹配tags的名字,获得相应的结果集。
有几种方法去匹配name,在这个文档中会一再的用到。
最简单用法是仅仅给定一个tag name值。下面的代码寻找文档中所有的
<B> Tag:
soup.findAll('b')
# [<b>one</b>, <b>two</b>]
你可以传一个正则表达式。下面的代码寻找所有以b开头的标签:
import re
tagsStartingWithB = soup.findAll(re.compile('^b'))
[tag.name for tag in tagsStartingWithB]
# [u'body', u'b', u'b']
你可以传一个list或dictionary。下面两个调用是查找所有的<TITLE>和<P>标签。 他们获得结果一样,但是后一种方法更快一些:
soup.findAll(['title', 'p'])
# [<title>Page title</title>,
# <p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
soup.findAll({'title' : True, 'p' : True})
# [<title>Page title</title>,
# <p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
你可以传一个True值,这样可以匹配每个tag的name:也就是匹配每个tag。
allTags = soup.findAll(True)
[tag.name for tag in allTags]
[u'html', u'head', u'title', u'body', u'p', u'b', u'p', u'b']
这看起来不是很有用,但是当你限定属性(attribute)值时候,使用True就很有用了。
你可以传callable对象,就是一个使用Tag对象作为它唯一的参数,并返回布尔值的对象。 findAll使用的每个作为参数的Tag对象都会传递给这个callable对象,
并且如果调用返回True,则这个tag便是匹配的。
下面是查找两个并仅两个属性的标签(tags):
soup.findAll(lambda tag: len(tag.attrs) == 2)
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
下面是寻找单个字符为标签名并且没有属性的标签:
soup.findAll(lambda tag: len(tag.name) == 1 and not tag.attrs)
# [<b>one</b>, <b>two</b>]
keyword参数用于筛选tag的属性。下面这个例子是查找拥有属性align且值为center的 所有标签:
soup.findAll(align="center")
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>]
如同name参数,你也可以使用不同的keyword参数对象,从而更加灵活的指定属性值的匹配条件。
你可以向上面那样传递一个字符串,来匹配属性的值。你也可以传递一个正则表达式,一个列表(list),一个哈希表(hash),
特殊值True或None,或者一个可调用的以属性值为参数的对象(注意:这个值可能为None)。
一些例子:
soup.findAll(id=re.compile("para$"))
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
soup.findAll(align=["center", "blah"])
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
soup.findAll(align=lambda(value): value and len(value) < 5)
# [<p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
特殊值True和None更让人感兴趣。 True匹配给定属性为任意值的标签,None匹配那些给定的属性值为空的标签。
一些例子如下:
soup.findAll(align=True)
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
[tag.name for tag in soup.findAll(align=None)]
# [u'html', u'head', u'title', u'body', u'b', u'b']
如果你需要在标签的属性上添加更加复杂或相互关联的(interlocking)匹配值, 如同上面一样,以callable对象的传递参数来处理Tag对象。
在这里你也许注意到一个问题。 如果你有一个文档,它有一个标签定义了一个name属性,会怎么样?
你不能使用name为keyword参数,因为Beautiful Soup 已经定义了一个name参数使用。
你也不能用一个Python的保留字例如for作为关键字参数。
Beautiful Soup提供了一个特殊的参数attrs,你可以使用它来应付这些情况。 attrs是一个字典,用起来就和keyword参数一样:
soup.findAll(id=re.compile("para$"))
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
soup.findAll(attrs={'id' : re.compile("para$")})
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
你可以使用attrs去匹配那些名字为Python保留字的属性,
例如class, for, 以及import;
或者那些不是keyword参数但是名字为Beautiful Soup搜索方法使用的参数名的属性,
例如name, recursive, limit, text,
以及attrs本身。
from BeautifulSoup import BeautifulStoneSoup
xml = '<person name="Bob"><parent rel="mother" name="Alice">'
xmlSoup = BeautifulStoneSoup(xml)
xmlSoup.findAll(name="Alice")
# []
xmlSoup.findAll(attrs={"name" : "Alice"})
# [parent rel="mother" name="Alice"></parent>]
对于CSS类attrs参数更加方便。例如class不仅是一个CSS属性,
也是Python的保留字。
你可以使用soup.find("tagName", {
"class" : "cssClass" })搜索CSS class,但是由于有很多这样的操作,
你也可以只传递一个字符串给attrs。
这个字符串默认处理为CSS的class的参数值。
from BeautifulSoup import BeautifulSoup
soup = BeautifulSoup("""Bob's <b>Bold</b> Barbeque Sauce now available in
<b class="hickory">Hickory</b> and <b class="lime">Lime</a>""")
soup.find("b", { "class" : "lime" })
# <b class="lime">Lime</b>
soup.find("b", "hickory")
# <b class="hickory">Hickory</b>
text 是一个用于搜索NavigableString对象的参数。
它的值可以是字符串,一个正则表达式,
一个list或dictionary,True或None,
一个以NavigableString为参数的可调用对象:
soup.findAll(text="one")
# [u'one']
soup.findAll(text=u'one')
# [u'one']
soup.findAll(text=["one", "two"])
# [u'one', u'two']
soup.findAll(text=re.compile("paragraph"))
# [u'This is paragraph ', u'This is paragraph ']
soup.findAll(text=True)
# [u'Page title', u'This is paragraph ', u'one', u'.', u'This is paragraph ',
# u'two', u'.']
soup.findAll(text=lambda(x): len(x) < 12)
# [u'Page title', u'one', u'.', u'two', u'.']
如果你使用text,任何指定给name 以及keyword参数的值都会被忽略。
recursive 是一个布尔参数(默认为True),用于指定Beautiful Soup遍历整个剖析树,
还是只查找当前的子标签或者剖析对象。下面是这两种方法的区别:
[tag.name for tag in soup.html.findAll()]
# [u'head', u'title', u'body', u'p', u'b', u'p', u'b']
[tag.name for tag in soup.html.findAll(recursive=False)]
# [u'head', u'body']
当recursive为false,只有当前的子标签<HTML>会被搜索。如果你需要搜索树,
使用这种方法可以节省一些时间。
设置limit 参数可以让Beautiful Soup
在找到特定个数的匹配时停止搜索。
文档中如果有上千个表格,但你只需要前四个,传值4到limit可以让你节省很多时间。
默认是没有限制(limit没有指定值).
soup.findAll('p', limit=1)
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>]
soup.findAll('p', limit=100)
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>,
# <p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>]
findall一样调用tag 一个小捷径。如果你像函数一样调用剖析对象或者Tag对象,
这样你调用所用参数都会传递给findall的参数,就和调用findall一样。
就上面那个文档为例:
soup(text=lambda(x): len(x) < 12)
# [u'Page title', u'one', u'.', u'two', u'.']
soup.body('p', limit=1)
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>]
find(name, attrs, recursive, text, **kwargs)好了,我们现在看看其他的搜索方法。他们都是有和 findAll 几乎一样的参数。
find方法是最接近findAll的函数,
只是它并不会获得所有的匹配对象,它仅仅返回找到第一个可匹配对象。
也就是说,它相当于limit参数为1的结果集。
以上面的 文档为例:
soup.findAll('p', limit=1)
# [<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>]
soup.find('p', limit=1)
# <p id="firstpara" align="center">This is paragraph <b>one</b>.</p>
soup.find('nosuchtag', limit=1) == None
# True
通常,当你看到一个搜索方法的名字由复数构成
(如findAll和findNextSiblings)时,
这个方法就会存在limit参数,并返回一个list的结果。但你
看到的方法不是复数形式(如find和findNextSibling)时,
你就可以知道这函数没有limit参数且返回值是单一的结果。
first哪里去了? 早期的Beautiful Soup 版本有一些first,fetch以及fetchPrevious方法。
这些方法还在,但是已经被弃用了,也许不久就不在存在了。
因为这些名字有些令人迷惑。新的名字更加有意义:
前面提到了,复数名称的方法名,比如含有All的方法名,它将返回一个
多对象。否则,它只会返回单个对象。