来源:机器之心






st.write(x, ‘squared is’, x * x)



-
每次用户交互均需要从头运行全部脚本。
-
Streamlit 根据 widget 状态为每个变量分配最新值。
-
缓存保证 Streamlit 重用数据和计算。












– EOF –
转自:https://mp.weixin.qq.com/s/tNWTzk5WkV0ZRNa3zeph-w
分享个人经验,保留阅读记录,做时间的朋友
每次用户交互均需要从头运行全部脚本。
Streamlit 根据 widget 状态为每个变量分配最新值。
缓存保证 Streamlit 重用数据和计算。
– EOF –
转自:https://mp.weixin.qq.com/s/tNWTzk5WkV0ZRNa3zeph-w
选自RealPython,作者:Jahongir Rahmonov,参与:魔王
机器之心编译
PyCharm 是一种 Python IDE,可以帮助程序员节约时间,提高生产效率。那么具体如何使用呢?本文从 PyCharm 安装到插件、外部工具、专业版功能等进行了一一介绍,希望能够帮助到大家。
PyCharm 安装
在 PyCharm 中写代码
在 PyCharm 中运行代码
在 PyCharm 中进行代码 debug 和测试
在 PyCharm 中编辑已有项目
在 PyCharm 中搜索和导航
在 PyCharm 中使用版本控制
在 PyCharm 中使用插件和外部工具
使用 PyCharm Professional 功能,如 Django 支持和科学模式
在 Mac 系统中使用快捷键 Ctrl+Shift+R,在 Windows 或 Linux 系统中,使用快捷键 Ctrl+Shift+F10。
右键单击背景,从菜单中选择「Run 『guess_game』」。
由于该程序具备__main__ 从句,你可以点击__main__ 从句左侧的绿色小箭头,选择「Run 『guess_game』」。
在 Mac 系统中使用 Ctrl+Shift+D 键,在 Windows 或 Linux 系统中使用 Shift+Alt+F9 键。
右键单击背景,选择「Debug 『guess_game』」。
点击__main__从句左侧的绿色小箭头,选择「Debug 『guess_game』」。
注意当前行被蓝色高亮显示。
Debug 窗口显示 random_int 及其值。记录该数字。(上图中该数字为 85。)
点击 F8 执行当前代码行,并执行到下一行代码。如有必要,你也可以使用 F7 跳转到当前行内的函数。随着你继续执行语句,变量的变化将自动呈现在 Debugger 窗口。
注意 Debugger 标签右侧有一个 Console 标签。Console 标签和 Debugger 标签相互独立。你可以在 Console 中与程序进行交互,在 Debugger 中执行 debug 动作。
转向 Console 标签,进入猜测过程。
键入左侧 Debugger 标签中显示的数字,点击 Enter 键。
转回 Debugger 标签。
再次点击 F8,计算 if 语句。注意现在你在第 14 行。为什么不是第 11 行呢?因为第 10 行的 if 语句被计算为 False。那么为什么当你键入数字后它算出来为 False 了呢?
仔细看第 10 行,注意我们在对比 user_guess 和一个错误的项。我们应该对比用户猜测的数字和 random_int,但此处我们对比的是 randint(从 random 包导入的函数)。
将 randint 更改为 random_int,按照同样的步骤重新开始 debug。你会发现,这一次到达的是第 11 行,第 10 行算出来为 True:
打开 Settings/Preferences → Tools → Python Integrated Tools 设置对话框。
在默认测试运行器字段中选择 pytest。
点击 OK 保存该设置。
在 Mac 系统中使用 Shift+Cmd+T 键,在 Windows 或 Linux 系统中使用 Ctrl+Shift+T。
右键单击该类的背景,选择「Go To and Test」。
在主菜单中吗,选择 Navigate → Test。
在 Mac 系统中使用 Ctrl+R 键,在 Windows 或 Linux 系统中使用 Shift+F10 键。
右键单击背景,选择「Run 『Unittests for test_calculator.py』」。
点击测试类名称左侧的绿色小箭头,选择「Run 『Unittests for test_calculator.py』」。
在主菜单中点击 File → Open。
在欢迎页面点击 Open。
在当前文件中搜索代码段:在 Mac 系统中使用 Cmd+F 键,在 Windows 或 Linux 系统中使用 Ctrl+F 键。
在整个项目中搜索代码段:在 Mac 系统中使用 Cmd+Shift+F 键,在 Windows 或 Linux 系统中使用 Ctrl+Shift+F 键。
搜索类:在 Mac 系统中使用 Cmd+O 键,在 Windows 或 Linux 系统中使用 Ctrl+N 键。
搜索文件:在 Mac 系统中使用 Cmd+Shift+O 键,在 Windows 或 Linux 系统中使用 Ctrl+Shift+N 键。
如果你不知道要搜索的是文件、类还是代码段,则搜索全部:按两次 Shift 键。
前往变量的声明:在 Mac 系统中使用 Cmd 键,在 Windows 或 Linux 系统中使用 Ctrl 键,然后单击变量。
寻找类、方法或文件的用法:使用 Alt+F7 键。
查看近期更改:使用 Shift+Alt+C 键,或者在主菜单中点击 View → Recent Changes。
查看近期文件:在 Mac 系统中使用 Cmd+E 键,在 Windows 或 Linux 系统中使用 Ctrl+E 键,或者在主菜单中点击 View → Recent Files。
多次跳转后在导航历史中前进和后退:在 Mac 系统中使用 Cmd+[ / Cmd+] 键,在 Windows 或 Linux 系统中使用 Ctrl+Alt+Left / Ctrl+Alt+Right 键。
选择要提交的文件
写下提交信息
在提交前执行各项检查
查看更改
点击右下角 Commit 按钮旁边的箭头,选择 Commit and Push…,从而一次性完成提交和 push。
添加多语言和多框架支持
使用快捷键提示(shortcut hint)、文件监视器(file watcher)等提升你的生产效率
利用代码练习,帮助你学习新的编程语言
在 Mac 系统中打开 Preferences,在 Windows 或 Linux 系统中打开 Settings。
选择 Languages and Frameworks。
选择 Django。
检查复选框 Enable Django support。
应用更改。
语法和错误高亮显示
代码补全
导航
block 名称补全
自定义标签和过滤器补全
标签和过滤器的快速文档
模板 debug 能力
原文地址:https://realpython.com/pycharm-guide/
转自:https://mp.weixin.qq.com/s/DEhDkpMOcNeq1PR5jA7TRw
作者:大江狗
来源:Python Web与Django开发
大家好,我是猫哥。今天分享一篇文章,作者利用Django 3.0 +Redis 3.4 +Celery 4.4开发了个小应用。应用不复杂,但知识点很多,非常适合新手练手。项目需求如下:
创建两个页面,一个用于创建页面,一个用于动态展示页面详情,并提供静态HMTL文件链接
一个页面创建完成后,使用Celery异步执行生成静态HTML文件的任务
使用redis作为Celery的Broker
使用flower监控Celery异步任务执行情况
项目完成后演示见下面动画。本项目的GitHub源码地址在最后,请耐心阅读。
国内用户使用pip安装python包特别慢,这主要是应为国内连接国外网络不稳定。为加速python包的安装,首先将pip安装源设置为国内的镜像,比如阿里云提供的镜像。
linux系统修改 ~/.pip/pip.conf (没有就创建一个), 内容如下:
[global]index-url = https://mirrors.aliyun.com/pypi/simple/
windows系统直接在user目录中创建一个pip目录,如:C:Usersxxpip,新建文件pip.ini,内容如下:
[global]index-url = http://mirrors.aliyun.com/pypi/simple/
使用pip命令安装Django.
pip install django==3.0.4 # 安装Django,所用版本为3.0.4
使用django-admin startproject myproject创立一个名为myproject的项目
django-admin startproject myproject
整个项目完整目录机构如下所示, 项目名为myproject, staticpage为app名。
项目中我们需要使用redis做Celery的中间人(Broker), 所以需要先安装redis数据库。redis网上教程很多,这里就简要带过了。
Windows下载地址:https://github.com/MSOpenTech/redis/releases
Linux下安装(Ubuntu系统):$ sudo apt-get install redis-server
本项目还需要安装如下依赖包,你可以使用pip命令逐一安装。
pip install redis==3.4.1
pip install celery==4.4.2
pip install eventlet # celery 4.0+版本以后不支持在windows运行,还需额外安装eventlet库
你还可以myproject目录下新建requirements.txt
加入所依赖的python包及版本,然后使用pip install -r requirements.txt
命令安装所有依赖。本教程所使用的django, redis和celery均为最新版本。
django==3.0.5
redis==3.4.1
celery==4.4.2
eventlet # for windows only
修改settings.py
新增celery有关的配置。celery默认也是有自己的配置文件的,名为celeryconfig.py
, 但由于管理多个配置文件很麻烦,我们把celery的配置参数也写在django的配置文件里。
# 配置celery时区,默认时UTC。
if USE_TZ:
timezone = TIME_ZONE
# celery配置redis作为broker。redis有16个数据库,编号0~15,这里使用第1个。
broker_url = 'redis://127.0.0.1:6379/0'
# 设置存储结果的后台
result_backend = 'redis://127.0.0.1:6379/0'
# 可接受的内容格式
accept_content = ["json"]
# 任务序列化数据格式
task_serializer = "json"
# 结果序列化数据格式
result_serializer = "json"
# 可选参数:给某个任务限流
# task_annotations = {'tasks.my_task': {'rate_limit': '10/s'}}
# 可选参数:给任务设置超时时间。超时立即中止worker
# task_time_limit = 10 * 60
# 可选参数:给任务设置软超时时间,超时抛出Exception
# task_soft_time_limit = 10 * 60
# 可选参数:如果使用django_celery_beat进行定时任务
# beat_scheduler = "django_celery_beat.schedulers:DatabaseScheduler"
# 更多选项见
# https://docs.celeryproject.org/en/stable/userguide/configuration.html
在settings.py
同级目录下新建celery.py
,添加如下内容:
# coding:utf-8
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery
# 指定Django默认配置文件模块
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
# 为我们的项目myproject创建一个Celery实例。这里不指定broker容易出现错误。
app = Celery('myproject', broker='redis://127.0.0.1:6379/0')
# 这里指定从django的settings.py里读取celery配置
app.config_from_object('django.conf:settings')
# 自动从所有已注册的django app中加载任务
app.autodiscover_tasks()
# 用于测试的异步任务
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
打开settings.py
同级目录下的__init__.py
,添加如下内容, 确保项目启动时即加载Celery实例
# coding:utf-8
from __future__ import absolute_import, unicode_literals
# 引入celery实例对象
from .celery import app as celery_app
__all__ = ('celery_app',)
网上很多django redis + celery的教程比较老了, 坑很多。比如新版原生的Celery已经支持Django了,不需要再借助什么django-celery和celery-with-redis这种第三方库了, 配置参数名也由大写变成了小写,无需再加CELERY前缀。另外当你通过app = Celery('myproject')
创建Celery实例时如果不指定Broker,很容易出现[ERROR/MainProcess] consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//: [Errno 111] Connection refused这个错误。
在Django中编写和执行自己的异步任务前,一定要先测试redis和celery是否安装好并配置成功。
首先你要启动redis服务。windows进入redis所在目录,使用redis-server.exe
启动redis。Linux下使用./redis-server redis.conf
启动,也可修改redis.conf将daemonize设置为yes, 确保守护进程开启。
启动redis服务后,你要先运行python manage.py runserver
命令启动Django服务器(无需创建任何app),然后再打开一个终端terminal窗口输入celery命令,启动worker。
# Linux下测试
Celery -A myproject worker -l info
# Windows下测试
Celery -A myproject worker -l info -P eventlet
如果你能看到[tasks]下所列异步任务清单如debug_task,以及最后一句celery@xxxx ready, 说明你的redis和celery都配置好了,可以开始正式工作了。
-------------- celery@DESKTOP-H3IHAKQ v4.4.2 (cliffs)
--- ***** -----
-- ******* ---- Windows-10-10.0.18362-SP0 2020-04-24 22:02:38
- *** --- * ---
- ** ---------- [config]
- ** ---------- .> app: myproject:0x456d1f0
- ** ---------- .> transport: redis://127.0.0.1:6379/0
- ** ---------- .> results: redis://localhost:6379/0
- *** --- * --- .> concurrency: 4 (eventlet)
-- ******* ---- .> task events: OFF (enable -E to monitor tasks in this worker)
--- ***** -----
-------------- [queues]
.> celery exchange=celery(direct) key=celery
[tasks]
. myproject.celery.debug_task
[2020-04-24 22:02:38,484: INFO/MainProcess] Connected to redis://127.0.0.1:6379/0
[2020-04-24 22:02:38,500: INFO/MainProcess] mingle: searching for neighbors
[2020-04-24 22:02:39,544: INFO/MainProcess] mingle: all alone
[2020-04-24 22:02:39,572: INFO/MainProcess] pidbox: Connected to redis://127.0.0.1:6379/0.
[2020-04-24 22:02:39,578: WARNING/MainProcess] c:usersmissenkapycharmprojectsdjango-static-html-generatorvenvlibsite-packagesceleryfixupsdjango.py:203: UserWarning: Using sett
ings.DEBUG leads to a memory
leak, never use this setting in production environments!
leak, never use this setting in production environments!''')
[2020-04-24 22:02:39,579: INFO/MainProcess] celery@DESKTOP-H3IHAKQ ready.
cd进入myproject
文件夹,使用python manage.py startapp staticpage
创建一个名为staticpage
的app。我们将创建一个简单的Page
模型,并编写两个视图(对应两个URLs),一个用于添加页面,一个用于展示页面详情。staticpage目录下我们将要编辑或创建5个.py文件,分别是models.py, urls.py, views.py, forms.py和tasks.py,其中前4个都是标准的Django项目文件,内容如下所示。最后一个tasks.py用于存放我们自己编写的异步任务,稍后我会详细讲解。
# staticpage/models.py
from django.db import models
import os
from django.conf import settings
class Page(models.Model):
title = models.CharField(max_length=100, verbose_name="标题")
body = models.TextField(verbose_name="正文")
def __int__(self):
return self.title
# 静态文件URL地址,比如/media/html/page_8.html
def get_static_page_url(self):
return os.path.join(settings.MEDIA_URL, 'html', 'page_{}.html'.format(self.id))
# staticpage/urls.py
from django.urls import path, re_path
from . import views
urlpatterns = [
# Create a page 创建页面
path('', views.page_create, name='page_create'),
# Page detail 展示页面详情。动态URL地址为/page/8/
re_path(r'^page/(?P<pk>d+)/
从page_create
视图函数中你可以看到我们在一个page实例存到数据库后调用了generate_static_page
函数在后台完成静态HTML页面的生成。如果我们不使用异步的化,我们要等静态HTML文件完全生成后才能跳转到页面详情页面, 这有可能要等好几秒。generate_static_page
就是我们自定义的异步任务,代码如下所示。Celery可以自动发现每个Django app下的异步任务,不用担心。
# staticpage/tasks.py
import os, time
from django.template.loader import render_to_string
from django.conf import settings
from celery import shared_task
@shared_task
def generate_static_page(page_id, page_title, page_body):
# 模拟耗时任务,比如写入文件或发送邮件等操作。
time.sleep(5)
# 获取传递的参数
page = {'title': page_title, 'body': page_body}
context = {'page': page, }
# 渲染模板,生成字符串
content = render_to_string('staticpage/template.html', context)
# 定义生成静态文件所属目录,位于media文件夹下名为html的子文件夹里。如目录不存在,则创建。
directory = os.path.join(settings.MEDIA_ROOT, "html")
if not os.path.exists(directory):
os.makedirs(directory)
# 拼接目标写入文件地址
static_html_path = os.path.join(directory, 'page_{}.html'.format(page_id))
# 将渲染过的字符串写入目标文件
with open(static_html_path, 'w', encoding="utf-8") as f:
f.write(content)
本例中我们生成的静态HTML文件位于media文件夹下的html子文件夹里,这样做有两个好处:
与Django的静态文件存储规范保持一致:用户产生的静态文件都放在media文件下,网站本身所依赖的静态文件都放于static文件夹下。
把所有产生的静态文件放在一个目录里与动态文件相分开,利于后续通过nginx部署。
本项目中还用到了3个模板,分别是base.html, detail.html和template.html。base.html和detail.html是没有任何样式的, 仅用于动态显示内容,template.html是用来生成静态文件的模板,是带样式的,这样你就可以很快区分动态页面和静态页面。由于我们后台生成静态文件至少需要5秒钟,我们在detail.html用了点javascript实现等5秒倒计时完成后显示生成的静态HTML文件地址。
3个模板均位于staticpage/templates/staticpage/
文件夹下,代码如下所示:
# base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加页面</title>
</head>
<body>
<h2>添加页面</h2>
<form name="myform" method="POST" action=".">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Submit</button>
</form>
</body>
</html>
# detail.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ page.title }}</title>
</head>
<body>
<h2>{{ page.title }}</h2>
<p>{{ page.body }}</p>
<p>倒计时: <span id="Time">5</span></p>
<p id="static_url" style="display:none;"> <small><a href='{{ page.get_static_page_url }}'>跳转到静态文件</a></small></p>
<script>
//使用匿名函数方法
function countDown(){
var time = document.getElementById("Time");
var p = document.getElementById("static_url");
//获取到id为time标签中的内容,现进行判断
if(time.innerHTML == 0){
//等于0时, 显示静态HTML文件URL
p.style.display = "block";
}else{
time.innerHTML = time.innerHTML-1;
}
}
//1000毫秒调用一次
window.setInterval("countDown()",1000);
</script>
</body>
</html>
# template.html 生成静态文件模板
{% load static %}
<html lang="en">
<head>
<title>{% block title %}Django文档管理{% endblock %} </title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
</head>
<body>
<nav class="navbar navbar-inverse navbar-static-top bs-docs-nav">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" >
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#"><strong>Django + Celery + Redis异步生成静态文件</strong></a>
</div>
<div class="collapse navbar-collapse" id="myNavbar">
<ul class="nav navbar-nav navbar-right">
{% if request.user.is_authenticated %}
<li class="dropdown">
<a class="dropdown-toggle btn-green" href="#"><span class="glyphicon glyphicon-user"></span> {{ request.user.username }} <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="#">My Account</a></li>
<li><a href="#">Logout</a></li>
</ul>
</li>
{% else %}
<li class="dropdown"><a class="dropdown-toggle btn-green" href="#"><span class="glyphicon glyphicon-user"></span> Sign Up</a></li>
<li class="dropdown"><a class="dropdown-toggle" href="#" ><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
{% endif %}
</ul>
</div>
</div>
</nav>
<!-- Page content of course! -->
<main id="section1" class="container-fluid">
<div class="container">
<div class="row">
<div class="col-sm-3 col-hide">
<ul>
<li> <a href="{% url 'page_create' %}">添加页面</a> </li>
</ul>
</div>
<div class="col-sm-9">
<h3>{{ page.title }}</h3>
{{ page.body }}
</div>
</div>
</div>
</main>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</body>
</html>
# 修改myproject/settings.py,添加如下内容
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'staticpage',
]
# 设置STATIC_URL和STATIC_ROOT
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
# 设置MEDIA_ROOT和MEDIA_URL
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
# 修改myproject/urls.py,添加如下内容
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include("staticpage.urls")),
]
# Django自带服务器默认不支持静态文件,需加入这两行。
if settings.DEBUG:
urlpatterns = urlpatterns + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns = urlpatterns + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
如果一切顺利,连续使用如下命令, 即可启动Django测试服务器。打开http://127.0.0.1:8000/即可看到我们项目开头的动画啦。注意:请确保redis和celery已同时开启。
python manage.py makemigrations
python manage.py migrate
python manage.py runserver
如果你要监控异步任务的运行状态(比如是否成功,是否有返回结果), 还可以安装flower这个Celery监控工具。
pip install flower
安装好后,你有如下两种方式启动服务器。启动服务器后,打开http://localhost:5555即可查看监控情况。
# 从terminal终端启动, proj为项目名
$ flower -A proj --port=5555
# 从celery启动
$ celery flower -A proj --address=127.0.0.1 --port=5555
监控异步任务还是很重要的,强烈建议安装flower。比如下图中有些任务就失败了。
https://github.com/shiyunbo/django-static-page-generator-celery-redis
送书活动
本书共13章,主要内容涵盖Python语法及数据分析方法。章主要介绍数据分析的概念,使读者有一个大致的印象,并简单介绍本书频繁使用的Python的5个第三方库。第2章主要做一些准备工作,手把手带读者搭建Python环境,包括Python 3.7.6的安装和pip的安装。第3章介绍Python编程基础。第4章到第7章介绍使用Python进行简单数据分析的基础库,包括NumPy、Pandas和Matplotlib库,并介绍使用正则表达式处理数据的方法。第8章到3章属于进阶内容,但也是Python数据分析的基础,结合机器学习介绍一些常见的用于数据分析的机器学习算法及常用的数学模型。
赠书规则
赠书本数:本次共包邮送书 3 本
, views.page_detail, name=’page_detail’),
]
# staticpage/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.urls import reverse
from .forms import PageForm
from .models import Page
from .tasks import generate_static_page
def page_create(request):
if request.method == ‘POST’:
form = PageForm(request.POST)
if form.is_valid():
page = form.save()
generate_static_page.delay(page.id, page.title, page.body)
return redirect(reverse(‘page_detail’, args=[str(page.pk)]))
else:
form = PageForm()
return render(request, ‘staticpage/base.html’, {‘form’: form})
def page_detail(request, pk):
page = get_object_or_404(Page, id=pk)
return render(request, ‘staticpage/detail.html’, {‘page’: page})
# staticpage/forms.py
from django import forms
from .models import Page
class PageForm(forms.ModelForm):
class Meta:
model = Page
exclude = ()
从page_create
视图函数中你可以看到我们在一个page实例存到数据库后调用了generate_static_page
函数在后台完成静态HTML页面的生成。如果我们不使用异步的化,我们要等静态HTML文件完全生成后才能跳转到页面详情页面, 这有可能要等好几秒。generate_static_page
就是我们自定义的异步任务,代码如下所示。Celery可以自动发现每个Django app下的异步任务,不用担心。
本例中我们生成的静态HTML文件位于media文件夹下的html子文件夹里,这样做有两个好处:
与Django的静态文件存储规范保持一致:用户产生的静态文件都放在media文件下,网站本身所依赖的静态文件都放于static文件夹下。
把所有产生的静态文件放在一个目录里与动态文件相分开,利于后续通过nginx部署。
本项目中还用到了3个模板,分别是base.html, detail.html和template.html。base.html和detail.html是没有任何样式的, 仅用于动态显示内容,template.html是用来生成静态文件的模板,是带样式的,这样你就可以很快区分动态页面和静态页面。由于我们后台生成静态文件至少需要5秒钟,我们在detail.html用了点javascript实现等5秒倒计时完成后显示生成的静态HTML文件地址。
3个模板均位于staticpage/templates/staticpage/
文件夹下,代码如下所示:
如果一切顺利,连续使用如下命令, 即可启动Django测试服务器。打开http://127.0.0.1:8000/即可看到我们项目开头的动画啦。注意:请确保redis和celery已同时开启。
如果你要监控异步任务的运行状态(比如是否成功,是否有返回结果), 还可以安装flower这个Celery监控工具。
安装好后,你有如下两种方式启动服务器。启动服务器后,打开http://localhost:5555即可查看监控情况。
监控异步任务还是很重要的,强烈建议安装flower。比如下图中有些任务就失败了。
https://github.com/shiyunbo/django-static-page-generator-celery-redis
转自:https://mp.weixin.qq.com/s/krlB0WdHpW1GSJhl2SinKw
为什么呢?与谷歌、百度等搜索引擎爬取网页信息不同,Shodan 爬取的是互联网上所有设备的 IP 地址及其端口号。
而随着智能家电的普及,家家户户都有许多电器连接到互联网,这些设备存在被入侵的可能性,这是十分危险的。
说了这么多,给大家体验下 shodan,让你们有更切身的理解。打开 shodan.io,在搜索框输入 Hikvision-Webs
:
你会搜素到这个品牌的摄像头设备遍及全球的IP及其暴露的端口号:
可以看到,这台机器暴露了 17、80、111、995、3128、5000、6000、20547 端口,黑客可以根据这些端口进行针对性的攻击。
不过也不需要过于担心,如果你的服务不存在漏洞,一般是无法攻入的。但有些端口号会暴露摄像头的 web 管理端,如下:
那么黑客可能可以用暴力破解的方式,强行进入摄像头后台管理端,获取到实时的录像。
谨记这会侵犯别人的隐私权,是违法的行为,我们是遵纪守法的好公民所以知道它的原理和危害就足够。我们的目的是运用技术保护好个人隐私,如非必要不将摄像头接入互联网,一定要接入的话,不能使用容易被破解的弱口令。
Shodan Web 端非常好用,但如果我们有从 Python 搜索的需求怎么办?
没关系,Shodan 官方也提供了 Python SDK 包,下面就来讲讲这个 SDK 包的使用。
开始之前,你要确保 Python 和 pip 已经成功安装在电脑上。
(可选1) 如果你用 Python 的目的是数据分析,可以直接安装 Anaconda,它内置了 Python 和 pip .
(可选2) 此外,推荐大家用 VSCode 编辑器,它有许多的优点。
请选择以下任一种方式输入命令安装依赖:
1. Windows 环境 打开 Cmd (开始-运行- CMD)。
2. MacOS 环境 打开 Terminal (command + 空格输入 Terminal)。
3. 如果你用的是 VSCode 编辑器 或 Pycharm,可以直接使用界面下方的 Terminal.
pip install shodan
使用 Shodan 必须注册账号,注册网址:https://account.shodan.io/register
输入完相关信息,点击 CREATE 会跳转到个人账户页:
此时 API Key 会显示你的API秘钥,请记录这个秘钥,后续会使用到这个秘钥去请求接口。
Shodan 本质上就是一个搜索引擎,你只需要输入搜索的关键词:
# 公众号:Python 实用宝典
# 2021-05-04
from shodan import Shodan
api = Shodan('你的API KEY')
def search_shodan(keyword):
# 调用搜索接口
result = api.search(keyword)
# 显示所有IP
for service in result['matches']:
print(service['ip_str'])
search_shodan("Hikvision-Webs")
结果如下:
可惜的是,普通 API 只能像这样搜索关键字,无法使用过滤条件如: Hikvision-Webs country:"US"
搜索美国境内的所有 Hikvision 网站管理端。
如果你想要使用过滤条件,Shodan 需要你升级API权限:
挺贵的,不过还好是一次性支付,永久使用。
Shodan 的用处当然不仅仅是在黑客攻防中,它还能用于统计。如果你想要了解哪些国家的使用这款摄像头的数量最多,可以使用 Facets 特性。
# 公众号:Python 实用宝典
# 2021-05-04
from shodan import Shodan
api = Shodan('你的API KEY')
def try_facets(query):
FACETS = [
'org',
'domain',
'port',
'asn',
('country', 3),
]
FACET_TITLES = {
'org': 'Top 5 Organizations',
'domain': 'Top 5 Domains',
'port': 'Top 5 Ports',
'asn': 'Top 5 Autonomous Systems',
'country': 'Top 3 Countries',
}
try:
# 使用 count() 方法可以不需要升级API,且比 search 方法更快。
result = api.count(query, facets=FACETS)
print('Shodan Summary Information')
print('Query: %s' % query)
print('Total Results: %sn' % result['total'])
# 显示每个要素的摘要
for facet in result['facets']:
print(FACET_TITLES[facet])
for term in result['facets'][facet]:
print('%s: %s' % (term['value'], term['count']))
except Exception as e:
print('Error: %s' % e)
try_facets("Hikvision-Webs")
得到结果如下:
从 Top 3 Countries 中可以看到,这款摄像头使用数量排名前三的国家分别是:美国、日本和德国。
没想到吧,Shodan 居然还能用于产品分析。同样地原理,如果你把关键词改为 apache
,你可以知道目前哪些国家使用apache服务器数量最多,最普遍被使用的版本号是什么。
简而言之,Shodan 是一个非常强大的搜索引擎,它在好人手里,能被发挥出巨大的潜能。如果 Shodan 落入坏人之手的话,那真是一个可怕的东西。
为了避免受到不必要的攻击,请大家及时检查所有联网设备的管理端的密码,如果有使用默认密码及弱口令,立即进行密码的更改,以保证服务的安全。
– EOF –
转自:https://mp.weixin.qq.com/s/wQjXtZCj-uY3L63husLDTg
在很多情况下,我们会有把 Python 对象进行序列化或反序列化的需求,比如开发 REST API,比如一些面向对象化的数据加载和保存,都会应用到这个功能。
这里看一个最基本的例子,这里给到一个 User 的 Class 定义,再给到一个 data 数据,像这样:
class User(object):
def __init__(self, name, age):
self.name = name
self.age = age
data = [{
'name': 'Germey',
'age': 23
}, {
'name': 'Mike',
'age': 20
}]
现在我要把这个 data 快速转成 User 组成的数组,变成这样:
[User(name='Germey', age=23), User(name='Mike', age=20)]
你会怎么来实现?
或者我有了上面的列表内容,想要转成一个 JSON 字符串,变成这样:
[{"name": "Germey", "age": 23}, {"name": "Mike", "age": 20}]
你又会怎么操作呢?
另外如果 JSON 数据里面有各种各样的脏数据,你需要在初始化时验证这些字段是否合法,另外 User 这个对象里面 name、age 的数据类型不同,如何针对不同的数据类型进行针对性的类型转换,这个你有更好的实现方案吗?
之前我介绍过 attrs 和 cattrs 这两个库,它们二者的组合可以非常方便地实现对象的序列化和反序列化。
譬如这样:
from attr import attrs, attrib
from cattr import structure, unstructure
@attrs
class User(object):
name = attrib()
age = attrib()
data = {
'name': 'Germey',
'age': 23
}
user = structure(data, User)
print('user', user)
json = unstructure(user)
print('json', json)
运行结果:
user User(name='Germey', age=23)
json {'name': 'Germey', 'age': 23}
好,这里我们通过 attrs 和 cattrs 这两个库来实现了单个对象的转换。
首先我们要肯定一下 attrs 这个库,它可以极大地简化 Python 类的定义,同时每个字段可以定义多种数据类型。
但 cattrs 这个库就相对弱一些了,如果把 data 换成数组,用 cattrs 还是不怎么好转换的,另外它的 structure 和 unstructure 在某些情景下容错能力较差,所以对于上面的需求,用这两个库搭配起来并不是一个最优的解决方案。
另外数据的校验也是一个问题,attrs 虽然提供了 validator 的参数,但对于多种类型的数据处理的支持并没有那么强大。
所以,我们想要寻求一个更优的解决方案。
这里推荐一个库,叫做 marshmallow,它是专门用来支持 Python 对象和原生数据相互转换的库,如实现 object -> dict,objects -> list, string -> dict, string -> list 等的转换功能,另外它还提供了非常丰富的数据类型转换和校验 API,帮助我们快速实现数据的转换。
要使用 marshmallow 这个库,需要先安装下:
pip3 install marshmallow
好了之后,我们在之前的基础上定义一个 Schema,如下:
class UserSchema(Schema):
name = fields.Str()
age = fields.Integer()
@post_load
def make(self, data, **kwargs):
return User(**data)
还是之前的数据:
data = [{
'name': 'Germey',
'age': 23
}, {
'name': 'Mike',
'age': 20
}]
这时候我们只需要调用 Schema 的 load 事件就好了:
schema = UserSchema()
users = schema.load(data, many=True)
print(users)
输出结果如下:
[User(name='Germey', age=23), User(name='Mike', age=20)]
这样,我们非常轻松地完成了 JSON 到 User List 的转换。
有人说,如果是单个数据怎么办呢,只需要把 load 方法的 many 参数去掉即可:
data = {
'name': 'Germey',
'age': 23
}
schema = UserSchema()
user = schema.load(data)
print(user)
输出结果:
User(name='Germey', age=23)
当然,这仅仅是一个反序列化操作,我们还可以正向进行序列化,以及使用各种各样的验证条件。
下面我们再来看看吧。
上面的例子我们实现了序列化操作,输出了 users 为:
[User(name='Germey', age=23), User(name='Mike', age=20)]
有了这个数据,我们也能轻松实现序列化操作。
序列化操作,使用 dump 方法即可
result = schema.dump(users, many=True)
print('result', result)
运行结果如下:
result [{'age': 23, 'name': 'Germey'}, {'age': 20, 'name': 'Mike'}]
由于是 List,所以 dump 方法需要加一个参数 many 为 True。
当然对于单个对象,直接使用 dump 同样是可以的:
result = schema.dump(user)
print('result', result)
运行结果如下:
result {'name': 'Germey', 'age': 23}
这样的话,单个、多个对象的序列化也不再是难事。
经过上面的操作,我们完成了 object 到 dict 或 list 的转换,即:
object <-> dict
objects <-> list
当然,上面的功能其实并不足以让你觉得 marshmallow 有多么了不起,其实就是一个对象到基本数据的转换嘛。但肯定不止这些,marshmallow 还提供了更加强大啊功能,比如说验证,Validation。
比如这里我们将 age 这个字段设置为 hello,它无法被转换成数值类型,所以肯定会报错,样例如下:
data = {
'name': 'Germey',
'age': 'hello'
}
from marshmallow import ValidationError
try:
schema = UserSchema()
user, errors = schema.load(data)
print(user, errors)
except ValidationError as e:
print('e.message', e.messages)
print('e.valid_data', e.valid_data)
这里如果加载报错,我们可以直接拿到 Error 的 messages 和 valid_data 对象,它包含了错误的信息和正确的字段结果,运行结果如下:
e.message {'age': ['Not a valid integer.']}
e.valid_data {'name': 'Germey'}
因此,比如我们想要开发一个功能,比如用户注册,表单信息就是提交过来的 data,我们只需要过一遍 Validation,就可以轻松得知哪些数据符合要求,哪些不符合要求,接着再进一步进行处理。
当然验证功能肯定不止这一些,我们再来感受一下另一个示例:
from pprint import pprint
from marshmallow import Schema, fields, validate, ValidationError
class UserSchema(Schema):
name = fields.Str(validate=validate.Length(min=1))
permission = fields.Str(validate=validate.OneOf(['read', 'write', 'admin']))
age = fields.Int(validate=validate.Range(min=18, max=40))
in_data = {'name': '', 'permission': 'invalid', 'age': 71}
try:
UserSchema().load(in_data)
except ValidationError as err:
pprint(err.messages)
比如这里的 validate 字段,我们分别校验了 name、permission、age 三个字段,校验方式各不相同。
如 name 我们要判断其最小值为 1,则使用了 Length 对象。permission 必须要是几个字符串之一,这里又使用了 OneOf 对象,age 又必须是介于某个范围之间,这里就使用了 Range 对象。
下面我们故意传入一些错误的数据,看下运行结果:
{'age': ['Must be greater than or equal to 18 and less than or equal to 40.'],
'name': ['Shorter than minimum length 1.'],
'permission': ['Must be one of: read, write, admin.']}
可以看到,这里也返回了数据验证的结果,对于不符合条件的字段,一一进行说明。
另外我们也可以自定义验证方法:
from marshmallow import Schema, fields, ValidationError
def validate_quantity(n):
if n < 0:
raise ValidationError('Quantity must be greater than 0.')
if n > 30:
raise ValidationError('Quantity must not be greater than 30.')
class ItemSchema(Schema):
quantity = fields.Integer(validate=validate_quantity)
in_data = {'quantity': 31}
try:
result = ItemSchema().load(in_data)
except ValidationError as err:
print(err.messages)
通过自定义方法,同样可以实现更灵活的验证,运行结果:
{'quantity': ['Quantity must not be greater than 30.']}
对于上面的例子,还有更优雅的写法:
from marshmallow import fields, Schema, validates, ValidationError
class ItemSchema(Schema):
quantity = fields.Integer()
@validates('quantity')
def validate_quantity(self, value):
if value < 0:
raise ValidationError('Quantity must be greater than 0.')
if value > 30:
raise ValidationError('Quantity must not be greater than 30.')
通过定义方法并用 validates 修饰符,使得代码的书写更加简洁。
如果要想定义必填字段,只需要在 fields 里面加入 required 参数并设置为 True 即可,另外我们还可以自定义错误信息,使用 error_messages 即可,例如:
from pprint import pprint
from marshmallow import Schema, fields, ValidationError
class UserSchema(Schema):
name = fields.String(required=True)
age = fields.Integer(required=True, error_messages={'required': 'Age is required.'})
city = fields.String(
required=True,
error_messages={'required': {'message': 'City required', 'code': 400}},
)
email = fields.Email()
try:
result = UserSchema().load({'email': 'foo@bar.com'})
except ValidationError as err:
pprint(err.messages)
对于序列化和反序列化字段,marshmallow 还提供了默认值,而且区分得非常清楚!如 missing 则是在反序列化时自动填充的数据,default 则是在序列化时自动填充的数据。
例如:
from marshmallow import Schema, fields
import datetime as dt
import uuid
class UserSchema(Schema):
id = fields.UUID(missing=uuid.uuid1)
birthdate = fields.DateTime(default=dt.datetime(2017, 9, 29))
print(UserSchema().load({}))
print(UserSchema().dump({}))
这里我们都是定义的空数据,分别进行序列化和反序列化,运行结果如下:
{'id': UUID('06aa384a-570c-11ea-9869-a0999b0d6843')}
{'birthdate': '2017-09-29T00:00:00'}
可以看到,在没有真实值的情况下,序列化和反序列化都是用了默认值。
这个真的是解决了我之前在 cattrs 序列化和反序列化时候的痛点啊!
在序列化时,Schema 对象会默认使用和自身定义相同的 fields 属性名,当然也可以自定义,如:
class UserSchema(Schema):
name = fields.String()
email_addr = fields.String(attribute='email')
date_created = fields.DateTime(attribute='created_at')
user = User('Keith', email='keith@stones.com')
ser = UserSchema()
result, errors = ser.dump(user)
pprint(result)
运行结果如下:
{'name': 'Keith',
'email_addr': 'keith@stones.com',
'date_created': '2014-08-17T14:58:57.600623+00:00'}
反序列化也是一样,例如:
class UserSchema(Schema):
name = fields.String()
email = fields.Email(load_from='emailAddress')
data = {
'name': 'Mike',
'emailAddress': 'foo@bar.com'
}
s = UserSchema()
result, errors = s.load(data)
运行结果如下:
{'name': u'Mike',
'email': 'foo@bar.com'}
对于嵌套属性,marshmallow 当然也不在话下,这也是让我觉得 marshmallow 非常好用的地方,例如:
from datetime import date
from marshmallow import Schema, fields, pprint
class ArtistSchema(Schema):
name = fields.Str()
class AlbumSchema(Schema):
title = fields.Str()
release_date = fields.Date()
artist = fields.Nested(ArtistSchema())
bowie = dict(name='David Bowie')
album = dict(artist=bowie, title='Hunky Dory', release_date=date(1971, 12, 17))
schema = AlbumSchema()
result = schema.dump(album)
pprint(result, indent=2)
这样我们就能充分利用好对象关联外键来方便地实现很多关联功能。
以上介绍的内容基本算在日常的使用中是够用了,当然以上都是一些基本的示例,对于更多功能,可以参考 marchmallow 的官方文档:https://marshmallow.readthedocs.io/en/stable/,强烈推荐大家用起来。
– EOF –