BeautifulSoup使用
Beautiful Soup是一个可以从HTML或XML文件中提取数据的Python库. 它能够通过你喜欢的转换器实现惯用的文档导航, 查找, 修改文档的方式. Beautiful Soup会帮你节省数小时甚至数天的工作时间.
之后会整理一下XPath的用法,在使用scrapy框架进行爬虫时,需要用到。
Xpath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历,也算比较好理解的。
一、创建BeautifulSoup对象
构造函数参数比较多,主要介绍前两个参数:
第一个参数是 markup,默认为空字符串。A string or a file-like object representing markup to be parsed.
第二个参数是 features,Desirable features of the parser to be used. 即文本解析器
可指定特定解析器:”lxml”, “lxml-xml”(唯一支持xml解析),”html.parser”(python内置), or “html5lib”
import urllib.request
from bs4 import BeautifulSoup
douban_path = "https://movie.douban.com"
response = urllib.request.urlopen(douban_path)
soup = BeautifulSoup(response, 'html.parser')
# 可以接受response对象
soup = BeautifulSoup(response.read().decode('utf-8'), 'html.parser')
# 可以接受字符串
soup = BeautifulSoup(open(test.html),'html.parser')
# 可以接受本地文件
soup.prettify() :有层次结构的输出html文档(父子兄弟关系),和开发者模式看到的差不多
soup.get_text(): 获取所有文字信息 对单个节点来说和 .string 一样
二、bs4四大对象种类
Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:
- Tag
- NavigableString
- BeautifulSoup
- Comment
(1).Tag
Tag通俗点讲就是HTML中的一个个标签,例如:
<title>_zhaowendao_</title>
得到 BeautifulSoup 对象后直接以属性形式输出, 但它查找的是在所有内容中的第一个符合要求的标签
print(soup.title)
# <title>
# 豆瓣电影
#</title>
print(soup.a)
# <a class="nav-login" href="https://www.douban.com/accounts/login?source=movie" rel="nofollow">登录</a>
对于Tag,有两个重要的属性:name和attrs
name:标签本身的名字,如标题h1, 段落p等等,soup 对象本身比较特殊,它的 name 即为 [document]
print(soup.name)
print(soup.title.name)
# [document]
# title
attrs:得到标签属性集合的字典形式,
print(soup.a.attrs)
# {'href': 'https://www.douban.com/accounts/login?source=movie', 'class': ['nav-login'], 'rel': ['nofollow']}
如果我们想要单独获取某个属性,可以这样,例如我们获取a标签的class是什么,两个等价的方法如下:
注意:当属性不存在时,使用 get 返回None,字典形式取值会报错
print(soup.a['href'])
print(soup.a.get('class'))
# https://www.douban.com/accounts/login?source=movie
# https://www.douban.com/accounts/login?source=movie
(2).NavigableString
用 .string获取标签内部的文字
注意:如果该Tag节点仅有单一子节点,将返回最里面的内容,如果有多个子节点,则返回None
print(soup.a.string)
# 登陆
print type(soup.a.string)
# <class 'bs4.element.NavigableString'>
如果遇到多个节点,这个时候需要使用 .strings或者 .stripped_strings(去空白符),进行遍历输出
for s in soup.body.stripped_strings:
print(s)
(3).BeautifulSoup
BeautifulSoup 对象表示的是一个文档的全部内容.大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性:
print type(soup.name)
# <class 'str'>
print soup.name
# [document]
print soup.attrs
#{} 空字典
(4).Comment
Comment对象是一个特殊类型的NavigableString对象
标签里的内容是注释,用 .string 来输出它的内容,输出内容将没有注释符号,这可能会给我们带来不必要的麻烦。
if type(soup.a.string)==bs4.element.Comment:
print soup.a.string
三、遍历文档树
(1)直接子节点(不包含孙节点)
contents: tag的content属性可以将tag的子节点以列表的方式输出
['\n', <script type="text/javascript">var _body_start = new Date();</script>, '\n', <link href="/im....]
children:它返回的不是一个 list,不过我们可以通过遍历获取所有子节点,它是一个 list 生成器对象
for child in soup.body.children:
print(child)
(2)所有子孙节点
.descendents
以递归形式遍历子孙节点
即先从最外层遍历一遍,之后从其子节点开始遍历,一直到最里层
print(soup.contents)
for i in soup.descendants:
print(i)
(3)父节点 .parent 所有父节点 .parents 递归形式需要遍历
(4)兄弟节点 .next_sibling, .previous_sibling 加s所有
四、搜索文档树
find_all(name, attrs, recursive, text, limit, **kwargs):
find_all() 方法搜索当前tag的所有tag子节点,并判断是否符合过滤器的条件。
(1)name参数
1.传递字符串:在搜索方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容,返回列表。
下面的例子用于查找文档中所有的<a>
标签:
print(soup.find_all("a"))
2.传递正则表达式:如果传入正则表达式作为参数,Beautiful Soup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以b开头的标签,这表示<body>
和<b>
标签都应该被找到
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
# body
# br
3.传递列表:如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回,下面代码找到文档中所有<title>
标签和<b>
标签
print(soup.find_all(['title','br']))
#<title>
# 豆瓣电影
#</title>
#<br/>
4.传递True: True 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点:
5.传方法: 接受一个参数,返回bool值,若为True则表示找到:
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
soup.find_all(has_class_but_no_id)
(2)attrs参数或者keyword(**kwargs)指定参数
soup.find_all(id="xxx")
soup.find_all(href=re.compile("xxx"))
soup.find_all("a", class_="xxx")
soup.find_all(attrs={"data-foo": "value"})
# 可以同时制定多个属性进行过滤
3)recursive参数
调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False。
4)text参数: 通过 text 参数可以搜搜文档中的字符串内容,与 name 参数的可选值一样, text 参数接受字符串 , 正则表达式 , 列表, True。仅返回字符串内容。
soup.find_all(text="豆瓣")
# ['豆瓣', '豆瓣']
5)limit参数
find_all() 方法返回全部的搜索结构,如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果。