专栏名称: dwzb
目录
相关文章推荐
阑夕  ·  如何评价? -20250319220503 ·  昨天  
界面新闻  ·  乐道强制销售员店内购车?总裁回应 ·  3 天前  
51好读  ›  专栏  ›  dwzb

BeautifulSoup全面总结

dwzb  · 掘金  ·  · 2018-04-13 07:02

正文

(注:网页解析库的代码都比较通俗易懂,看理论讲解不如直接看代码,自己多写就能对常用方法了然于胸。本文是从整体框架上进行总结,更适合在对库有基本的了解之后再详细跟着文章思路查缺补漏。所以建议本文阅读顺序为:先不看文字,挑代码来看(这时挑简单的来看,看不懂的代码不要管),知道那些代码都是做什么的,自己动手写,多试错,然后看后面的实战。了解库的基本使用流程之后,能自己解析一些东西了,再跟着文章的思路把细节补上,之后就能运用自如了。)

beautifulsoup库应该是初学爬虫听的最多的一个解析库了,本文就来讲解一下这个库的用法。

本文分为如下几个部分

  • 解析html流程说明
  • 识别标签
  • 提取内容
  • 查看标签信息
  • 其他提取细节

解析html流程说明

爬取网页信息,可以理解成从html代码中抽取我们需要的信息,而html代码由众多标签组成,我们要做的就分为两个部分

  • 精确定位标签
  • 从标签中提取内容

先说第二个,在找到对应标签位置后,我们要的信息一般会存储在标签的两个位置中

  • 标签中的内容
  • 属性值

比如下面这行标签

<p><a href='www.wenzi.com'>文字</a></p>

一般要提取“文字”部分,或者那个链接 www.wenzi.com 部分

那么如何精确定位到标签呢?只能依靠标签名和标签属性来识别。下面一个例子提供识别的大致思路,之后会具体总结

<title>标题</title>
<body>
    <ul class='list1'>
        <li>列表1第1项</li>
        <li>列表1第2项</li>
    </ul>
    <p class='first'>文字1</p>
    <p class='second'>文字2</p>
    <ul class='list2'>
        <li>列表2第1项</li>
        <li>列表2第2项</li>
    </ul>
</body>
  • 如果要提取“标题”,只需要使用 title 标签名来识别,因为只出现过一次 title 标签
  • 如果要提取“文字1”,不能只使用 p 标签,因为“文字2”也对应了 p 标签,所以要用 p 标签且 class 属性是 'second' 来识别
  • 如果“文字1”和“文字2”都要,就可以循环获取所有 p 标签提取内容
  • 如果想提取列表1中的两项,就不能直接循环获取 li 标签了,因为列表2中也有 li 标签。此时需要先识别其父节点,先定位到 <ul class='list1'> 这个标签上(通过 ul 标签和 class 属性是 list1 定位)。此时在这个标签里,所有 li 都是我们想要的了,直接循环获取就可以了

这里展示一下使用beautifulsoup实现上述提取的代码,先对这个库的提取思路有一个大概的印象

a = '''<title>标题</title>
<body>
    <ul class='list1'>
        <li>列表1第1项</li>
        <li>列表1第2项</li>
    </ul>
    <p class='first'>文字1</p>
    <p class='second'>文字2</p>
    <ul class='list2'>
        <li>列表2第1项</li>
        <li>列表2第2项</li>
    </ul>
</body>'''

from bs4 import BeautifulSoup
soup = BeautifulSoup(a, "html.parser")
soup.title.text # '标题'
soup.find('p', attrs={'class':'first'}).text # '文字1'
soup.find_all('p') # [<p class="first">文字1</p>, <p class="second">文字2</p>], 再分别从中提取文字
soup.find('ul', attrs={'class':'list1'}).find_all('li') # [<li>列表1第1项</li>, <li>列表1第2项</li>]

识别的大致思路就是这样。

从细节上看,一个完整解析库需要实现三个部分的功能

从提取内容上看

  • 提取标签内容
  • 提取标签属性值

从识别标签上看

  • 只根据标签来识别
    • 找到名为a的标签(只有一个a标签时)
    • 找到所有名为a的标签
    • 找到名为a或b的标签
    • 根据正则表达式提取标签
  • 同时根据标签和属性识别
    • (要求在能识别标签的前提下,能相似地识别属性)比如
    • 找到 标签名为a且B属性的值是b 的标签
    • 找到 标签名为a且B属性的值是b且C属性是c 的标签
    • 找到 标签名为a且B属性的值是b或c 的标签
    • 找到 标签名为a且(不)包含B属性 的标签
    • 找到 标签名为a且B属性的值包含b字符串(各种正则式匹配) 的标签
  • 根据标签内内容来识别,也是类似如上划分,最好能用正则表达式匹配内容
  • 根据位置识别
    • 找到 第i个a标签
    • 找到 第i个和第j个a标签

提供一些查看当前标签信息的方法,方便调试

  • 获得标签名
  • 获得标签所有属性及值
  • 检查标签是否有某属性

除此之外这个库还有一些提取细节

  • 多层嵌套标签问题
  • find_all 的简写方法
  • find_all 的其他参数

下面按照上述实现的功能顺序来讲解

(其实到最后会发现这个库唯一一个要重点掌握的方法是 find_all )

首先加载库

from bs4 import BeautifulSoup

识别标签

本节分为如下几个部分

  • 只根据标签来识别
  • 根据标签和属性识别
  • 根据标签内内容来识别
  • 根据位置识别

只根据标签来识别

这部分分为如下几种情况

  • 找到名为a的标签(查找唯一标签)
  • 找到所有名为a的标签
  • 找到名为a或b的标签
  • 根据正则表达式提取标签

查找唯一标签时,beautifulsoup库中提供三种方法

  • 对象的属性调用,直接提取该名字的标签,但是如果有很多该标签只能找到第一个
  • find 方法,也只能找到第一个
  • find_all 方法,找到所有该标签,返回一个list,如果只找到一个也是返回list,用 [0] 提取
a = '''
<h1>标题1</h1>
<h2>标题2</h2>
<h2>标题3</h2>
'''

# 将提取到的字符串转化为beautifulsoup的对象
soup = BeautifulSoup(a, "html.parser")

# 提取唯一标签
soup.h1
soup.find('h1')
soup.find_all('h1')[0]
# 上面三条结果都是
# <h1>标题1</h1>

如果要找到所有某标签,或者某几种标签及根据正则表达式提取,只能用 find_all ,返回一个列表

soup.find_all('h2')
# [<h2>标题2</h2>, <h2>标题3</h2>]

soup.find_all(['h1','h2'])
# [<h1>标题1</h1>, <h2>标题2</h2>, <h2>标题3</h2>]

# 使用正则表达式
import re
soup.find_all(re.compile('^h'))
# [<h1>标题1</h1>, <h2>标题2</h2>, <h2>标题3</h2>]

根据标签和属性识别

这部分分为如下几种情况

  • (要求在能识别标签的前提下,能用相似的方法识别属性)比如
  • 找到 标签名为a且B属性的值是b 的标签
  • 找到 标签名为a且B属性的值是b且C属性是c 的标签
  • 找到 标签名为a且B属性的值是b或c 的标签
  • 找到 标签名为a且(不)包含B属性 的标签
  • 找到 标签名为a且B属性的值包含b字符串(各种正则式匹配) 的标签

要参考属性提取标签时,只有 find find_all 两种方法, find 总是返回找到的第一个,而 find_all 会返回所有,如果想要第一个就提取即可,因此这里全用 find_all 来讲,其实只是加一个 attrs 参数







请到「今天看啥」查看全文