快速上手BeautifulSoup4

安装

安装BeautifulSoup

可通过系统的包管理工具安装:

1
apt-get install Python-bs4

或者通过python的包管理工具pip进行安装:

1
pip install beautifulsoup4 -i http://mirrors.aliyun.com/pypi/simple/

安装解析器

python内置了HTML解析器,若要使用第三方的解析器,如lxml,则需根据操作系统和python的版本选择对应的包进行安装,若要离线安装,windows下可点击该链接下载。

1
2
3
apt-get install Python-lxml

pip install lxml

或纯python实现的html5lib,其解析方式与浏览器相同。

1
2
3
apt-get install Python-html5lib

pip install html5lib
解析器 使用方法 优势 劣势
python标准库 BeautifulSoup(html, ‘html.parser’) python的内置标准库
执行速度适中
文档容错能力强
Python2.7.3或3.2.2前的版本中文容错能力差
lxml HTML解析器 BeautifulSoup(html, ‘lxml’) 速度快
文档容错能力强
需要安装c语言库
lxml XML解析器 BeautifulSoup(html, [‘lxml’, ‘xml’]) 速度快
唯一支持XML的解析器
需要安装c语言库
html5lib BeautifulSoup(html, ‘html5lib’) 最好的容错性
以浏览器的方式解析文档
生成HTML5格式的文档
速度慢
不依赖外部扩展

使用技巧

假设有一个网页html,创建一个BeautifulSoup对象,并指定解析器。

1
2
3
4
5
6
7
8
9
from bs4 import BeautifulSoup

