Python从零开始写爬虫(二)BeautifulSoup库使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011436666/article/details/74028308

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库, BeautifulSoup在解析的时候是依赖于解析器的,它除了支持Python标准库中的HTML解析器,还支持一些第三方的解析器比如lxml等。可以从其官网得到更详细的信息:http://beautifulsoup.readthedocs.io/zh_CN/latest/#。在windows环境下,直接通过pip install beautifulsoup4安装使用它把,同时也可以将lxml解析库安装上pip install lxml

一、BeautifulSoup基础

1、打开文件

如下代码所示,用lxml方式解析文件,也可以使用html.parser方式。如果担心使用的文件不规则,可以使用soup.prettify()方法来格式化文档。

# coding=utf-8

import requests
from bs4 import BeautifulSoup

#创建BeautifulSoup对象,打开文件
r = requests.get('http://music.163.com/')
soup = BeautifulSoup(r.text, "lxml")
print(soup.title)
#<title>网易云音乐</title>

soup2 = BeautifulSoup(open("D:\music.html",encoding="utf-8",errors="ignore"),"lxml")
print(soup2.title)
#<title>网易云音乐</title>

2、四大对象

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag , NavigableString , BeautifulSoup , Comment .

Tag对象:
Tag对象和xml、html原生文档中的tag相同。tag就是一个个标签,如下面html片段当中的meta、title等等。

<meta name="baidu-site-verification" content="cNhJHKEzsD" />
<meta charset="utf-8">
<meta property="qc:admins" content="27354635321361636375" />
<title>网易云音乐</title>
<meta name="applicable-device" content="pc,mobile">
<script type="text/javascript">

使用BeautifulSoup对象,可以方便的获取tag,如下所示:

soup2 = BeautifulSoup(open("D:\\test.html",encoding="utf-8",errors="ignore"),"lxml")
#获取meta这个tag对象
tag = soup2.meta
print(type(tag))
#<class 'bs4.element.Tag'>

可以看到,很方便的便获取到了Tag对象,Tag对象有两个重要的属性name和attrs,怎么获取这两个属性呢?使用print(tag.name)、print(tag.attrs)即可,其输出为:

meta
{'name': 'baidu-site-verification', 'content': 'cNhJHKEzsD'}

可以看见,默认输出了第一个meta tag的数据,属性是以dictionary形式输出的,获取特定的属性就可以用字典的相关方法了:

print(tag.attrs["name"])
print(tag.attrs.get("content"))
#输出为:
#baidu-site-verification
#cNhJHKEzsD

NavigableString对象:
这个对象呢,就是用来获取tag中的文字的,还是用上面的例子:

tag = soup2.title
print(tag.string)
print(type(tag.string))
#网易云音乐
#<class 'bs4.element.NavigableString'>

这样我们就轻松获取到了标签里面的内容。如果tag包含了多个子节点tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None。这时可以使用 .strings 来循环获取。

BeautifulSoup对象:
BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag。比如上面使用BeautifulSoup对象调用文档中的tag信息。因为 BeautifulSoup 对象并不是真正的HTML或XML的tag,所以它没有name和attribute属性.但有时查看它的 .name 属性是很方便的,所以 BeautifulSoup 对象包含了一个值为 “[document]” 的特殊属性 .name:

soup2 = BeautifulSoup(open("D:\\test.html",encoding="utf-8",errors="ignore"),"lxml")
print(soup2.name)
#[document]

Comment 对象:
Tag , NavigableString , BeautifulSoup 几乎覆盖了html和xml中的所有内容,但是还有一些特殊对象.就像文档的注释部分。比如文档中含有这样一行:<b><!--Hey, buddy. Want to buy a used parser?--></b>,使用以下代码来查看输出:

扫描二维码关注公众号,回复: 3861369 查看本文章
b_tag = soup2.b
print(b_tag.string)
print(type(b_tag.string))
#输出为:
#Hey, buddy. Want to buy a used parser?
#<class 'bs4.element.Comment'>

可以看到,输出的仅仅是注释的文字部分,已经把注释符号去掉了,要注意这个小细节。

二、遍历文档树

1、直接子节点

一个Tag可能包含多个字符串或其它的Tag,这些都是这个Tag的子节点.Beautiful Soup提供了许多操作和遍历子节点的属性。

