静态网页爬虫①

处理数据

前面我们说过了通过 requests 库获取数据,这里我们要说如何处理数据

处理数据我们需要用到一个强大的第三方库——BeautifulSoup !

“美味的汤,绿色的浓汤,在热气腾腾的盖碗里装!谁不愿意尝一尝,这样的好汤?晚餐用的汤,美味的汤!”

BeautifulSoup 库的名字取自刘易斯·卡罗尔在《爱丽丝梦游仙境》里的同名诗歌。就像它在仙境中的说法一样,BeautifulSoup 尝试化平淡为神奇。它通过定位 HTML 标签来格式化和组织复杂的网页源代码,用简单易用的 Python 对象为我们展现出 HTML 结构信息

处理数据分为两步:

  • 解析数据:将网页源代码解析成 Python 能“读懂”的格式
  • 提取数据:将网页源代码中无关数据过滤掉,只提取出我们所需要的数据

解析数据

我们以豆瓣读书 Top250 为例,它的网址是:https://book.douban.com/top250。

我们来看看如何将其网页源代码解析成 BeautifulSoup 对象:

import requests
from bs4 import BeautifulSoup

headers = {
  'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36'
}
res = requests.get('https://book.douban.com/top250', headers=headers)
soup = BeautifulSoup(res.text, 'html.parser')

Tips:相比之前我们添加 headers 参数,这是为了应对豆瓣的反爬虫机制。

我们通过 from bs4 import BeautifulSoup 语句导入 BeautifulSoup,然后使用 BeautifulSoup(res.text, 'html.parser') 语句将网页源代码的字符串形式解析成了 BeautifulSoup 对象

创建 BeautifulSoup 对象时需要传入两个参数,第一个参数是要解析的 HTML 文本,即网站源代码的字符串形式(res.text)。第二个参数是 解析HTML 的解析器,html.parser 是 Python 中内置的解析器,较为简单方便.

在这里插入图片描述
我们将网页源代码解析成了 BeautifulSoup 对象,如果我们将他打印出来会发现竟然和原来的网页源代码(也就是 res.text)一模一样!既然都一样,我们何苦费这么大力将网页源代码解析成 BeautifulSoup 对象 呢?

相比字符串,BeautifulSoup 对象 里有很多强大的方法和属性。通过这些方法和属性,我们就能方便快捷地提取出我们所需要的数据。

提取数据

BeautifulSoup 对象 里的方法和属性有很多,我们这里只提及其中最常用的一些,这些足以应付大多数场景。

find() 方法和 find_all() 方法

BeautifulSoup 对象 里的 find() 和 find_all() 是我们提取数据最常用的两个方法。借助它们,我们可以过滤掉 HTML 页面里的无用数据,轻松地找到我们需要的数据。

我们来看一下 find() 和 find_all() 的作用和区别:
在这里插入图片描述
我们可以通过例子来更好地理解他们:
假设我们获取到的网页源代码如下:

<div class="content">
  <a href="https://douban.com">登录/注册</a>
  <h1>豆瓣读书 Top 250</h1>
  <div class="artile">
    <a href="https://movie.douban.com">豆瓣电影</a>
    <div class="item">
      <a href="https://book.douban.com/subject/1770782/">追风筝的人</a>
    </div>
    <div class="item">
      <a href="https://book.douban.com/subject/25862578/">解忧杂货店</a>
    </div>
    <div class="item">
      <a href="https://book.douban.com/subject/1084336/">小王子</a>
    </div>
  </div>
</div>
soup = BeautifulSoup(res.text, 'html.parser')
print(soup.find('a'))
# 输出:<a href="https://douban.com">登录/注册</a>
print(soup.find_all('a'))
# 输出:[
# <a href="https://douban.com">登录/注册</a>,
# <a href="https://movie.douban.com">豆瓣电影</a>,
# <a href="https://book.douban.com/subject/1770782/">追风筝的人</a>,
# <a href="https://book.douban.com/subject/25862578/">解忧杂货店</a>,
# <a href="https://book.douban.com/subject/1084336/">小王子</a>
# ]

