Python 21 Django 实用小案例1

时间:2022-09-21 15:56:18

实用案例

验证码与验证

   KindEditor

     组合搜索的实现

  单例模式

     beautifulsoup4

验证码与验证

需要安装Pillow模块

pip stall pillow

1、首先需要借助pillow模块用来画一个验证码图形,这里单独封装了一个py文件,调用一个方法就好了

 #!/user/bin/env python
# -*-coding: utf-8-*-
import random
from PIL import ImageDraw,ImageFont,Image,ImageFilter def random_check_code(width=120, height=30, char_length=5, font_file='wryh.ttf', font_size=28):
code = []
img = Image.new(mode='RGB', size=(width, height), color=(255, 255, 255))
draw = ImageDraw.Draw(img, mode='RGB') def rndChar():
"""
生成随机字母
:return:
"""
return chr(random.randint(65, 90)) def rndColor():
"""
生成随机颜色
:return:
"""
return (random.randint(0, 255), random.randint(10, 255), random.randint(64, 255)) # 写文字
font = ImageFont.truetype(font_file, font_size)
for i in range(char_length):
char = rndChar()
code.append(char)
h = random.randint(0, 4)
draw.text([i * width / char_length, h], char, font=font, fill=rndColor()) # 写干扰点
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor()) # 写干扰圆圈
for i in range(40):
draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
x = random.randint(0, width)
y = random.randint(0, height)
draw.arc((x, y, x + 4, y + 4), 0, 90, fill=rndColor()) # 画干扰线
for i in range(5):
x1 = random.randint(0, width)
y1 = random.randint(0, height)
x2 = random.randint(0, width)
y2 = random.randint(0, height)
draw.line((x1, y1, x2, y2), fill=rndColor()) img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) #加滤镜,可以增加颜色的不同
return img, ''.join(code)

生成随机验证码

函数的参数都已在调用的时候修改。

2、登陆界面设计

假设验证码跟登录页面在同一函数一起生成,那么每次刷新验证码都需要整个页面一起重新加载;显然,这是不合理的。所以可以确定验证码跟登录界面是2个视图函数控制的。

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body style="margin: 0 auto;">
<div id="main">
<form method="post" action="/login/">
{% csrf_token %}
<p><label>用户名:</label><input type="text" name="user" /></p>
<p><label>密码:</label><input type="password" name="pwd" /></p>
<p><label>验证码:</label><input type="text" name="checkcode" /><img src="/check_code.html" /></p>
<p><input type="submit" /></p>
</form>
</div>
</body>
</html>

login html

 def login(request):
if request.method == 'GET':
return render(request, 'login.html')

login 视图函数

3、验证码

将验证码图片对象返回到模板

 def check_code(request):
stream = BytesIO() # 申请一段内存
img, code = random_check_code() # 获取随机码跟随机码图片对象
img.save(stream, 'PNG') # 将随机码对象保存到内存对象中
request.session['CheckCode'] = code # 将随机字符串保存到session
return HttpResponse(stream.getvalue()) # 返回内存中的随机码图片对象

4、如何刷新验证码呢

直接将原路由系统通过点击事件赋值给src,浏览器默认是不会进行刷新的;所以这里有一个小技巧,我们可以获取src的值,在末尾加上一个?,这样就可以实现点击刷新了。

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body style="margin: 0 auto;">
<div id="main">
<form method="post" action="/login/">
{% csrf_token %}
<p><label>用户名:</label><input type="text" name="user" /></p>
<p><label>密码:</label><input type="password" name="pwd" /></p>
<p><label>验证码:</label><input type="text" name="checkcode" /><img src="/check_code.html" onclick="ImgChange(this);"/></p>
<p><input type="submit" /></p>
</form>
</div>
</body>
<script>
function ImgChange(ths) {
ths.src = ths.src + '?'
}
</script>
</html>

修改过的login html

开启验证码验证功能

 def login(request):
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
checkcode = request.POST.get('checkcode')
code_session = request.session['CheckCode']
print(checkcode)
print(code_session)
if checkcode.upper() == request.session['CheckCode'].upper():
return HttpResponse('验证成功')
else:
return render(request, 'login.html')

修改后的login 视图函数

KindEditor

1、官网下载

http://kindeditor.net/demo.php

2、文件夹解压文件说明

├── asp                          asp示例
├── asp.net asp.net示例
├── attached 空文件夹,放置关联文件attached
├── examples HTML示例
├── jsp java示例
├── kindeditor-all-min.js 全部JS(压缩)
├── kindeditor-all.js 全部JS(未压缩)
├── kindeditor-min.js 仅KindEditor JS(压缩)
├── kindeditor.js 仅KindEditor JS(未压缩)
├── lang 支持语言
├── license.txt License
├── php PHP示例
├── plugins KindEditor内部使用的插件
└── themes KindEditor主题

