1. 概述

正如标题,本文不会涉及到如何抓取感兴趣网页的相关知识,所关注的重点是如何对所抓取到的网页内容进行解析并抽取到感兴趣的内容。本文将主要介绍BS4在简化文档导航,查找方面提供的便利方法。

2. 实战

2.1 样例文本

站在前人的肩膀上,这里我们就直接复用官网上的例子;这里为了便于对比,我所粘贴的是经过格式化的,可读性更强的版本(即经过soup.prettify()处理过的)

<html>
 <head>
  <title>
   The Dormouse's story
  </title>
 </head>
 <body>
  <p class="title">
   <b>
    The Dormouse's story
   </b>
  </p>
  <p class="story">
   Once upon a time there were three little sisters; and their names were
   <a class="sister" href="http://example.com/elsie" id="link1">
    Elsie
   </a>
   ,
   <a class="sister" href="http://example.com/lacie" id="link2">
    Lacie
   </a>
   and
   <a class="sister" href="http://example.com/tillie" id="link2">
    Tillie
   </a>
   ; and they lived at the bottom of a well.
  </p>
  <p class="story">
   ...
  </p>
 </body>
</html>
2.2 简明代码

然后针对上述样例文档,我们来看看常用的一些方法,记住我们的目标是快速入门——先会走,再考虑跑的问题(本来写了不少注释的,但后来又都给删除了;相比较之下,与其让代码被大量的注释淹没,还不如通过对比的方式去理解每个方法的用途)。

# ---------- BeautifulSoup
# 构建一个BeautifulSoup实例,这是我们进行文档导航,查找指定节点的起点;
# 这里的参数html_doc: 
#	1. 可以是一段字符串,  例如 "<html>data</html>"
#	2. 也可以是文件句柄, 即 open("xxxx.html")的返回值
soup = BeautifulSoup(html_doc, "html.parser")
# <class 'bs4.BeautifulSoup'>
print(type(soup))
# 生成可读性更好的文档
print(soup.prettify())

print(soup.name)
# 本质是 soup.find("title")的简写形式, 还可以连读, 例如soup.p.b
print(soup.title)

# ---------- Tag
pTag = soup.p
# 查找所有的直接子节点, 区别是 contents 返回值是列表, 而children返回值则是生成器
print(pTag.contents)
print(pTag.children)
# 查找所有的子孙节点
print(pTag.descendants)
# 获取<p>节点中的 class属性的值
print(pTag.get("class"))
# 获取到tag中包含的所有文本内容, 包括子孙tag中的内容
print(pTag.get_text())

# 获取某个元素的父节点
print(pTag.parent)
# 在同级节点之间导航
print(pTag.previous_sibling)
print(pTag.next_sibling)

# ---------- NavigableString
pTagStr = pTag.string
# 替换成其它的字符串
pTagStr.replace_with("LQ")

# ---------- Comment
# Comment 对象是一个特殊类型的 NavigableString 对象

3. find系列方法

在BS4的使用过程中,我们需要对find系列有一个比较全面的了解,这样将可以节省大量的时间。

find系列方法虽然看似有好几个,但它们的方法参数都是一样;因此只需要掌握其中一个,其它的也就顺理成章了。

这里我们就以最常用的 find_all( name , attrs , recursive , text , **kwargs ) 为例来进行讲解:

  1. name参数,使用该参数可以查找所有名字为 name 的Tag,Html文档中的字符串对象会被自动忽略掉,并不会进入筛选候选名单。该参数值可以使用字符串,正则表达式,列表,方法或是 True。
  2. **kwargs参数,使用该参数将把该参数当作指定名字tag的属性来搜索,例如soup.find_all(id='link2') 搜索的是 包含id属性, 且值为 link2 的Tag。参数值也是可以使用字符串,正则表达式,列表,方法或是 True。
  3. attrs 参数,有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性,此时attrs 参数就能派上用场——attrs 参数定义一个字典参数来搜索包含特殊属性的tag。例如 soup.find_all(attrs={"data-foo": "value"})
  4. text 参数,通过该参数我们可以搜索文档中的字符串内容。这个特性在处理某些需求时会有奇效。和前面的一样,text 参数接受字符串,正则表达式,列表,方法或是 True。
  5. recursive参数,有时我们可能只是想要搜索tag的直接子节点,此时可以使用参数 recursive=False,这样既可以节省处理时间,又能节约处理内存。
  6. limit参数,该参数用于限制返回结果的数量,效果与SQL中的limit关键字类似,BS4在搜索到的结果数量达到 limit 的限制时,就会停止搜索返回结果。样例如下:soup.find_all("a", limit=2)

讨论完了这些方法参数之后,我们再来看下上面讨论过程中一再出现的 " 字符串,正则表达式,列表,方法或是 True",它们的术语其实是 过滤器(Filter),作用是充当进行筛选操作时的筛选条件;因为需求的千差万别,所以BS4提供了由简单到复杂的五种方式,下面就用示例分别来分别讲解下:

# --------- name
soup.find_all("a") # 字符串
# 找出所有以b开头的标签,这表示<body>和<b>标签都应该被找到
soup.find_all(re.compile("^b") # 正则表达式

# ------- **kwargs参数
soup.find_all(href=re.compile("elsie"))
soup.find_all(id=True) #True,只要包含id属性的都算上

# ------- attrs参数
data_soup.find_all(attrs={"data-foo": "value"})

# ------- text参数
soup.find_all(text=["Tillie", "Elsie", "Lacie"]) # 列表
soup.find_all(text=re.compile("Dormouse")) # 正则表达式  
soup.find_all("a", text="Elsie")  # 和其他参数混合使用
soup.find_all(text = lambda x: x == x.parent.string) # 方法

4. 总结

Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库,它能够通过你喜欢的转换器实现惯用的文档导航,查找,修改文档的功能;对于新手而言,BS4是爬虫入门的优先选择项。

10-04 19:22