我的爬虫学习之旅 (六) BeautifulSoup

本文详细介绍BeautifulSoup库的使用方法,包括创建解析对象、标签提取、属性获取、结点关系操作及CSS选择器应用,同时提供了实战案例,适合初学者快速掌握网页解析技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:

BeautifulSoup:美味的汤。是一个强大又方便的python网页解析库,可以从网页里HTML或XML文件中提取数据。Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。


一、简单入门:

 

首先导入BeautifulSoup这个库,若报错则需要在命令行中安装这个库。

from bs4 import BeautifulSoup

首先需要创建一个相应的BS解析对象,使用BeautifulSoup()这个方法。

html = '''
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><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()) #prettify:美化,装饰

<html>
 <head>
  <title>
   The Dormouse's story
  </title>
 </head>
 <body>
  <p class="title">
   <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>

通过BS对象直接对标签名的引用,可以获得标签对应的内容。

print(soup.title)
print(soup.p)
print(soup.a)

尽管有很多的<a>标签或<p>标签,但这样在不加条件的情况下总是输出从头开始的第一个对应标签。

<title>The Dormouse's story</title>
<p class="title"><b>The Dormouse's story</b></p>
<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>

获取标签的名称以及标签内的内容

print(soup.title.name)
print(soup.title.string)
# soup对象支持嵌套

title
The Dormouse's story

获取标签的属性值

print(soup.p["class"]) # 括号内为双引号
# print(soup.p.get('class'))

同样,在有多个同类标签的情况下输出第一个结果: 

['title']

 用get()方法同样可以获得相应的属性值:

for i in soup.find_all('a'):
    print(i.get('href')) # 括号内为单引号

http://example.com/elsie
http://example.com/lacie
http://example.com/tillie

 这两种获得标签属性值的方法区别是[]内是双引号,()内是单引号

find_all() :

获取所有指定类型的标签,返回一个列表:

print(soup.find_all('a')) # 注意括号内要加引号

[<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

find(): 

获取指定id标识的标签:

print(soup.find(id='link3'))

<a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>


二、结点关系:

标签同样拥有祖先与孩子的关系逻辑。

获取子结点

print(soup.p.contents)

[<b>The Dormouse's story</b>] # 以列表形式返回

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

 0 <b>The Dormouse's story</b> # 以迭代器的形式返回

 获取父节点:

print(soup.p.parent.name) # 获取父节点的名称
print(soup.p.parent) # 获取包含父节点的所有标签内容

 body
<body>
<p class="title" id="robot"><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>

获取祖先结点内容:

for i in soup.p.parents: # 返回一个迭代器
    print(i)

兄弟结点:

# soup.a.next_sibling 获取下一个兄弟标签内容
# souo.a.previous_sinbling 获取上一个兄弟标签内容

# soup.a.next_siblings 获取后面的兄弟节点
# soup.a.previous_siblings 获取前面的兄弟节点


三、查找标签:

BS提供find()与findall()方法查找标签,在前面已近介绍过,findall()方法可以对查找到的内容进行嵌套查找:

content = '''
<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-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
        </ul>
    </div>
</div>
'''

soup = BeautifulSoup(content, 'lxml')

for i in soup.find_all('ul'):
    print(i.find_all('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>]

查找特定属性值的标签:

对于如向class这种属性的属性值,应使用attrs{"标签名":"属性值"}的方法

print(soup.find_all(attrs={"class": "element"}))

对于id这种特殊属性的属性值,可以直接在findall()中书写: 

print(soup.find_all(id = 'list-2'))
# 查找出与text属性相关的内容,并以列表的方式显示,通常用于做对比的情况
print(soup.find_all(text="Foo"))

 

# 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()返回第一个符合条件的节点

四、CSS选择器:

XPath也支持CSS选择器的形式来访问标签,对于前端熟悉的人来说是十分方便的。

语法规则:

  • • : 表示class
  • # :表示id
  • <标签1> <标签2>:表示标签1内部的所有的标签2
  • <标签1>,<标签2>:表示标签1与标签2

 

对BS对象使用select()方法可以通过CSS语法来获取标签,以列表的形式返回:

content = '''
<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-small" id="list-2">
            <li class="element1">Foo</li>
            <li class="element2">Bar</li>
        </ul>
    </div>
</div>
'''

print(soup.select('.panel .panel-heading'))
print(soup.select('ul li'))
print(soup.select('#list-2  .element1'))

[<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>]

 通过get_text()方法来获取标签内容:

for i in soup.select('li'):
    print(i.get_text)

<bound method Tag.get_text of <li class="element">Foo</li>>
<bound method Tag.get_text of <li class="element">Bar</li>>
<bound method Tag.get_text of <li class="element">Jay</li>>
<bound method Tag.get_text of <li class="element1">Foo</li>>
<bound method Tag.get_text of <li class="element2">Bar</li>>

获取标签内的属性值,通过attrs[]属性:

for i in soup.select('ul'):
    # print(i["class"])
    print(i.attrs["class"])

五、BS实战:

通过BS库来爬取豆瓣电影250中的所有海报图片并保存到本地:

import requests
import os
from bs4 import BeautifulSoup
from requests.exceptions import RequestException


def get_one_page(url):
    try:
        res = requests.get(url)
        if res.status_code == 200:
            return res.text
        return None
    except RequestException:
        return None

def save_pic(all_list):
    for item in all_list:
        num = item.find('em').text
        src = item.find('img')["src"]
        name = item.find('img')["alt"]
        # 首先得到<img>中的src链接并获取相应
        res = requests.get(src)
        with open( '.\\out\\'  + num + '.' + name + '.jpg', 'wb') as f:
            print("正在写入海报:" + name)
            f.write(res.content)  # 写入相应的二进制格式
            f.close()

def parse_page(context):
    soup = BeautifulSoup(context, 'lxml')
    all_list = soup.select('.item')
    save_pic(all_list)

def main(offset):
    url = "https://movie.douban.com/top250?start=" + str(offset)
    context = get_one_page(url)
    parse_page(context)

if __name__ == "__main__":
    # 创建out文件
    os.mkdir("out")
    for i in range(10):
        main(i * 25)

参考:

https://www.cnblogs.com/zhaof/p/6930955.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值