我是吃货之再战BeatifulSoup
前言
菜谱的重要性不言而喻,只要会一些烹饪手法,准备好食材,那做出一桌好菜只是时间问题。继爬虫初体验之BeautifulSoup这篇文章后,本文将使用BeautifulSoup来一波实战,从网上爬取一个菜谱存储在Excel表格中。
页面分析
在这个项目里,选取的网站是“下厨房”。它有一个栏目叫做“本周最受欢迎 ”,收录了当周最受人喜爱的菜谱。
如图:
在进行爬取之前,先去看看它的robots协议
通过阅读robots
协议,我们要爬取的/explore/
不在禁止爬取的列表内。
另外,/recipe/
是每一道菜的详情页面,记录了这道菜的做法。爬取/recipe/
是不被允许的,如果真要爬/recipe/
里的信息,也能爬取到,但不推荐,这里就不再讲述了。
大致浏览网页后,我们需要拿到的信息有:菜名、食材、和菜名所对应的详情页URL
获取数据
由于网站可能会采取各种各样的反爬措施,服务器会限制我们这些“投机取巧”的爬虫,降低访问压力,毕竟成千上万次的访问对代码来说就是一个for循环的事儿。
那这就有一个问题,服务器怎么判断访问者是一个普通的用户(通过浏览器),还是一个爬虫者(通过代码)呢?
每一个请求,都会有一个Request Headers
,我们把它称作请求头。它里面会有一些关于该请求的基本信息,比如:这个请求是从什么设备什么浏览器上发出?这个请求是从哪个页面跳转而来?
如图所示:
如上图,user-agent
(中文:用户代理)会记录你电脑的信息和浏览器版本。referer
(中文:引用来源)则记录了这个请求,最初的起源是来自哪个页面。
如果我们想告知服务器,我们不是爬虫,而是一个正常的浏览器,就要去修改user-agent
。倘若不修改,那么这里的默认值就会是Python,会被服务器认出来。如果爬取失败的话,我们就需要尝试添加headers
参数并在本地运行。
那么如何添加Request Headers呢?
Requests模块允许我们去修改Headers的值。通过它的官方文档(若是失效,可以点击这个中文文档,找到Request Headers,你会看到:
很自然地,代码信手拈来:
# 为躲避反爬机制,伪装成浏览器的请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/527.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/527.36'}
# 获取数据
res_foods = requests.get('http://www.xiachufang.com/explore/', headers=headers)
请求头的设置是非常简单的,可以直接复制自己浏览器的信息,并粘贴过来。
解析并提取数据
单击右键,选择检查,或者直接按F12。查看网站的HTML文档,如图所示:
点击标签,一一展开后,如图所示:
在对比看了多个标签后,找出规律,我们就能定位到菜名的所在位置,是<a>
标签内的文本,还顺带找到了详情页URL的所在位置。
如上图,<a>
标签里的属性href
,其值是/recipe/106733852/
。点击它,就会跳转到这道菜的详情页。
所以到时候,我们可以去提取<a>
标签。接着,先用text
拿到它的文本,再使用href
获取到半截URL,再和https://www.xiachufang.com/
做拼接。
对于食材,我们会发现有的是<a>
标签里的纯文本,有的是<span>
标签里的纯文本。它们有共同父级标签,是<p class="ing ellipsis">
。
所以到这里这里就产生了两个思路去爬取信息:
-
先去爬取所有的最小父级标签
<div class="info pure-u">
,然后针对每一个父级标签,想办法提取里面的菜名、URL、食材。 -
分别提取所有的菜名、所有的URL、所有的食材。然后让菜名、URL、食材给一一对应起来
# 参数1为要解析的文本 参数2为解析器 返回beautifulsoup对象
bs_foods = BeautifulSoup(res_foods.text, 'html.parser')
#思路一:
# 查找最小父级标签 返回一个resultset 可以看作是list
list_foods = bs_foods.find_all('div', class_='info pure-u')
# 创建一个空列表,用于存储信息
list_all = []
for food in list_foods:
# tag.text() 提取tag中的文字
# tag['属性名'] 输入参数:属性名,可以提取tag中该属性的值
tag_a = food.find('a')
# 菜名,使用strip()函数去掉头尾多余的空格
name = tag_a.text.strip()
# 获取URL
URL = 'http://www.xiachufang.com' + tag_a['href']
tag_p = food.find('p', class_='ing ellipsis')
# 食材,使用strip()函数去掉多余的空格
ingredients = tag_p.text.strip()
# 将菜名、URL、食材,封装为列表,添加进list_all
list_all.append([name, URL, ingredients])
#思路二:
# 查找包含菜名和URL的<p>标签
tag_name = bs_foods.find_all('p',class_='name')
# 查找包含食材的<p>标签
tag_ingredients = bs_foods.find_all('p',class_='ing ellipsis')
# 创建一个空列表,用于存储信息
list_all = []
# 启动一个循环,次数等于菜名的数量
for x in range(len(tag_name)):
# 提取信息,封装为列表。
list_food = [tag_name[x].text.strip(),tag_name[x].find('a')['href'],tag_ingredients[x].text.strip()]
# 将信息添加进list_all
list_all.append(list_food)
存储数据
与操作csv文件类似,操作Excel文件需要借助openpyxl模块。可以参考官方文档
安装方法:window电脑在终端输入命令:pip install openpyxl
,按下enter键;mac电脑在终端输入命令:pip3 install openpyxl
,按下enter键
在使用openpyxl模块前,我们需要明晰一个概念,一个Excel文档也称为一个工作簿(workbook),每个工作簿里可以有多个工作表(worksheet),当前打开的工作表又叫活动表。每个工作表里有行(row)和列。
# 创建新的workbook(工作簿)对象,就是创建新的空Excel文件
wb = Workbook()
# wb.active就是获取这个工作簿的活动表,通常就是第一个工作表
sheet = wb.active
# 可以用.title给工作表重命名。现在第一个工作表的名称就会由原来默认的“sheet1”改为"menu"
sheet.title = 'menu'
for i in list_all:
# 遍历list_all,同时把遍历的内容添加到表格里,实现了多行写入
sheet.append(i)
# 保存新建的Excel文件,并命名为menu.xlsx
wb.save('menu.xlsx')
代码总览
# 调用requests库
import requests
# 引用BeautifulSoup库
from bs4 import BeautifulSoup
# 引用模块openpyxl
from openpyxl import Workbook
# 第一部分 爬取信息
# 为躲避反爬机制,伪装成浏览器的请求头
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36(KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36'}
# 获取数据
res_foods = requests.get('http://www.xiachufang.com/explore/', headers=headers)
# 解析数据
# 参数1为要解析的文本 参数2为解析器 返回beautifulsoup对象
bs_foods = BeautifulSoup(res_foods.text, 'html.parser')
# 查找最小父级标签 返回一个resultset 可以看作是list
list_foods = bs_foods.find_all('div', class_='info pure-u')
# 创建一个空列表,用于存储信息
list_all = []
for food in list_foods:
# tag对象的属性和方法
# find() find_all() 提取tag中的tag
# tag.text() 提取tag中的文字
# tag['属性名'] 输入参数:属性名,可以提取tag中该属性的值
tag_a = food.find('a')
# 菜名,使用strip()函数去掉头尾多余的空格
name = tag_a.text.strip()
# 获取URL
URL = 'http://www.xiachufang.com' + tag_a['href']
tag_p = food.find('p', class_='ing ellipsis')
# 食材,使用strip()函数去掉多余的空格
ingredients = tag_p.text.strip()
# 将菜名、URL、食材,封装为列表,添加进list_all
list_all.append([name, URL, ingredients])
# 第二部分 excel表格的写入
# 创建新的workbook(工作簿)对象,就是创建新的空Excel文件
wb = Workbook()
# wb.active就是获取这个工作簿的活动表,通常就是第一个工作表
sheet = wb.active
# 可以用.title给工作表重命名。现在第一个工作表的名称就会由原来默认的“sheet1”改为"menu"
sheet.title = 'menu'
for i in list_all:
# 遍历list_all,同时把遍历的内容添加到表格里,实现了多行写入
sheet.append(i)
# 保存新建的Excel文件,并命名为menu.xlsx
wb.save('menu.xlsx')
END
看完是不是觉得很简单? 希望各位掘友看完之后能有所收获。
转载自:https://juejin.cn/post/7119046617881116708