.contents 和 .children

tag的 .contents 属性可以将tag的子节点以列表的方式输出:

head_tag = soup.head
#输出head tag中的子节点的个数
print(len(head_tag.contents))
#子节点的内容,列表形式返回
print(head_tag.contents)

tag的 .children属性可以将tag的子节点循环输出:

for child in soup.head.children:
    print(child)

遇到的问题,下面这个文档执行上面代码的输出和期望的不太一致:

<html>
<head>
<meta charset="utf-8"/>
<meta name="baidu-site-verification" content="cNhJHKEzsD"/>
<meta property="qc:admins" content="27354635321361636375"/>
</head>
</html>

输出为:

#为什么会有那么多换行符?
7
['\n', <meta charset="utf-8"/>, '\n', <meta content="cNhJHKEzsD" name="baidu-site-verification"/>, '\n', <meta content="27354635321361636375" property="qc:admins"/>, '\n']

2、所有子节点

.contents 和 .children 属性仅包含tag的直接子节点,.descendants 属性可以对所有tag的子孙节点进行递归循环。


3、节点内容

单节点:如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点。

多节点:如果tag中包含多个字符串 ,可以使用 .strings 来循环获取,使用 .stripped_strings 可以去除多余空白内容。


4、其他节点

其他各种节点有很多,不逐个解释了,这里只列出它们:

节点名称 属性名称
父节点 .parent 属性
全部父节点 .parents 属性
兄弟节点 .next_sibling .previous_sibling 属性
全部兄弟节点 .next_siblings .previous_siblings 属性
前后节点 .next_element .previous_element 属性(针对所有节点,部分层次)
所有前后节点 .next_elements .previous_elements 属性

~

这些属性的使用和前面子节点的使用类似,后面会陆续用到。

三、搜索文档树

BeautifulSoup定义了很多搜索方法,这里着重介绍find_all() ,其它方法的参数和用法类似。

find

find_all( name , attrs , recursive , string , **kwargs )

find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。常用的过滤器的类型有以下几种:

  • 字符串: 最简单的过滤器是字符串.在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容
  • 正则表达式: 如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容
  • 列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回
  • True:True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
  • 方法:如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False

下面介绍一下find_all()方法中的参数:

1、name 参数:
可以查找所有名字为 name 的tag,字符串对象会被自动忽略掉。搜索 name 参数的值可以使任一类型的 过滤器 ,字符串,正则表达式,列表,方法或是 True。

3、recursive 参数:
调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False。

r = requests.get('http://music.163.com/#/discover/toplist')
soup = BeautifulSoup(r.text, "lxml")
print(soup.title)
#<title>网易云音乐</title>
print(soup.html.find_all("title",recursive = False))
#[]

只有 find_all() 和 find() 支持 recursive 参数。

4、string 参数:
通过 string 参数可以搜搜文档中的字符串内容.与 name 参数的可选值一样, string 参数接受 字符串 , 正则表达式 , 列表, True。

5、keyword 参数(kwargs ):
如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索,如果包含一个名字为 id 的参数,Beautiful Soup会搜索每个tag的”id”属性。

按CSS搜索:从Beautiful Soup的4.1.1版本开始,可以通过 class_ 参数搜索有指定CSS类名的tag。可以通过CSS值完全匹配,也可以使用各种过滤。

limit 参数:
使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果。

find_all() 的简写方法: find_all几乎是Beautiful Soup中最常用的搜索方法,所以我们定义了它的简写方法. BeautifulSoup 对象和 tag 对象可以被当作一个方法来使用,这个方法的执行结果与调用这个对象的 find_all() 方法相同,下面两行代码是等价的:

soup.find_all("a")
soup("a")

这两行代码也是等价的:

soup.title.find_all(string=True)
soup.title(string=True)

find()与 find_all() 方法的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果。

四、CSS选择器

Beautiful Soup支持大部分的CSS选择器 ,在 Tag 或 BeautifulSoup 对象的 .select() 方法中传入字符串参数, 即可使用CSS选择器的语法找到tag。
CSS选择器的语言可以参考笔者之前的文章:Selenium自动化测试-入门
或者官方资料: CSS 选择器参考手册

猜你喜欢

转载自blog.csdn.net/u011436666/article/details/74028308