前言
学会了多协程后,再也没有了漫长的等待,快的飞起,咱们一步一步分析。做完这个项目,既用协程知识实践,又能获得一份食物热量表,为减肥做准备,两全其美~
往期爬虫文章推荐,好玩才推荐:
震惊!!一小伙竟然使用 python爬虫来算命
【小白可学会系列二】用python爬虫框架Scrapy来完成一个小项目
【小白可学会系列三】爬虫爬取千万数据如何快速保存,看这里!
0.分析爬取网站:
我向你推荐一个网站——https://food.hiyd.com/。这个网站可以查询食物的热量数据,还可以用来学习健身动作。
首先请你打开HI运动的链接:让我们来观察一下这个网页:
然后可以看到11个常见的食物分类。
我们程序员身体健康很重要,必然关注碳水含量那我们就打开杂豆主食这一栏,我们可以看到712页事物的记录。包含了这个分类里食物的名字,及其热量信息,里面甚至还包含了一些品牌食物的热量表。这样我们地目标就有了:用多协程爬取HI运动11个常见食物分类里的食物信息(包含食物名、热量、食物详情页面链接)
有了目标以后,我们接着开始分析。采用爬虫的四步走战略:获取数据→解析数据→提取数据→存储数据。
1.获取数据:
右击打开’检查‘工具,点击Network,然后刷新页面。点击第0个请求food.hiyd.com
,看Response
Response
里就有食物的信息,说明我们想要的数据存在HTML里。
接着看看m.food.hiyd.com
请求的Headers
,可以发现HI运动的网页请求方式是get
。也就是说可以用requests.get()
获取数据。
接着点击进入每个常见食物分类的页面,观察一下它们的网址构成有何规律。
点击第一个分类【谷薯芋、杂豆、主食】,网址显示的是:
http:https://food.hiyd.com/list-1-html
点击第二个分类【蛋类、肉类及制品】,网址变成:
可以预见:网址的list
参数代表着常见食物分类,后面的数字代表着这是第几个类。多点击几个常见食物分类看看,就能验证我们的猜想。
总结一下规律:所有的网址构成都符合这样一个规律:
https://food.hiyd.com/list-数字-html
唯一一个特例是【菜肴】这个分类:虽然它的网址构成和上面的规律是一样的,但是它的url中带有的数字和【其它】分类不是相邻的。
接着我们观察一下每个分类下带有食物记录的页面网址有什么规律。以【谷薯芋、杂豆、主食】这个分类为例,点击翻到第2页的食物记录,我们看看网址又会发生怎样的变化。
总结一下我们上面发现的规律,可以得出HI运动每个食物类别的每一页食物记录的网址规律——http://food.hiyd.com/list-数字-html?page=数字
完成了第一步,我们再来看爬虫的第二步和第三步:解析数据和提取数据。
2.解析,提取数据:
既然HI运动里食物热量的数据都存在HTML里,只要先搞清楚HTML的结构,就可以用BeautifulSoup模块来解析并获得这些数据。NICE!开搞
我们仍然使用“检查”工具,看Elements
,点击光标,把鼠标移到食物【米饭】这里,你就会发现在<li >
元素下,藏有食物的信息,包括食物详情的链接、食物名和热量。没错,你就会发现:所有食物信息都放在其对应的<li>…</li>
标签里。每页食物记录里有20个食物,刚好对应上网页源代码里的20个<li>…</li>
标签。
也就是说我们可以用find_all/find
就能提取出<li>…</li>
标签下的食物详情链接、名称和热量。
3.编写代码:
#导入所需的库和模块:
import gevent,requests,bs4,openpyxl
from gevent import monkey
from gevent.queue import Queue
#让程序变成异步模式
monkey.patch_all()
导入所需模块,并根据前面分析得出的网址规律,构造出前3个常见食物类别的前3页食物记录的网址和第11个常见食物类别的前3页食物记录的网址,同时把这些网址放进队列,并打印出来。
#导入所需的库和模块:
import gevent,requests,bs4,openpyxl
from gevent import monkey
from gevent.queue import Queue
#让程序变成异步模式
monkey.patch_all()
# 创建队列对象,并赋值给work
work = Queue()
# 前3个分类的前3页的食物记录的网址:
url_1 = "https://food.hiyd.com/list-{type}-html?page={page}"
for x in range(1, 4):
for y in range(1, 4):
real_url = url_1.format(type=x, page=y)
work.put_nowait(real_url)
# 通过两个for循环,能设置分类的数字和页数的数字
# 然后,把构造好的网址用put_nowait方法添加进队列里
# 第11个分类的前3页的食物记录的网址:
url_2 = "https://food.hiyd.com/list-132-html?page={page}"
for x in range(1, 4):
real_url = url_2.format(page=x)
work.put_nowait(real_url)
# 通过for循环,能设置第11个常见食物分类的食物的页数。
# 然后,把构造好的网址用put_nowait方法添加进队列里。
print(work)
# 打印队列
这里我用了Queue()方法创建了空的队列。通过两个for循环,构造了前3个常见食物分类的前3页的食物记录的网址。
由于第11个常见食物分类的网址比较特殊,要分开构造。然后把构造好的网址用put_nowait方法,都放进队列里,然后把它打印出来。
这里我仅仅爬取这12个网址作为演示。如果真的把HI运动的11个常见食物分类里的所有页数的食物都爬取下来,会给HI运动的服务器带来很大的负担,这样的做法很是不道义,我也建议你别这么去做。接着,我们就要使用gevent帮我们爬取数据,这就是本文的核心啦。
我们需要定义一个爬取函数。请认真阅读和理解下方的代码。
def crawler(job):# 定义crawler函数
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36'}
# 添加请求头
while not job.empty():
# 当队列不是空的时候,就执行下面的程序
url = job.get_nowait()
# 用get_nowait()方法从队列里把刚刚放入的网址提取出来
res = requests.get(url, headers=headers)
# 用requests.get获取网页源代码
bs_res = bs4.BeautifulSoup(res.text, 'html.parser')
# 用BeautifulSoup解析网页源代码
category = bs_res.find('b').text
# 用find提取出<b>标签的内容,当前页面所属的分类
foods = bs_res.find_all('li')
# 用find_all提取出<li>标签的内容
for food in foods:# 遍历foods
food_name = food.find('a').find_all('div')[1].find('h3').text
# 用find_all在<li>标签下,提取出第二个<div>标签中<h3>标签中的文本,也就是食物名称
food_calorie = food.find('a').find_all('div')[1].find('p').text
# 用find_all在<li>标签下,提取出第二个<div>标签中<p>标签中的文本,也就是食物热量
food_url = 'http:' + food.find('a')['href']
# 用find_all在<li>标签下,提取出唯一一个<a>标签中href属性的值,跟'http:'组合在一起,就是食物详情页的链接
print([category, food_name, food_calorie, food_url])
# 打印食物的名称
接下来我们用gevent.spawn()
创建任务和用gevent.joinall()
执行任务,启动协程,就能开始爬取我们想要的数据。
# 导入所需的库和模块:
from gevent import monkey
#让程序变成异步模式
monkey.patch_all()
import gevent,requests,bs4,openpyxl,time
from gevent.queue import Queue
from openpyxl import load_workbook,Workbook,worksheet
# 创建队列对象,并赋值给work
work = Queue()
# 前3个分类的前3页的食物记录的网址:
url_1 = "https://food.hiyd.com/list-{type}-html?page={page}"
for x in range(1, 4):
for y in range(1, 4):
real_url = url_1.format(type=x, page=y)
work.put_nowait(real_url)
# 通过两个for循环,能设置分类的数字和页数的数字
# 然后,把构造好的网址用put_nowait方法添加进队列里
# 第11个分类的前3页的食物记录的网址:
url_2 = "https://food.hiyd.com/list-132-html?page={page}"
for x in range(1, 4):
real_url = url_2.format(page=x)
work.put_nowait(real_url)
# 通过for循环,能设置第11个常见食物分类的食物的页数。
# 然后,把构造好的网址用put_nowait方法添加进队列里。
print(work)
# 打印队列
def crawler(job):# 定义crawler函数
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36'}
# 添加请求头
while not job.empty():
# 当队列不是空的时候,就执行下面的程序
url = job.get_nowait()
# 用get_nowait()方法从队列里把刚刚放入的网址提取出来
res = requests.get(url, headers=headers)
# 用requests.get获取网页源代码
bs_res = bs4.BeautifulSoup(res.text, 'html.parser')
# 用BeautifulSoup解析网页源代码
category = bs_res.find('b').text
# 用find提取出<b>标签的内容,当前页面所属的分类
foods = bs_res.find_all('li')
# 用find_all提取出<li>标签的内容
for food in foods:# 遍历foods
food_name = food.find('a').find_all('div')[1].find('h3').text
# 用find_all在<li>标签下,提取出第二个<div>标签中<h3>标签中的文本,也就是食物名称
food_calorie = food.find('a').find_all('div')[1].find('p').text
# 用find_all在<li>标签下,提取出第二个<div>标签中<p>标签中的文本,也就是食物热量
food_url = 'http:' + food.find('a')['href']
# 用find_all在<li>标签下,提取出唯一一个<a>标签中href属性的值,跟'http:'组合在一起,就是食物详情页的链接
print([category, food_name, food_calorie, food_url])
# 打印食物的名称
tasks_list = []
# 创建空的任务列表
for x in range(5):
# 相当于创建了5个爬虫
task = gevent.spawn(crawler(work))
# 用gevent.spawn()函数创建执行crawler()函数的任务
tasks_list.append(task)
# 往任务列表添加任务
gevent.joinall(tasks_list)
# 用gevent.joinall方法,启动协程,执行任务列表里的所有任务,让爬虫开始爬取网站
到这里,所有爬取部分的代码就完成啦!只要添加存储数据的代码,我们就完成了整个项目。
我用的是openpyxl模块来做存储数据
下面就是完整的代码了
from gevent import monkey
monkey.patch_all()
import gevent, requests, bs4, openpyxl
from gevent.queue import Queue
# 让程序变成异步模式
# 创建队列对象,并赋值给work
work = Queue()
# 前3个分类的前3页的食物记录的网址:
url_1 = "https://food.hiyd.com/list-{type}-html?page={page}"
for x in range(1, 4):
for y in range(1, 4):
real_url = url_1.format(type=x, page=y)
work.put_nowait(real_url)
# 通过两个for循环,能设置分类的数字和页数的数字
# 然后,把构造好的网址用put_nowait方法添加进队列里
# 第11个分类的前3页的食物记录的网址:
url_2 = "https://food.hiyd.com/list-132-html?page={page}"
for x in range(1, 4):
real_url = url_2.format(page=x)
work.put_nowait(real_url)
# 通过for循环,能设置第11个常见食物分类的食物的页数。
# 然后,把构造好的网址用put_nowait方法添加进队列里。
print(work)
# 打印队列
def crawler(job): # 定义crawler函数
headers = {
'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36'}
# 添加请求头
while not job.empty():
# 当队列不是空的时候,就执行下面的程序
url = job.get_nowait()
# 用get_nowait()方法从队列里把刚刚放入的网址提取出来
res = requests.get(url, headers=headers)
# 用requests.get获取网页源代码
bs_res = bs4.BeautifulSoup(res.text, 'html.parser')
# 用BeautifulSoup解析网页源代码
category = bs_res.find('b').text
# 用find提取出<b>标签的内容,当前页面所属的分类
foods = bs_res.find_all('li')
# 用find_all提取出<li>标签的内容
result_list = []
# 创建空的list用来存储结果
for food in foods: # 遍历foods
food_name = food.find('a').find_all('div')[1].find('h3').text
# 用find_all在<li>标签下,提取出第二个<div>标签中<h3>标签中的文本,也就是食物名称
food_calorie = food.find('a').find_all('div')[1].find('p').text
# 用find_all在<li>标签下,提取出第二个<div>标签中<p>标签中的文本,也就是食物热量
food_url = 'http:' + food.find('a')['href']
# 用find_all在<li>标签下,提取出唯一一个<a>标签中href属性的值,跟'http:'组合在一起,就是食物详情页的链接
# 打印食物的名称
result_list.append([category, food_name, food_calorie, food_url])
# 将数据中的类别当作sheet_name
sheet_name = result_list[0][0]
# 判断当前sheetname 是否已经创建
if sheet_name in wb.get_sheet_names():
# 如果创建了就是用当前sheet
sheet = wb[sheet_name]
else:
# 如果没有创建则创建sheet_name
sheet = wb.create_sheet(sheet_name)
# 并且添加标题
sheet.append(["类别", "食物名称", "热量", "链接"])
for row in result_list:
# 将数据写入表格中
sheet.append(row)
#打开需要存储的文件
wb = openpyxl.Workbook()
tasks_list = []
# 创建空的任务列表
for x in range(5):
# 相当于创建了5个爬虫
task = gevent.spawn(crawler, work)
# 用gevent.spawn()函数创建执行crawler()函数的任务
tasks_list.append(task)
# 往任务列表添加任务
gevent.joinall(tasks_list)
# 用gevent.joinall方法,启动协程,执行任务列表里的所有任务,让爬虫开始爬取网站
# 获取默认的Sheet表并删除表中内容
wb.remove(wb['Sheet'])
wb.save("result.xlsx")
wb.close()
得到了这份热量表,还怕减肥时不知道吃啥吗?
喜欢的话就点个赞和关注吧~
🤞🤞🤞🤞