Task03 离线物料系统的构建
本节内容主要讲的是上图中红框框起来的部分,也就是离线自动化构建用户和物料的画像,这部分内容在新闻推荐系统中是为系统源源不断添加新物料的途径,由于我们的物料是通过爬虫获取的,所以还需要对爬取的数据进行处理,也就是构造新闻的画像。对于用户侧的画像则是需要每天将新注册的用户添加到用户画像库中,对于在系统中产生了行为的用户,我们还需要定期的更新用户的画像(长短期)。下面分别从物料侧和用户侧两个方面来详细解释这两类画像在系统中是如何自动化构建的。
1 离线物料系统基本流程
- 新闻物料爬取:主要采用
scrapy
爬虫工具,在每天晚上23点将当天的新闻内容从网页中进行抓取,存入MongoDB的SinaNews
数据库中。 - 物料画像构建:更新当天新闻动态画像,将用户对前一天新闻的交互,包括阅读、点赞和收藏等行为(动态)存入Redis中;对物料画像处理,将新闻静态和动态数据分别存入对应的Redis中。
- 用户画像构建:用户通过前端注册页面,进行用户注册,将用户信息存入MySQL的用户注册信息表(register_user)中;用户通过阅读、点赞及收藏新闻,将用户行为数据存入MySQL的用户阅读信息表(user_read)、用户点赞信息表(user_likes)和用户收藏信息表(user_collections);将当天的新注册用户基本信息及其行为数据构造用户画像,存入MongoDB中的
UserProtrai
集合中。 - 自动化构建画像:将物料画像构建和用户画像构建进行整合,构建整个自动化流程。
2 scrapy与新闻爬取
2.1 创建新闻爬取项目
使用scrapy startproject sinanews
创建项目
sinanews--------------------------------------项目python模块
+---__init__.py-------------------------------模块初始化文件
+---items.py----------------------------------items文件,用于定义类对象
+---middlewares.py----------------------------中间件,用于配置请求头、代理、cookie、会话维持等
+---pipelines.py------------------------------管道文件,用于将爬取的数据进行持久化存储
+---run.py------------------------------------单独运行项目的脚本,用于自动化爬取
+---settings.py-------------------------------配置文件,用于配置数据库
+---spiders-----------------------------------爬取新闻模块
| +---__init__.py---------------------------爬取新闻模块初始化文件
| +---sina.py-------------------------------爬取新浪新闻的具体逻辑,解析网页内容
scrapy.cfg------------------------------------项目配置文件
2.2 爬取流程
- 通过调用
start_request()
方法开始爬取,并作为返回请求的迭代器 - 通过提供的URL链接,由
scrapy
下载,调用parse()
方法进行处理 - 通过回调
parse()
方法,解析网页并返回在items.py
中定义的对象,可由下一层parse_content()
方法处理新闻内容 - 通过回调
parse_content()
方法,解析新闻内容
2.3 新闻爬取业务逻辑
- 每天定时从新浪新闻网站中爬取新闻数据,存入MongoDB数据库中,通过程序监控爬取新闻的状态
- 每天只爬取当天的新闻,通过单独去重处理重复的新闻
2.4 获取新浪新闻网页的请求链接
其中:
pageid
:固定,153lid
:新闻分类,根据字典进行映射num
:每页新闻个数page
:页号
2.5 核心文件的主要内容
items.py
:定义新闻数据的对象sina.py
:主要爬取新闻数据,通过新闻分类映射字典,遍历获取新闻网页,解析网页获取新闻数据pipelines.py
:连接MongoDB数据库,处理每一条数据,如果在24小时范围内,则存储到MongoDB的SinaNews
库的new_<current_date>
集合中
3 物料画像构建
新物料来源
首先要说的就是新物料的来源,物料是通过每天在新闻网站上爬取获取的,这里要说明的一点就是,新闻爬取是每天凌晨的时候爬取前一天的新闻,这么做的原因是可以爬到更多的物料,缺点就是物料的时效性会延迟一天,新爬取的物料存储在MongoDB中。
物料画像的更新主要有一下几个方面:
- 新物料画像添加到物料库中
- 旧物料画像,通过用户的交互记录进行更新
具体的逻辑就是遍历今天爬取的所有文章,然后通过文章的title来判断这篇文章是否已经在物料库中(新闻网站有可能有些相同的文章会出现在多天)来去重。然后再根据我们定义的一些字段,给画像相应的字段初始化,最后就是存入画像物料池中。
关于旧物料画像的更新,这里就需要先了解一下旧物料哪些字段会被用户的行为更新。下面是新闻列表展示页,我们会发现前端会展示新闻的阅读、喜欢及收藏次数。而用户的交互(阅读、点赞和收藏)会改变这些值。
3.1 物料画像更新的业务逻辑
- 将新物料画像添加到物料特征库(MongoDB的
NewsRecSys
库的FeatureProtrail
集合)中 - 将用户的行为记录(阅读、点赞和收藏)更新到旧物料(新闻动态画像)
- 将所有的物料特征库数据复制到
RedisPortrail
集合中,用于给前端展示
3.2 核心函数的代码逻辑
代码位于materials/material_process/news_protrait.py
update_new_items()
方法:添加新物料到物料库(FeatureProtrail
集合)update_dynamic_feature_protrail()
方法:获取Redis中的用户行为(又称“新闻动态画像”),更新物料库中的点赞(likes
)、收藏(collections
)和阅读次数(read_num
)字段的值update_redis_mongo_protrail_data()
方法:删除在RedisPortrail
集合中的数据(该集合存放前一天的物料库数据,作为当天Redis中的新闻数据备份),把最新的物料库数据存入该集合中,去掉一些不用给前端展示的字段内容(类似于DO->VO)
3.3 将更新之后的物料添加到Redis中
-
使用方法:
news_detail_to_redis()
方法,代码位于materials/material_process/news_to_redis.py
-
具体代码逻辑:删除所有Redis中的内容,将新闻静态数据(news_id、title、ctime、content、cate、url)存入Redis[1]中,将新闻动态画像(likes、collections、read_num)存入Redis[2]中,具体格式如下:
# 当天新闻静态数据(static_news_detail:新闻ID :{新闻ID、标题、发布时间、新闻内容、类别、URL链接}) static_news_info_db_num = 1 # 动态新闻画像(dynamic_news_detail:新闻ID :{用户行为:阅读、点赞、收藏}) dynamic_news_info_db_num = 2Copy to clipboardErrorCopied
4 用户画像构建
对于用户画像的更新来说主要分为两方面:
- 新注册用户画像的更新
- 老用户画像的更新
由于我们系统中将所有注册过的用户都放到了一个表里面(新、老用户),所以每次更新画像的话只需要遍历一遍注册表中的所有用户。再说具体的画像构建逻辑之前,得先了解一下用户画像中包含哪些字段
4.1 用户画像更新的业务逻辑
- 将用户的新闻曝光数据保存到MySQL中,用于进行去重
- 更新用户画像,主要更新用户的静态信息和动态信息(阅读、点赞、收藏等相关指标数据),存储到MongoDB中
4.2 核心函数的代码逻辑
user_exposure_to_mysql()
方法(代码位于materials/user_process/user_to_mysql.py
):将用户的新闻曝光数据从Redis(用户ID:{(新闻ID: 曝光时刻)})存入到MySQL中exposure_<current_date>
表中update_user_protrail_from_register_table()
方法(代码位于materials/user_process/user_protrail.py
):将当天新用户(注册用户)和老用户的静态信息和动态信息(阅读、点赞、收藏等相关指标数据)都添加到用户画像库中(MongoDB的NewsRecSys
库的UserProtrail
集合)_user_info_to_dict()
方法(代码位于materials/user_process/user_protrail.py
):将用户基本属性和用户的动态信息进行组合,对于已存在的指标数据(分为爱好和收藏,每个分类都包括历史喜欢最多的Top3的新闻类别、历史喜欢新闻的Top3的关键词、用户喜欢新闻的平均热度、用户15天内喜欢的新闻数量)进行更新,对于未存在的指标数据进行初始化
5 自动化构建画像
上面分别对用户侧和物料侧的画像构建进行了介绍,接下来就是要将上面所有的过程都自动化运行,并且设置好定时任务,其实最核心的一点就是一定要在清除redis数据之前,完成用户和物料画像的构建,下面是构建整个自动化的流程。
物料更新脚本:process_material.py
from material_process.news_protrait import NewsProtraitServer
from material_process.news_to_redis import NewsRedisServer
def process_material():
"""物料处理函数
"""
# 画像处理
protrail_server = NewsProtraitServer()
# 处理最新爬取新闻的画像,存入特征库
protrail_server.update_new_items()
# 更新新闻动态画像, 需要在redis数据库内容清空之前执行
protrail_server.update_dynamic_feature_protrail()
# 生成前端展示的新闻画像,并在mongodb中备份一份
protrail_server.update_redis_mongo_protrail_data()
if __name__ == "__main__":
process_material()
用户画像更新脚本: process_user.py
from user_process.user_to_mysql import UserMysqlServer
from user_process.user_protrail import UserProtrail
"""
1. 将用户的曝光数据从redis落到mysql中。
2. 更新用户画像
"""
def process_users():
"""将用户数据落 Mysql
"""
# 用户mysql存储
user_mysql_server = UserMysqlServer()
# 用户曝光数据落mysql
user_mysql_server.user_exposure_to_mysql()
# 更新用户画像
user_protrail = UserProtrail()
user_protrail.update_user_protrail_from_register_table()
if __name__ == "__main__":
process_users()
redis数据更新脚本:update_redis.py
from material_process.news_protrait import NewsProtraitServer
from material_process.news_to_redis import NewsRedisServer
def update():
"""物料处理函数
"""
# 新闻数据写入redis, 注意这里处理redis数据的时候是会将前一天的数据全部清空
news_redis_server = NewsRedisServer()
# 将最新的前端展示的画像传到redis
news_redis_server.news_detail_to_redis()
if __name__ == "__main__":
update()
最后将上面三个脚本穿起来的shell脚本offline_material_and_user_process.sh:
#!/bin/bash
python=/home/recsys/miniconda3/envs/news_rec_py3/bin/python
news_recsys_path="/home/recsys/news_rec_server"
echo "$(date -d today +%Y-%m-%d-%H-%M-%S)"
# 为了更方便的处理路径的问题,可以直接cd到我们想要运行的目录下面
cd ${news_recsys_path}/materials
# 更新物料画像
${python} process_material.py
if [ $? -eq 0 ]; then
echo "process_material success."
else
echo "process_material fail."
fi
# 更新用户画像
${python} process_user.py
if [ $? -eq 0 ]; then
echo "process_user.py success."
else
echo "process_user.py fail."
fi
# 清除前一天redis中的数据,更新最新今天最新的数据
${python} update_redis.py
if [ $? -eq 0 ]; then
echo "update_redis success."
else
echo "update_redis fail."
fi
echo " "
6 总结
本次任务,主要介绍了离线物料系统的构建,包括新闻物料爬取、物料画像构建、用户画像构建和自动化构建画像;
- 新闻物料爬取:采用
scrapy
爬虫技术,将新闻内容从网页上爬取存入MongoDB数据库 - 物料画像构建:更新当天新闻动态画像,并对物料画像进行处理,得到特征物料,并将新闻的静态和动态数据分别存入相应的Redis中
- 用户画像构建:保存用户的新闻曝光数据并存入MySQL中,更新用户画像数据并存入MongoDB中
- 自动化构建画像:使用
crontab
命令,定时执行整合后的shell
脚本
第一次看爬虫还有构建画像的东西,因为系统的前后端都要与离线物料系统进行交互,所以弄清楚物料的整理和流向是理清架构的一个很好的线索。