5

爬虫系列 | 6、详解爬虫中BeautifulSoup4的用法

 3 years ago
source link: https://segmentfault.com/a/1190000039030926
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

bs4,全称 BeautifulSoup 4 , 它是Python独有的一种解析方式。也就是说只有Python语言才可以通过这种方式去解析数据。

BeautifulSoup 3 只支持Python2,所以已经被淘汰了。

官网的介绍是这样的

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

看起来很复杂,我用自己的理解,通俗的解释一下

我们知道一个网页的源代码,是由多个标签组成,比如<html>、<div>、<td>、<span>等等组成的,而bs4就是用来帮我们精确定位标签位置,从而获取标签或者标签属性中内容的工具。bs4默认自带的解析器,但是官方推荐的是更强大 速度更快的 lxml解析器

其他解析器的优缺点

736FBbu.png!mobile

一、bs4的安装

pip install bs4
pip install lxml

使用bs4解析时,推荐使用lxml解析器。这个在用xpath解析的时候也会用到

二、bs4解析原理

  • 首先实例化一个BeautifulSoup对象,并且将页面源代码加载到这个对象里
  • 调用BeautifulSoup对象中的相关属性或者方法进行标签定位和数据提取

1、如何实例化BeautifuSoup对象

a. 导入bs4包

from bs4 import BeautifulSoup

b.实例化对象

网页源代码,又分为本地已经持久化的HTML文件和网络上直接获取的源代码。

如果是本地已经持久化的文件,可以通过下面的方式将源代码加载到bs4对象中

fp = open('xxx.html', 'r', encoding='utf-8')
# lxml:解析器
soup = BeautifulSoup(fp, 'lxml')

如果是通过requests库获取的网页源代码,通过下面的方式进行加载

response = requests.get(url)
html = response.text
soup = BeautifulSoup(html, 'lxml')

c.数据解析的方法和属性

bs4能够将复杂的HTML转换成一个树形结构,每个节点都是Python对象。

soup.tagName(标签名): 返回的是文档中第一次出现tagName对应的标签及其相应内容

soup.tageName1.tageName2:返回tag1中tage2的标签及其内容

soup.find:等同于soup.tagName,返回第一个匹配到的对象

soup.find_all:返回所有的匹配到的对象。

通过查看源码会发现,find的本质其实就是调用了find_all, 然后返回第一个元素

参数解释:

  • name :要查找的标签名(字符串、正则、方法、True)
  • attrs: 标签的属性
  • recursive: 递归
  • text: 查找文本
  • **kwargs :其它 键值参数
def find(self, name=None, attrs={}, recursive=True, text=None,
             **kwargs):
        """Return only the first child of this Tag matching the given
        criteria."""
        r = None
        l = self.find_all(name, attrs, recursive, text, 1, **kwargs)
        if l:
            r = l[0]
        return r

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iXUox6yw-1611066850753)(C:UsersAdministratorAppDataRoamingTyporatypora-user-imagesimage-20210103164834540.png)]

上图是我从某网站截取的部分画面,翻译成HTML如下(只保留了对本次分析有用的部分,为了方便阅读删除了地址的域名信息)

<html>
 <head><titel>测试Title</titel></head>
 <body>
  <div class="test">
      <ul>
          <li> <a href="zhaosi.html">![](123456789.jpg)<p>尼古拉斯赵四</p></a> </li>
      </ul>
  </div>
  <div class="nr_zt w1180">
   <ul>
    <li> <a id="star" href="zhengshuang.html">![](5940f2cd6b759.jpg)<p>郑爽</p></a> </li>
    <li> <a id="star" href="zhuyilong.html">![](5b56e0fabf5bf.jpg)<p>朱一龙</p></a> </li>
    <li> <a id="star" href="zhoudongyu.html">![](5a28b93be8155.jpg)<p>周冬雨</p></a> </li>
    <li> <a id="star" href="huyitian_1.html">![](5aa36dfbe5f61.jpg)<p>胡一天</p></a> </li>
    <li> <a id="star" href="yiyangqianxi.html">![](5a28d243b0382.jpg)<p>易烊千玺</p></a> </li>
    <li> <a id="star" href="dilireba.html">![](5a28b69334087.jpg)<p>迪丽热巴</p></a> </li>
   </ul>
  </div>
 </body>
</html>

看下面几个例子

# 获取第一个li标签
# <li> <a href="http://www.win4000.com/mt/zhengshuang.html">![](http://pic1.win4000.com/tj/2017-06-14/5940f2cd6b759.jpg)<p>郑爽</p></a> </li>
print(soup.li)
# # 获取第一个li标签中a标签
# <a href="http://www.win4000.com/mt/zhengshuang.html">![](http://pic1.win4000.com/tj/2017-06-14/5940f2cd6b759.jpg)<p>郑爽</p></a>
print(soup.li.a)
#获取第一个li标签中a标签
print(soup.find('li').a)
# 获取所有li标签
print(soup.find_all('li'))
# 获取title标签
print(soup.title)
# 获取a标签的父级标签
print(soup.a.parent)
# 获取a标签的父级标签的名字
print(soup.a.parent.name)

