Django-Celery-Flower实现异步和定时爬虫及其监控邮件告警

1 Django中集成celery

python 复制代码
# Django --->python 的web框架
	-web项目--》用浏览器访问
# celery--->分布式异步任务框架
	-定时任务
    -延迟任务
    -异步任务

1.1 安装模块

python 复制代码
#官方文档:https://docs.celeryq.dev/en/stable/django/first-steps-with-django.html

pip install Django==3.2.22
pip install celery
pip install redis     # celery 的中间件借助于redis--》redis模块
pip install eventlet  # 在windows环境下需要安装eventlet包

# 创建django项目:名字叫 django_celery_crawl
# 保证django能运行

1.2 在项目路径下创建 celery.py

python 复制代码
import os
from celery import Celery

# 1 导入django的配置文件---》后续在celery的任务中,就可以直接使用django的orm,缓存。。。
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_celery_crawl.settings')
# 2 实例化得到celery对象
app = Celery('proj')
# 3 celery的配置,使用django 配置文件中的配置--》刚刚写的配置
app.config_from_object('django.conf:settings')

# 4 这句话会去所有app中,自动查找 tasks.py 文件,作为任务文件
app.autodiscover_tasks()

1.3 django 的配置文件 settings.py

python 复制代码
############# celery的配置信息######
#1 Broker配置,使用Redis作为消息中间件
BROKER_URL = 'redis://127.0.0.1:6379/1'
#2 BACKEND配置,使用redis
RESULT_BACKEND = 'redis://127.0.0.1:6379/2'
#3 序列化方案--》json
ACCEPT_CONTENT = ['json']
TASK_SERIALIZER = 'json'
# 结果序列化方案
RESULT_SERIALIZER = 'json'

# 4 任务结果过期时间,秒
TASK_RESULT_EXPIRES = 60 * 60 * 24

#5 时区配置
TIMEZONE = 'Asia/Shanghai'

1.4 以后只需要再不同app中,写tasks.py

python 复制代码
# 注册任务

from celery import shared_task


# 以后使用shared_task 替换掉 app.task

# 任务函数
@shared_task
def add(a, b):
    return a + b

1.5 在项目目录下的 init.py中加入

python 复制代码
from .celery import app as celery_app

__all__ = ('celery_app',)

1.6 启动celery的worker-写一个提交异步任务的视图函数

1.6.1 启动worker

python 复制代码
# 1 启动worker
celery -A django_celery_crawl worker -l debug -P eventlet

1.6.2 写一个视图函数测试

python 复制代码
############### 总路由中 urls.py###############
from django.contrib import admin
from django.urls import path,include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app01/', include('app01.urls')),  # http://127.0.0.1:8000/app01/send/  异步发邮件
]


###############分路由中 urls.py###############
from django.contrib import admin
from django.urls import path
from .views import send_mail_view
urlpatterns = [
    path('send/', send_mail_view),
]
###############视图函数中 views.py###############
from django.shortcuts import render, HttpResponse
from .tasks import send_email
def send_mail_view(request):
    # 取出用户要发送的邮箱
    to_user = request.GET.get('user')
    # 异步发送
    res = send_email.delay(to_user)
    print(res.id)
    return HttpResponse('邮件已发送,id号为%s' % res.id)

1.6.3 在浏览器输入:

python 复制代码
http://127.0.0.1:8000/app01/send/?user=616564099@qq.com

1.7 celery 架构图

2 实现定时任务

python 复制代码
# celery集成到django后,以后定时任务的编写,要写到django的配置文件中

2.1 django的配置文件配置

python 复制代码
from datetime import timedelta
CELERYBEAT_SCHEDULE = {
    'every_5_second': {
        'task': 'app01.tasks.add',
        'schedule': timedelta(seconds=5),
        'args': (33, 44),
    }
}

2.2 重启worker---启动beat

python 复制代码
# 启动worker
celery -A django_celery_crawl worker -l debug -P eventlet
# 启动beat
celery -A django_celery_crawl beat -l debug

3 通过Admin 后台管理添加定时任务

python 复制代码
# 存在问题:
	如果要再新加一个定时任务(爬美女图片)
    我们只能修改代码 :settings.py-->加入代码
    重启worker,重启beat--》才能行
    有些麻烦
    
    
# 通过点点点,就能自动添加定时任务
	-Admin 后台管理添加定时任务

3.1 引入--安装djiango-celery-beat

