因为一部遮天,我用三种语言实现了腾讯国漫评分系统(一)

前言

"仙路尽头谁为峰,一见无始道成空。"

2013年初读遮天,十年后遮天的动漫正式上线。依稀记得高中记忆力"三十年河东三十年河西"的萧炎和独断万古的荒天帝。不知道当年经典绝伦的小说,如今改成动漫口碑如何。

于是就打开腾讯视频看看评分,每个视频都要点开才能看到评分和介绍。随即就萌生了用技术整合国漫评分内容的想法。最后历经一周,完成了一个简单的评分展示系统。

静态展示:

动态展示:

一. 国漫数据采集

分析评分数据

首先进入一个动漫的播放页,页面主要有左侧的评分数据,和右侧的简介数据。

评分数据获取

json 复制代码
POST https://pbaccess.video.qq.com/trpc.message.grade_adapter.GradeService/GetGradeDetail?video_appid=3000010&vplatform=2

{
  "cid": "mcv8hkc8zk8lnov"
}

先研究一下评分数据如何获取,在控制台可以找到从后台请求的数据内容。

从请求返回的数据可以看到,可以获取到评分、点评人数、推荐比例等数据。接着对url进行分析,看如何才能获取到这些数据。

可以看到GetGradeDetail 的url,只有一个cid参数。

简介数据获取

json 复制代码
POST https://pbaccess.video.qq.com/trpc.universal_backend_service.page_server_rpc.PageServer/GetPageData?video_appid=3000010&vplatform=2&vversion_name=8.2.97
{
  "req_from": "web",
  "cid": "mzc00200s86alsp",
  "vid": "a0047tbsjbs",
  "lid": "",
  "page_type": "detail_operation",
  "page_id": "detail_page_introduction"
}

这里需要两个参数,cid和vid,根据我的理解,cid就是cartoon_id,是一部动漫的唯一标识,vid是video_id,是每部动漫每一集的唯一标识。那么就来看cid和vid是如何来获取。

我是通过国漫列表页跳转到播放页的,所以就去列表页看看如何获取cid。

国漫列表

进入腾讯视频的国漫列表,看一下国漫列表。

这种有侧边栏的网站,基本上都是异步请求数据,然后渲染到展示区域。下拉动漫列表:

可以看到动漫区域一直在刷新,这样就肯定了之前的想法。

cid

1. 分析请求

F12进入开发者工具,通过搜索能功能找到对应的url。

这里可以看到每部动漫的cid。然后对playload进行分析,查看请求的参数信息。

参数是一个json串,有很多参数,我们通过查看js源码,来确定这些参数是如何生成的。

2. 分析参数

通过搜索来找到对应的请求部分源码:

可以看到请求参数里面有:x、l、R、n、video_guid几个可变参数。通过分析和debug最后得知,每次请求的变化的只有x,即page_index和page,其他的参数都是固定值。

这里就举例一下:比如channel_id对应的n为什么是100119。

最后一行有个vS()方法,就是调用了上面的请求,i.value对应的就是形参n。i的生成可以在第五行代码中看到,用了一个lambda函数,遍历过滤n.channelListData.channleList。打印此变量:

过滤条件就是是channel_ename == t.channelId,这里的t.channelId通过打印发现是"cartoon",可以看到channel_ename为cartoon对应的channel_id为100119.

那么t是怎么来的呢,t是setup的参数,而setup是用来解构props的,所以t就是props,props在vue中用来接收父组件传值。

所以t就是父组件传给渲染动漫列表的组件一个参数值,其中包含channel_id。接下来的工作就是获取vid。

3. 获取cid

上面已经分析完请求了,接着就是利用python的requests模块,构建请求.通过对返回的json分析,获取目标数据。

从最后一行代码可以看到,数据在CardList[1]中获取,然后层层解构,遍历获取cid。

这样就将第一页前30个国漫的cid获取到了。当我修改变量获取第二页数据,即index = 1的时候,程序开始报下标越界的错误,那么应该是没有获取到数据,我们debug一下。

可以看到第一页数据,是从CardList[1] 获取,第二页数据就变成了CardList[0]。这是因为请求第一页的时候,需要返回筛选条件列表,放在了CardList[0]中。到了第二页就不需要了,所以这里要修改代码做判断。

然后就是对爬取的index做一个限制,目前设置为20次,即爬取20页,爬取每一页sleep(3)。这样就可以获取所有动漫的cid。

vid

