关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
上次开发了随机推荐,为了提高更多博文的曝光率。
但随机推荐只能让藏在深处的博文多露几次脸,不一定可以让访客点击。
那么可以推荐一些访客喜欢的、感兴趣的博文,提高点击率,即智能推荐功能(或叫猜你喜欢)。
为了让博文之间有关联,我上一篇博文就写了新增关键字功能。利用关键字让博文之间有关联性。
接下来需要把访客阅读过的博文记录下来。
由于访客不一定是我博客注册并登录的用户。所以不太需要把这些数据记录到服务器。可以把数据已cookie的形式保存在客户端。
此处有个小细节:我们应该需要保存多少条历史阅读记录。
全部?不需要,也不合适。因为随着不断写博客,博文只会越来越多。而且全部记录的话,无法突出最新的阅读记录。访客的阅读喜好和博客的内容是有时效性。也就是说,最新的阅读记录权重最高,依次递减。
综合考虑,我决定只记录前10条的阅读记录。这样可以确保时效性。
打开Blog应用的views.py文件,添加如下方法:
#记录最新阅读的前10篇博文
def record_read(request, current_blog_id, response):
#获取最近阅读的10篇博客id列表
cookie_name = "new_readed"
cookie_value = request.COOKIES.get(cookie_name, '')
#该cookie的形式 "1|2|3|4|5"
read_list = cookie_value.split('|')
#写入当前博客的id
current_blog_id = str(current_blog_id)
if read_list.count(current_blog_id) == 0:
read_list.append(current_blog_id)
#判断是否有超过10个,若超过,则移除
if len(read_list)>10:
read_list.pop(0)
#写入可保留长时间的cookie
d = datetime.datetime.now() + datetime.timedelta(days=1000)
t = time.mktime(d.timetuple())
response.set_cookie(cookie_name, '|'.join(read_list), t, httponly=True)
return True该方法需要传递3个参数:request请求对象、当前博客id和response响应对象。
将最近阅读的博客ID用'|'隔开写入一个可以保留长时间的cookie中。
每次打开每篇博文就执行一下该方法。在响应打开每篇博文的方法内调用该方法:
#coding:utf-8
from django.shortcuts import render_to_response
from django.template import RequestContext
def blog_show(request,id):
#省略其他不相关的代码
response = render_to_response("blog/blog_single.html", data, context_instance=RequestContext(request))
record_read(request, id, response) #写入最近阅读记录的cookie
return response先获取response对象,调用record_read方法,写入cookie信息之后,再返回给前端页面。
该部分是这整个智能推荐最复杂也是最核心的部分。由于该功能计算时间可能会比较长,采用ajax的方式比较合适。
首先,先获取最近的阅读记录和这些博客包含的关键字。在Blog应用的views.py文件中,添加如下方法:
def like_blogs(request):
#读取最近阅读的博客id列表
cookie_name = "new_readed"
cookie_value = request.COOKIES.get(cookie_name, '')
#把字符串转成数字
value_list = cookie_value.split('|')
read_list = []
keywords = {}
for value in value_list:
try:
value = int(value)
blog = Blog.objects.get(id=value)
#若上面两步没有报错,则写入列表
read_list.append(value)
#获取对应的关键字,并累计计数
for keyword in blog.keywords.all():
keyword_id = keyword.id
keywords[keyword_id] = keywords.get(keyword_id, 0) + 1
except:
pass通过该方法获取到最近阅读列表read_list和关键字字典keywords。该字典键名是关键字的id,键值是该关键字出现的次数。
关键字出现的次数可作为访客阅读喜欢的权重。某些类型的博客阅读越多,就说明该访客偏好这些内容。
接着,再通过关键字字典获取对应的博客列表。该博客列表为候选列表。
#获取关键字对应的博文,作为推荐候选
blogs = {}
for keyword_id, k_times in keywords.items():
keyword = Keyword.objects.get(id=keyword_id)
blogs_sub = keyword.blog_set.all()
#累计统计次数
for blog in blogs_sub:
blog_id = blog.id
#排除最近阅读列表中存在的
if not blog_id in read_list:
blogs[blog_id] = blogs.get(blog_id, 0) + k_times #1*k_times最后一句代码,加上k_times原本是1*k_times。原本加1即可,但关键字是有权重。需要乘以该关键字出现的次数,省略为k_times。同样该候选列表是一个字典,键名是博客id,键值是权重。该权重越大,说明访客偏好越大。但访客可能已经阅读过了这个字典中的博文,所以需要剔除去掉。
另外,我想获取的智能推荐列表需要10篇文章。可能上面的候选列表不足10个。不足10个就使用之前随机推荐的方法产生。
也可能该候选列表超过10个,那么需要根据权重数排序,获取最高的前10个。
引用numpy库:
import numpy as np
代码如下:
#把数据和键值分离
blog_ids = blogs.keys()
blog_times = blogs.values()
list_num = 10
if len(blog_ids)<list_num:
#随机推荐个数 = 候选列表个数 + 最近阅读个数 + 10
rand_count = list_num + len(blog_ids) + len(read_list)
rand_list = Blog.objects.order_by('?')[:rand_count]
#随机列表剔除候选列表和最近阅读的(随机列表与候选列表和最近阅读列表的并集取差集)
rand_ids = map(lambda x:x.id, rand_list)
rand_ids = list(set(rand_ids) - (set(read_list) | set(blog_ids)))
#补全推荐列表
like_list = blog_ids + rand_ids
like_list = like_list[:list_num]
elif len(blog_ids)==list_num:
#刚好10个,直接使用该列表
like_list = blog_ids
else:
#大于10个,取比重最高的前10个
dataset = np.array(blog_times) * -1 #每个元素乘以-1
sorted_dataset = dataset.argsort() #升序排序(相当于乘-1之前倒序排序)
like_list = []
for i in range(list_num):
like_list.append(blog_ids[sorted_dataset[i]])这部分代码,有两个地方需要注意:
1)不足10个采用集合运算,快速剔除重复的部分。

