Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[任务]: 开发 b 站爬虫,爬取 ioclub 账号下的新评论,转发到 qq 群 #35

Open
2 tasks done
trdthg opened this issue Nov 14, 2024 · 7 comments
Open
2 tasks done
Assignees
Labels
easy 难度:简单 task

Comments

@trdthg
Copy link
Collaborator

trdthg commented Nov 14, 2024

如何认领

请直接在下方回复,我会把 issue 的 assignees 设置为你

认领者需要遵守以下要求

  • 每两周必须在评论中更新进度,无论是学习进度还是开发进度
  • 如果1 个月没有任何进展, 则认定为认领失败, assign 将被取消,可以由其他人认领
  • 如遇到节假日等特殊情况,同步进度日期可以顺延

注意事项

  • 学习也算进展,所以小白不必担心
  • 以上日期需要按照实际情况调整
  • 任务按情况可以被多人认领合作完成

需求描述

我们需要开发一个机器人同步 b 站评论信息,原因如下:

  • b 站号虽然是公告账号,但是手机号只有一个,只能被一个人实际日常使用
  • 重要评论 (例如一些提问等) 不能及时同步给演讲人

实现方式

  • [已完成,请群内咨询] 直接 F12 抓包 b 站获取评论的接口
  • 获取处理信息
      1. 使用任意语言的 http 库和 json 库
      1. 直接使用 bash 脚本,curl 和 jq 等工具
  • [已经有类似实践,详情可群内咨询 @heky12356 ] 转发到 qq

文档资料

难度

简单 (什么都不会也能学习做)

能力要求

  • 需要能够正常访问 github,并注册账号
  • 能自学 json 格式,如何使用 python/go/js/bash(任意一个) 解析处理 json
  • 需要能和群友沟通
  • 对 b 站爬虫感兴趣
  • 能自学 go 或 python 基础
    • 安装运行
    • 写一个函数,变量,数组,for 循环。
    • 知道怎么安装第三方库并使用
      • go: go mod
      • python: pip

关闭 Issue 前请确认以下内容

  • 需要上传代码:<PR 链接>
  • 需要参加茶话会分享:<issue 链接>
@trdthg trdthg added easy 难度:简单 task labels Nov 14, 2024
@trdthg
Copy link
Collaborator Author

trdthg commented Nov 15, 2024

api 方案

  1. 直接访问视频链接找一下评论 api, 爬虫慢慢轮询所有视频
  1. 使用创作中心下的 评论管理 api, 可以轮询账户下所有视频的评论
  • 效率高,只需要轮询一个接口,缺点是必须登录,必须有 SESSIONDATA 才能访问

根据我们的需求,第二个方案似乎更好

但是有些问题:

  • 目前不知道 SESSIONDATA 会不会过期,如果过期,还需要登录方案,或者定期手动更新 SESSIONDATA

机器人对接方案

  1. 使用 bash + jq 脚本,现有的 go 语言机器人直接调用 bash 脚本
  2. 使用 python + flask server,go 直接调用 http 接口
  3. 使用 go 编写爬虫,语言相同直接调用
  4. 使用 python 编写机器人,算了吧
  5. 爬到数据后插入数据库,机器人每天直接查询数据库

方法 3 应该是最好的

补充现有 qq 机器人仓库: https://github.com/io-club/IOGAS-QQ

@heky12356
Copy link
Contributor

@trdthg
Copy link
Collaborator Author

trdthg commented Nov 15, 2024

openhome.bilibili.com/doc

官方开放平台好像没有提供我们需要的评论 api

socialsisteryi.github.io/bilibili-API-collect/docs/comment/list.html

可以用作参考,不过没有创作中心 api,所以还是要自己扒下来

linyuye/Bilibili_crawler

好像没有帮助,还没仔细看,不过没有看到评论字样


我还看到了一个: https://nemo2011.github.io/bilibili-api/#/, 但是好像连评论 api 都没有

@trdthg
Copy link
Collaborator Author

trdthg commented Nov 17, 2024

SESSION_DATA

这是你的真实 SESSIONDATA 吗,建议不要直接贴到这里,为了你自己的账号安全


