Navigating Trees
findAll
函数负责发现基于名字和属性的标签。然而,如果你需要发现一个文档中基于位置的标签,该如何做呢?那就是 Navigating Trees (NT)出现的原因。在第一章中,我们看到了以一个单方向的导航的BS树:
bsObj.tag.subTag.anotherSubTag
现在让我们看看下面的例子, http://www.pythonscraping.com/pages/page3.html
该网页以一种树形结构组织:
在下面的某些部分,我们也会使用一个相同的HTML结构作为一个例子。
处理子女(children)和后代(descendants)
- 子女:是最接近父辈下面的一个标签
- 后代:是父辈下面的任意一层
比如说:tr
是table
的子女,而tr, th, td, img, span
是tag
的后代。因此,子女都是后代,但后代并不一定是子女。
通常而言,BS函数一直处理当前标签的后代。比如:bsObj.body.h1
。
相似的,bsObj.div.findAll("img")
会发现文档下面的第一个div
标签,然后检索 img
标签。
如果你仅仅想那些是子女的后代,你可以使用.children
标签:
from url lib.request import url open
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bsObj = BeautifulSoup(html)
for child in bsObj.find("table",{"id":"giftList"}).children:
print(child)
输出结果为:
这段代码会输出giftList
表格下的产品行。如果使用descenants
,则大约二十打的标签会输出,包括了img, span, td
标签。输出结果为:
这就是子女与后代之间的区别。
处理兄弟姐妹
BS中next_siblings
函数使得可以从表格中更好的收集数据,特别是标题行:
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bsObj = BeautifulSoup(html)
for sibling in bsObj.find("table",{"id":"giftList"}).tr.next_siblings:
print(sibling)
结果为:
从结果可知,这段代码输出了所有产品行,除了表格的的第一行。为什么第一行跳过了呢?两个原因:(1)对象不能是它自己的兄弟姐妹,因此,对象不会包含在这个列表中;(2)这个函数仅仅调用了下一个兄弟姐妹。如果我们去选择列表中间的一个行,可以调用next_siblings
,然后仅仅下一个兄弟姐妹会被返回。所以,选择title
行,然后调用next_siblings
,我们可以选择所在的列表,除了标题行。
做特殊的选择
如果我们选择bsObj.table.tr
甚至仅仅是bsObj.tr
,其结果就是列表的第一行。然而,我还是选择了一个长的代码:
bsObj.find("table",{"id":"giftList"}).tr
尽管它看上去仅有一个表格,它很容易失去一些东西。除此之外,网页的布局会随时发生变化。在网页中的第一个标签的样子很有可能在后来变成网页中的第二个标签,或者第三个标签。为了使你的爬虫更加的健壮,最好的方式是做标签选择的时候,尽可能的特别。所以要充分利用标签属性。
作为next_siblings
的补充,previous_siblings
函数也是很有帮助的,如果很容易的选择出兄弟姐妹的标签的末尾。
当然,也有next_sibling
和 previous_sibling
函数,仅仅返回列表中的单个标签。
处理父辈
当爬虫网页的时候,你会发现你需要发现标签父辈的次数少于需要发现子女或者兄弟姐妹。发现BS父辈的函数,.parent
和 .parents
。比如:
from urllib.request import urlopen
from urllib.error import HTTPError, URLError
from bs4 import BeautifulSoup
html = urlopen("http://www.pythonscraping.com/pages/page3.html")
bsObj = BeautifulSoup(html)
print(bsObj.find("omg",{"src":"../img/gifts/img1.jpg"}).parent.previous_sibling.get_text())
输出结果为:”$15.00”。
为什么结果会是这样呢?下面是树形结构:
- 首先选择图像标签;
- 选择标签的父辈;
- 选择父辈的前一个兄弟姐妹;
- 选择标签文本。