2)大于10个,用numpy排序,快速获取前10个。有兴趣可以了解argsort方法。
最后,再把这些数组组成json,返回给前端。该like_blogs方法全部代码如下。
引用部分:
#coding:utf-8 from django.http import HttpResponse from django.core.urlresolvers import reverse #url逆向解析 import numpy as np import json import datetime, time
代码部分:
#智能推荐
def like_blogs(request):
data = {}
try:
#读取最近阅读的博客id列表
cookie_name = "new_readed"
cookie_value = request.COOKIES.get(cookie_name, '')
#把字符串转成数字
value_list = cookie_value.split('|')
read_list = []
keywords = {}
for value in value_list:
try:
value = int(value)
blog = Blog.objects.get(id=value)
#若上面两步没有报错,则写入列表
read_list.append(value)
#获取对应的关键字,并累计计数
for keyword in blog.keywords.all():
keyword_id = keyword.id
keywords[keyword_id] = keywords.get(keyword_id, 0) + 1
except:
pass
#获取关键字对应的博文,作为推荐候选
blogs = {}
for keyword_id, k_times in keywords.items():
keyword = Keyword.objects.get(id=keyword_id)
blogs_sub = keyword.blog_set.all()
#累计统计次数
for blog in blogs_sub:
blog_id = blog.id
#排除最近阅读列表中存在的
if not blog_id in read_list:
blogs[blog_id] = blogs.get(blog_id, 0) + k_times #1*k_times
#把数据和键值分离
blog_ids = blogs.keys()
blog_times = blogs.values()
list_num = 10
if len(blog_ids)<list_num:
#随机推荐个数 = 候选列表个数 + 最近阅读个数 + 10
rand_count = list_num + len(blog_ids) + len(read_list)
rand_list = Blog.objects.order_by('?')[:rand_count]
#随机列表剔除候选列表和最近阅读的(随机列表与候选列表和最近阅读列表的并集取差集)
rand_ids = map(lambda x:x.id, rand_list)
rand_ids = list(set(rand_ids) - (set(read_list) | set(blog_ids)))
#补全推荐列表
like_list = blog_ids + rand_ids
like_list = like_list[:list_num]
elif len(blog_ids)==list_num:
#刚好10个,直接使用该列表
like_list = blog_ids
else:
#大于10个,取比重最高的前10个
dataset = np.array(blog_times) * -1 #每个元素乘以-1
sorted_dataset = dataset.argsort() #升序排序(相当于乘-1之前倒序排序)
like_list = []
for i in range(list_num):
like_list.append(blog_ids[sorted_dataset[i]])
#返回数据
data['code'] = 0
data['message'] = "OK"
items = []
for blog_id in like_list:
blog = Blog.objects.get(id = blog_id)
item = {}
item['caption'] = blog.caption
item['url'] = reverse('detailblog', args = [blog_id,])
items.append(item)
data['items'] = items
except Exception as e:
data['code'] = 400
data['message'] = e.message
finally:
return HttpResponse(json.dumps(data), content_type = 'application/json')再加上urls路由设置:
url(r'^get_like_blogs$', like_blogs, name='get_like_blogs'),
我采用ajax访问该地址,获取数据:
<div class="side-list">
<h4>
<span class="glyphicon glyphicon-book"></span>
猜你喜欢
</h4>
<ul id="like_blogs_list">
<li>猜测中,请稍等...</li>
</ul>
</div>
<script type="text/javascript">
//获取猜测列表
$.ajax({
type:"GET",
url:"{%url 'get_like_blogs'%}",
cache:false,
dataType:'json',
success:function(data){
$("#like_blogs_list").children().remove(); //移除子节点
if(data['code']==0){
for (var i = 0; i < data['items'].length; i++) {
var url = data['items'][i]['url'];
var caption = data['items'][i]['caption'];
var str = '<li><a href="'+url+'" target=_blank>'+caption+'</a></li>';
$("#like_blogs_list").append(str);
};
}else{
$("#like_blogs_list").append('<li>'+data['message']+'</li>');
}
},
error:function(XMLHttpRequest, textStatus, errorThrown){
$("#like_blogs_list").children().remove(); //移除子节点
$("#like_blogs_list").append('<li>猜测卡壳了,猜不了T_T</li>');
}
});
</script>首先,先不点击打开任何博文,猜你喜欢的列表都是随机推荐的。

接着,再打开其中一篇博文。我打开一篇Excel的(当然,大部分博文的关键字我已经添加上去了)。猜你喜欢的列表发生了变化,前面几个都是和Excel相关的文章。

接着再多点击其他博文,总体测试效果还是相当不错。
18168819160@163.com
QAQ我怎么试都获取不到json的值,页面上显示不出来(爆哭)😭
2019-01-26 17:06 回复