如何获取HTML中的href?

分析:href是a标签中的一个属性,而a标签又在li标签中

在bs4中提取标签中的属性可以通过attrs来获取

from bs4 import BeautifulSoup

fp = open('baidu.html', 'r', encoding='utf-8')
soup = BeautifulSoup(fp, 'lxml')

# 如果获取一个可以这样写
result = soup.a.attrs['href']
# zhaosi.html
print(result)

# 获取全部,可通过先获取a标签 然后遍历获取
all_result = soup.find_all('a')
for i in all_result:
    print(i.attrs['href'])

print("* " * 40)
#  如果我只想获取id = star的href,需要先对id进行筛选
# 返回所有包含id=star的a标签
star_result = soup.find_all('a', id='star')
for i in star_result:
    print(i.attrs['href'])

# 返回包含id的标签(只要有id属性,并且有值的标签都返回)
soup.find_all(id=True)

# 假设尼古拉斯赵四 不是第一个a标签中的内容.提取对应的href
# 需要先定位class=‘test’对应div的位置
# 方法一:
result = soup.find('div', 'test')
print(result.a['href'])

# 方法二(class为python中关键字,因此查找html中的class属性需要添加个下划线 class_)
result1 = soup.find('div', class_='test')
print(result1.a['href'])

# 方法三
result2 = soup.find('div', attrs={'class': 'test'})

# 获取第一个a标签中的文本内容
print(soup.a.text)

a_result = soup.find_all('a')
for i in a_result:
    # 生成的是一个迭代器
    print(i.strings)
    print(list(i.strings))
    print(i.string)
    print(i.text)

其他补充

# 返回子孙节点
# children返回迭代器
result = soup.a.children
for i in result:
    print(i)

# 返回子孙节点, contents返回列表
r = soup.a.contents
print(r)

# 可以通过正则对某个属性进行匹配
# 比如返回href中以zh开头的标签
import re
reg = re.compile('^zh')
result = soup.find_all(href=reg)
print(result)

选择器

bs4非常强大,还支持css选择器。通过select来完成

<html>
 <head><titel>测试Title</titel></head>
 <body>
  <div class="test">
      <ul>
          <li> <a href="zhaosi.html">![](123456789.jpg)<p>尼古拉斯赵四</p></a> </li>
      </ul>
  </div>
  <div class="nr_zt w1180">
   <ul>
    <li> <a id="star" href="zhengshuang.html">![](5940f2cd6b759.jpg)<p>郑爽</p></a> </li>
    <li> <a id="star" href="zhuyilong.html">![](5b56e0fabf5bf.jpg)<p>朱一龙</p></a> </li>
    <li> <a id="star" href="zhoudongyu.html">![](5a28b93be8155.jpg)<p>周冬雨</p></a> </li>
    <li> <a id="star" href="huyitian_1.html">![](5aa36dfbe5f61.jpg)<p>胡一天</p></a> </li>
    <li> <a id="star" href="yiyangqianxi.html">![](5a28d243b0382.jpg)<p>易烊千玺</p></a> </li>
    <li> <a id="star" href="dilireba.html">![](5a28b69334087.jpg)<p>迪丽热巴</p></a> </li>
   </ul>
  </div>
 </body>
</html>
from bs4 import BeautifulSoup

fp = open('baidu.html', 'r', encoding='utf-8')
soup = BeautifulSoup(fp, 'lxml')

# 返回一个所有a标签的列表
result = soup.select('a')
# 返回第一个
result1 = soup.select('a')[0]

"""
class选择器 : .className
"""
# 一层一层的进行选择,用 > 连接  即 > : 表示一个层级
# 输出 class = nr_zt 下ul下的li下的a标签集合
a = soup.select('.nr_zt > ul > li > a')
# 多个层级关联,使用 空格。
# 输出 class= 'nr_zt' 下的a标签集合
b = soup.select('.nr_zt a')


"""
id选择器: # idName
"""
result = soup.select('#star')

# 通过href属性查找,返回列表
soup.select('a[href="zhengshuang.html"]')

# 获取对应标签中img标签的src值
a = soup.select('a[href="zhengshuang.html"]')[0]
print(a.img['src']) # 5940f2cd6b759.jpg

以上就是bs4的常用操作代码,实际上在具体的爬虫过程中,匹配的方式比较灵活,所以大家也不用可以的去背,只需要记住其原理即可。

7viM3aq.png!mobile


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK