博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

我们前面在试各种功能的时候都是在admin下把数据硬插在table里,但是这明显不符合我们的日常使用环境,博客里最常用的就是添加新的文章了,所以这里我们就看一看文章的添加是怎么实现的。

富文本编辑器

 看一看博客园在添加文章的页面,新的文章是怎么添加进去的呢?

博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

 

 这个图片里的文本编辑器就是一个富文本编辑器(富文本编辑器,Rich Text Editor, 简称 RTE, 是一种可内嵌于浏览器,所见即所得的文本编辑器。)因为我们在编辑文本的时候包含了各种各样的样式,其实真实情况下我们是写了一个html的代码,这个代码点击上面的图标是可以显示出来的,只不过富文本编辑器是一种所见即所得的效果,更利于我们的编辑。但是最终生成的还是一堆html类型的字符串。

kindeditor

在新文章的页面中我们就使用了KindEditor这个富文本编辑器,点击官网查看,下载以后放在static文件夹中,就可以直接调用了,在HTML页面中就是定义一个textarea的标签,然后导入Kindeditor,把他绑定给这个textarea

<textarea id="editor_id" name="content" style="width:700px;height:300px;">
&lt;strong&gt;HTML内容&lt;/strong&gt;
</textarea>

<script charset="utf-8" src="/editor/kindeditor.js"></script>
<script charset="utf-8" src="/editor/lang/zh-CN.js"></script>
<script>
        KindEditor.ready(function(K) {
                window.editor = K.create('#editor_id');
        });
</script>

至于create函数中还可以添加好多参数,可以直接在官网上查到。

官网上的文档说的很清楚,如何在页面中加载KindEditor,这里就不说了。但是下面的一个用法是要着重讲一下的:图片的上传。

KindEditor中上传图片

 默认的KindEditor是有两个图片上传的按钮的,但是上传以后是没有任何行为的(我们也没有告诉他上传到服务器的哪里?怎么上传)!所以我么就要给K.create()中添加下面几个参数

