推荐阅读:
黑马头条推荐项目知识点总结(一)
https://blog.youkuaiyun.com/better_zhao/article/details/107239145
在上述步骤中,我们已经将业务数据和用户行为数据同步到了推荐系统数据库当中,接下来,我们就要对文章数据和用户数据进行分析,构建文章画像和用户画像。
本文我们主要讲解如何构建文章画像。文章画像由关键词和主题词组成,我们将每个词的 IDF 权重和 TextRank 权重的乘积作为关键词权重,筛选出权重最高的 K 个词作为关键词;将 TextRank 权重最高的 K 个词与 TF-IDF 权重最高的 K 个词的共现词作为主题词。
首先,在 Hive 中创建文章数据库 article,用于存放离线文章画像计算过程的中间表数据和最后的结果数据article_profile.
# 新建article数据库,存放文章画像相关的中间表和最后的结果表,
create database if not exists article comment "artcile information" location '/user/hive/warehouse/article.db/';
在开始计算前,先定义一个基类,设置spark初始化常用的公共部分,比如appName,执行的核数,启动方式等。避免每次都重复写那么多的代码,新建一个__init__.py文件(文章画像所有的工程文件都放在offline文件夹中),添加如下内容
from pyspark import SparkConf
from pyspark.sql import SparkSession
class SparkSessionBase(object):
SPARK_APP_NAME = None
SPARK_URL = "yarn"
SPARK_EXECUTOR_MEMORY = "2g"
SPARK_EXECUTOR_CORES = 2
SPARK_EXECUTOR_INSTANCES = 2
ENABLE_HIVE_SUPPORT = False
def _create_spark_session(self):
conf = SparkConf() # 创建spark config对象
config = (
("spark.app.name", self.SPARK_APP_NAME), # 设置启动的spark的app名称,没有提供,将随机产生一个名称
("spark.executor.memory", self.SPARK_EXECUTOR_MEMORY), # 设置该app启动时占用的内存用量,默认2g
("spark.master", self.SPARK_URL), # spark master的地址
("spark.executor.cores", self.SPARK_EXECUTOR_CORES), # 设置spark executor使用的CPU核心数,默认是1核心
("spark.executor.instances", self.SPARK_EXECUTOR_INSTANCES)
)
conf.setAll(config)
# 利用config对象,创建spark session
if self.ENABLE_HIVE_SUPPORT:
return SparkSession.builder.config(conf=conf).enableHiveSupport().getOrCreate()
else:
return SparkSession.builder.config(conf=conf).getOrCreate()
一、计算文章完整信息
为了计算文章画像,需要将文章信息表(news_article_basic)、文章内容表(news_article_content)及频道表(news_channel)进行合并,从而得到完整的文章信息,通常使用 Spark SQL 进行处理。
1.在hive中新建一张表article_data用于接收我们合并的完整文章信息
# 新建artice_data,存放合并后的内容,其中sentence:文章标题+内容+频道名字的合并结果
CREATE TABLE article_data(
article_id BIGINT comment "article_id",
channel_id INT comment "channel_id",
channel_name STRING comment "channel_name",
title STRING comment "title",
content STRING comment "content",
sentence STRING comment "sentence")
COMMENT "toutiao news_channel"
LOCATION '/user/hive/warehouse/article.db/article_data';
2.利用spark sql语句操作hive数仓,进行表合并工作
from offline import SparkSessionBase # 导入基类
class OriginArticleData(SparkSessionBase): #新建一个对象,继承基类
SPARK_APP_NAME = "mergeArticle"
SPARK_URL = "yarn"
ENABLE_HIVE_SUPPORT = True
def __init__(self):
self.spark = self._create_spark_session()
oa = OriginArticleData() # 新建OriginArticleData的对象,这样就可以调用这个对象的方法spark
oa.spark.sql("use toutiao") # 调用的spark使用sql语句的形式是spark.sql(这里写sql语句),使用头条数据库
# 根据文章的id,将hive中的前两个文章相关的表:news_article_basic中的article_id,channel_id,title和news_article_content中的content合并
# 这里采用内连接,避免两张表不同步,我们只取两张表都有内容的文章
basic_content = oa.spark.sql(
"select a.article_id, a.channel_id, a.title, b.content from news_article_basic a inner join news_article_content b on a.article_id=b.article_id)
到这一步,实现了需要的字段在basic_content中,注意spark sql生成的是DataFrame
baisc_content
结果如下所示
目前channel只有id,但是我们后续处理需要的是名字,所以还要通过这个表left join news_channel那张表得到channel_name
# Spark DataFrame提供了registerTempTable这样的接口,可以将DataFrame注册成临时表,便于后续的各种查询操作等。如select, join等。
basic_content.registerTempTable("temparticle")
channel_basic_content = oa.spark.sql("select t.*, n.channel_name from temparticle t left join news_channel n on t.channel_id=n.channel_id")
channel_basic_content
结果如下所示
3.合并相关字段内容至一个字段中
利用 concat_ws()
方法,将 channel_name, title, content 这 3 列数据合并为一列 sentence,并将结果写入文章完整信息表 article_data 中
# 利用concat_ws方法,将多列数据合并为一个长文本内容(频道,标题以及内容合并)
import pyspark.sql.functions as F
import gc
oa.spark.sql("use article") # 切换至hive数仓的article数据库(这个数据库我们之前创建用于存放文章画像的结果表和中间表)
# spark sql的DaraFrame查看数据的语法:df.select()
# pyspark.sql.functions有个函数concat_ws,可以将多个列合并为一列,第一个参数:指定列合并后之间的分隔符,后面就是要合并的列
# alias 给concat_ws合并后的新列起个名字
sentence_df = channel_basic_content.select("article_id", "channel_id", "channel_name", "title", "content", \
F.concat_ws(
",",
channel_basic_content.channel_name,
channel_basic_content.title,
channel_basic_content.content
).alias("sentence")
)
sentence_df.write.insertInto("article_data")
sentence_df
结果如下所示,文章完整信息包括 article_id, channel_id, channel_name, title, content, sentence,其中 sentence 为 channel_name, title, content 合并而成的长文本内容
二、计算tf-idf
前面我们得到了文章的完整内容信息,接下来,我们要先对文章进行分词,然后计算每个词的 TF-IDF 权重,将 TF-IDF 权重最高的 K 个词作为文章的关键词。首先,先回顾一下tf-idf香瓜内容
------------------------------------------回顾TF-IDF-------------------------------------------
TF-IDF(Term Frequency-Inverse Document Frequency, 词频-逆文件频率)是一种统计方法,用以评估一个词语对于一个文件集或一个语料库中的一份文件的重要程度,其原理可概括为:
一个词语在一篇文章中出现次数越多,同时在所有文档中出现次数越少,越能够代表该文章
计算公式:TF-IDF = TF * IDF
,其中:
词频(TF):一个词或短句在该文档中出现的次数
逆文档频率(IDF):总文档数除以出现该词的文档数