我们在国漫列表页点击连接进入播放页的时候,是先进入v.qq.com/x/cover/[ci...

我们先对cid.html页面进行分析。

在html网页中发现了vid的列表信息,对于网页中数据的提取一般使用正则表达式。这样在我们获取了cid之后,就能获取vid。

至此,cid和vid都获取到了。

获取评分和简介

首先构建请求参数,cid和vid设置为空字符。

将cid放到参数中,发起评分grade_url请求。

来获取image_url(封面图片url)、热度、评分、推荐区间比例等数据。接着将cid、vid(从vids列表中任取一个即可)放到动漫简介请求参数中,发起请求。

从返回值可以获取到各种标签数据,对json解析,获取自己需要的数据。

封面图片处理

从获取的image_url中可以下载封面图片,图片存储我准备了三种方案:

  1. 将image_url直接存入,通过url直接引用
  2. 将图片下载到本地目录,然后通过命名的方式与动漫信息关联
  3. 将图片转换成base64存入到MySQL中

方案一可能会在请求的时候出现跨域等问题,而且必须联网,从而请求失败。方案二将图片下载到本地,比较方便。方案三就是会对服务器网络和MySQL的IO造成压力,这里是测试,所以问题不大,这里我选用了方案三。

从image_url中获取图片bytes,然后经过一些工具类转换成base64字符串。

python 复制代码
urllib.request.urlretrieve(url=image_url, filename='tmp.jpg')
image_source = Image.open('tmp.jpg')
byte_source = BytesIO()
image_source.save(byte_source, format="JPEG")
byte_data = byte_source.getvalue()
base64_str = base64.b64encode(byte_data).decode("ascii")

从image_url请求的是avif的bytes,这里我直接使用urllib将图片下载保存成jpg格式。然后利用Image和BytesIO模块将二进制转换成base64的字符串。

在img标签中,通过src引入base64和引入图片路径是一样的效果。

但是这个方案在最后又被否决了,原因就是:转换成base64之后,MySQL中的varchar和Text都装不下,所以我又选择了方案二,将图片按照cid命名下载到了本地。

数据存储

设计一个存储模块,将上面的评分数据和简介数据存储到MySQL中,这里先根据定义表、数据字段。

建表

sql 复制代码
create table cartoon(
  cid varchar(35) not null primary key,
  name varchar(50),
  title varchar(100),
  score varchar(5),
  promoter_score varchar(20),
  evaluate_number varchar(30),
  type_ varchar(4) comment '类型id',
  year varchar(10),
  tag_text varchar(10) comment 'VIP' ,
  main_genres varchar(20) comment '类型',
  hotval varchar(20) comment '热度',
  episode_all int(8) comment '全集',
  dimension varchar(100) comment '评分比例',
  update_notify_desc varchar (100) comment '更新周期',
  update_time varchar(20) comment '自定义数据修改日期',
  cover_description text comment '描述'
) default charset='utf8';

进入MySQL执行建表语句

开发程序

利用python的pymysql开发数据存储模块,这里一共简单实现了两个功能:

  1. 根据cid判断数据库中有没有这条数据存在
python 复制代码
sql = f"select cid from cartoon where cid = '{cid}'"
cursor = conn.cursor()
cursor.execute(sql)
result = cursor.fetchone()
if result:
    # 可以更新评分、热度、时间等字段
    print(name, '已经存在于数据库中...')
    continue

如果存在于数据库的话,可以执行update更新评分、热度等信息,这里先不实现,只是使用continue跳出循环,然后采集下一条数据。在程序的运行过程中,如果出现异常,重新启动程序,这些数据就可以避免再重新获取。

  1. 数据存储到MySQL
python 复制代码
sql = f'''insert into cartoon (cid, name, title, score, promoter_score, evaluate_number, type_, year, tag_text, main_genres, hotval, episode_all, dimension, update_notify_desc, update_time, cover_description) 
     values ('{cid}', '{name}', '{title}', '{score}', '{promoter_score}', '{evaluate_number}', '{type_}', '{year}', '{tag_text}', '{main_genres}', '{hotval}', '{episode_all}', '{dimension}', '{update_notify_desc}', '{update_time}', '{cover_description}')'''
cursor = conn.cursor()
cursor.execute(sql)
conn.commit()

启动程序,开始爬取数据。

最后在数据库中查看爬取的国漫信息。

数据采集优化

上面请求的数据都是json格式,因为不是所有的json返回的都是全字段,很多的json都没有一些字段。所以在爬取过程中,需要根据报错信息一直调整自己的代码。

例如在解析字符串的时候,判断json里是否有这个字段,json中的json是否是NoneType,否则都会报错。下面就是针对于评分数据json的处理:

从图中可以看到,动漫信息可能是从enough 或者lack字段获取,而且还有是NoneType。我对这种情况的处理就是:如果没有评分数据,通过continue跳出这个国漫信息的爬取。

结语

本篇文章详细介绍了,如何使用python爬虫获取腾讯国漫信息,从程序开发思路和程序设计的脚步逐步深入,采集数据放到了MySQL中。下一篇文章会讲讲使用vue如何开发评分系统的前端页面。

相关推荐
z千鑫5 分钟前
【人工智能】深入理解PyTorch:从0开始完整教程!全文注解
人工智能·pytorch·python·gpt·深度学习·ai编程
MessiGo29 分钟前
Python 爬虫 (1)基础 | 基础操作
开发语言·python
Tech Synapse34 分钟前
Java根据前端返回的字段名进行查询数据的方法
java·开发语言·后端
.生产的驴35 分钟前
SpringCloud OpenFeign用户转发在请求头中添加用户信息 微服务内部调用
spring boot·后端·spring·spring cloud·微服务·架构
微信-since811921 小时前
[ruby on rails] 安装docker
后端·docker·ruby on rails
肥猪猪爸1 小时前
使用卡尔曼滤波器估计pybullet中的机器人位置
数据结构·人工智能·python·算法·机器人·卡尔曼滤波·pybullet
LZXCyrus1 小时前
【杂记】vLLM如何指定GPU单卡/多卡离线推理
人工智能·经验分享·python·深度学习·语言模型·llm·vllm
Enougme1 小时前
Appium常用的使用方法(一)
python·appium
懷淰メ1 小时前
PyQt飞机大战游戏(附下载地址)
开发语言·python·qt·游戏·pyqt·游戏开发·pyqt5
hummhumm2 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j