zoukankan      html  css  js  c++  java
  • 《Python3网络爬虫开发实战》读书笔记4(第4章:解析库的使用Beautiful Soup)

    4.2 使用Beautiful Soup

    1、简介

    Beautiful Soup提供一些简单的、Python式的函数来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。

    Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为UTF-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时你仅仅需要说明一下原始编码方式就可以了。

    Beautiful Soup已成为和lxml、html6lib一样出色的Python解释器,为用户灵活地提供不同的解析策略或强劲的速度。

    2、准备工作

    确保已经正确安装好了Beautiful Soup和lxml。

    3、解释器

    表4-3 Beautiful Soup支持的解析器

    解析器

    使用方法

    优势

    劣势

    Python标准库

    BeautifulSoup(markup, "html.parser")

    Python的内置标准库、执行速度适中、文档容错能力强

    Python 2.7.3及Python 3.2.2之前的版本文档容错能力差

    lxml HTML解析器

    BeautifulSoup(markup, "lxml")

    速度快、文档容错能力强

    需要安装C语言库

    lxml XML解析器

    BeautifulSoup(markup, "xml")

    速度快、唯一支持XML的解析器

    需要安装C语言库

    html5lib

    BeautifulSoup(markup, "html5lib")

    最好的容错性、以浏览器的方式解析文档、生成HTML5格式的文档

    速度慢、不依赖外部扩展

    lxml解析器有解析HTML和XML的功能,而且速度快,容错能力强,所以推荐使用它。在后面,Beautiful Soup的用法实例也统一用lxml解析器来演示。

    4、基本用法

    from bs4 import BeautifulSoup
    
    html = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
    <p class="title" name="dromouse"><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>
    """
    
    soup = BeautifulSoup(html,'lxml')
    print(soup.prettify())
    print(soup.title.string)

    运行结果:

    <html>
     <head>
      <title>
       The Dormouse's story
      </title>
     </head>
     <body>
      <p class="title" name="dromouse">
       <b>
        The Dormouse's story
       </b>
      </p>
      <p class="story">
       Once upon a time there were three little sisters; and their names were
       <a class="sister" href="http://example.com/elsie" id="link1">
        <!-- Elsie -->
       </a>
       ,
       <a class="sister" href="http://example.com/lacie" id="link2">
        Lacie
       </a>
       and
       <a class="sister" href="http://example.com/tillie" id="link3">
        Tillie
       </a>
       ;
    and they lived at the bottom of a well.
      </p>
      <p class="story">
       ...
      </p>
     </body>
    </html>
    The Dormouse's story

    首先,调用prettify()方法。这个方法可以把要解析的字符串以标准的缩进格式输出。这里需要注意的是,输出结果里面包含bodyhtml节点,也就是说对于不标准的HTML字符串BeautifulSoup,可以自动更正格式。这一步不是由prettify()方法做的,而是在初始化BeautifulSoup时就完成了。

    然后调用soup.title.string,这实际上是输出HTML中title节点的文本内容。所以,soup.title可以选出HTML中的title节点,再调用string属性就可以得到里面的文本了。

    5、节点选择器

    • 选择元素
    html = """
    <html><head><title>The Dormouse's story</title></head>
    <body>
    <p class="title" name="dromouse"><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>
    """
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.title)
    print(type(soup.title))
    print(soup.title.string)
    print(soup.head)
    print(soup.p)

    运行结果:

    <title>The Dormouse's story</title>
    <class 'bs4.element.Tag'>
    The Dormouse's story
    <head><title>The Dormouse's story</title></head>
    <p class="title" name="dromouse"><b>The Dormouse's story</b></p>

    首先打印输出title节点的选择结果,输出结果正是title节点加里面的文字内容。接下来,输出它的类型,是bs4.element.Tag类型,这是Beautiful Soup中一个重要的数据结构。经过选择器选择后,选择结果都是这种Tag类型。Tag具有一些属性,比如string属性,调用该属性,可以得到节点的文本内容,所以接下来的输出结果正是节点的文本内容。

    接下来,我们又尝试选择了head节点,结果也是节点加其内部的所有内容。最后,选择了p节点。不过这次情况比较特殊,我们发现结果是第一个p节点的内容,后面的几个p节点并没有选到。也就是说,当有多个节点时,这种选择方式只会选择到第一个匹配的节点,其他的后面节点都会忽略。

    • 提取信息

    (1)获取名称

    可以利用name属性获取节点的名称。

    print(soup.title.name)

    运行结果:

    title

    (2)获取属性

    每个节点可能有多个属性,比如idclass等,选择这个节点元素后,可以调用attrs获取所有属性:

    print(soup.p.attrs)
    print(soup.p.attrs['name'])

    运行结果:

    {'class': ['title'], 'name': 'dromouse'}
    dromouse

    可以看到,attrs的返回结果是字典形式,它把选择的节点的所有属性和属性值组合成一个字典。接下来,如果要获取name属性,就相当于从字典中获取某个键值,只需要用中括号加属性名就可以了。

    其实这样有点烦琐,还有一种更简单的获取方式:可以不用写attrs,直接在节点元素后面加中括号,传入属性名就可以获取属性值了。样例如下:

    print(soup.p['name'])
    print(soup.p['class'])

    运行结果:

    dromouse
    ['title']

    这里需要注意的是,有的返回结果是字符串,有的返回结果是字符串组成的列表。比如,name属性的值是唯一的,返回的结果就是单个字符串。而对于class,一个节点元素可能有多个class,所以返回的是列表。在实际处理过程中,我们要注意判断类型。

    (3)获取内容

    可以利用string属性获取节点元素包含的文本内容,比如要获取第一个p节点的文本:

    print(soup.p.string)

    运行结果:

    The Dormouse's story

    再次注意一下,这里选择到的p节点是第一个p节点,获取的文本也是第一个p节点里面的文本。

    • 嵌套选择

    在上面的例子中,我们知道每一个返回结果都是bs4.element.Tag类型,它同样可以继续调用节点进行下一步的选择。比如,我们获取了head节点元素,我们可以继续调用head来选取其内部的head节点元素。

    • 关联选择

    在做选择的时候,有时候不能做到一步就选到想要的节点元素,需要先选中某一个节点元素,然后以它为基准再选择它的子节点、父节点、兄弟节点等,这里就来介绍如何选择这些节点元素。

    (1)子节点和子孙节点

    选取节点元素之后,如果想要获取它的直接子节点,可以调用contents属性,示例如下:

    html = """
    <html>
        <head>
            <title>The Dormouse's story</title>
        </head>
        <body>
            <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">
                    <span>Elsie</span>
                </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>
    """
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.p.contents)

    运行结果:

    ['
                Once upon a time there were three little sisters; and their names were
                ', <a class="sister" href="http://example.com/elsie" id="link1">
    <span>Elsie</span>
    </a>, '
    ', <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, ' 
                and
                ', <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>, '
                and they lived at the bottom of a well.
            ']

    可以看到,返回结果是列表形式。p节点里既包含文本,又包含节点,最后会将它们以列表形式统一返回。

    需要注意的是,列表中的每个元素都是p节点的直接子节点。比如第一个a节点里面包含一层span节点,这相当于孙子节点了,但是返回结果并没有单独把span节点选出来。所以说,contents属性得到的结果是直接子节点的列表。

    同样,我们可以调用children属性得到相应的结果:

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.p.children)
    for i,child in enumerate(soup.p.children):
        print(i,child)

    运行结果:

    <list_iterator object at 0x00000254990B3288>
    0 
                Once upon a time there were three little sisters; and their names were
                
    1 <a class="sister" href="http://example.com/elsie" id="link1">
    <span>Elsie</span>
    </a>
    2 
    
    3 <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
    4  
                and
                
    5 <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
    6 
                and they lived at the bottom of a well.
            

    还是同样的HTML文本,这里调用了children属性来选择,返回结果是生成器类型。接下来,我们用for循环输出相应的内容。

    enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。

    如果要得到所有的子孙节点的话,可以调用descendants属性:

    print(soup.p.descendants)
    for i,child in enumerate(soup.p.descendants):
        print(i,child)

    运行结果:

    <generator object Tag.descendants at 0x000001CF548A4AC8>
    0 
                Once upon a time there were three little sisters; and their names were
                
    1 <a class="sister" href="http://example.com/elsie" id="link1">
    <span>Elsie</span>
    </a>
    2 
    
    3 <span>Elsie</span>
    4 Elsie
    5 
    
    6 
    
    7 <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
    8 Lacie
    9  
                and
                
    10 <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
    11 Tillie
    12 
                and they lived at the bottom of a well.

    此时返回结果还是生成器。遍历输出一下可以看到,这次的输出结果就包含了span节点。descendants会递归查询所有子节点,得到所有的子孙节点。

    (2)父节点和祖先节点

    如果要获取某个节点元素的父节点,可以调用parent属性:

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.a.parent)

    运行结果:

    <p class="story">
                Once upon a time there were three little sisters; and their names were
                <a class="sister" href="http://example.com/elsie" id="link1">
    <span>Elsie</span>
    </a>
    <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> 
                and
                <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
                and they lived at the bottom of a well.
            </p>

    这里我们选择的是第一个a节点的父节点元素。很明显,它的父节点是p节点,输出结果便是p节点及其内部的内容。

    需要注意的是,这里输出的仅仅是a节点的直接父节点,而没有再向外寻找父节点的祖先节点。如果想获取所有的祖先节点,可以调用parents属性:

    html = """
    <html>
        <body>
            <p class="story">
                <a href="http://example.com/elsie" class="sister" id="link1">
                    <span>Elsie</span>
                </a>
            </p>
    """
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(type(soup.a.parents))
    print(list(enumerate(soup.a.parents)))

    运行结果:

    <class 'generator'>
    [(0, <p class="story">
    <a class="sister" href="http://example.com/elsie" id="link1">
    <span>Elsie</span>
    </a>
    </p>), (1, <body>
    <p class="story">
    <a class="sister" href="http://example.com/elsie" id="link1">
    <span>Elsie</span>
    </a>
    </p>
    </body>), (2, <html>
    <body>
    <p class="story">
    <a class="sister" href="http://example.com/elsie" id="link1">
    <span>Elsie</span>
    </a>
    </p>
    </body></html>), (3, <html>
    <body>
    <p class="story">
    <a class="sister" href="http://example.com/elsie" id="link1">
    <span>Elsie</span>
    </a>
    </p>
    </body></html>)]

    可以发现,返回结果是生成器类型。这里用列表输出了它的索引和内容,而列表中的元素就是a节点的祖先节点。

    (3)兄弟节点

    html = """
    <html>
        <body>
            <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">
                    <span>Elsie</span>
                </a>
                Hello
                <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>
    """
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print('Next Sibling',soup.a.next_sibling)
    print('Prev Sibling',soup.a.previous_sibling)
    print('Next Slibings',list(enumerate(soup.a.next_siblings)))
    print('Prev Slibings',list(enumerate(soup.a.previous_siblings)))

    运行结果:

    Next Sibling 
                Hello
                
    Prev Sibling 
                Once upon a time there were three little sisters; and their names were
                
    Next Slibings [(0, '
                Hello
                '), (1, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>), (2, ' 
                and
                '), (3, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>), (4, '
                and they lived at the bottom of a well.
            ')]
    Prev Slibings [(0, '
                Once upon a time there were three little sisters; and their names were
                ')]

    可以看到,这里调用了4个属性,其中next_siblingprevious_sibling分别获取节点的下一个和上一个兄弟元素,next_siblingsprevious_siblings则分别返回所有前面和后面的兄弟节点。

    (4)提取信息

    前面讲解了关联元素节点的选择方法,如果想要获取它们的一些信息,比如文本、属性等,也用同样的方法,示例如下:

    html = """
    <html>
        <body>
            <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">Bob</a><a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 
            </p>
    """
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print('Next Sibling:')
    print(type(soup.a.next_sibling))
    print(soup.a.next_sibling)
    print(soup.a.next_sibling.string)
    print('Parent:')
    print(type(soup.a.parents))
    print(list(soup.a.parents)[0])
    print(list(soup.a.parents)[0].attrs['class'])

    运行结果:

    Next Sibling:
    <class 'bs4.element.Tag'>
    <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
    Lacie
    Parent:
    <class 'generator'>
    <p class="story">
                Once upon a time there were three little sisters; and their names were
                <a class="sister" href="http://example.com/elsie" id="link1">Bob</a><a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
    </p>
    ['story']

    如果返回结果是单个节点,那么可以直接调用stringattrs等属性获得其文本和属性;如果返回结果是多个节点的生成器,则可以转为列表后取出某个元素,然后再调用stringattrs等属性获取其对应节点的文本和属性。

    6、方法选择器

    • find_all()

    find_all,顾名思义,就是查询所有符合条件的元素。给它传入一些属性或文本,就可以得到符合条件的元素,它的功能十分强大。

    它的API如下:

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

    (1)name

    可以根据节点名来查询:

    html='''
    <div class="panel">
        <div class="panel-heading">
            <h4>Hello</h4>
        </div>
        <div class="panel-body">
            <ul class="list" id="list-1">
                <li class="element">Foo</li>
                <li class="element">Bar</li>
                <li class="element">Jay</li>
            </ul>
            <ul class="list list-small" id="list-2">
                <li class="element">Foo</li>
                <li class="element">Bar</li>
            </ul>
        </div>
    </div>
    '''
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.find_all(name='ul'))
    print(type(soup.find_all(name='ul')[0]))

    运行结果:

    [<ul class="list" id="list-1">
    <li class="element">Foo</li>
    <li class="element">Bar</li>
    <li class="element">Jay</li>
    </ul>, <ul class="list list-small" id="list-2">
    <li class="element">Foo</li>
    <li class="element">Bar</li>
    </ul>]
    <class 'bs4.element.Tag'>

    这里我们调用了find_all()方法,传入name参数,其参数值为ul。也就是说,我们想要查询所有ul节点,返回结果是列表类型,长度为2,每个元素依然都是bs4.element.Tag类型。

    因为都是Tag类型,所以依然可以进行嵌套查询。还是同样的文本,这里查询出所有ul节点后,再继续查询其内部的li节点:

    for ul in soup.find_all(name='ul'):
        print(ul.find_all(name='li'))

    运行结果:

    [<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>]
    [<li class="element">Foo</li>, <li class="element">Bar</li>]

    返回结果是列表类型,列表中的每个元素依然还是Tag类型。

    接下来,就可以遍历每个li,获取它的文本了:

    for ul in soup.find_all(name='ul'):
        print(ul.find_all(name='li'))
        for li in ul.find_all(name='li'):
            print(li.string)

    运行结果:

    [<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>]
    Foo
    Bar
    Jay
    [<li class="element">Foo</li>, <li class="element">Bar</li>]
    Foo
    Bar

    (2)attrs

    除了根据节点名查询,我们也可以传入一些属性来查询,示例如下:

    html='''
    <div class="panel">
        <div class="panel-heading">
            <h4>Hello</h4>
        </div>
        <div class="panel-body">
            <ul class="list" id="list-1" name="elements">
                <li class="element">Foo</li>
                <li class="element">Bar</li>
                <li class="element">Jay</li>
            </ul>
            <ul class="list list-small" id="list-2">
                <li class="element">Foo</li>
                <li class="element">Bar</li>
            </ul>
        </div>
    </div>
    '''
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.find_all(attrs={'id':'list-1'}))
    print(soup.find_all(attrs={'name':'elements'}))

    运行结果:

    [<ul class="list" id="list-1" name="elements">
    <li class="element">Foo</li>
    <li class="element">Bar</li>
    <li class="element">Jay</li>
    </ul>]
    [<ul class="list" id="list-1" name="elements">
    <li class="element">Foo</li>
    <li class="element">Bar</li>
    <li class="element">Jay</li>
    </ul>]

    对于一些常用的属性,比如idclass等,我们可以不用attrs来传递。比如,要查询idlist-1的节点,可以直接传入id这个参数。还是上面的文本,我们换一种方式来查询:

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.find_all(id='list-1'))
    print(soup.find_all(class_='element'))

    运行结果:

    [<ul class="list" id="list-1" name="elements">
    <li class="element">Foo</li>
    <li class="element">Bar</li>
    <li class="element">Jay</li>
    </ul>]
    [<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>, <li class="element">Foo</li>, <li class="element">Bar</li>]

    这里直接传入id='list-1',就可以查询idlist-1的节点元素了。而对于class来说,由于class在Python里是一个关键字,所以后面需要加一个下划线,即class_='element',返回的结果依然还是Tag组成的列表。

    (3)text

    text参数可用来匹配节点的文本,传入的形式可以是字符串,可以是正则表达式对象,示例如下:

    html='''
    <div class="panel">
        <div class="panel-body">
            <a>Hello, this is a link</a>
            <a>Hello, this is a link, too</a>
        </div>
    </div>
    '''
    import re
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.find_all(text=re.compile('link')))

    运行结果:

    ['Hello, this is a link', 'Hello, this is a link, too']
    • find()

    除了find_all()方法,还有find()方法,只不过后者返回的是单个元素,也就是第一个匹配的元素,而前者返回的是所有匹配的元素组成的列表。示例如下:

    html='''
    <div class="panel">
        <div class="panel-heading">
            <h4>Hello</h4>
        </div>
        <div class="panel-body">
            <ul class="list" id="list-1">
                <li class="element">Foo</li>
                <li class="element">Bar</li>
                <li class="element">Jay</li>
            </ul>
            <ul class="list list-small" id="list-2">
                <li class="element">Foo</li>
                <li class="element">Bar</li>
            </ul>
        </div>
    </div>
    '''
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.find(name='ul'))
    print(type(soup.find(name='ul')))
    print(soup.find(class_='list'))

    运行结果:

    <ul class="list" id="list-1">
    <li class="element">Foo</li>
    <li class="element">Bar</li>
    <li class="element">Jay</li>
    </ul>
    <class 'bs4.element.Tag'>
    <ul class="list" id="list-1">
    <li class="element">Foo</li>
    <li class="element">Bar</li>
    <li class="element">Jay</li>
    </ul>

    这里的返回结果不再是列表形式,而是第一个匹配的节点元素,类型依然是Tag类型。

    另外,还有许多查询方法,其用法与前面介绍的find_all()find()方法完全相同,只不过查询范围不同,这里简单说明一下。

    • find_parents()find_parent():前者返回所有祖先节点,后者返回直接父节点。
    • find_next_siblings()find_next_sibling():前者返回后面所有的兄弟节点,后者返回后面第一个兄弟节点。
    • find_previous_siblings()find_previous_sibling():前者返回前面所有的兄弟节点,后者返回前面第一个兄弟节点。
    • find_all_next()find_next():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。
    • find_all_previous()find_previous():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。

    7、CSS选择器

    Beautiful Soup还提供了另外一种选择器,那就是CSS选择器。如果对Web开发熟悉的话,那么对CSS选择器肯定也不陌生。如果不熟悉的话,可以参考http://www.w3school.com.cn/cssref/css_selectors.asp了解。

    使用CSS选择器时,只需要调用select()方法,传入相应的CSS选择器即可,示例如下:

    html='''
    <div class="panel">
        <div class="panel-heading">
            <h4>Hello</h4>
        </div>
        <div class="panel-body">
            <ul class="list" id="list-1">
                <li class="element">Foo</li>
                <li class="element">Bar</li>
                <li class="element">Jay</li>
            </ul>
            <ul class="list list-small" id="list-2">
                <li class="element">Foo</li>
                <li class="element">Bar</li>
            </ul>
        </div>
    </div>
    '''
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    print(soup.select('.panel .panel-heading'))
    print(soup.select('ul li'))
    print(soup.select('#list-2 .element'))
    print(type(soup.select('ul')[0]))

    运行结果:

    [<div class="panel-heading">
    <h4>Hello</h4>
    </div>]
    [<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>, <li class="element">Foo</li>, <li class="element">Bar</li>]
    [<li class="element">Foo</li>, <li class="element">Bar</li>]
    <class 'bs4.element.Tag'>

    这里我们用了3次CSS选择器,返回的结果均是符合CSS选择器的节点组成的列表。例如,select('ul li')则是选择所有ul节点下面的所有li节点,结果便是所有的li节点组成的列表。

    最后一句打印输出了列表中元素的类型。可以看到,类型依然是Tag类型。

    • 嵌套选择

    select()方法同样支持嵌套选择。例如,先选择所有ul节点,再遍历每个ul节点,选择其li节点,样例如下:

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    for ul in soup.select('ul'):
        print(ul.select('li'))

    运行结果:

    [<li class="element">Foo</li>, <li class="element">Bar</li>, <li class="element">Jay</li>]
    [<li class="element">Foo</li>, <li class="element">Bar</li>]
    • 获取属性

    我们知道节点类型是Tag类型,所以获取属性还可以用原来的方法。仍然是上面的HTML文本,这里尝试获取每个ul节点的id属性:

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    for ul in soup.select('ul'):
        # print(ul.select('li'))
        print(ul['id'])
        print(ul.attrs['id'])

    运行结果:

    list-1
    list-1
    list-2
    list-2

    可以看到,直接传入中括号和属性名,以及通过attrs属性获取属性值,都可以成功。

    • 获取文本

    要获取文本,当然也可以用前面所讲的string属性。此外,还有一个方法,那就是get_text(),示例如下:

    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    for li in soup.select('li'):
        print('Get Text:',li.get_text())
        print('String:',li.string)

    运行结果:

    Get Text: Foo
    String: Foo
    Get Text: Bar
    String: Bar
    Get Text: Jay
    String: Jay
    Get Text: Foo
    String: Foo
    Get Text: Bar
    String: Bar

    可以看到,二者的效果完全一致。

    到此,Beautiful Soup的用法基本就介绍完了,最后做一下简单的总结。

    • 推荐使用lxml解析库,必要时使用html.parser。
    • 节点选择筛选功能弱但是速度快。
    • 建议使用find()或者find_all()查询匹配单个结果或者多个结果。
    • 如果对CSS选择器熟悉的话,可以使用select()方法选择。
  • 相关阅读:
    进程与线程
    HDOJ搜索专题之Catch That Cow
    HDOJ搜索专题之Red and Black
    COJ1026(过河卒)
    HDOJ搜索专题之Prime Ring Problem
    COJ1113(Emperor And His Knight)
    HDOJ搜索专题之胜利大逃亡
    HDOJ搜索专题之翻纸牌游戏
    HDOJ搜索专题之Counting Sheep
    HDOJ搜索专题之Robot Motion
  • 原文地址:https://www.cnblogs.com/cathycheng/p/11428418.html
Copyright © 2011-2022 走看看