在这一章中,我们会试着分解复杂的HTML网页,去抽取我们寻找的信息。
让我们先假设你有一些目标内容,这些目标可以是一个名字,活着是静态的文本块。这个内容可能是掩藏在20个标签的深度,且没有有帮助的标签或者是HTML属性。则有可能写出下面的代码:
bsObj.findAll("table")[4].findAll("tr")[2].find("td").findAll("div")[1].find("a")
这段代码看上去是不太好的。除了从审美角度,网页的细微的变化都可能破坏网络爬虫。所以,你该怎么做呢?本章会给出相应的解答。
BS的另一项服务
通过属性来寻找标签,用标签列表来处理,然后解析树形导航。
你遇到的每个网站几乎都包含样式表(stylesheet)。尽管你可能会认为网站的样式层是专门为了浏览器设计的,人为解释可能是一件糟糕的事情,然后CSS的出现为网络爬虫带来了福利。CSS依赖于HTML元素的不同将他们定为不同。也就是说,一些标签可能看上去像这样:
<span class="green"></span>
而其它的则看上去像这样:
<span class="red"></span>
网络爬虫可以很容易地基于他们的类(class)分离出这两个不同的标签。比如说,他们可以使用BS去抓取所有的红色文本,而没有绿色文本。因为CSS依赖于这些可被识别的属性去对网站进行合适地定型,你几乎可以得到这样的保证,在当前的大多数网站中,这些类和ID属性是大量存在的。
让我们来构造一个网络爬虫去抓取“http://www.pythonscraping.com/pages/warandpeace.html”。
在这个网页中,故事中说的话都被标红,而名字都是被标绿了。看这个网页的源代码,你可以看到 span 标签指明了合适的CSS类,下面是网页源代码的一个例子:
"<span class="red"> Heavens! what a virulent attack!</span>" replied <span class="green">the prince</span>, not in the least disconcerted by this reception.
我们可以抓取整个网页,然后构造一个BS对象:
from urllib.request import urlopen
from urllib.error import URLError, HTMLError
from bs4 import BeautifulSoup
html = urlopen(http://www.pythonscraping.com/pages/warandpeace.html)
bsObj = BeautifulSoup(html)
使用BS对象,我们可以使用findAll 函数去抓取合适的名词的Python列表,通过仅仅选择< span class="green">< /span> 标签(findAll 是一个极其灵活的函数,在本书中我们会在后面经常使用):
nameList = bsObj.findAll("span",{"class":"green"})
for name in nameList:
print(name.get_text())
相应的输出结果为:
findAll函数
返回一个列表
有两个参数,第一个是字符串,说明标签;第二个是一个字典,说明里面的关键字。
点击运行,结果列举了文本中的所有合适的名词。相比于开头我们所使用的寻找方法,这里我们调用 bsObj.findAll(tagName, tagAttributes) 。
获得了人名序列之后,程序迭代序列中的所有人名,然后打印他们。
什么时候使用
get_text和 保护标签
.get_text()清除了文档中的所有标签,然后返回一个仅仅包含文本的字符串。比如,如果你正在处理一大块包含超链接的文本,段落,以及其它标签,所有的这些都会被清除,然后留下没有标签的文本快。
注意:相比于在文本中,在BS对象中你能更容易地找到你想要的。调用.get_text()应该是你最终要做的事情,在这之前还有打印,存储,活着修改你的最终数据。通常而言,你应该尽可能时间长地去保护文档的标签结构。
BS中的 find() 和 findAll()
BS的find() 和 findAll() 很可能是你使用最多的两个函数。有了它们,你能很容易地过滤掉HTML网页,从而发现想要的标签,或者是一个带有不同属性的标签。
这两个函数是极其相似的,正如BS文档给出的定义:
findAll(tag, attributes, recursive, text, limit, keywords)
find(tag, attributes, recursive, text, keywords)
你会发现你的
95%
时间都只需要用到头两个参数: tage 和 attributes。不管则样,我们下面看看这里面所有的参数。
tag: 你可以输入一个标签的 字符串 的名字,甚至可以是字符串标签名字的Python序列。比如:下面的代码会返回文档中 header 标签的序列:
.findAll({"h1","h2","h3","h4","h5","h6"})
attributes: 用Python字典来表示一个属性:key:value 。
.findAll("span",{"class":"red", "class":"green"})
recursive: 布尔类型。你想要深入文档多少?如果该参数等于 True,findAll 函数会查找孩子,以及孩子的孩子,只要那些标签匹配该参数。如果是false,它仅仅会寻找文档的最顶层的标签。默认情况下,findAll 会一直寻找(True);除非你考虑性能,否则无需设置。
text: 该参数是不常用的。它是基于标签的文本内容的匹配,而不是标签自身的属性匹配。比如,如果我们想要知道 “the price”被标签标识的次数:
nameList = bsObj.findAll(text = "the price")
print(len(nameList))
这个输出结果为:7.
limit: find 等同于 findAll 中的 limit=1。你可能会设置这个,当你仅仅对检索第
x
个项有兴趣。需要说明的是,给你的头几个项仅仅是为了他们发生,而不一定是你想要的头几个。
keyword: 你可以选择那些饱含一个特殊属性的标签。比如,
allText = bsObj.findAll(id = "text")
print(allText[0]_get_text)
对
keyword的附加说明
keyword在某些情况下,是非常有用的。但是,这个功能缺失有点冗余。通过一些方法,能够达到相同的目的。比如:
bsObj.findAll(id="text")
bsObj.findAll("",{"id":"text"})
除此之外,也可能会出现一些错误。比如,”class“是Python的预留单词,编程人员不可以使用这个。因此,如果我们试着用下面的语句,则有可能出线语义错误:
bsObj.findAll(class = "green")
当然,下面的语句没有这样的问题:
bsObj.findAll(class_ = "green")
其他的BS对象
迄今为止,我们已经见识了两种类型的BS对象:
- BS对象: bsObj.div.h1
Tag对象: find,findall
在BS库中,还有其它的对象,虽然使用的很少,但仍然重要:
NavigableString对象: 用于表示标签内的文本,而不是标签本身。Comment对象: 用于发现内容标签的HTML内容,<!--like this one-->
这四个对象是你在BS中仅会遇到的对象。
本文介绍如何使用BeautifulSoup库解析复杂HTML网页,提取特定文本内容,如基于CSS类选择器抓取文本,并讨论了find与findAll函数的区别及使用场景。
1153

被折叠的 条评论
为什么被折叠?



