一小伙用python爬虫除去了减肥路上的绊脚石

前言

学会了多协程后,再也没有了漫长的等待,快的飞起,咱们一步一步分析。做完这个项目,既用协程知识实践,又能获得一份食物热量表,为减肥做准备,两全其美~


往期爬虫文章推荐,好玩才推荐:

震惊!!一小伙竟然使用 python爬虫来算命

【小白可学会系列二】用python爬虫框架Scrapy来完成一个小项目

【小白可学会系列三】爬虫爬取千万数据如何快速保存,看这里!


0.分析爬取网站:

我向你推荐一个网站——https://food.hiyd.com/。这个网站可以查询食物的热量数据,还可以用来学习健身动作。
首先请你打开HI运动的链接:让我们来观察一下这个网页:

饮食热量查询,减肥饮食推荐,食物数据库 - Hi运动食品库

img

然后可以看到11个常见的食物分类。

img

我们程序员身体健康很重要,必然关注碳水含量那我们就打开杂豆主食这一栏,我们可以看到712页事物的记录。包含了这个分类里食物的名字,及其热量信息,里面甚至还包含了一些品牌食物的热量表。这样我们地目标就有了:用多协程爬取HI运动11个常见食物分类里的食物信息(包含食物名、热量、食物详情页面链接)

img

有了目标以后,我们接着开始分析。采用爬虫的四步走战略:获取数据→解析数据→提取数据→存储数据。

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

点击第二个分类【蛋类、肉类及制品】,网址变成:

蛋类、肉类及制品热量查询,卡路里查询 - Hi运动食品库

可以预见:网址的list参数代表着常见食物分类,后面的数字代表着这是第几个类。多点击几个常见食物分类看看,就能验证我们的猜想。

总结一下规律:所有的网址构成都符合这样一个规律:

https://food.hiyd.com/list-数字-html

唯一一个特例是【菜肴】这个分类:虽然它的网址构成和上面的规律是一样的,但是它的url中带有的数字和【其它】分类不是相邻的。

接着我们观察一下每个分类下带有食物记录的页面网址有什么规律。以【谷薯芋、杂豆、主食】这个分类为例,点击翻到第2页的食物记录,我们看看网址又会发生怎样的变化。

img
总结一下我们上面发现的规律,可以得出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()

得到了这份热量表,还怕减肥时不知道吃啥吗?


喜欢的话就点个赞和关注吧~
🤞🤞🤞🤞

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python中的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

弈鸣coding

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

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

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

打赏作者

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

抵扣说明:

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

余额充值