3、基本使用

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div style="margin: 0 auto; width: 500px;height: 300px;">
<textarea id="content"></textarea>
</div> <script src="/static/jquery-3.2.1.js"></script>
<script src="/static/kindeditor/kindeditor-all-min.js"></script>
<script>
$(function() {
KindEditor.create("#content", {
width: '400px',
height: '200px' })
})
</script>
</body>
</html>

4、详细参数

http://kindeditor.net/docs/option.html

5、上传文件示例

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/upload_kind/" method="post"> <div style="margin: 0 auto; width: 500px;height: 300px;">
<textarea id="content"></textarea>
</div>
</form>
<script src="/static/jquery-3.2.1.js"></script>
<script src="/static/kindeditor/kindeditor-all-min.js"></script>
<script>
$(function() {
KindEditor.create("#content", {
width: '400px',
height: '200px',
extraFileUploadParams:{'csrfmiddlewaretoken':"{{ csrf_token }}"},
uploadJson:'/upload_img/',
fileManagerJson: '/upload_file_manage/',
allowImageUpload: true,
allowFileManager:true
})
})
</script>
</body>
</html>

html

 def upload_img(request):
f = request.FILES.get('imgFile')
import os
path = os.path.join("static/images", f.name)
with open(path, 'wb') as file_obj:
for chunck in f.chunks():
file_obj.write(chunck)
import json
dic = {
'error': 0,
'url': '/' + path,
'message': '错误了...'
}
return HttpResponse(json.dumps(dic)) def upload_file_manage(request):
import os,time,json
dic = {}
root_path = 'C:/Users/Administrator/Desktop/DownTimeAnalysis/static/'
static_root_path = '/static/'
request_path = request.GET.get('path')
if request_path:
abs_current_dir_path = os.path.join(root_path, request_path)
move_up_dir_path = os.path.dirname(request_path.rstrip('/'))
dic['moveup_dir_path'] = move_up_dir_path + '/' if move_up_dir_path else move_up_dir_path else:
abs_current_dir_path = root_path
dic['moveup_dir_path'] = '' dic['current_dir_path'] = request_path
dic['current_url'] = os.path.join(static_root_path, request_path) file_list = []
for item in os.listdir(abs_current_dir_path):
abs_item_path = os.path.join(abs_current_dir_path, item)
a, exts = os.path.splitext(item)
is_dir = os.path.isdir(abs_item_path)
if is_dir:
temp = {
'is_dir': True,
'has_file': True,
'filesize': 0,
'dir_path': '',
'is_photo': False,
'filetype': '',
'filename': item,
'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path)))
}
else:
temp = {
'is_dir': False,
'has_file': False,
'filesize': os.stat(abs_item_path).st_size,
'dir_path': '',
'is_photo': True if exts.lower() in ['.jpg', '.png', '.jpeg'] else False,
'filetype': exts.lower().strip('.'),
'filename': item,
'datetime': time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(os.path.getctime(abs_item_path)))
} file_list.append(temp)
dic['file_list'] = file_list
return HttpResponse(json.dumps(dic))

View

路由系统

    url(r'^kind/$', views.kind),
url(r'^upload_img/', views.upload_img), # 前面有一个kind,视图函数可以获取参数dir来区分是文件还是其他。
url(r'^upload_file_manage/', views.upload_file_manage),

Python 21 Django 实用小案例1Python 21 Django 实用小案例1Python 21 Django 实用小案例1

Python 21 Django 实用小案例1

6、XSS过滤特殊标签

处理依赖

pip3 install beautifulsoup4
 #!/usr/bin/env python
# -*- coding:utf-8 -*-
from bs4 import BeautifulSoup class XSSFilter(object):
__instance = None def __init__(self):
# XSS白名单
self.valid_tags = {
"font": ['color', 'size', 'face', 'style'],
'b': [],
'div': [],
"span": [],
"table": [
'border', 'cellspacing', 'cellpadding'
],
'th': [
'colspan', 'rowspan'
],
'td': [
'colspan', 'rowspan'
],
"a": ['href', 'target', 'name'],
"img": ['src', 'alt', 'title'],
'p': [
'align'
],
"pre": ['class'],
"hr": ['class'],
'strong': []
} @classmethod
def instance(cls):
if not cls.__instance:
obj = cls()
cls.__instance = obj
return cls.__instance def process(self, content):
soup = BeautifulSoup(content, 'lxml')
# 遍历所有HTML标签
for tag in soup.find_all(recursive=True):
# 判断标签名是否在白名单中
if tag.name not in self.valid_tags:
tag.hidden = True
if tag.name not in ['html', 'body']:
tag.hidden = True
tag.clear()
continue
# 当前标签的所有属性白名单
attr_rules = self.valid_tags[tag.name]
keys = list(tag.attrs.keys())
for key in keys:
if key not in attr_rules:
del tag[key] return soup.renderContents() if __name__ == '__main__':
html = """<p class="title">
<b>The Dormouse's story</b>
</p>
<p class="story">
<div name='root'>
Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a>
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>;
and they lived at the bottom of a well.
<script>alert(123)</script>
</div>
</p>
<p class="story">...</p>""" v = XSSFilter.instance().process(html)
print(v)

