爬虫-BS4

简介
BeautifulSoup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。BeautifulSoup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时你仅仅需要说明一下原始编码方式就可以了。BeautifulSoup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度

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

标签选择器

# 选择元素
soup.p # 标签加其内部的所有内容,且只获取第一个匹配的标签

type(soup.p) # bs4.element.Tag
bs4.element.Tag
soup.p
<p class="title" name="dromouse"><b>The Dormouse‘s story</b></p>
data = []
for i in soup.find(‘body‘).children:
    data.append(i)
data
[‘\n‘,
 <p class="title" name="dromouse"><b>The Dormouse‘s story</b></p>,
 ‘\n‘,
 <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>,
 ‘\n‘,
 <p class="story">...</p>,
 ‘\n‘]
print(soup.body.prettify())
<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>
# 提取信息
soup.p.string
# 获取名称
soup.p.name 
soup.p.parent.name
# 获取属性
print(soup.p.attrs) # 返回字典
print(soup.p.attrs[‘name‘])
print(soup.p[‘name‘])
# 获取内容
print(soup.p.string)
soup.title.string

type(soup.title.string)

type(soup.title.contents)

soup.head

soup.p

soup.p.string
"The Dormouse‘s story"

嵌套选择

# 每一返回结果是bs4.element.Tag
print(type(soup.p))
print(type(soup.p.b))

print(soup.p.string)
print(soup.p.b.string)
<class ‘bs4.element.Tag‘>
<class ‘bs4.element.Tag‘>
The Dormouse‘s story
The Dormouse‘s story

关联选择

# 选取到了一个节点元素之后,如果想要获取它的直接子节点可以调用contents属性
print(soup.body.contents)  # contents返回的是列表类型
[‘\n‘, <p class="title" name="dromouse"><b>The Dormouse‘s story</b></p>, ‘\n‘, <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>, ‘\n‘, <p class="story">...</p>, ‘\n‘]
print(soup.title.contents)
["The Dormouse‘s story"]
list_ = soup.body.contents
len(list_)
7

子节点和子孙节点

# print(soup.body.children)
for i,child in enumerate(soup.body.children):
    print(i,child)
0 

1 <p class="title" name="dromouse"><b>The Dormouse‘s story</b></p>
2 

3 <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>
4 

5 <p class="story">...</p>
6

?

print(soup.body.descendants) # 生成器,递归查询所有子孙节点
for i,childs in enumerate(soup.body.descendants):
    print(i,childs)
<generator object Tag.descendants at 0x000002435F075ED0>
0 

1 <p class="title" name="dromouse"><b>The Dormouse‘s story</b></p>
2 <b>The Dormouse‘s story</b>
3 The Dormouse‘s story
4 

5 <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>
6 Once upon a time there were three little sisters; and their names were

7 <a class="sister" href="http://example.com/elsie" id="link1"><!-- Elsie --></a>
8  Elsie 
9 ,

10 <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>
11 Lacie
12  and

13 <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
14 Tillie
15 ;
and they lived at the bottom of a well.
16 

17 <p class="story">...</p>
18 ...
19

?

父节点和祖先节点

print(soup.a.parent) # 返回其直接父节点即其下内容
type(soup.a.parent) # bs4.element.Tag
<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>





bs4.element.Tag
print(soup.a.parents) # 
for i,des in enumerate(soup.a.parents):
    print(i,des)
<generator object Tag.descendants at 0x000002435F075ED0>
0 <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>
1 <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>
2 <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>
3 <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>

兄弟节点

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 Siblings‘, list(enumerate(soup.a.next_siblings)))
print(‘Prev Siblings‘, 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 Siblings [(0, ‘\n            Hello\n            ‘), (1, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>), (2, ‘ \n            and\n            ‘), (3, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>), (4, ‘\n            and they lived at the bottom of a well.\n        ‘)]
Prev Siblings [(0, ‘\n            Once upon a time there were three little sisters; and their names were\n            ‘)]

方法选择器

# find_all()、find()等方法,我们可以调用方法然后传入相应等参数就可以灵活地进行查询了
find_all(name , attrs , recursive , text , **kwargs)
find_all,顾名思义,就是查询所有符合条件的元素,可以给它传入一些属性或文本来得到符合条件的元素
soup.find_all(True) # 返回所有标签
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>
‘‘‘
soup = BeautifulSoup(html, ‘lxml‘)

name属性

print(soup.find_all(name=‘ul‘)) # 查询所有ul标签,返回list
print(‘#‘*50)
print(type(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.ResultSet‘>
<class ‘bs4.element.Tag‘>
for ul in soup.find_all(name=‘ul‘):
    print(ul.find_all(name=‘li‘)) # 结果返回列表类型,列表种的每个元素依然是Tagl类型
    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

attrs属性

print(soup.find_all(attrs={‘class‘:‘element‘})) # 传入的是attrs参数,参数的类型是字典类型

print(soup.find_all(‘ul‘,attrs={‘id‘:‘list-1‘}))
[<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,class可以不用attrs来传递,而直接用id和class_

print(soup.find_all(class_=‘element‘))

print(soup.find_all(id=‘list-1‘))
[<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>]

text属性

# 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(name,attrs,recursive,text,**kwargs)
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(neme=‘div‘)) # Return only the first child of this Tag matching the given
print(‘#‘*10)
print(soup.find(name=‘ul‘))
print(‘#‘*10)
print(soup.find(attrs={‘panel-heading‘}))
print(‘#‘*10)
print(soup.find(class_=‘element‘))
None
##########
<ul class="list" id="list-1">
<li class="element">Foo</li>
<li class="element">Bar</li>
<li class="element">Jay</li>
</ul>
##########
<div class="panel-heading">
<h4>Hello</h4>
</div>
##########
<li class="element">Foo</li>
soup.find_parent() # 返回直接父节点
soup.find_parents() # 返回所有祖先节点
soup.find_next_sibling()# 返回后面第一个兄弟节点
soup.find_next_siblings() # 返回后面所有兄弟节点
soup.find_next() # 返回第一个符合条件的节点
soup.find_all_next() # 返回节点后所有符合条件的节点

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‘)) # 符合CSS选择器的节点组成的列表

print(soup.select(‘ul li‘)) # 符合CSS选择器的节点组成的列表

print(soup.select(‘#list-2 .element‘)) # 符合CSS选择器的节点组成的列表
print(type(soup.select(‘ul‘)[0])) # print(type(soup.select(‘ul‘)[0]))
[<div class="panel-heading">
<h4>Hello</h4>
</div>]

嵌套选择

soup.select(‘ul‘)
[<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>]
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>]
# 获取属性
for ul in soup.select(‘ul‘):
    print(ul[‘id‘])
    print(ul.attrs[‘id‘])
    print(ul.get_text()) # 获取文本
list-1
list-1

Foo
Bar
Jay

list-2
list-2

Foo
Bar

?

print(soup.find_all(‘div‘)[1])
[out]
<div class="panel-heading">
<h4>Hello</h4>
</div>
soup.find_all(‘div‘)[1].get_text()
[out]
‘\nHello\n‘
print(soup.find_all(‘div‘)[1].h4.string)
[out]
Hello
print(soup.find_all(‘div‘)[1].string)
[out]
None