Navigating剖析树
Tag
对象都有如下含有所有的成员的列表(尽管,某些实际的成员值可能为
None
).
NavigableString
对象也有下面这些成员,除了
contents
和
string
成员。
上面那个
例子中, <HEAD>
Tag
的parent是<HTML>
Tag
. <HTML>
Tag
的parent是
BeautifulSoup
剖析对象自己。 剖析对象的parent是
None
. 利用
parent
,你可以向前遍历剖析树。
soup.head.parent.name#u'html'
soup.head.parent.parent.__class__.__name__#'BeautifulSoup'
soup.parent == None#True
contents
使用
parent
向前遍历树。使用
contents
向后遍历树。
contents
是
Tag
的有序列表,
NavigableString
对象包含在一个页面元素内。只有最高层的剖析对象和
Tag
对象有
contents
。
NavigableString
只有strings,不能包含子元素,因此他们也没有
contents
.
在上面的
例子中,
contents
的第一个<P>
Tag
是个列表,包含一个
NavigableString
("This is paragraph "), 一个<B>
Tag
, 和其它的
NavigableString
(".")。而
contents
的<B>
Tag
: 包含一个
NavigableString
("one")的列表。
pTag = soup.p
pTag.contents#[u'This is paragraph ', <b>one</b>, u'.']
pTag.contents[1].contents#[u'one']
pTag.contents[0].contents#AttributeError: 'NavigableString' object has no attribute 'contents'
string
为了方便,如果一个标签只有一个子节点且是字符串类型,这个自己可以这样访问
tag.string
,等同于
tag.contents[0]
的形式。 在上面的
例子中,
soup.b.string
是个
NavigableString
对象,它的值是Unicode字符串"one". 这是剖析树中<B>
Tag
的第一个string。
soup.b.string#u'one'
soup.b.contents[0]#u'one'
但是
soup.p.string
是
None
, 剖析中的第一个<P>
Tag
拥有多个子元素。
soup.head.string
也为
None
, 虽然<HEAD> Tag只有一个子节点,但是这个子节点是
Tag
类型 (<TITLE>
Tag
),不是
NavigableString
。
soup.p.string == None#True
soup.head.string == None#True
nextSibling
和previousSibling
使用它们你可以跳往在剖析树中同等层次的下一个元素。在上面的
文档中, <HEAD>
Tag
的
nextSibling
是<BODY>
Tag
,因为<BODY>
Tag
是在<html>
Tag
的下一层。 <BODY>标签的
nextSibling
为
None
, 因为<HTML>下一层没有标签是直接的在它之后。
soup.head.nextSibling.name#u'body'
soup.html.nextSibling == None#True
相应的<BODY>
Tag
的
previousSibling
是<HEAD>标签, <HEAD>
Tag
的
previousSibling
为
None
:
soup.body.previousSibling.name#u'head'
soup.head.previousSibling == None#True
更多例子:<P>
Tag
的第一个
nextSibling
是第二个 <P>
Tag
。第二个<P>
Tag里的<B>Tag
的previousSibling
是 NavigableString
"This is paragraph"。 这个NavigableString
的previousSibling
是None
, 不会是第一个<P> Tag
里面的任何元素。
soup.p.nextSibling#<p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>
secondBTag = soup.findAlll('b')[1]
secondBTag.previousSibling#u'This is paragraph'
secondBTag.previousSibling.previousSibling == None#True
next
和previous
使用它们可以按照soup处理文档的次序遍历整个文档,而不是它们在剖析树中看到那种次序。例如<HEAD>
Tag
的
next
是<TITLE>
Tag
, 而不是<BODY>
Tag
。这是因为在
原始文档中,<TITLE> tag 直接在<HEAD>标签之后。
soup.head.next#u'title'
soup.head.nextSibling.name#u'body'
soup.head.previous.name#u'html'
Where
next
and
previous
are concerned, a
Tag
's
contents
come before its
nextSibling
. 通常不会用到这些成员,但有时使用它们能够非常方便地从剖析树获得不易找到的信息。
你可以像遍历list一样遍历一个标签(
Tag
)的
contents
。这非常有用。类似的,一个Tag的有多少child可以直接使用len(tag)而不必使用len(tag.contents)来获得。以上面那个
文档中的为例:
for i in soup.body:
print i#<p id="firstpara" align="center">This is paragraph <b>one</b>.</p>#<p id="secondpara" align="blah">This is paragraph <b>two</b>.</p>
len(soup.body)#2
len(soup.body.contents)#2
使用标签(tag)名作为成员
像剖析对象或Tag对象的成员一样使用Tag名可以很方便的操作剖析树。前面一些例子我们已经用到了这种方式。以上述
文档为例,
soup.head
获得文档第一个<HEAD>标签:
soup.head#<head><title>Page title</title></head>
通常,调用
mytag.foo
获得的是
mytag
的第一个child,同时必须是一个<FOO>
标签
。如果在
mytag
中没有<FOO>
标签
,
mytag.foo
返回一个
None
。你可以使用这中方法快速的读取剖析树:
soup.head.title#<title>Page title</title>
soup.body.p.b.string#u'one'
你也可以使用这种方法快速的跳到剖析树的某个特定位置。例如,如果你担心<TITLE> tags会离奇的在<HEAD> tag之外,你可以使用
soup.title
去获得一个HTML文档的标题(title),而不必使用
soup.head.title
:
soup.title.string#u'Page title'
soup.p
跳到文档中的第一个 <P> tag,不论它在哪里。
soup.table.tr.td
跳到文档总第一个table的第一列第一行。
这些成员实际上是下面
first
方法的别名,
这里更多介绍。 这里提到是因为别名使得一个定位(zoom)一个结构良好剖析树变得异常容易。
获得第一个<FOO> 标签另一种方式是使用
.fooTag
而不是
.foo
。例如,
soup.table.tr.td
可以表示为
soup.tableTag.trTag.tdTag
,甚至为
soup.tableTag.tr.tdTag
。如果你喜欢更明确的知道表示的意义,或者你在剖析一个标签与Beautiful Soup的方法或成员有冲突的XML文档是,使用这种方式非常有用。
from BeautifulSoup import BeautifulStoneSoup
xml = '<person name="Bob"><parent rel="mother" name="Alice">'
xmlSoup = BeautifulStoneSoup(xml)
xmlSoup.person.parent # A Beautiful Soup member#<person name="Bob"><parent rel="mother" name="Alice"></parent></person>
xmlSoup.person.parentTag # A tag name#<parent rel="mother" name="Alice"></parent>
如果你要找的标签名不是有效的Python标识符,(例如
hyphenated-name
),你就需要使用
first
方法了。