社区文档构建中,欢迎编辑。 社区答疑群(非官方):717421103,点点小课堂(腾讯会议):5696651544

全站通知:

基于Python的API示例/自动获取cookie

阅读

    

2023-09-28更新

    

最新编辑:Lu_23333

阅读:

  

更新日期:2023-09-28

  

最新编辑:Lu_23333

来自WIKI实验室WIKI_BWIKI_哔哩哔哩
跳到导航 跳到搜索
页面贡献者 :
Lu_23333

注意,新版本的chrome可能会限制读取cookie文件。如果一定要读取,可以参考这里

用法:

from mwclient import Site
# import CookieGetter form other file

site = Site("wiki.biligame.com/"+site_name, path="/")
site.login(cookies=CookieGetter.get("Chrome"))
# or
site.login(cookies=CookieGetter.get("Edge"))
# 该干嘛干嘛

可以自动读取.biligame.com 的SESSDATA Cookie,用于操作API。

"""
 CookieGetter:读取浏览器cookie以供操作BWiki API
"""
import os
import json
import sqlite3
import warnings
from pathlib import Path

from base64 import b64decode
from win32crypt import CryptUnprotectData
from cryptography.hazmat.primitives.ciphers.aead import AESGCM


class CookieGetter:
    """
    读取浏览器cookie以供操作BWiki API。仅支持Windows下的Chrome和新版Edge

    从浏览器获取 .biligame.com 的SESSDATA Cookie,者可以用于操作API。

    相当于自动化了用户手动复制Cookie的步骤。

    *注意*:此Cookie关系BWiki账号安全,请勿泄露给任何人,包括B站工作人员。

    *注意*:这不是值得提倡的做法。由于BWiki没提供常规API登录方式,决定用此下策。
    另一种方案是账户密码登陆,这会使用B站账户密码,我们更不提倡模拟登录B站
    """

    @staticmethod
    def get(browser: str, wiki: str = ""):
        """ 从浏览器数据库提取cookie
            目标是:".biligame.com" 下 / 中的cookie

            可选:
            - wiki.biligame.com 下 /wiki_name/ 中的cookie
        """
        if not browser:
            raise ValueError("parameter 'browser' cannot be empty")

        cookies = {}
        cookies.update(CookieGetter._get_cookie(browser, ".biligame.com", "/", "SESSDATA"))

        # 可选,获取目标 wiki 的 cookie。但是实测仅需biligame的SESSDATA
        # cookies.update(_get_cookie(browser, "wiki.biligame.com", f"/{wiki}/"))
        return cookies

    # noinspection SqlResolve
    @staticmethod
    def _get_cookie(browser: str, host: str, path: str, name: str):
        """ 从浏览器数据库读取cookie,这不是值得提倡的方法。
        由于BWiki没提供常规API登录方式,相比于手动复制cookie,决定用此下策。
        :param browser: 浏览器
        :param host: 要获取cookie的域名
        :param path: 要获取那个路径下的cookie
        :param name: 要获取的cookie名
        :return: cookie dict
        """
        cookie_path = CookieGetter._get_cookie_path(browser)

        # query data from browser database
        # only query SESSDATA from "host/path"
        with sqlite3.connect(cookie_path) as conn:
            cursor = conn.cursor()
            sql = f"select name,encrypted_value from cookies where host_key=? and path=? and name='{name}'"
            data = cursor.execute(sql, [host, path]).fetchall()
            cursor.close()

        name, encrypted_value = data[0]
        value = CookieGetter._decode_cookie(browser, encrypted_value)

        return {name: value}

    @staticmethod
    def _decode_cookie(browser, encrypted_value):
        """ 解密 cookie 内容
        :param browser: 浏览器
        :param encrypted_value: Local State 数据库 cookies 表中的 encrypted_value
        :return: str:解密内容
        """
        if encrypted_value[0:3] != b'v10':  # data is bytes
            warnings.warn(f"未知cookie编码。这可能导致cookie解析失败。"
                          f"详情:cookie 中的 encrypted_value 预期v10开头,实际是{encrypted_value[0:5]}。")

        result = AESGCM(CookieGetter._get_key(browser)).decrypt(encrypted_value[3:15], encrypted_value[15:], None)
        result = result.decode("UTF8")
        return result

    @staticmethod
    def _get_key(browser):
        """ 从特定浏览器获取解密 cookie 的 key """
        local_state = CookieGetter._get_local_state_path(browser)
        state_data = Path(local_state).read_text("UTF8")
        key = json.loads(state_data)['os_crypt']['encrypted_key']
        key = CryptUnprotectData(b64decode(key)[5:])[1]
        return key

    @staticmethod
    def _get_cookie_path(browser: str):
        """ 获取指定浏览器的 Cookies 路径 """
        data = {
            "edge": r'\Microsoft\Edge\User Data\Default\Network\Cookies',
            "chrome": r'\Google\Chrome\User Data\Default\Network\Cookies',
        }
        return os.environ['LOCALAPPDATA'] + data[browser.lower()]

    @staticmethod
    def _get_local_state_path(browser: str):
        """ 获取指定浏览器的 Local State 路径 """
        data = {
            "edge": r'\Microsoft\Edge\User Data\Local State',
            "chrome": r'\Google\Chrome\User Data\Local State',
        }
        return os.environ['LOCALAPPDATA'] + data[browser.lower()]