原来为了简单方便,自己小网站上的文章页的相关内容推荐就是从数据库里随机抽取数据来填充一个列表,所以一点相关性都没有,更本没有办法引导用户去访问推荐内容。

算法选择

如何能做到相似内容的推荐呢,碍于小网站还跑在虚拟主机上(对的,连一个自己完整可控的服务器都没有),所以可以想的办法不多,条件限制在 只能用PHP+MySql。所以我想到的办法就是通过Tags来匹配相似文章进行推荐。如果两篇文章的TAGS 比较相似

比如:文章A 的TAGS为: [A,B,C,D,E]
文章B 的 TAGS 为:[A,D,E,F,G]
文章C 的 TAGS 为:[C,H,I,J,K]

通过眼睛我们能很方便的发现,文章B和文章A更为相似,因为它们有三个关键字相同分别为:[A,D,E],哪如何用计算机来判断它们的相似度呢,这里我们用jaccard相似度的最基本应用来计算它们的相似度

jaccard相似度

给定两个集合A,B,Jaccard 系数定义为A与B交集的大小与A与B并集的大小的比值,定义如下:

文章A和文章B的交集为 [A,D,E],大小为3,并集为[A,B,C,D,E,F,G],大小为7,3/7=0.4285...
而文章A和文章C交集为 [C],大小为1,并集为[A,B,C,D,E,H,I,J,K],大小为9, 1/9=0.11111...

这样就可以得出文章A,B比文章A,C更为相似,有了这个算法,计算机就可以来判断两篇文章的相似度了。

具体的推荐思想

给定一篇文章,获取该文章的关键字TAGS,然后通过以上算法去数据库比对所有文章的相似度,获取最相似的N篇文章进行推荐。

实现过程

第一 TAGS的获取

文章的TAGS是通过TF-IDF算法,提取文章中的高频词,选取N个作为TAGS,对于中文的文章来说还涉及到一个中文分词的问题,因为是虚拟主机的关系,这步的工作我用python(为什么用Python ,jieba分词,真香)在本地写了一个程序,完成所有文章的分词,词频统计,生成TAGS,并写回服务器的数据库。由于本文是写推荐的算法,所以分词和建立TAGS的部分就不具体展开了,而且不同的系统有不同的TAGS建立方式。

第二 TAGS的存储

建立两张表,用于存储TAGS
tags,用于存所有tag的名称


+-------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+------------+------+-----+---------+-------+
| tag | text | YES | | NULL | |
| count | bigint(20) | YES | | NULL | |
| tagid | int(11) | NO | PRI | 0 | |
+-------+------------+------+-----+---------+-------+

tag_map 建立tag和文章的映身关系。


+-----------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------+------+-----+---------+-------+
| id | bigint(20) | NO | PRI | 0 | |
| articleid | bigint(20) | YES | | NULL | |
| tagid | int(11) | YES | | NULL | |
+-----------+------------+------+-----+---------+-------+

tag_map存的数据类似如下:


+----+-----------+-------+
| id | articleid | tagid |
+----+-----------+-------+
| 1 | 776 | 589 |
| 2 | 776 | 471 |
| 3 | 776 | 1455 |
| 4 | 776 | 1287 |
| 5 | 776 | 52 |
| 6 | 777 | 1386 |
| 7 | 777 | 588 |
| 8 | 777 | 109 |
| 9 | 777 | 603 |
| 10 | 777 | 1299 |
+----+-----------+-------+

其实做相似推荐的时候,只需要用到tag_map表就可以了,因为tagid和tag name 是一一对应的。

具体编码

1.获取所有文章对应的TAGID


mysql> select articleid, GROUP_CONCAT(tagid) as tags from tag_map GROUP BY articleid;
+-----------+--------------------------+
| articleid | tags |
+-----------+--------------------------+
| 12 | 1178,1067,49,693,1227 |
| 13 | 196,2004,2071,927,131 |
| 14 | 1945,713,1711,2024,49 |
| 15 | 35,119,9,1,1180 |
| 16 | 1182,1924,2200,181,1938 |
| 17 | 46,492,414,424,620 |
| 18 | 415,499,153,567,674 |
| 19 | 1602,805,691,1613,194 |
| 20 | 2070,1994,886,575,1149 |
| 21 | 1953,1961,1534,2038,1393 |
+-----------+--------------------------+

通过以上SQL,可以一次性查询所用文章,极其对应的所有tag
在PHP,我们可以把tags变成数组。


public function getAllGroupByArticleId(){
//缓存查询数据,因为这个是全表数据,而且不更新文章不会变化,便是每次推荐都要从数据库里获取一次数据,对性能肯定会有影响,所以做个缓存。
if($cache = CacheHelper::getCache()){
return $cache;
}
$query_result = $this->query('select articleid, GROUP_CONCAT(tagid) as tags from tag_map GROUP BY articleid'); $result = [];
foreach($query_result as $key => $value){
//用articleid 做key ,值是该id下的所有tagID数组。
$result[$value['articleid']] = explode(",",$value['tags']);
} CacheHelper::setCache($result, 86400); return $result; }

有了这个的返回结果,就比较好办了,接下去的工作就是去应用jaccard相似度这个算法了,具体就看代码吧。


/**
* [更据指定文章返回相似的文章推荐]
* @param $articleid 指定的文章ID
* @param $top 要返回的推荐条数
* @return Array 推荐条目数组
*/
function getArticleRecommend($articleid, $top = 5){
if($cache = CacheHelper::getCache()){
return $cache;
}
try{
$articleid = intval($articleid);
$m = new TagMapModel();
$all_tags = $m->getAllGroupByArticleId();//调用上面的函数返回所有文章的tags
$finded = $all_tags[$articleid];//因为上面是包含所有文章了,所以肯定包含了当前文章。 unset($all_tags[$articleid]);//把当前文章从数组中删除,不然自己和自己肯定是相似度最高了。 $jaccard_arr = []; //用于存相似度
foreach ($all_tags as $key => $value) {
$intersect =array_intersect($finded, $value); //计算交集
$union = array_unique(array_merge($finded, $value)); //计算并集 $jaccard_arr[$key] = (float)(count($intersect) / count($union));
} arsort($jaccard_arr); //按相似度排序,最相似的排最前面 $jaccard_keys = array_keys($jaccard_arr);//由于数组的key就是文章id,所以这里把key取出来就可以了
array_splice($jaccard_keys, $top);//获取前N条推荐 //到这里我们就已经得到了,最相似N篇文章的ID了,接下去的工作就是通过这几个ID,从数据库里把相关信息,查询出来就可以了 $articleModels = new \Api\Model\ArticleModel();
$recommendArticles = $articleModels->getRecommendByTag($jaccard_keys);
CacheHelper::setCache($recommendArticles, 604800); //缓存7天
return $recommendArticles;
} catch (\Exception $e) {
throw new \Exception("获取推荐文章错误");
}
}

虽然简单,短短几条代码,但是效果还是可以的,推荐的文章有了一定的相似度,肯定可以带来更好的用户体验,实例 ,你们可以看看 https://www.wx2share.com/Arti...

原文地址:https://segmentfault.com/a/1190000016389327

