2 HTML分解(1)

在这一章中,我们会试着分解复杂的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% 时间都只需要用到头两个参数: tageattributes。不管则样,我们下面看看这里面所有的参数。

tag 你可以输入一个标签的 字符串 的名字,甚至可以是字符串标签名字的Python序列。比如:下面的代码会返回文档中 header 标签的序列:

.findAll({"h1","h2","h3","h4","h5","h6"})

attributes 用Python字典来表示一个属性:key:value

.findAll("span",{"class":"red", "class":"green"})

recursive 布尔类型。你想要深入文档多少?如果该参数等于 TruefindAll 函数会查找孩子,以及孩子的孩子,只要那些标签匹配该参数。如果是false,它仅仅会寻找文档的最顶层的标签。默认情况下,findAll 会一直寻找(True);除非你考虑性能,否则无需设置。

text 该参数是不常用的。它是基于标签的文本内容的匹配,而不是标签自身的属性匹配。比如,如果我们想要知道 “the price”被标签标识的次数:

nameList = bsObj.findAll(text = "the price")
print(len(nameList))

这个输出结果为:7.

limit find 等同于 findAll 中的 limit=1。你可能会设置这个,当你仅仅对检索第 x <script type="math/tex" id="MathJax-Element-165">x</script> 个项有兴趣。需要说明的是,给你的头几个项仅仅是为了他们发生,而不一定是你想要的头几个。

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中仅会遇到的对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值