它俩的用法基本一样,都是传入 HTML 标签名称,返回符合该 HTML 标签的数据。区别是 find() 方法只返回第一个符合条件的标签,而 find_all() 方法返回所有符合条件的标签列表。他们的返回值分别是 BeautifulSoup 中的 Tag 对象 和由 Tag 对象组成的列表。(后面会提到)

除了传入 HTML 标签名称 外,这两个方法还支持传入 HTML 属性 进行筛选,返回符合条件的数据。举个例子:

# 查找 id='doubanapp-tip' 的 div 标签
soup.find('div', id='doubanapp-tip')
# 查找所有 class='rating_nums' 的 span 标签
soup.find_all('span', class_='rating_nums')

class 和 id 这两个 HTML 属性 具有很强的标识性,因此是数据筛选中最常用的两个属性,我们要重点关注。

Tips:因为 class 是 Python 中定义类的关键字,因此用 class_ 表示 HTML 中的 class。

通过 id、class 等 HTML 属性的筛选,我们就可以快速准确的找到我们需要的数据。当一个条件无法精确定位到我们想要的数据时,我们还可以传入多个 HTML 属性进行筛选,返回同时符合这些条件的数据。

我们再来看个例子:

# 查找 id='doubanapp-tip' 且 class='rating_nums' 的 div 标签
soup.find('div', id='doubanapp-tip', class_='rating_nums')

Tag对象

BeautifulSoup 将 HTML 中的元素封装成了 Tag 对象和 BeautifulSoup 对象 一样,Tag 对象 里也有 find() 和 find_all() 方法。因此,我们可以不断地调用这两个方法,一层一层地找到我们需要的数据。我们还是以前面的 HTML 代码为例提取其中的书名:

<div class="content">
  <a href="https://douban.com">登录/注册</a>
  <h1>豆瓣读书 Top 250</h1>
  <div class="books">
    <a href="https://movie.douban.com">豆瓣电影</a>
    <div class="item">
      <a href="https://book.douban.com/subject/1770782/">追风筝的人</a>
    </div>
    <div class="item">
      <a href="https://book.douban.com/subject/25862578/">解忧杂货店</a>
    </div>
    <div class="item">
      <a href="https://book.douban.com/subject/1084336/">小王子</a>
    </div>
  </div>
</div>

我们可以看到,书名在 a 标签 中。但如果直接使用 soup.find_all(‘a’) 的话,第二行的“登录/注册”和第五行的“豆瓣电影”也会被获取到,因此我们需要将这些无效数据过滤掉。

我们分析一下不难发现,书名在 class=“item” 的 div 标签 里的 a 标签 内。我们只要先找到所有 class=“item” 的 div 标签,然后再找到其中的 a 标签 即可,因此我们可以像下面这样来获取书名的数据:

# 找到所有 class_='item' 的 div 标签
items = soup.find_all('div', class_='item')
for i in items:
  # 找到 class_='item' 的 div 标签中的 a 标签
  print(i.find('a'))
# 输出:
# <a href="https://book.douban.com/subject/1770782/">追风筝的人</a>
# <a href="https://book.douban.com/subject/25862578/">解忧杂货店</a>
# <a href="https://book.douban.com/subject/1084336/">小王子</a>

这样,我们就找到了所有书名的数据。此时返回的还是 Tag 对象。如果我们只想要书名和对应的链接呢?这就用到了 Tag 对象 的 text 属性和 HTML 属性名取值。

items = soup.find_all('div', class_='item')
for i in items:
  tag = i.find('a')
  # 获取 text 属性
  name = tag.text
  # 获取 href 属性值
  link = tag['href']
  print(name, link)
# 输出:
# 追风筝的人 https://book.douban.com/subject/1770782/
# 解忧杂货店 https://book.douban.com/subject/25862578/
# 小王子 https://book.douban.com/subject/1084336/

