美文网首页
Python爬虫利器:Beautiful Soup的使用(二)

Python爬虫利器:Beautiful Soup的使用(二)

作者: _Amauri | 来源:发表于2018-09-29 10:24 被阅读26次

    上一篇文章介绍了 BeautifulSoup 的安装以及基本对象类型。

    本次介绍使用 bs4 对 HTML 文档树的遍历。

    先把本文用到的例子贴上:

    str = """
    <!DOCTYPE html>
    <html>
    <head><title>bs4 test</title></head>
    <body>
        <h1>bs4 test</h1>
        <div>
            <ul>
                <li><a>PHP</a></li>
                <li><a>Python</a></li>
                <li><a>Golang</a></li>
            </ul>
        </div>
        <p><span>a</span><i>b</i><em></em></p>
    </body>
    </html>
    """
    

    文档树的遍历:

    文档树的遍历包括以下四部分:

    1. 子节点
    2. 父节点
    3. 兄弟节点
    4. 回退和前进

    一、子节点

    一个标签可能包含多个字符串或者其他标签,这些标签都属于子节点。要获取子节点,首先需要得到一个 Tag 对象:

    获取一个 Tag 对象最简单的方式是用 bs4 对象点上要获取的标签的名字,同时支持链式调用。

    bs4 = BeautifulSoup(str, "lxml")
    div_tag = bs4.div
    ul_tag = bs4.div.ul
    

    .contents :

    tag 对象的 .contents 属性可以将 tag 的子节点以列表的方式输出,不包含孙节点:

    ul_tag.contents
    # ['\n', <li><a>PHP</a></li>, '\n', <li><a>Python</a></li>, '\n', <li><a>Golang</a></li>, '\n']
    

    字符串没有 .contents 属性,因为字符串没有子节点。

    .children:

    .children 生成器,可以对 tag 的直接子节点进行循环:

    for child in ul_tag.children:
        print(child)
    # <li><a>PHP</a></li> <li><a>Python</a></li> <li><a>Golang</a></li>
    

    .descendants:

    .descendants 属性可以对所有 tag 的子孙节点进行递归循环:

    for child in ul_tag.descendants:
        print(child)
    

    .string:

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

    title_tag = bs4.title 
    print(title_tag.string)  # bs4 test
    

    如果一个 tag 仅有一个子节点,那么这个 tag 也可以使用 .string 方法,输出结果与当前唯一子节点(也就是 title 节点)的 .string 结果相同。

    head_tag = bs4.head
    print(head_tag.string)  # bs4 test
    

    如果 tag 包含了多个子节点,tag 就无法确定 .string 方法应该调用哪个子节点的内容,所以输出结果是 None:

    print(div_tag.string) # None
    

    .strings 和 stripped_strings:

    对于上边 tag 包含了多个子节点的问题,可以使用 .strings 来循环获取:

    for str in div_tag.strings:
        print(str)
    # PHP   Python   Golang
    

    .stripped_strings 可以去除多余空白内容。

    二、父节点

    .parent:

    .parent 属性来获取某个标签或字符串的父节点,比如:

    print(title_tag.parent) # <head><title>bs4 test</title></head>
    h1_tag = bs4.h1
    print(h1_tag.string.parent) # <h1>bs4 test</h1>
    

    .parents:

    .parents 属性可以递归得到元素的所有父辈节点。

    for p in h1_tag.parents:
        print(p.name)
    # body   html   [document]
    

    三、兄弟节点

    首先先看一下例子中的这一行:

    #<p><span>a</span><i>b</i><em>c</em></p>
    
    p_tag = bs4.p
    print(p_tag.prettify())
    #<p>
    # <span>
    #  a
    # </span>
    # <i>
    #  b
    # </i>
    # <em>
    #  c
    # </em>
    #</p>
    
    
    <span><i><em>都是<p>的子节点,所以这三个可以被称为兄弟节点。
    

    .next_sibling 和 .previous_sibling:

    通过以上两个属性可以查询兄弟节点。

    print(p_tag.i.next_sibling) # <em>c</em>
    print(p_tag.i.previous_sibling) # <span>a</span>
    

    要注意的点:

    在这里<span>没有 previous_sibling 属性,因为它是同级节点中的第一个。相反,<em>没有 next_sibling 属性。

    字符串“a,b,c”不是兄弟节点,因为它们的父节点不同。

    由于我们上边的例子是写的一行,在实际中 .next_sibling 和 .previous_sibling 属性通常是字符串或空白。

    如果示例是如下方式则 .next_sibling 和 .previous_sibling 获取到的是空白。

    <p>
        <span>a</span>
        <i>b</i>
        <em>c</em>
    </p>
    

    .next_siblings 和 .previous_siblings:

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

    for sibling in p_tag.span.next_siblings:
        print(repr(sibling))
    #'\n'
    #<i>b</i>
    #'\n'
    #<em>c</em>
    #'\n'
    
    for prev in p_tag.em.previous_siblings:
        print(repr(prev))
    #'\n'
    #<i>b</i>
    #'\n'
    #<span>a</span>
    #'\n'
    

    四、回退和前进

    HTML解析器把文档字符串转换成一连串的事件:
    打开<html>标签 -> 打开<head>标签 -> 打开<title>标签 -> 添加一段字符串 -> 关闭<title>标签 ...
    Beautiful Soup提供了重现解析器初始化过程的方法。

    .next_element 和 .previous_element:

    .next_element 属性指向解析过程中下一个被解析的对象(字符串或tag)。

    print(h1_tag.next_element) # bs4 test
    因为这个结果是在<h1>标签被解析之后的解析内容,所以输出字符串。

    print(h1_tag.next_element.previous_element) # <h1>bs4 test</h1>
    

    h1_tag.next_element 输出的是“bs4 test”字符串,因为 .previous_element 指向当前被解析的对象的前一个解析对象,所以这里输出<h1>bs4 test</h1>。

    .next_elements 和 .previous_elements:

    通过 .next_elements 和 .previous_elements 的迭代器可以向前或向后访问文档的解析内容。

    str2 = "<p><span>a</span><i>b</i><em>c</em></p>"
    bs42 = BeautifulSoup(str2, "lxml")
    for element in bs42.p.next_elements:
        print(element)
    # <span>a</span>
    # a
    # <i>b</i>
    # b
    # <em>c</em>
    # c
    

    相关文章

      网友评论

          本文标题:Python爬虫利器:Beautiful Soup的使用(二)

          本文链接:https://www.haomeiwen.com/subject/bqfmoftx.html