XSS 示例

 #!/usr/bin/env python
# -*- coding:utf-8 -*-
from bs4 import BeautifulSoup class XSSFilter(object):
__instance = None def __init__(self):
# XSS白名单
self.valid_tags = {
"font": ['color', 'size', 'face', 'style'],
'b': [],
'div': [],
"span": [],
"table": [
'border', 'cellspacing', 'cellpadding'
],
'th': [
'colspan', 'rowspan'
],
'td': [
'colspan', 'rowspan'
],
"a": ['href', 'target', 'name'],
"img": ['src', 'alt', 'title'],
'p': [
'align'
],
"pre": ['class'],
"hr": ['class'],
'strong': []
} def __new__(cls, *args, **kwargs):
"""
单例模式
:param cls:
:param args:
:param kwargs:
:return:
"""
if not cls.__instance:
obj = object.__new__(cls, *args, **kwargs)
cls.__instance = obj
return cls.__instance def process(self, content):
soup = BeautifulSoup(content, 'lxml')
# 遍历所有HTML标签
for tag in soup.find_all(recursive=True):
# 判断标签名是否在白名单中
if tag.name not in self.valid_tags:
tag.hidden = True
if tag.name not in ['html', 'body']:
tag.hidden = True
tag.clear()
continue
# 当前标签的所有属性白名单
attr_rules = self.valid_tags[tag.name]
keys = list(tag.attrs.keys())
for key in keys:
if key not in attr_rules:
del tag[key] return soup.renderContents() if __name__ == '__main__':
html = """<p class="title">
<b>The Dormouse's story</b>
</p>
<p class="story">
<div name='root'>
Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister c1" style='color:red;background-color:green;' id="link1"><!-- Elsie --></a>
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tilffffffffffffflie</a>;
and they lived at the bottom of a well.
<script>alert(123)</script>
</div>
</p>
<p class="story">...</p>""" obj = XSSFilter()
v = obj.process(html)
print(v)

基于__new__实现单例模式示例

7、保存

直接form提交,视图函数接收对应的textarea的name就好了。

 def upload_kind(request):
print(request.POST.get('content'))
return HttpResponse('ok')

View

Python 21 Django 实用小案例1

Python 21 Django 实用小案例1

组合搜索

Python 21 Django 实用小案例1

大家一定对上面这张图不陌生,这就是一个很经典的组合搜索。

下面我们来做一个简单的搜索。

 class ArticleType(models.Model):
caption = models.CharField(max_length=32) class Category(models.Model):
name = models.CharField(max_length=32) class Article(models.Model):
title = models.CharField(max_length=32)
content = models.CharField(max_length=255)
category = models.ForeignKey(to='Category')
article_type = models.ForeignKey(to='ArticleType')

models.py

HTML
 def article(request, *args, **kwargs):
result = models.Article.objects.all()
return render(request, 'article.html', {'result':result})

View

去数据库补充点数据,然后就可以运行了。

Python 21 Django 实用小案例1

先把所有的选择项列出来。

 <h1>过滤条件</h1>
<div class="condition-div">
<div><a>全部</a>
{% for row in article_type %}#}
<a>{{ row.caption }}</a>
{% endfor %}#}
</div>
<div><a>全部</a>
{% for row in category%}#}
<a>{{ row.name}}</a>
{% endfor %}#}
</div>
</div>

HTML

 def article(request, *args, **kwargs):
article_type_list = models.ArticleType.objects.all()
category = models.Category.objects.all()
result = models.Article.objects.all()
return render(
request,
"article.html",
{
"result": result,
"article_type": article_type_list,
"category": category
}
)

view

这时候再来好好研究一下网页的逻辑

Python 21 Django 实用小案例1

点完SUV,跳到了SUV的页面,对SUV进行删选,但是后面还有一串字符,-0-1-1等等,不难发现这是用正则做的路由参数,加上p可以固定不同的类型对应不同的位置参数。

    url(r'^article-(?P<article_type_id>\d+)-(?P<category_id>\d+).html', views.article),

但是,还有一个问题,比如选择了能源为汽油,又要选择座位来组合怎么做呢?

首先,需要先记录下当前的筛选参数,当然,url已经做到了,所以我们修改下URL

    url(r'^article-(?P<article_type_id>\d+)-(?P<category_id>\d+).html', views.article, name='article'),

看到name,大家一定记起来了,这里运用到了反转,记录动态URL。先记录下url里面的参数,再把参数传递给前台,告诉前台,那么做能源筛选的时候,是不是可以直接生成一个url,记录下所有其他选择的标签的参数,再加入自身的id。上代码:

 def article(request, *args, **kwargs):
