Python3爬虫笔记 -- 解析库Beautiful Soup

 ̄綄美尐妖づ 提交于 2019-12-02 15:13:57

1、简介

  • Beautiful Soup:Python的一个HTML或XML的解析库,借助网页的结构和属性等特性来解析网页。有了它,我们不用再去写一些复杂的正则表达式,只需要简单的几条语句,就可以完成网页中某个元素的提取。
  • Beautiful Soup在解析时实际上依赖解析器,这里推荐使用lxml解析器,在初始化Beautiful Soup时,把第二个参数改为lxml即可:
from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>Hello</p>', 'lxml')
print(soup.p.string)

2、基本用法

  • 初始化BeautifulSoup时,完成对不完整的html代码的补全
  • prettify():把要解析的字符串以标准的缩进格式输出
  • soup.title.string:是输出HTML中title节点的文本内容;soup.title会输出整个title节点
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.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

3、节点选择器

  • 直接调用节点的名称就可以选择节点元素,再调用string属性就可以得到节点内的文本了,这种选择方式速度非常快。如果单个节点结构层次非常清晰,可以选用这种方式来解析

选择元素

  • 当有多个节点时,这种选择方式只会选择到第一个匹配的节点,其他的后面节点都会忽略,如下例中的p节点。
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>

提取信息

(1) 获取名称

  • 可以利用name属性获取节点的名称
print(soup.title.name)

#输出结果
title

(2) 获取属性

  • 调用attrs获取所有属性
print(soup.p.attrs)
print(soup.p.attrs['name'])

#运行结果
{'class': ['title'], 'name': 'dromouse'}
dromouse
  • 更为简便的写法是省略attrs
  • 这里需要注意的是,有的返回结果是字符串,有的返回结果是字符串组成的列表。比如,name属性的值是唯一的,返回的结果就是单个字符串。而对于class,一个节点元素可能有多个class,所以返回的是列表。
print(soup.p['name'])
print(soup.p['class'])

# 输出结果
dromouse
['title']

(3) 获取内容

利用string属性获取节点元素包含的文本内容

嵌套选择

  • 每一个返回结果都是bs4.element.Tag类型,它同样可以继续调用节点进行下一步的选择
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.head.title)
print(type(soup.head.title))
print(soup.head.title.string)

#输出结果
<title>The Dormouse's story</title>
<class 'bs4.element.Tag'>
The Dormouse's story

关联选择

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

(1) 子节点和子孙节点

  • contents:得到的结果是所有直接子节点的列表,孙节点会包含在字节点中而不会单独列出
soup = BeautifulSoup(html, 'lxml')
print (soup.p.contents)
  • children:返回结果是生成器,返回所有的子孙节点,包含在字节点中的孙节点也会单独列出
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.children)
#enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。
for i, child in enumerate(soup.p.children):
    print(i, child)

(2) 父节点和祖先节点

parent属性:获取某个节点元素的直接父节点
parents属性:获取某个节点元素的所有祖先

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

(3) 兄弟节点

from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print('Next Sibling', soup.a.next_sibling)
print('Prev Sibling', soup.a.previous_sibling)
print('Next Siblings', list(enumerate(soup.a.next_siblings)))
print('Prev Siblings', list(enumerate(soup.a.previous_siblings)))

(4) 提取信息

  • 如果返回结果是单个节点,那么可以直接调用string、attrs等属性获得其文本和属性;如果返回结果是多个节点的生成器,则可以转为列表后取出某个元素,然后再调用string、attrs等属性获取其对应节点的文本和属性。
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'])

4、方法选择器

find_all()

  • 给它传入一些属性或文本,查询所有符合条件的元素。

(1) name

  • 返回所有ul节点组成的列表,每个元素依然都是bs4.element.Tag类型
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(name='ul'))
print(type(soup.find_all(name='ul')[0]))
  • 因为每个节点都是Tag类型,所以依然可以嵌套查询
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)

(2) attrs

  • 根据一些属性来查询
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(attrs={'id': 'list-1'}))
print(soup.find_all(attrs={'name': 'elements'}))
  • 对于一些常用的属性,比如id和class等,我们可以不用attrs来传递。比如,要查询id为list-1的节点,可以直接传入id这个参数。
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all(id='list-1'))
#由于class在Python里是一个关键字,所以后面需要加一个下划线
print(soup.find_all(class_='element'))

(3) text

  • 可用来匹配节点的文本,传入的形式可以是字符串,可以是正则表达式对象
import re
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>
'''
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()

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

其他查询方法

  • 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():前者返回节点后所有符合条件的节点,后者返回第一个符合条件的节点。

5、CSS选择器

  • 使用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'))
#选择所有ul节点下面的所有li节点
print(soup.select('ul li'))
print(soup.select('#list-2 .element'))
print(type(soup.select('ul')[0]))

嵌套选择

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

获取属性

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

获取文本

  • 要获取文本,当然也可以用前面所讲的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)

6、总结

  • 推荐使用lxml解析库,必要时使用html.parser。
  • 节点选择筛选功能弱但是速度快。
  • 建议使用find()或者find_all()查询匹配单个结果或者多个结果。
  • 如果对CSS选择器熟悉的话,可以使用select()方法选择。
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!