python 复制代码
pip install django-celery-beat

3.2 在app中注册djiango-celery-beat

python 复制代码
INSTALLED_APPS = [
    ...
    'app01',
    'django_celery_beat' # 注册刚刚第三方模块

]

3.3 配置django的时区

python 复制代码
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False

3.4 在setting中配置调度器

python 复制代码
CELERYBEAT_SCHEDULER = 'django_celery_beat.schedulers.DatabaseScheduler' 
# 只要配了这个,原来celery中的定时任务统一不能用了,需要我们手动配置了

3.5 数据迁移

python 复制代码
#1  django 使用mysql数据库
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'django_celery_crawl', # 去mysql中创建数据库
        'HOST':'127.0.0.1',
        'PORT':3306,
        'USER':'root',
        'PASSWORD':'123' # 自己写自己的密码
    }
}
# 2 安装mysqlclient
pip install mysqlclient

#3  去mysql 中创建 django_celery_crawl 库

#4  django-celery-beat 这个app会产生表--》以后只要咱么在表里添加记录,就能制定定时任务
python manage.py makemigrations
python manage.py migrate

3.6 使用admin后台管理插入数据

python 复制代码
# 咱么如果想向mysql数据表中,插入数据
	-可以使用navicat插入
    -django提供了一个后台管理--》登录到后台管理,可以图形化界面录入数据
    -但是需要个账号登录:命令创建账号
    	python manage.py createsuperuser
    -我创建的是:
    	账号:  admin 
        密码 : 123456

3.7 美化admin

python 复制代码
# 1  simpleui 美化admin
# 2 开源地址
https://gitee.com/tompeppa/simpleui
# 3 文档地址
https://newpanjing.github.io/simpleui_docs/config.html
    
# 4 安装
pip3 install django-simpleui

# 5 app中注册
INSTALLED_APPS = [
    'simpleui',
    ...
]

3.8 手动添加任务

python 复制代码
# 1 启动worker,beat
# 启动worker
celery -A django_celery_crawl worker -l debug -P eventlet
# 启动beat
celery -A django_celery_crawl beat -l debug

# 2 在admin中手动添加

4 通过Admin查看任务运行情况

python 复制代码
# 在控制台监控任务执行情况或者去redis中查看,还不是很方便,最好是能够通过web界面看到任务的执行情况,如有多少任务在执行,有多少任务执行失败了等。

# 这个Celery也是可以做到了,就是将任务执行结果写到数据库中,通过web界面显示出来。

这里要用到django-celery-results插件。

通过插件可以使用Django的orm作为结果存储,这样的好处在于我们可以直接通过django的数据查看到任务状态,同时为可以制定更多的操作

4.1 安装 django-celery-results

python 复制代码
pip install django-celery-results

4.2 app中注册

python 复制代码
INSTALLED_APPS = (
...,
'django_celery_results',
)

4.3 修改配置文件

python 复制代码
# 让之前backend----》结果存储放在redis中----》改到放到数据中---》因为在数据库中的咱们可以通过admin查看

#RESULT_BACKEND = 'redis://127.0.0.1:6379/1'

# 使用使用django orm 作为结果存储
CELERY_RESULT_BACKEND = 'django-db'  #使用django orm 作为结果存储

4.4 迁移数据库

python 复制代码
# 执行迁移命令 
python manage.py makemigrations
python manage.py migrate

4.5 在admin中查看即可

5 通过Flower监控celery运行情况

python 复制代码
如果不想通django的管理界面监控任务的执行,还可以通过Flower插件来进行任务的监控。Flower的界面更加丰富,可以监控的信息更全

Flower 是一个用于监控和管理 Celery 集群的开源 Web 应用程序。它提供有关 Celery  workers 和tasks状态的实时信息

# Flower可以:
1 实时监控celery的Events
    -查看任务进度和历史记录
    -查看任务详细信息(参数、开始时间、运行时间等)

2 远程操作
    -查看workers 状态和统计数据
    -关闭并重新启动workers 实例
    -控制工作池大小和自动缩放设置
    -查看和修改工作实例消耗的队列
    -查看当前正在运行的任务
    -查看计划任务(预计到达时间/倒计时)
    -查看保留和撤销的任务
    -应用时间和速率限制
    -撤销或终止任务

3 Broker 监控
	-查看所有 Celery 队列的统计信息

5.1 修改结果存储为redis

python 复制代码
RESULT_BACKEND = 'redis://127.0.0.1:6379/2'