获取评论的接口感觉可以了,但是现在只是一个脚本,完全无法和 qq 机器人对接

  1. 现在机器人使用 go 语言编写,想要和 python 代码交互,有下面一些方法:
    • 把数据存储在数据库里,qq 机器人直接查询数据库,得到爬取到的评论数据
    • 使用 python 编写一个 http server (例如使用 flask), 提供一个 http 接口,让 qq 机器人通过接口调用你的爬虫脚本
  2. 你需要设计一个方法,让 qq 机器人知道,哪些数据是没转发过的。我有一些思路:
    • 爬到数据后,记得保存数据的发送时间
      • 机器人可以保存一个 "上次转发时间" 的变量, 根据它来区分未发送数据
      • 或者直接在数据库中保存一列 "是否转发过" 作为标记
  3. 我觉得你可以快速学习一下 go 语言,把目前的代码用 go 重写一下,因为工作量不大,对接简单.
    • request, json 解析,go 里面都有标准库可以直接处理

另外 @kksk-code

  1. 你可以简单学习一下 git, 然后在 github 上创建一个代码仓库上传上去,方便我们检查,运行,更新
  2. github 评论支持使用 markdown 语法
    • 主要学习: 标题,列表,加粗,引用,粗体/斜体,代码块 即可满足 99.9% 使用情况
      • 你上面的代码有些缩进样式丢失了,使用代码块可以保留缩进
      • 使用代码块,下面的代码块会自动添加高亮,非常易读
         import os
        
         def main():
             pass
        
         if name == "main":
             main()
    • 这里有一些教程:

这两个都属于必知必会的东西, 可以今早学习

@kksk-code
Copy link

kksk-code commented Nov 17, 2024

#成品代码

import requests

from datetime import datetime

