关于本站
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 回复