基于Tags的简单内容推荐的实现的更多相关文章

  1. 基于KNN的相关内容推荐

    如果做网站的内容运营,相关内容推荐可以帮助用户更快地寻找和发现感兴趣的信息,从而提升网站内容浏览的流畅性,进而提升网站的价值转化.相关内容 推荐最常见的两块就是“关联推荐”和“相关内容推荐”,关联推荐 ...

  2. [转] 基于 Apache Mahout 构建社会化推荐引擎

    来源:http://www.ibm.com/developerworks/cn/java/j-lo-mahout/index.html 推荐引擎简介 推荐引擎利用特殊的信息过滤(IF,Informat ...

  3. 基于 Apache Mahout 构建社会化推荐引擎

    基于 Apache Mahout 构建社会化推荐引擎 http://www.ibm.com/developerworks/cn/views/java/libraryview.jsp 推荐引擎利用特殊的 ...

  4. HBase在搜狐内容推荐引擎系统中的应用

    转自:http://www.aboutyun.com/thread-7297-1-1.html Facebook放弃Cassandra之后,对HBase 0.89版本进行了大量稳定性优化,使它真正成为 ...

  5. 基于RxJava2+Retrofit2简单易用的网络请求实现

    代码地址如下:http://www.demodashi.com/demo/13473.html 简介 基于RxJava2+Retrofit2实现简单易用的网络请求,结合android平台特性的网络封装 ...

  6. 基于modelsim-SE的简单仿真流程—上

    基于modelsim-SE的简单仿真流程 编写RTL功能代码 要进行功能仿真,首先得用需要仿真的模块,也就是RTL功能代码,简称待测试的模块,该模块也就是在设计下载到FPGA的电路.一个电路模块想要有 ...

  7. 基于Spark ALS构建商品推荐引擎

    基于Spark ALS构建商品推荐引擎   一般来讲,推荐引擎试图对用户与某类物品之间的联系建模,其想法是预测人们可能喜好的物品并通过探索物品之间的联系来辅助这个过程,让用户能更快速.更准确的获得所需 ...

  8. 基于lucene实现自己的推荐引擎

    基于lucene实现自己的推荐引擎 推荐常用算法之-基于内容的推荐 推荐算法

  9. (一)熟悉执行流程——基于ThinkPHP3.2的内容管理框架OneThink学习

    ThinkPHP作为国内具有代表性的PHP框架,经过多年的发展,受到越来越多公司与开发者的青睐.我也在忙里偷闲中抽出部分时间,来学习这个优秀的框架.在开始学习这个框架时,最好通过实例来学习,更容易结合 ...

随机推荐

  1. 《随笔》pyqt 获取 TreeWidget 选中项的内容

    感谢朋友支持本博客,欢迎共同探讨交流.因为能力和时间有限,错误之处在所难免.欢迎指正! 假设转载,请保留作者信息. 博客地址:http://blog.csdn.net/qq_21398167 原博文地 ...

  2. 19 个必须知道的 VS 快捷键

    本文将为大家列出在Visual Studio中常用的快捷键,正确熟练地使用快捷键,将大大提高你的编程工作效率. 项目相关的快捷键 Ctrl + Shift + B = 生成项目 Ctrl + Alt ...

  3. selenium实例学习地址

    一个完整的maven配置selenium webdriver工程实例   http://www.spasvo.com/ceshi/open/kygncsgj/Selenium/201312209580 ...

  4. 【cl】控制台执行Java程序

    1.设置CLASSPATH环境变量,指向package所在的目录,一般是项目文件夹的bin目录 2.执行 java  package.ClassName linux: 方法一: 步骤1:export ...

  5. 公司须要内部的地图服务,准备自己去开发可是成本太高,如今有没有专门为企业提供GIS地图开发的产品呀?大概价格多少?

    公司须要内部的地图服务,准备自己去开发可是成本太高,如今有没有专门为企业提供GIS地图开发的产品呀?大概价格多少?

  6. Linux实时查看日志,访问前10IP 和相关命令

    Nginx日志分析可以获得很多有用的信息,现在来试试最基本的,获取最多访问的前10个IP地址及访问次数. 既然是统计,那么awk是必不可少的,好用而高效. 命令如下: awk '{a[$1] += 1 ...

  7. WEEX学习网站

    https://github.com/alibaba/weex https://alibaba.github.io/weex/  官网     http://weex.help/topic/57792 ...

  8. [.Net] Excel导入导出各种方式分析

    1.引言 1.1解决哪些问题   现在很多公司用的导出基本上采用的通过gridView导出excel,此种导出存在以下几种问题 1.数据量大的时候有时导出有时会让浏览器卡死,因为导出的excel不是真 ...

  9. FZU2150 Fire Game

    题目: 两个熊孩子在n*m的平地上放火玩,#表示草,两个熊孩子分别选一个#格子点火,火可以向上向下向左向右在有草的格子蔓延,点火的地方时间为0,蔓延至下一格的时间依次加一.求烧完所有的草需要的最少时间 ...

  10. HIT Software Construction Lab6引发出来对锁的问题的探究

    前言 做完lab5开始做lab6了鸭,哈工大计算机学院的学生永不停歇.在做lab6的时候,我在想移动猴子是锁一整个ladder(ADT)还是只锁一个ladder的一个域Monkey数组呢?这两个好像差 ...