bilibili-api icon indicating copy to clipboard operation
bilibili-api copied to clipboard

[漏洞] 使用 `user.User.get_dynamics` 方法获取不到任何动态,且抛出 `TypeError: 'NoneType' object is not iterable` 错误

Open WindowsSov8forUs opened this issue 2 years ago • 5 comments

Python 版本: 3.11.2

模块版本: 16.2.0

运行环境: Windows

模块路径: bilibili_api.user

解释器: cpython

报错信息:

Traceback (most recent call last):
  File "f:\VS Code File\test\test.py", line 24, in <module>
    asyncio.run(main())
  File "C:\Users\qwert\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "C:\Users\qwert\AppData\Local\Programs\Python\Python311\Lib\asyncio\runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\qwert\AppData\Local\Programs\Python\Python311\Lib\asyncio\base_events.py", line 653, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "f:\VS Code File\test\test.py", line 19, in main
    info = await u.get_dynamics()
           ^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\qwert\AppData\Roaming\Python\Python311\site-packages\bilibili_api\user.py", line 645, in get_dynamics
    for card in data["cards"]:
TypeError: 'NoneType' object is not iterable

报错代码:

import json
import asyncio

from bilibili_api import user, sync, Credential, settings, video

settings.proxy = 'http://127.0.0.1:{port}'

sessdata = '{sessdata}'
bili_jct = '{bili_jct}'
buvid3 = '{buvid3}'
dedeuserid = '{dedeuserid}'
ac_time_value = '{ac_time_value}'

credential: Credential = Credential(sessdata=sessdata, bili_jct=bili_jct, buvid3=buvid3, dedeuserid=dedeuserid, ac_time_value=ac_time_value)

u = user.User(572139515, credential=credential)

async def main():
    info = await u.get_dynamics()

    with open('test_dynamics.json', 'w', encoding='utf-8') as file:
        json.dump(info, file, ensure_ascii=False, indent=4)

asyncio.run(main())

报错代码处敏感信息已替换,实际运行过程中是正确获取了的

试过了两个不同的 uid ,确定两个 UP 都是有着大量动态的,但是仍然会在获取时抛出错误 查看源码发现错误出现在 user.User.get_dynamics() 方法的这里:

    async def get_dynamics(self, offset: int = 0, need_top: bool = False) -> dict:
        ...
        # card 字段自动转换成 JSON。
        # print(data) # 我添加了 print 代码以检查获取到的 data
        if "cards" in data: # > 这里应当是判为 False 的,因为实际上并没有获取到数据,但是在 data 中存在该字段,导致抛出错误
            for card in data["cards"]: # > 在 data 中该字段的实际值为 None ,不符合预期
                card["card"] = json.loads(card["card"])
                card["extend_json"] = json.loads(card["extend_json"])
        return data

在添加 print(data) 代码行后,运行上述代码,得到了如下输出:

{'has_more': 0, 'cards': None, 'next_offset': 0}

可以看出,虽然 "card" 字段存在于字典中,但是它的值是 None ,导致 if "cards" in data 语句并没有正确地将这种情况排除掉

现在的问题是:

  1. if 语句并没有将字段存在但值为 None 的情况排除,使 if 内代码段进行了 for card in None 的操作,导致抛出错误。这个很好修改,只需要将 if 语句改为 if data.get("card", None) is not None 就可以了
  2. 理论上应当获取到数据的情况并没有获取到合法的数据,且获取过程中没有任何错误抛出。这个是出现问题的根本原因,且我并没有在 issues 或 16.2.1 的 release 中看到有关的信息(也可能只是我漏看了),不知道是什么情况

WindowsSov8forUs avatar Apr 05 '24 07:04 WindowsSov8forUs

看到了 user.User.get_dynamics_new 接口,但是相应的文档并没有被添加到文档中,建议进行相应的修改,以及在 user.User.get_dynamics 接口未能获取到数据时可以自动跳转到 user.User.get_dynamics_new 接口中,而不是需要用户自行修改

WindowsSov8forUs avatar Apr 05 '24 08:04 WindowsSov8forUs

看到了 user.User.get_dynamics_new 接口,但是相应的文档并没有被添加到文档中,建议进行相应的修改,以及在 user.User.get_dynamics 接口未能获取到数据时可以自动跳转到 user.User.get_dynamics_new 接口中,而不是需要用户自行修改

基本上不会提供主动切换

z0z0r4 avatar Apr 05 '24 08:04 z0z0r4

不过倒是有必要添加 @deprecated 相关的说

https://github.com/Nemo2011/bilibili-api/issues/737

z0z0r4 avatar Apr 05 '24 08:04 z0z0r4

这是因为前几天批站改动态api了吧

LeonardHalf avatar Apr 10 '24 06:04 LeonardHalf

我试了一下,现在访问动态api需要在请求头里加cookie项,不然就会被风控

LeonardHalf avatar Apr 10 '24 08:04 LeonardHalf