<script>
    KindEditor.ready(function(K){
        window.editor = K.create('#editor_id',{
            uploadJson:'/upload/',
    })
</script>

就是指定了上传图片时候对应的url请求。然后上传一下会发现有下面的错误

博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

 

 

 很明显,发生了跨站请求错误,所以要添加csrf参数,这个参数的添加在一个新的配置参数中

1 uploadJson:'/upload/',
2 extraFileUploadParams:{
3     csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()
4 },
5 filePostName:"upload_img"   //指定上传文件的key

但是一定要记得在前面加上{%csrf_token%}才行啊,要不是jQuery取不到这个csrf的值。

最后一行的filePostName的作用我们下面马上会说明。

既然上传图片是用到一个url,所以我们要在路由总设置好这个url和它对应的视图函数

 1 def upload(request):
 2     print(request.FILES)
 3     obj = request.FILES.get('upload_img')
 4     path = os.path.join(settings.MEDIA_ROOT,'img/',obj.name)
 5     print(obj.name)
 6 
 7     with open(path,'wb') as f:
 8         for line in obj:
 9             f.write(line)
10     return HttpResponse('OK')

我们在settings.py中定义了一个静态文件的上传路径(/media/)为了管理的方便,我们在这个路径下新建一个img/文件夹来存放上传的图片文件。所以path就是项目的路径加上img后面是文件名。而request.FILE.get的值就是刚刚我们在模板中定义的filePostName

最后用常规的文件句柄把数据写入文件就行了。

上传完毕,就有下面的效果

博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

 

 不要看标题上的上传错误,那个是定义出来的显示内容。主体内的OK是我们在视图中通过HttpResponse返回的字符串。然后在看看media文件夹内,是不是有了我们新上传的图片!

上传图片的显示

在常规操作中,我们一点上传图片的时候在文本编辑框中会出现我们上传的图片,这个操作就是需要我们视图中加一个返回的字典

 1 def upload(request):
 2     obj = request.FILES.get('upload_img')
 3     path = os.path.join(settings.MEDIA_ROOT,'img/',obj.name)
 4 
 5     with open(path,'wb') as f:
 6         for line in obj:
 7             f.write(line)
 8 
 9     ret = {
10         'error':0,
11         'url':'/media/img/'+obj.name,
12     }
13 
14     return HttpResponse(json.dumps(ret))

我们返回了一个字典,里面是一个状态字和一个url,这个url就是我们保存图片的路径,一定要注意,这个路径不是绝对路径!这样就完成了上传图片的显示

博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

 

 

富文本编辑器里内容的保存

 新的文章保存,有一个字段比较重要:desc,我们在访问博客园的时候会看到有个这样的文章简介,就是标题下面那个

博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

 

 

 这个简介就是把文章的内容截取了一个固定的长度,我们可以先用切片的方法来试一下

 1 def new_article(request,username):
 2     if request.method == 'POST':
 3         title = request.POST.get('title')
 4         content = request.POST.get('article_content')
 5 
 6         user = request.user
 7 
 8         desc = content[:150]
 9 
10         models.Article.objects.create(user=user,desc=desc)
11         print(title,content)
12 
13     return render(request,'new_article.html')

结果会是这种显示效果

博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

 

 

 这是因为我们从页面的富文本编辑器传过来的字符串是一堆HTML形式的代码,字符串在做切片的时候是按照整个HTML文件作为一个大字符串来切片的,图示中的效果还算好,有些时候可能由于文章中的一些特定的标签在切片的时候被切掉,导致整个页面的效果崩溃。这就要用到一个新的库——BeautifulSoup。我们可以先通过下面的案例看一看这个beautifulsoup的作用是什么

from bs4 import BeautifulSoup
s = "<div>BeautifulSoup测试</div>"
soup = BeautifulSoup(s,'html.parser')
print('soup:\n',soup)
print('soup.text:\n',soup.text)

##########输出##########
soup:
 <div>BeautifulSoup测试</div>
soup.text:
 BeautifulSoup测试

我们可以通过soup.text获取到各种标签之间的字符串,就是页面上显示的内容。所以就可以把上面的那段视图代码修改成下面的方式

 1 from bs4 import BeautifulSoup
 2 def new_article(request,username):
 3     if request.method == 'POST':
 4         title = request.POST.get('title')
 5         content = request.POST.get('article_content')
 6 
 7         soup = BeautifulSoup(content,'html.parser')
 8         user = request.user
 9 
10         article_obj = models.Article.objects.create(user=user,desc=soup.text[0:150],title=title)
11         models.ArticleDetail.objects.create(content = content,article = article_obj)
12         print(title,content)
13 
14     return render(request,'new_article.html')

 

简易的XSS攻击预防

XSS攻击

 我们在前面提到过XSS攻击,这里可以再演示一下:如果我们新写的文章是这样的:

博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

 

 注意,添加的字符串类型选择的是html,那么我们在打开这个文章页面的时候就会有个弹框弹出来

博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

 

 这还是最简单的alert攻击,如果加上了死循环什么的浏览器就崩溃了。

看一下百度百科对XSS攻击的定义:

XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容

简单的XSS防御

有一个最简单暴力的方法就是对整个添加的字符串进行正则搜索,把定义的关键字率除掉,就可以了。但是BeautifulSoup给我们提供了一个功能,对指定的标签类型进行过滤

from bs4 import BeautifulSoup
s = "<div>BeautifulSoup测试</div> \
    <a href='http://127.0.0.1:8000'>a标签</a> \
        <script> \
        alert('XSS攻击测试')\
        </script>"

soup = BeautifulSoup(s,'html.parser')

for tag in soup.find_all():
    if tag.name in ['script','a']:
        tag.decompose()

print(soup)

##########输出值##########
<div>BeautifulSoup测试</div>  

可以看一下,我们可以用find_all的方式来拿到所有的tag,如果这个tag的样式是我们指定的(script和a标签),直接删掉。不是的就保留下来。

也就是我们直接定义一个非法的标签列表就行了。

但是还是有一点问题,像博客园一样,我们是可以插入程序段,里面包含了script的类型,那么用这个方式就被过滤掉了,留个悬念,以后再讲!

 

上一篇:BeautifulSoup


下一篇:解析数据的Beautiful Soup 模块