class BilibiliCommentScraper:
    def __init__(self, cookies=None, headers=None):
        self.cookies = cookies or {
            'SESSDATA': '你的SESSDATA'
        }
        self.headers = headers or {
            'User-Agent': '你的UserAgent',
            'Content-Type': 'application/json',
        }

    def convert_timestamp_to_readable(self, ctime):
        """将时间戳转换为易懂的日期时间格式"""
        timestamp = datetime.fromtimestamp(ctime)
        return timestamp.strftime('%Y-%m-%d %H:%M:%S')

    def get_oid_from_bv(self, bv_id):
        """获取视频的 OID (aid)"""
        api_url = f"https://api.bilibili.com/x/web-interface/view?bvid={bv_id}"
        response = requests.get(api_url, headers=self.headers, cookies=self.cookies)
        if response.status_code == 200:
            data = response.json()
            if data['code'] == 0:
                return data['data']['aid']
        return None

    def get_comments(self, oid, page=1, ps=20):
        """获取视频的评论数据"""
        url = "https://api.bilibili.com/x/v2/reply/main"
        params = {
            'type': 1,
            'oid': oid,
            'next': page,
            'ps': ps,
            'mode': 3#热度排序
        }

        response = requests.get(url, headers=self.headers, cookies=self.cookies, params=params)
        if response.status_code == 200:
            data = response.json()
            if 'data' in data and 'replies' in data['data']:
                return data['data']
        return {}

    def get_replies(self, reply_id, oid):
        """获取评论的回复"""
        url = "https://api.bilibili.com/x/v2/reply/reply"
        params = {
            'oid': oid,
            'type': 1,
            'rpid': reply_id
        }

        response = requests.get(url, headers=self.headers, cookies=self.cookies, params=params)
        if response.status_code == 200:
            data = response.json()
            if 'data' in data and 'replies' in data['data']:
                return data['data']['replies']
        return []

    def print_comments_and_replies(self, bv_id, data):
        """打印评论及其回复"""
        print(f"\n视频 BV: {bv_id} 的评论:")

        top_replies_printed = False
        
        # 打印置顶评论(只打印一次)
        if 'top_replies' in data:
            top_replies = data['top_replies']
            for top_reply in top_replies:
                name = top_reply['member']['uname']
                sex = top_reply['member']['sex']
                content = top_reply['content']['message']
                like = top_reply['like']
                ctime = top_reply['ctime']
                readable_time = self.convert_timestamp_to_readable(ctime)
                print(f"置顶评论: 昵称: {name}, 性别: {sex}, 评论: {content}, 点赞: {like}, 时间: {readable_time}")

                # 获取置顶评论的回复
                if 'replies' in top_reply:
                    for reply in top_reply['replies']:
                        reply_name = reply['member']['uname']
                        reply_content = reply['content']['message']
                        reply_like = reply['like']
                        reply_ctime = reply['ctime']
                        readable_reply_time = self.convert_timestamp_to_readable(reply_ctime)
                        print(f"  回复: {reply_name}, 内容: {reply_content}, 点赞: {reply_like}, 时间: {readable_reply_time}")

            top_replies_printed = True

        # 打印普通评论
        for comment in data['replies']:
            name = comment['member']['uname']
            sex = comment['member']['sex']
            content = comment['content']['message']
            like = comment['like']
            ctime = comment['ctime']
            readable_time = self.convert_timestamp_to_readable(ctime)
            print(f"昵称: {name}, 性别: {sex}, 评论: {content}, 点赞: {like}, 时间: {readable_time}")

            # 获取评论的回复
            if 'replies' in comment:
                for reply in comment['replies']:
                    reply_name = reply['member']['uname']
                    reply_content = reply['content']['message']
                    reply_like = reply['like']
                    reply_ctime = reply['ctime']
                    readable_reply_time = self.convert_timestamp_to_readable(reply_ctime)
                    print(f"  回复: {reply_name}, 内容: {reply_content}, 点赞: {reply_like}, 时间: {readable_reply_time}")

    def get_multiple_videos_comments(self, bv_ids, total_pages=3):
        """获取多个视频的评论"""
        for bv_id in bv_ids:
            print(f"\n正在获取视频 {bv_id} 的评论...")
            oid = self.get_oid_from_bv(bv_id)
            if oid:
                print(f"视频的 OID: {oid}")
                for page in range(1, total_pages + 1):
                    print(f"正在获取第 {page} 页评论...")
                    data = self.get_comments(oid, page)
                    if data:
                        self.print_comments_and_replies(bv_id, data)
                    else:
                        print(f"第 {page} 页没有评论或请求失败")
            else:
                print(f"无法获取视频 {bv_id} 的 OID")

# 使用类的示例
def main():
    # 用户输入 BV 号
    bv_ids = input('请输入多个视频的 BV 号,使用逗号分隔: ').split(',')
    bv_ids = [bv_id.strip() for bv_id in bv_ids]  # 去掉空格

    # 创建 BilibiliCommentScraper 实例并获取评论
    scraper = BilibiliCommentScraper()
    scraper.get_multiple_videos_comments(bv_ids)

if __name__ == "__main__":
    main()

@trdthg
Copy link
Collaborator Author

trdthg commented Nov 24, 2024

# 获取评论数据
def get_comments(oid, page=1, ps=20):
    url = "https://api.bilibili.com/x/v2/reply/main"
    params = {
        ...
        'mode': 3  # 按热度排序
    }

请问关于 mode 参数的文档在哪里?我在 bilibili-API-collect 里没有找到

@trdthg
Copy link
Collaborator Author

trdthg commented Nov 24, 2024

我认为你的代码中每次分页都会有所有的置顶评论属于预期行为:

  • 我没有发现任何有关 置顶 的相关控制参数,懒加载和列表都是,无法控制指定评论是否展示,或者分页行为
  • 我 f12 扒了一下测试了一下,向下滚动时后续发出的评论请求,他们的返回值也会带有置顶评论
  • b 站本身 up 主只能设置一个置顶评论,查了一些资料有人说,联合投稿可以每人置顶一个,不过就算联合投稿全加起来,数量上也不会很多,没有分页或者控制的必要。(推测置顶评论和视频在一个数据库表里都有可能)

所以建议只有第一次打印置顶评论

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
easy 难度:简单 task
Projects
None yet
Development

No branches or pull requests

3 participants