# from django.urls import reverse
# url = reverse('article', kwargs=kwargs)
# print(url) # 强行带了波url的节奏,其实并不需要用到,因为url传过来的id已经被处理成字典形式了,可以区分开的。我们拿到**kwargs
condition = {}
for k, v in kwargs.items():
kwargs[k] = int(v)
if v == '':
pass
else:
condition[k] = v #这里的逻辑是因为全部这个标签,数据库的类别ID是自增的,所以设置0是最保险的。在后台把所有为0的全部过滤掉,不然会查询不到数据的。
article_type_list = models.ArticleType.objects.all()
category = models.Category.objects.all()
# result = models.Article.objects.filter(article_type_id=1, category_id=2)
result = models.Article.objects.filter(**condition)
return render(
request,
"article.html",
{
"result": result,
"article_type": article_type_list,
"category": category,
"arg_dict": kwargs,
}
)

View

 <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.condition-div a{
display: inline-block;
padding: 2px 3px;
margin: 3px 5px;
border: 1px solid slategrey;
}
.condition-div a.active{
background-color: lightslategrey;
}
</style>
</head>
<body>
<h1>过滤条件</h1>
<div class="condition-div">
<div>
{% if arg_dict.article_type_id == 0 %}
<a class="active" href="/app01/article-0-{{ arg_dict.category_id }}.html">全部</a>
{% else %}
<a href="/app01/article-0-{{ arg_dict.category_id }}.html">全部</a>
{% endif %}
{% for row in article_type %}
{% if row.id == arg_dict.article_type_id %}
<a class="active" href="/app01/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
{% else %}
<a href="/app01/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
{% endif %}
{% endfor %}
{# {% filter_article_type article_type arg_dict %}#}
</div>
<div> {% if arg_dict.category_id == 0 %}
<a class="active" href="/app01/article-{{ arg_dict.article_type_id }}-0.html">全部</a>
{% else %}
<a href="/app01/article-{{ arg_dict.article_type_id }}-0.html">全部</a>
{% endif %}
{% for row in category %}
{% if row.id == arg_dict.category_id %}
<a class="active" href="/app01/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html">{{ row.name }}</a>
{% else %}
<a href="/app01/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html">{{ row.name }}</a>
{% endif %}
{% endfor %}
</div>
</div> <h1>查询结果</h1>
<ul>
{% for row in result %}
<li>{{ row.od }} - {{ row.title }}</li>
{% endfor %}
</ul> </body>
</html>

html

Python 21 Django 实用小案例1

这时候基本上就已经做好了,前台html里面的if else就不具体说了,主要就是加一个active class.

现在这个前台看着太乱了,就是要强行少代码,怎么办?大家一定觉得很夸张。。。怎么减代码??一脸懵逼,二脸懵逼,三脸懵逼。。。。。。

给大家体个醒,simple_tag,你一定会激动地叫起来,喔~~

刚好复习一下simple_tag 的制作思路,首先项目里面建一个templatetags文件夹,里面新建任意一个py文件。

Python 21 Django 实用小案例1

 #!/user/bin/env python
# -*-coding: utf-8-*-
from django import template
from django.utils.safestring import mark_safe
register = template.Library() @register.simple_tag
def filter_all(arg_dict, k):
if k == 'article_type_id':
n1 = arg_dict['article_type_id']
n2 = arg_dict['category_id']
if n1 == 0:
ret = '<a class="active" href="/app01/article-0-%s.html">全部</a>' % n2
else:
ret = '<a href="/app01/article-0-%s.html">全部</a>' % n2
else:
n1 = arg_dict['category_id']
n2 = arg_dict['article_type_id']
if n1 == 0:
ret = '<a class="active" href="/app01/article-%s-0.html">全部</a>' % n2
else:
ret = '<a href="/app01/article-%s-0.html">全部</a>' % n2
return mark_safe(ret) @register.simple_tag
def filter_article_type(article_type, arg_dict):
'''
{% for row in article_type %}
{% if row.id == arg_dict.article_type_id %}
<a class="active" href="/app01/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
{% else %}
<a href="/app01/article-{{ row.id }}-{{ arg_dict.category_id }}.html">{{ row.caption }}</a>
{% endif %}
{% endfor %}
:return:
'''
ret = []
for row in article_type:
if row.id == arg_dict['article_type_id']:
temp = '<a class="active" href="/app01/article-%s-%s.html">%s</a>' % \
(row.id, arg_dict['category_id'], row.caption)
else:
temp = '<a href="/app01/article-%s-%s.html">%s</a>' % (row.id, arg_dict['category_id'], row.caption)
ret.append(temp)
return mark_safe(''.join(ret)) @register.simple_tag
def filter_category(category, arg_dict):
'''
{% for row in category %}
{% if row.id == arg_dict.category_id %}
<a class="active" href="/app01/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html">{{ row.name }}</a>
{% else %}
<a href="/app01/article-{{ arg_dict.article_type_id }}-{{ row.id }}.html">{{ row.name }}</a>
{% endif %}
{% endfor %}
:return:
'''
ret = []
for row in category:
if row.id == arg_dict['category_id']:
temp = '<a class="active" href="/app01/article-%s-%s.html">%s</a>' % \
(arg_dict['article_type_id'], row.id, row.name)
else:
temp = '<a href="/app01/article-%s-%s.html">%s</a>' % \
(arg_dict['article_type_id'], row.id, row.name)
ret.append(temp)
return mark_safe(''.join(ret))

simple_tag

 {% load filter %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
.condition-div a{
display: inline-block;
padding: 2px 3px;
margin: 3px 5px;
border: 1px solid slategrey;
}
.condition-div a.active{
background-color: lightslategrey;
}
</style>
</head>
<body>
<h1>过滤条件</h1>
<div class="condition-div">
<div>
{% filter_all arg_dict 'article_type_id' %}
{% filter_article_type article_type arg_dict %}
</div>
<div>
{% filter_all arg_dict 'category_id' %}
{% filter_category category arg_dict %}
</div>
</div> <h1>查询结果</h1>
<ul>
{% for row in result %}
<li>{{ row.od }} - {{ row.title }}</li>
{% endfor %}
</ul> </body>
</html>

这样才看着高端嘛。。。给一个不会Django的看到这个模板,直接疯了,什么鬼。。。我还是回家种田吧。。。

单例模式

单例模式其实算是对于面向对象的基础,类的一次补充。按照之前的思路,每次对于类的方法的调用,都需要实例化,这样就会产生多个实例化对象。

看下面这个示例

 class Foo:
instance = None
def __init__(self):
pass def process(self):
print(123) obj1 = Foo()
obj2 = Foo()
print(id(obj1), id(obj2))

输出结果:

1892383886976 1892383887032

对于上面这种情况,根本不需要实例化多个对象来处理,某种意义上是一种浪费。。。所以我们可以简单修改下。

 class Foo:
instance = None
def __init__(self):
pass @classmethod
def get_instance(cls):
if Foo.instance:
return Foo.instance
else:
Foo.instance = Foo()
return Foo.instance def process(self):
print(123) obj1 = Foo.get_instance()
obj2 = Foo.get_instance()
print(id(obj1), id(obj2))

结果

2694976409216 2694976409216

这里判断了已经有一个实例化对象,就返回了实例化对象内存地址给后面的实例化过程。

单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。当你希望在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

比如,某个服务器程序的配置信息存放在一个文件中,客户端通过一个 AppConfig 的类来读取配置文件的信息。如果在程序运行期间,有很多地方都需要使用配置文件的内容,也就是说,很多地方都需要创建 AppConfig 对象的实例,这就导致系统中存在多个 AppConfig 的实例对象,而这样会严重浪费内存资源,尤其是在配置文件内容很多的情况下。事实上,类似 AppConfig 这样的类,我们希望在程序运行期间只存在一个实例对象。

使用模块

其实,Python 的模块就是天然的单例模式,因为模块在第一次导入时,会生成 .pyc 文件,当第二次导入时,就会直接加载 .pyc 文件,而不会再次执行模块代码。因此,我们只需把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。如果我们真的想要一个单例类,可以考虑这样做:

 # mysingleton.py
class My_Singleton(object):
def foo(self):
pass my_singleton = My_Singleton()

将上面的代码保存在文件 mysingleton.py 中,然后这样使用:

from mysingleton import my_singleton

my_singleton.foo()

使用__new__

这里还是用最先引入话题的实例来讲解。其实上面的那个类方法算是低级的单例模式,因为我们改变了类的实例化方法了。

 class Foo(object):
instance = None
def __init__(self):
self.name = 'dandy' def __new__(cls, *args, **kwargs):
if not Foo.instance:
Foo.instance = object.__new__(cls, *args, **kwargs)
return Foo.instance def process(self):
print(123) obj1 = Foo()
obj2 = Foo()
print(obj1, obj2)
<__main__.Foo object at 0x000001F599138EB8> <__main__.Foo object at 0x000001F599138EB8>

beautifulsoup4

快速开始

 html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p> <p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p> <p class="story">...</p>
""" from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc) print(soup.prettify())
# <html>
# <head>
# <title>
# The Dormouse's story
# </title>
# </head>
# <body>
# <p class="title">
# <b>
# The Dormouse's story
# </b>
# </p>
# <p class="story">
# Once upon a time there were three little sisters; and their names were
# <a class="sister" href="http://example.com/elsie" id="link1">
# Elsie
# </a>
# ,
# <a class="sister" href="http://example.com/lacie" id="link2">
# Lacie
# </a>
# and
# <a class="sister" href="http://example.com/tillie" id="link2">
# Tillie
# </a>
# ; and they lived at the bottom of a well.
# </p>
# <p class="story">
# ...
# </p>
# </body>
# </html>

几个简单的浏览结构化数据的方法:

soup.title
# <title>The Dormouse's story</title> soup.title.name
# u'title' soup.title.string
# u'The Dormouse's story' soup.title.parent.name
# u'head' soup.p
# <p class="title"><b>The Dormouse's story</b></p> soup.p['class']
# u'title' soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a> soup.find_all('a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
# <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>] soup.find(id="link3")
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>

遍历寻找<a>标签的链接:

for link in soup.find_all('a'):
print(link.get('href')) # http://example.com/elsie
# http://example.com/lacie
# http://example.com/tillie

获取文档中所有文字内容:

print(soup.get_text())

# The Dormouse's story
#
# The Dormouse's story
#
# Once upon a time there were three little sisters; and their names were
# Elsie,
# Lacie and
# Tillie;
# and they lived at the bottom of a well.
#
# ...

主要解析器,优缺点:

解析器 使用方法 优势 劣势
Python标准库 BeautifulSoup(markup, "html.parser")
  • Python的内置标准库
  • 执行速度适中
  • 文档容错能力强
  • Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器 BeautifulSoup(markup, "lxml")
  • 速度快
  • 文档容错能力强
  • 需要安装C语言库
lxml XML 解析器

BeautifulSoup(markup, ["lxml", "xml"])

BeautifulSoup(markup, "xml")

  • 速度快
  • 唯一支持XML的解析器
  • 需要安装C语言库
html5lib BeautifulSoup(markup, "html5lib")
  • 最好的容错性
  • 以浏览器的方式解析文档
  • 生成HTML5格式的文档
  • 速度慢
  • 不依赖外部扩展

推荐使用lxml作为解析器,因为效率更高. 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定.

提示: 如果一段HTML或XML文档格式不正确的话,那么在不同的解析器中返回的结果可能是不一样的,查看 解析器之间的区别 了解更多细节

将一段文档传入BeautifulSoup 的构造方法,就能得到一个文档的对象, 可以传入一段字符串或一个文件句柄.

from bs4 import BeautifulSoup

soup = BeautifulSoup(open("index.html"))

soup = BeautifulSoup("<html>data</html>")

首先,文档被转换成Unicode,并且HTML的实例都被转换成Unicode编码

BeautifulSoup("Sacré bleu!")
<html><head></head><body>Sacré bleu!</body></html>

然后,Beautiful Soup选择最合适的解析器来解析这段文档,如果手动指定解析器那么Beautiful Soup会选择指定的解析器来解析文档

对象的种类

Beautiful Soup将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种: Tag , NavigableString , BeautifulSoup , Comment .

Tag

Tag 对象与XML或HTML原生文档中的tag相同:

soup = BeautifulSoup('<b class="boldest">Extremely bold</b>')
tag = soup.b
type(tag)
# <class 'bs4.element.Tag'>

Name & Attributes

每个tag都有自己的名字,通过 .name 来获取:
tag.name
# u'b' 如果改变了tag的name,那将影响所有通过当前Beautiful Soup对象生成的HTML文档:
tag.name = "blockquote"
tag
# <blockquote class="boldest">Extremely bold</blockquote> 一个tag可能有很多个属性. tag <b class="boldest"> 有一个 “class” 的属性,值为 “boldest” . tag的属性的操作方法与字典相同:
tag['class']
# u'boldest' 也可以直接”点”取属性, 比如: .attrs :
tag.attrs
# {u'class': u'boldest'} tag的属性可以被添加,删除或修改. 再说一次, tag的属性操作方法与字典一样
tag['class'] = 'verybold'
tag['id'] = 1
tag
# <blockquote class="verybold" id="1">Extremely bold</blockquote> del tag['class']
del tag['id']
tag
# <blockquote>Extremely bold</blockquote> tag['class']
# KeyError: 'class'
print(tag.get('class'))
# None

多值属性

HTML 4定义了一系列可以包含多个值的属性.在HTML5中移除了一些,却增加更多.最常见的多值的属性是 class (一个tag可以有多个CSS的class). 还有一些属性 rel , rev , accept-charset , headers , accesskey . 在Beautiful Soup中多值属性的返回类型是list:

css_soup = BeautifulSoup('<p class="body strikeout"></p>')
css_soup.p['class']
# ["body", "strikeout"] css_soup = BeautifulSoup('<p class="body"></p>')
css_soup.p['class']
# ["body"]

如果某个属性看起来好像有多个值,但在任何版本的HTML定义中都没有被定义为多值属性,那么Beautiful Soup会将这个属性作为字符串返回

id_soup = BeautifulSoup('<p id="my id"></p>')
id_soup.p['id']
# 'my id'

将tag转换成字符串时,多值属性会合并为一个值

rel_soup = BeautifulSoup('<p>Back to the <a rel="index">homepage</a></p>')
rel_soup.a['rel']
# ['index']
rel_soup.a['rel'] = ['index', 'contents']
print(rel_soup.p)
# <p>Back to the <a rel="index contents">homepage</a></p>

如果转换的文档是XML格式,那么tag中不包含多值属性

xml_soup = BeautifulSoup('<p class="body strikeout"></p>', 'xml')
xml_soup.p['class']
# u'body strikeout'

  

遍历字符串

字符串常被包含在tag内.Beautiful Soup用 NavigableString 类来包装tag中的字符串:

tag.string
# u'Extremely bold'
type(tag.string)
# <class 'bs4.element.NavigableString'>

一个 NavigableString 字符串与Python中的Unicode字符串相同,并且还支持包含在 遍历文档树 和 搜索文档树 中的一些特性. 通过 unicode() 方法可以直接将 NavigableString 对象转换成Unicode字符串:

unicode_string = unicode(tag.string)
unicode_string
# u'Extremely bold'
type(unicode_string)
# <type 'unicode'>

tag中包含的字符串不能编辑,但是可以被替换成其它的字符串,用 replace_with() 方法:

tag.string.replace_with("No longer bold")
tag
# <blockquote>No longer bold</blockquote>

NavigableString 对象支持 遍历文档树 和 搜索文档树 中定义的大部分属性, 并非全部.尤其是,一个字符串不能包含其它内容(tag能够包含字符串或是其它tag),字符串不支持 .contents 或 .string 属性或 find() 方法.

如果想在Beautiful Soup之外使用 NavigableString 对象,需要调用 unicode() 方法,将该对象转换成普通的Unicode字符串,否则就算Beautiful Soup已方法已经执行结束,该对象的输出也会带有对象的引用地址.这样会浪费内存.

更多请参考:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html

 result = '''<p>千万人<strong>较</strong>去年我</p>
<p>请问千万人<span style="color: #ff6600;">群若群感通片没人</span>呢 而非狗肉<span style="font-family: 'Microsoft YaHei';">人GV奇</span>偶偶陪<span style="font-family: 仿宋;">你围殴</span>既然v</p>
<p>我打完分配人</p>
<script>alert(123)</script>''' from bs4 import BeautifulSoup
soup = BeautifulSoup(result, 'html.parser')
tag = soup.find('script') # 查询script标签
tag.clear() # 清除掉标签,2个script:<script></script>
tag.hidden = True # 隐藏标签中的内容 span = soup.find('span') # 找到span标签
del span.attrs['style'] # 删除所有属性
content = soup.decode() #
print(content) ########
<p>千万人<strong>较</strong>去年我</p>
<p>请问千万人<span>群若群感通片没人</span>呢 而非狗肉<span style="font-family: 'Microsoft YaHei';">人GV奇</span>偶偶陪<span style="font-family: 仿宋;">你围殴</span>既然v</p>
<p>我打完分配人</p>

对于标签的白名单

 result = '''<p>千万人<strong>较</strong>去年我</p>
<p>请问千万人<span style="color: #ff6600;">群若群感通片没人</span>呢 而非狗肉<span style="font-family: 'Microsoft YaHei';">人GV奇</span>偶偶陪<span style="font-family: 仿宋;">你围殴</span>既然v</p>
<p>我打完分配人</p>
<script>alert(123)</script>''' from bs4 import BeautifulSoup
soup = BeautifulSoup(result, 'html.parser')
tags = ['p', 'span'] for tag in soup.find_all():
if tag.name in tags:
pass
else:
tag.hidden = True
tag.clear() content = soup.decode()
print(content)
##################
<p>千万人去年我</p>
<p>请问千万人<span style="color: #ff6600;">群若群感通片没人</span>呢 而非狗肉<span style="font-family: 'Microsoft YaHei';">人GV奇</span>偶偶陪<span style="font-family: 仿宋;">你围殴</span>既然v</p>
<p>我打完分配人</p>
 result = '''<p class='c1' id='p1'>千万人<strong>较</strong>去年我</p>
<p>请问千万人<span id='span11' class='span-class' style="color: #ff6600;">群若群感通片没人</span>呢 而非狗肉<span style="font-family: 'Microsoft YaHei';">人GV奇</span>偶偶陪<span style="font-family: 仿宋;">你围殴</span>既然v</p>
<p>我打完分配人</p>
<script>alert(123)</script>''' from bs4 import BeautifulSoup
soup = BeautifulSoup(result, 'html.parser')
tags = {
'p': ['class'],
'span': ['id',]
} for tag in soup.find_all():
if tag.name in tags:
pass
else:
tag.hidden = True
tag.clear()
continue
input_attrs = tag.attrs
valid_attrs = tags[tag.name]
for key in list(input_attrs.keys()):
if key in valid_attrs:
pass
else:
del tag.attrs[key]
content = soup.decode()
print(content)

Python 21 Django 实用小案例1的更多相关文章

  1. Python Django 实用小案例2

    动态导入模块 Django返回序列化数据  动态导入模块 在Django里面,经常会看到一些方法或者类是动态导入,尤其是以settings文件为代表,经常把一些类放在里面动态调配,比如随便拿Djang ...

  2. 【转】Python 30个实用小Tips

    1. 原地交换两个数字 Python 提供了一个直观的在一行代码中赋值与交换(变量值)的方法,请参见下面的示例: x, y = 10, 20 print(x, y) x, y = y, x print ...

  3. python字符串和列表小案例

    python 目录 python 一.字符串 1.给定一个字符串,利用切片将字符串反转 2.给定一个字符串,将空格替换为逗号 3.给定一个字符串,大写改为小写 4.str = '' ,li = ['l ...

  4. 17&period;Selenium&plus;Python日期控件小案例

    1.web上的控件种类十分多,但是大致分为2种,一种为类型是input的且可以输入,第二种为类型是input的且属性为readonly,文本框不可编辑 2.第一种类型为可以输入的,直接send_key ...

  5. 批量下载网站图片的Python实用小工具(下)

    引子 在 批量下载网站图片的Python实用小工具 一文中,讲解了开发一个Python小工具来实现网站图片的并发批量拉取.不过那个工具仅限于特定网站的特定规则,本文将基于其代码实现,开发一个更加通用的 ...

  6. Django——9 博客小案例的实现

    Django  博客小案例的实现 主要实现博客的增删改查功能 主页index.html  -->  展示添加博客和博客列表的文字,实现页面跳转 添加页add.html  --> 输入文章标 ...

  7. Python趣味实用小工具

    代码地址如下:http://www.demodashi.com/demo/12918.html python 趣味实用小工具 概述 用python实现的三个趣味实用小工具: 图片转Execl工具 , ...

  8. Django 09 博客小案例

    Django 09 博客小案例 urls.py from django.urls import path from . import views urlpatterns = [ path('index ...

  9. python购物车小案例

    python购物车小案例# 案列描述:有一个小型水果店里面有水果(苹果:¥8/kg,香蕉:¥5/kg,芒果:¥15/kg,葡萄:¥12/kg),客户带了100元钱进店选购水果.# 1.客户输入相应序号 ...

随机推荐

  1. daydayup2 codeforces143D

    这题很考察分析 题意:让你构造n*m的矩阵,使得不存在两个点的距离的平方为5 1若n=1  ,答案为m 2若m=1,答案为n 3若n=2 则这样 110011001100.. 110011001100 ...

  2. history&period;js 一个无刷新就可改变浏览器栏地址的插件(不依赖jquery)

    示例: http://browserstate.github.io/history.js/demo/     简介   HTML4有一些对浏览历史的前进后退API的支持如:   window.hist ...

  3. erlang集成开发环境搭配配置出现的问题

    问题:Unable to create the selected preference page.  com.avaya.exvantage.ui.interfaces.eclipse.plugin  ...

  4. c&num; 解密微信encryptedData字段

    参考链接:https://www.cnblogs.com/jetz/p/6384809.html 我写了一个工具方法,直接照搬链接中的方法,还有一个工具类. public class Encrypt ...

  5. oracle高级分组

    基本group by用法 create table test_table(a varchar(20),b varchar(20),c varchar(20)) insert into test_tab ...

  6. 4星&vert;《钱的历史》:大英博物馆的钱币简史,彩图众多不适合在kindle上阅读

    钱的历史(大英博物馆权威出品,一部金钱简史) 大英博物馆的两位钱币馆馆长的作品.非常专业.基本是世界钱币简史.从钱币的发展变迁讲到涉及到的历史大事,重心当然是欧洲的钱币史,中国.印度也各安排了一章. ...

  7. Activity公用跳转到主Activity

    public class VSession { private VSession() { } public static VSession getInstance() { if (session == ...

  8. clipbrd剪切板查看器

    本文,我们来学习一下简单的概念,即,如何查看系统剪贴版里面有什么内容?   如果要想看.或者验证系统剪贴版里面都有什么内容,最为简单的方法就是通过"粘贴"的操作来验证!   但是, ...

  9. MFC--串口编程---WIN API的方式将串扣操作封装在线程类中

    串口采集数据 本文档介绍的是如何获取串口原始数据并将原始数据解析成可处理或可展示的数据. 一.串口采集有很多方式: 1).MFC有一个专门的控件,直接编程采集,一个控件只能采集一个串口,而且串口名字比 ...

  10. 深入解析Vuex实战总结

    这篇文章主要介绍了Vuex的初探与实战小结,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下.如有不足之处,欢迎批评指正. 1.背景 最近在做一个单页面的管理后台项目,为了提高开 ...