5.2 安装flower和启动

python 复制代码
#1  安装
pip install flower

# 2 启动
celery -A django_celery_crawl flower --port-5555

#3 浏览器访问:
http://127.0.0.1:5555/
        
# 4 启动 worker,beat,flower
	-一般先启动flower

6 任务执行成功或失败告警(邮件)

python 复制代码
虽然可以通过界面来监控了,但是我们想要得更多,人不可能天天盯着界面看吧,如果能实现任务执行失败或成功就自动发邮件告警就好了。这个Celery当然也是没有问题的

6.1 task.py 中加入

python 复制代码
# 注册任务
import time

from celery import shared_task

### 写个类,继承 Task,写方法
from celery import Task

from django.core.mail import send_mail
from django.conf import settings


class SendEmailTask(Task):
    def on_success(self, retval, task_id, args, kwargs):
        # 任务执行成功,会执行这个
        info = f'爬虫任务成功-- 任务id是:{task_id} , 参数是:{args} , 执行成功 !'
        # 发送邮件--》django中发送邮件
        send_mail('celery监控成功告警', info, settings.EMAIL_HOST_USER, ['616564099@qq.com'])

    def on_failure(self, exc, task_id, args, kwargs, einfo):
        # 任务执行失败,会执行这个
        info = f'爬虫任务失败-- 任务id是:{task_id} , 参数是:{args} , 执行失败,请去flower中查看原因 !'
        # 发送邮件--》django中发送邮件
        send_mail('celery监控爬虫失败告警', info, settings.EMAIL_HOST_USER, ['616564099@qq.com'])

    def on_retry(self, exc, task_id, args, kwargs, einfo):
        # 任务执行重试,会执行这个
        print('重试了!!!')


# 以后使用shared_task 替换掉 app.task

# 任务函数
@shared_task
def add(a, b):
    return a + b


@shared_task
def send_email(to_user):
    time.sleep(2)
    return '发送邮件成功:%s' % to_user


@shared_task(base=SendEmailTask, bind=True)  # 只要执行它成功或失败,都会给 616564099@qq.com发送邮件
def crawl_cnblogs(self):
    print('爬去cnblogs网站技术博客了')
    return True

6.2 setting中 邮箱配置

python 复制代码
#### 发送邮件配置
EMAIL_HOST = 'smtp.qq.com'  # 如果是 163 改成 smtp.163.com
EMAIL_PORT = 465
EMAIL_HOST_USER = ''  # 帐号
EMAIL_HOST_PASSWORD = ''  # 密码
DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
#这样收到的邮件,收件人处就会这样显示
#DEFAULT_FROM_EMAIL = 'lqz<'306334678@qq.com>'
EMAIL_USE_SSL = True   #使用ssl
#EMAIL_USE_TLS = False # 使用tls

6.3 在admin中添加任务执行

python 复制代码
# 1 admin中添加任务,只执行一次
# 2 执行完了 flower可以监控到
# 3 也能收到邮件通知

7 定时爬取技术类文章-邮件通知

python 复制代码
# 1 爬取目标网站:https://www.cnblogs.com/
	-爬取它首页的推荐文章
    	-文章标题
        -文章作者
        -文章url地址
        -文章摘要
    -每隔一天爬一次
    	-可能会重复--》去重
        
# 2 安装
	-pip install requests
    -pip install beautifulsoup4

7.1 models.py 创建表

python 复制代码
from django.db import models


class Article(models.Model):
    title = models.CharField(max_length=64)
    url = models.CharField(max_length=64)
    author = models.CharField(max_length=64)
    desc = models.TextField()

# 迁移
python manage.py makemigrations
python manage.py migrate

# 数据库才有这个表

7.2 在task中写

python 复制代码
# 注册任务
import time

from celery import shared_task

### 写个类,继承 Task,写方法
from celery import Task

from django.core.mail import send_mail
from django.conf import settings


class SendEmailTask(Task):
    def on_success(self, retval, task_id, args, kwargs):
        # 任务执行成功,会执行这个
        info = f'爬虫任务成功-- 任务id是:{task_id} , 参数是:{args} , 执行成功 !'
        # 发送邮件--》django中发送邮件
        send_mail('celery监控成功告警', info, settings.EMAIL_HOST_USER, ['616564099@qq.com'])

    def on_failure(self, exc, task_id, args, kwargs, einfo):
        # 任务执行失败,会执行这个
        info = f'爬虫任务失败-- 任务id是:{task_id} , 参数是:{args} , 执行失败,请去flower中查看原因 !'
        # 发送邮件--》django中发送邮件
        send_mail('celery监控爬虫失败告警', info, settings.EMAIL_HOST_USER, ['616564099@qq.com'])

    def on_retry(self, exc, task_id, args, kwargs, einfo):
        # 任务执行重试,会执行这个
        print('重试了!!!')

