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
的方法名,它将返回一个
多对象。否则,它只会返回单个对象。