我们通过 Tag 对象 的 text 属性拿到了 a 标签里的文字内容,即 追风筝的人 等。然后我们通过和字典取值一样的方式,将 HTML 属性名 作为键,得到了对应属性的值。这里是以 href 属性为例,其他的 HTML 属性也同样可以。

在这里插入图片描述
我们来总结一下 Tag 对象 的常用属性和方法:
在这里插入图片描述

CSS选择器

有没有什么方法可以直接就找到我们需要的数据,而不用多次查找吗?

答案是肯定的,需要用到 CSS 选择器。

在 CSS 选择器中,# 代表 id,. 代表 class。比如:#login 表示 id=‘login’ 的所有元素,.item 表示 class=‘item’ 的所有元素。

我们也可以直接通过标签名选择对应的元素,比如:a 表示所有的 a 元素,p 表示所有的 p 元素。

它们也可以组合在一起,选择同时符合条件的元素,比如:a#login 表示所有 id=‘login’ 的 a 元素,p.item 表示所有 class=‘item’ 的 p 元素,#login.item 表示所有 id=‘login’ 且 class=‘item’ 的元素,.item.book 表示所有 class 同时为 item 和 book 的元素。

需要注意的是,选择同时符合条件的元素,选择器之间不能有空格,如果写成 .item .book 就是另一个意思了。这是新的知识点——子元素选择。

当两个选择器之间加了空格,表示子元素选择。还是以 .item .book 为例,它表示选择所有 class=‘item’ 的元素里面 class=‘book’ 的元素,即嵌套在 class=‘item’ 的元素里面 class=‘book’ 的元素。

这个嵌套可以是任意层级的,只要在里面就行,不要求直接嵌套在第一层。如果只需要直接嵌套在第一层符合条件的元素,可以用 > 分隔。比如:.item > .book。

来看个例子感受一下它们的区别:

from bs4 import BeautifulSoup

html = '''
<div class="item">
  <p class="book">小王子</p>
  <div class="hot">
    <p class="book">追风筝的人</p>
  </div>
</div>'''

soup = BeautifulSoup(html, 'html.parser')

print(soup.select('.item.book'))
# 输出:[]

print(soup.select('.item .book'))
# 输出:[<p class="book">小王子</p>, <p class="book">追风筝的人</p>]

print(soup.select('.item > .book'))
# 输出:[<p class="book">小王子</p>]

了解了 CSS 选择器的基本语法后,我们来看看如何在 BeautifulSoup 中使用。

BeautifulSoup 对象 有一个 select() 方法,我们将 CSS 选择器 传进去即可直接找到我们需要的元素。上面查找在 class=“item” 的 div 标签 里的 a 标签 的代码就可以这样写:

items = soup.select('div.item a')
for i in items:
  name = i.text
  link = i['href']
  print(name, link)
# 输出:
# 追风筝的人 https://book.douban.com/subject/1770782/
# 解忧杂货店 https://book.douban.com/subject/25862578/
# 小王子 https://book.douban.com/subject/1084336/

静态网页爬虫的过程

对于静态网页爬虫的过程,可以总结成下图:
在这里插入图片描述
我们现在对豆瓣top250中的前25个电影的名字以及名字进行爬取:

import requests
from bs4 import BeautifulSoup

headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Safari/537.36 Edg/101.0.1210.32'
}

#首先对网页发出请求并获得响应
req = requests.get('https://movie.douban.com/top250',headers = headers)
#将网页的源代码形式解析
soup = BeautifulSoup(req.text,'html.parser')

#进行元素的第一次提取
result1 = soup.select('.item .pic')

num = 0
for i in result1:
    num += 1
    name = i.select('a img')[0]['alt']
    link = i.select('a')[0]['href']
    print(num,' ',name,link)

结果:
在这里插入图片描述

  • 55
    点赞
  • 72
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 38
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 38
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十八岁讨厌编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值