###爬取技术博客#####
import requests
from bs4 import BeautifulSoup
from .models import Article
from redis import Redis


@shared_task(base=SendEmailTask, bind=True)  # 只要执行它成功或失败,都会给 616564099@qq.com发送邮件
def crawl_cnblogs(self):
    # 拿到redis链接
    conn = Redis(host='127.0.0.1', port=6379)
    res = requests.get('https://www.cnblogs.com/')
    soup = BeautifulSoup(res.text, 'html.parser')
    # 查找:-文章标题-文章作者-文章url地址-文章摘要
    # 找到所有文章
    article_list = soup.find_all(name='article', class_='post-item')
    # 循环一个个文章
    for article in article_list:
        title = article.find(name='a', class_='post-item-title').text
        url = article.find(name='a', class_='post-item-title').attrs.get('href')
        author = article.find(name='a', class_='post-item-author').span.text
        desc = article.find(name='p', class_='post-item-summary').text.strip()
        print(f'''
        文章名字:{title}
        文章地址:{url}
        文章作者:{author}
        文章摘要:{desc}
        ''')
        # 去重,不能有重复,如果重复了,就不存到数据库中了---》集合可以去重--》redis集合去重-->根据url去重
        # 只要redis的urls的这个集合中,有了这个地址,我们就不存了--》去重了

        res = conn.sadd('urls', url)
        if res:  # 返回1 表示放进集合中了,表示集合中没有,存数据库   返回0,表示集合中有过,不存了
            # 保存到数据库中--》创建一个Article的数据表
            Article.objects.create(title=title, url=url, author=author, desc=desc)

    return True

7.3 以后只需要在后台管理添加定时任务

python 复制代码
# 比如一天爬一次
	-所有服务都启动,不用管了
    

8 定时爬取美女图片-告警案例

python 复制代码
# 1 爬取目标网站:https://pic.netbian.com
	- 爬给定的页面中的图片保存到本地
		https://pic.netbian.com/tupian/33985.html
        https://pic.netbian.com/tupian/32967.html
python 复制代码
### 爬取图片
import os
# 爬取美女图片
@shared_task(base=SendEmailTask, bind=True)
def crawl_photo(self,url):
    res = requests.get(url)
    res.encoding = 'gbk'
    soup = BeautifulSoup(res.text, 'html.parser')
    ul = soup.find('ul', class_='clearfix')
    img_list = ul.find_all(name='img', src=True)
    for img in img_list:
        try:
            url = img.attrs.get('src')
            if not url.startswith('http'):
                url = 'https://pic.netbian.com' + url
            print(url)
            res1 = requests.get(url)
            name = url.split('-')[-1]
            with open(os.path.join(settings.BASE_DIR,'img',name), 'wb') as f:
                for line in res1.iter_content():
                    f.write(line)
        except Exception as e:
            continue
复制代码
相关推荐
lmxnsI6 分钟前
后端项目打包发布
ide·python·pycharm
搬砖的果果6 分钟前
爬虫代理服务要怎么挑选?
网络·爬虫·网络协议·tcp/ip
HsuHeinrich35 分钟前
流程图(二)利用python绘制网络图
python·数据可视化
爱学习的Allan1 小时前
使用 pyreqs 快速创建 requirements.txt & PyCharm 中 UnicodeDecodeError 问题
ide·python·pycharm·pip
HackKong1 小时前
高校网络安全_网络安全之道
java·网络·c++·python·学习·web安全·黑客技术
yangjiwei02072 小时前
数据结构-排序
数据结构·python
秋天下着雨2 小时前
apifox调用jar程序
java·python·jar
bs_1012 小时前
【保姆式】python调用api通过机器人发送文件到飞书指定群聊
python·机器人·飞书
Redamancy_Xun2 小时前
软件老化分析
python·程序人生·安全威胁分析·可信计算技术·安全架构
geovindu2 小时前
python: Oracle Stored Procedure query table
数据库·python·mysql·postgresql·oracle·sqlserver·mssql