添加up画像统计量
This commit is contained in:
parent
008097764f
commit
4e792dc722
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
./venv/
|
||||
.idea/
|
||||
data/
|
||||
data/
|
||||
*/.DS_Store
|
109
main.py
109
main.py
@ -78,20 +78,19 @@ class BiliWebCrawler:
|
||||
# 获取视频最高分辨率(基于dimension对象)
|
||||
max_width = 0
|
||||
max_height = 0
|
||||
for format_info in video_data.get('formats', []):
|
||||
dimension = format_info.get('dimension', {})
|
||||
width = dimension.get('width', 0)
|
||||
height = dimension.get('height', 0)
|
||||
rotate = dimension.get('rotate', 0)
|
||||
dimension = video_data.get('dimension', {})
|
||||
width = dimension.get('width', 0)
|
||||
height = dimension.get('height', 0)
|
||||
rotate = dimension.get('rotate', 0)
|
||||
|
||||
# 处理视频旋转(当rotate=1时宽高互换)
|
||||
if rotate == 1:
|
||||
width, height = height, width
|
||||
# 处理视频旋转(当rotate=1时宽高互换)
|
||||
if rotate == 1:
|
||||
width, height = height, width
|
||||
|
||||
# 通过像素总量比较分辨率
|
||||
if (width * height) > (max_width * max_height):
|
||||
max_width = width
|
||||
max_height = height
|
||||
# 通过像素总量比较分辨率
|
||||
if (width * height) > (max_width * max_height):
|
||||
max_width = width
|
||||
max_height = height
|
||||
|
||||
# 将分辨率格式化为 "宽x高" 的字符串
|
||||
resolution_str = f"{max_width}x{max_height}" if max_width and max_height else "未知"
|
||||
@ -105,23 +104,96 @@ class BiliWebCrawler:
|
||||
tag_data = [tag['tag_name'] for tag in tag_json.get('data', [])]
|
||||
|
||||
info = {
|
||||
'BV号': self.bvid,
|
||||
'title': video_data.get('title', ''),
|
||||
'up主': video_data.get('owner', {}).get('name', ''),
|
||||
'up主名称': video_data.get('owner', {}).get('name', ''), # 新增字段
|
||||
'up主UID': video_data.get('owner', {}).get('mid', ''), # 新增UID字段
|
||||
'播放量': video_data.get('stat', {}).get('view', 0),
|
||||
'弹幕量': video_data.get('stat', {}).get('danmaku', 0),
|
||||
'点赞量': video_data.get('stat', {}).get('like', 0),
|
||||
'投币量': video_data.get('stat', {}).get('coin', 0),
|
||||
'收藏量': video_data.get('stat', {}).get('favorite', 0),
|
||||
'分享量': video_data.get('stat', {}).get('share', 0),
|
||||
'评论量': video_data.get('stat', {}).get('reply', 0),
|
||||
'发布时间的timestamp': video_data.get('pubdate', 0),
|
||||
'发布时间': time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(video_data.get('pubdate', 0))),
|
||||
'分区': video_data.get('tname', ''),
|
||||
'标签': tag_data,
|
||||
'视频方向': self._get_video_orientation(video_data.get('dimension', {})),
|
||||
'视频最高分辨率': resolution_str,
|
||||
'视频类型': video_data.get('copyright', 0),
|
||||
'视频分p数': len(video_data.get('pages', []))
|
||||
'视频类型': ["","自制", "转载"][video_data.get('copyright', 0)],
|
||||
'视频分p数': len(video_data.get('pages', [])),
|
||||
'视频总时长': self.get_video_length(video_data.get('pages', [])),
|
||||
'简介': video_data.get('desc', '').replace('\n', '\\n'),
|
||||
}
|
||||
|
||||
return info
|
||||
|
||||
def get_video_length(self,pages):
|
||||
"""获取视频总时长"""
|
||||
length = 0
|
||||
for page in pages:
|
||||
length += page.get('duration', 0)
|
||||
return length
|
||||
|
||||
def _get_video_orientation(self, dimension):
|
||||
"""判断视频方向(横屏/竖屏)"""
|
||||
width = dimension.get('width', 0)
|
||||
height = dimension.get('height', 0)
|
||||
rotate = dimension.get('rotate', 0)
|
||||
|
||||
# 处理视频旋转(90度或270度旋转时需要交换宽高)
|
||||
if rotate in [1, 3]:
|
||||
width, height = height, width
|
||||
|
||||
return "横屏" if width >= height else "竖屏"
|
||||
|
||||
# 在类方法中添加以下新方法(建议放在 get_video_info 方法之后)
|
||||
def get_up_info(self, mid):
|
||||
"""获取UP主详细信息"""
|
||||
if not mid:
|
||||
return None
|
||||
|
||||
url = f"https://api.bilibili.com/x/web-interface/card?mid={mid}&photo=false"
|
||||
resp = self._safe_request(url)
|
||||
if not resp:
|
||||
return None
|
||||
|
||||
try:
|
||||
data = resp.json().get('data', {})
|
||||
card = data.get('card')
|
||||
up_info = {
|
||||
'uid': mid,
|
||||
'昵称': card['name'],
|
||||
'性别': card['sex'],
|
||||
'头像': card['face'],
|
||||
'等级': card['level_info']['current_level'],
|
||||
'粉丝数': card['fans'],
|
||||
'稿件数': data['archive_count'],
|
||||
'获赞数': data['like_num'],
|
||||
}
|
||||
except Exception as e:
|
||||
print(f"解析UP主数据失败: {str(e)}")
|
||||
return None
|
||||
try:
|
||||
# 获取投稿列表
|
||||
archive_url = f'https://api.bilibili.com/x/space/arc/search?mid={mid}&ps=30'
|
||||
archive_resp = self._safe_request(archive_url)
|
||||
if archive_resp and archive_resp.status_code == 200:
|
||||
archive_data = archive_resp.json()
|
||||
print(archive_data)
|
||||
videos = archive_data.get('data', {}).get('list', {}).get('vlist', [])
|
||||
|
||||
# 计算30天前的时间戳
|
||||
month_ago = time.time() - 30 * 86400
|
||||
# 统计符合时间条件的视频
|
||||
recent_count = sum(1 for v in videos if v.get('created') > month_ago)
|
||||
up_info['近一个月投稿数'] = recent_count
|
||||
except Exception as e:
|
||||
print(f"获取投稿数据失败: {str(e)}")
|
||||
|
||||
return up_info
|
||||
|
||||
def get_danmaku(self):
|
||||
"""获取弹幕数据"""
|
||||
if not self.bvid:
|
||||
@ -277,6 +349,13 @@ class BiliWebCrawler:
|
||||
comments_filename = os.path.join(video_dir, f'{self.bvid}_{len(comments)}_comments.csv')
|
||||
self.save_to_csv(comments, comments_filename)
|
||||
|
||||
# 新增UP主信息记录
|
||||
print("正在获取UP主信息...")
|
||||
up_info = self.get_up_info(video_info.get('up主UID'))
|
||||
up_info['BV号'] = self.bvid
|
||||
up_csv_path = os.path.join(base_dir, 'up_info.csv')
|
||||
self.save_to_csv([up_info], up_csv_path, mode='a')
|
||||
|
||||
print(f"抓取完成!结果已保存到 {video_dir}/")
|
||||
else:
|
||||
print("未获取到视频信息,无法进行抓取。")
|
||||
|
56
指标体系构建.md
Normal file
56
指标体系构建.md
Normal file
@ -0,0 +1,56 @@
|
||||
## 外在属性指标
|
||||
### 1.基础流量指标
|
||||
需要视频BV号。
|
||||
- [x] 播放量
|
||||
- [x] 点赞量
|
||||
- [x] 投币量
|
||||
- [x] 收藏量
|
||||
- [x] 分享量
|
||||
- [x] 评论数
|
||||
- [x] 弹幕数
|
||||
### 2.up画像指标
|
||||
需要up主UID。
|
||||
- [x] 粉丝数
|
||||
- [x] 总获赞数
|
||||
- [x] 投稿数
|
||||
- [ ] 近一个月投稿数
|
||||
### 3.衍生指标
|
||||
- [x] 点赞率=点赞量/播放量
|
||||
- [x] 互动率=(点赞量+投币量+收藏量+分享量+评论数+弹幕数)/播放量
|
||||
- [x] 外溢系数=分享量/收藏量(反映内容外溢性)
|
||||
## 内在属性指标
|
||||
### 1.内容属性指标
|
||||
- [x] 时长
|
||||
- [x] 发布时间
|
||||
- [x] 标题
|
||||
- [x] 分区
|
||||
- [x] 标签
|
||||
- [x] 最高清晰度
|
||||
### 2.内容结构指标
|
||||
- [ ] 是否分章节
|
||||
- [x] 是否分P
|
||||
- [ ] 是否有字幕
|
||||
## 特殊指标(可能无法直接爬取,需特殊处理)
|
||||
同时需要人工智能和能工智人。
|
||||
### 1.较易处理
|
||||
- [x] 是否为联合投稿(Sy:这个可以直接获取,没这么麻烦)
|
||||
- [ ] 是否为系列作品(标题关键词分析)
|
||||
- [x] 原创or搬运(只能投一个币为搬运)(Sy:这个也可以直接获取,没这么麻烦)
|
||||
- [x] 横屏or竖屏(Sy:这个还是可以直接获取,没这么麻烦)
|
||||
- [ ] <mark>是否进入热门</mark>(作为机器学习的预测目标)
|
||||
### 2.较难处理
|
||||
产生较大数据量。
|
||||
- [ ] 标题原创性(见DS代码)
|
||||
- [ ] 标签区分度(见DS代码)
|
||||
- [ ] 弹幕情感倾向得分(单独处理)
|
||||
- [ ] 封面(图片分析可以单独写一章了)
|
||||
|
||||
## 展望(即做不了的)
|
||||
鉴于标题吸引力、剪辑质量、音频质量、封面设计、BGM体验等因素涉及主观判断,
|
||||
平均播放时长这种B站几年没做外显(没法算完播率)无法直接获取,
|
||||
领域垂直度等指标很难算,
|
||||
点击率等指标爬不到,
|
||||
我们希望后来者能克服这些困难进一步分析(如利用问卷等),
|
||||
也可以考虑时间序列等因素构建更复杂的模型。
|
||||
|
||||
(总之我们不做)
|
Loading…
x
Reference in New Issue
Block a user