soup = BeautifulSoup(html, "lxml)

soup.prettify() # 缩进打印
soup.title # 获取title标签的内容
soup.div # 获取第一个div标签的内容
soup.find(id="uq") # 获取id="u1"的标签
soup.find_all(class_="item") # 查找所有class属性有item的标签

注:灵活利用IDE的打断点进行debug可以快速调试,拿到自己需要的元素

对象种类

~将复杂HTML文档转换成一个复杂的树形结构,每个节点都是python对象,所有对象可以归为以下四类:

Tag

Tag通俗点讲就是HTML中的一个个标签,它的类型是bs4.element.Tag,对于Tag有两个重要的属性,是name和attrs。

1
2
3
4
5
6
# 每个tag都有自己的名字,通过.name来获取
tag.name

# 一个tag可能有多个属性,其操作方式与字典相同,也可以通过点的方式获取
tag["class"]
tag.attrs

注:如果标签中某个属性有多值,则解析后返回的类型是list,但是转换的文档是xml时除外。

用于获取标签内部的文字,如下:

1
2
3
soup.title.string

print(type(bs.title.string))

BeautifulSoup

表示的是一个文档的全部内容,大部分时候可以将其看作为一个特殊的Tag,可通过以下方式获取其名称和属性:

1
2
3
4
soup.name
soup.attrs

print(type(soup))

Comment

是一个特殊类型的NavigableString对象,其输出的内容不包括注释符号

1
2
3
4
5
soup.a # <a class="mnav"><!--新闻--></a>

soup.a.string # 新闻

print(type(soup.a.string)) # <class 'bs4.element.Comment'>

遍历文档树

子节点

.contents和.children

  • contents:获取Tag的所有子节点,返回一个list

    1
    2
    3
    soup.contents

    soup.contents[0].name
  • children:获取Tag的所有子节点,返回一个生成器

    1
    2
    for child in soup.body.children:
    print(child)

    .descendants

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

    .string

  • 如果tag只有一个子节点,这个tag可以使用.string方法获取,如果有多个子节点,则返回为None

    .strings和.stripped_strings

  • 若果tag中包含多个字符串,可以使用.strings循环获取

  • 如果字符串包含很多空格或空行,可以使用.stripped_strings去除

    父节点

    .parent

  • 通过.parent获取某个元素的父节点

  • 顶层节点的父节点是BeautifulSoup对象

  • BeautifulSoup对象的.parent是None

    .parents

  • 通过.parents可以递归获取元素的所有父节点

    兄弟节点

    .next_sibling和.previous_sibling

  • 使用 .next_sibling 和 .previous_sibling 属性来查询兄弟节点

    .next_siblings和.previous_siblings

  • 通过 .next_siblings 和 .previous_siblings 属性可以对当前节点的兄弟节点迭代输出

    搜索文档树

    这里主要列举find_allfind方法以及CSS选择器,其他方法可参考官方文档。

    find_all

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

    name参数

  • 字符串过滤:会查找与字符串完全匹配的内容

    1
    2
    a_list = soup.find_all("a")
    print(a_list)
  • 正则表达式过滤:如果传入的是正则表达式,那么BeautifulSoup4会通过search()来匹配内容

    1
    2
    3
    a_list = soup.find_all(re.compile("a"))
    for item in a_list:
    print(item)
  • 列表:如果传入一个列表,BeautifulSoup4将会与列表中的任一元素匹配到的节点返回

    1
    2
    3
    a_list = soup.find_all(["meta", "link"])
    for item in a_list:
    print(item)
  • 方法:传入一个方法,根据方法来匹配

    1
    2
    3
    4
    5
    6
    def name_is_exists(tag):
    return tag.has_attr("name")

    a_list = soup.find_all(name_is_exists)
    for item in a_list:
    print(item)

    kwargs

    如果一个指定名字的参数不是搜索内置的参数名,搜索时会把该参数当作指定名字tag的属性来搜索

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 查询id=head的Tag
    t_list = soup.find_all(id="head")
    print(t_list)

    # 查询href属性包含ss1.bdstatic.com的Tag
    t_list = soup.find_all(href=re.compile("http://news.baidu.com"))
    print(t_list)

    # 查询所有包含class的Tag(注:class要加上_进行区分)
    t_list = soup.find_all(class_=True)
    for item in t_list:
    print(item)

    attrs

    不是所有的属性都可以使用上面的方式进行搜索,比如HTML的data-*属性,这时可以使用attrs参数,定义一个字典来搜索包含特殊属性的tag。

    1
    2
    3
    t_list = soup.find_all(attrs={"data-foo":"value"})
    for item in t_list:
    print(item)

    string

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

    1
    2
    3
    4
    5
    t_list = soup.find_all(string="Elsie")

    t_list = soup.find_all(string=["Tillie", "Elsie", "Lacie"])

    t_list = soup.find_all(string=re.compile("Dormouse"))

    limit

    传入一个limit参数来限制返回的数量。

    1
    2
    3
    t_list = soup.find_all("a", limit=2)
    for item in t_list:
    print(item)

    recursive

    默认会检索当前tag的所有子孙节点,如果只想搜索直接子节点,可以使用该参数。

    1
    soup.html.find_all(recursive=False)

    find

    find( name , attrs , recursive , string , **kwargs )
    返回符合条件的第一个Tag,即当我们要取一个值的时候就可以用该方法

    1
    soup.find("head").find("title")

    CSS选择器

  • 通过tag标签名查找

    1
    2
    3
    soup.select("title")

    soup.select("a")
  • 通过tag标签逐层查找:

    1
    2
    3
    soup.select("body a")

    soup.select("html head title")
  • 找到某个tag标签下的直接子标签:

    1
    2
    3
    soup.select("head > title")

    soup.select("p > #link1")
  • 找到兄弟节点标签:

    1
    2
    3
    soup.select("#link1 ~ .sister")

    soup.select("#link1 + .sister")
  • 通过class属性查找:

    1
    2
    3
    soup.select(".sister")

    soup.select("[class~=sister]")
  • 通过tag的id查找:

    1
    2
    3
    soup.select('#link1')

    soup.select("a#link2")
  • 同时用多种css选择器查询:

    1
    soup.select("#link1,#link2")
  • 通过是否存在某个属性来查找:

    1
    soup.select("a[href]")
  • 通过属性的值来查找:

    1
    2
    3
    4
    5
    6
    7
    soup.select('a[href="http://example.com/elsie"]')

    soup.select'a[href^="http://example.com"]')

    soup.select('a[href$="tillie"]')

    soup.select('a[href*=".com/el"]')
  • 通过语言设置来查找:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    multilingual_markup = """
    <p lang="en">Hello</p>
    <p lang="en-us">Howdy, y'all</p>
    <p lang="en-gb">Pip-pip, old fruit</p>
    <p lang="fr">Bonjour mes amis</p>
    """
    multilingual_soup = BeautifulSoup(multilingual_markup)
    multilingual_soup.select('p[lang|=en]')
    # [<p lang="en">Hello</p>,
    # <p lang="en-us">Howdy, y'all</p>,
    # <p lang="en-gb">Pip-pip, old fruit</p>]
  • 返回查找到的元素的第一个

    1
    soup.select_one(".sister")

    修改文档树

    BeautifulSoup的强项是文档树的搜索,但同时也可以方便的修改文档树,这部分可详细参考官方文档。