1. Beautiful Soup的简介
BeautifulSoup是python的一个库,可以很方便地提取出HTML或XML标签中的内容。
2. Beautiful Soup 安装
BeautifulSoup3 目前已经停止开发,现在使用比较多的是beautifulSoup4,导入时我们需要用 import bs4 。
据说 BS4 对 Python3 的支持不够好,不过我还没啥感觉。
Windows
如果你电脑里同时安装了python2和python3,那么请这样做:
python2:pip install beautifulsoup4
python3:pip3 install beautifulsoup4
如果pip3 install beautifulsoup4不好使的话,可以使用:py -3 - m install beautifulsoup4
安装了Py3.5出现’HTMLParseError’的同学可以看这个方法:
出现No module named bs4的看这个方法:
Linux
sudo apt-get install python-bs4
Mac
sudo easy_install pip
pip install beautifulsoup4
3.官方文档
4.使用方法
↓↓↓↓↓↓↓↓↓↓↓↓↓以下代码部分引用官方文档,只进行了一些补充说明
html_doc = """
<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 href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
html_doc是一个字符串
首先导入bs4
from bs4 import BeautifulSoup
然后
soup = BeautifulSoup(html_doc, 'html.parser')
print(soup.prettify())
也可以用本地 HTML 文件来创建对象
soup = BeautifulSoup(open('index.html'))
返回标签
print soup.title
#<title>The Dormouse's story</title>
print soup.p
#<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
BeautifulSoup 对象包含了一个值为 "[document]” 的特殊属性 .name
soup.name
#u'[document]'
find_all()可以得到所有的标签,或是通过名字得到比一个tag更多的内容:
soup.find_all('a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
.contents 和 .children
tag的 .contents 属性可以将tag的子节点以列表的方式输出:
head_tag = soup.head
head_tag
# <head><title>The Dormouse's story</title></head>
head_tag.contents
[<title>The Dormouse's story</title>]
title_tag = head_tag.contents[0]
title_tag
# <title>The Dormouse's story</title>
title_tag.contents
# [u'The Dormouse's story']
通过tag的 .children 生成器,可以对tag的子节点进行循环,可以用来显示文本内容:
for child in title_tag.children:
print(child)
# The Dormouse's story
.descendants
.contents 和 .children 属性仅包含tag的直接子节点.例如,标签只有一个直接子节点
head_tag.contents
# [<title>The Dormouse's story</title>]
但是标签也包含一个子节点:字符串 “The Dormouse’s story”,这种情况下字符串 “The Dormouse’s story”也属于标签的子孙节点. .descendants 属性可以对所有tag的子孙节点进行递归循环:
for child in head_tag.descendants:
print(child)
# <title>The Dormouse's story</title>
# The Dormouse's story
↑↑↑↑↑↑↑↑↑可以发现是依次输出的
.string
如果tag只有一个 NavigableString 类型子节点,那么这个tag可以使用 .string 得到子节点:
title_tag.string
# u'The Dormouse's story'
如果一个tag仅有一个子节点,那么这个tag也可以使用 .string 方法,输出结果与当前唯一子节点的 .string 结果相同:
head_tag.contents
# [<title>The Dormouse's story</title>]
head_tag.string
# u'The Dormouse's story'
如果tag包含了多个子节点,tag就无法确定 .string 方法应该调用哪个子节点的内容, .string 的输出结果是 None
:
print(soup.html.string)
# None
↑↑↑↑↑↑↑↑↑.string可以返回标签内部的文本信息,但是只能返回一个确定的信息,多了就不行了
.strings 和 stripped_strings
如果tag中包含多个字符串 ,可以使用 .strings 来循环获取:
for string in soup.strings:
print(repr(string))
输出的字符串中可能包含了很多空格或空行,使用 .stripped_strings 可以去除多余空白内容:
for string in soup.stripped_strings:
print(repr(string))
.parent
通过 .parent 属性来获取某个元素的父节点.在例子“爱丽丝”的文档中,标签是标签的父节点:
title_tag.parent
# <head><title>The Dormouse's story</title></head
.parents
通过元素的 .parents 属性可以递归得到元素的所有父辈节点
link = soup.a
link
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
for parent in link.parents:
if parent is None:
print(parent)
else:
print(parent.name)
# p
# body
# html
# [document]
# None
find_all()
在find_all() 方法中传入一个字符串参数,Beautiful Soup会查找与字符串完整匹配的内容:
soup.find_all('b')
# [<b>The Dormouse's story</b>]
传入正则表达式作为参数,BeautifulSoup会通过正则表达式的 match() 来匹配内容.下面例子中找出所有以b开头的标签,这表示<body>
和<b>
标签都应该被找到:
import re
for tag in soup.find_all(re.compile("^b")):
print(tag.name)
# body
# b
如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>
标签和<b>
标签:
soup.find_all(["a", "b"])
# [<b>The Dormouse's story</b>,
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
方法
如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
下面方法校验了当前元素,如果包含 class 属性却不包含 id 属性,那么将返回 True:
def has_class_but_no_id(tag):
return tag.has_attr('class') and not tag.has_attr('id')
将这个方法作为参数传入 find_all() 方法,将得到所有
标签:
soup.find_all(has_class_but_no_id)
# [<p class="title"><b>The Dormouse's story</b></p>,
# <p class="story">Once upon a time there were...</p>,
# <p class="story">...</p>]
find()
find()与 find_all() 唯一的区别是find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果.