百度网盘开放平台接入(Python)

百度网盘开放平台接入(Python)

百度网盘开放平台接入链接:https://pan.baidu.com/union/doc/nksg0sbfs
接入流程
1.请在官网首页右上角找到登录入口,使用百度账号进行登录。如果您还没有百度账号,请按照百度账号统一注册流程进行注册。
2.登录后,点击官网首页图中【立即申请】或个人中心【申请认证账号并接入】进入申请接入页面。
3.按照指示完成认证后,可前往控制台创建应用。
4.选择应用类别,填写应用名称、描述后,即可创建应用。
5.应用创建成功后将获取对应的Appid、AppKey(AK)、SecretKey (SK)和Signkey。
6.配置redirect_uri 没有回调内容的,地址也可以不配置
获取access_token
最重要的一步,如何获取access_token???
1.了解百度OAuth,获取百度授权
链接:http://developer.baidu.com/wiki/index.php?title=docs/oauth
一般以第一个授权流程为主

获取授权

获取code(最好是先登录百度账号,可以直接获取用户信息,否则还需要先登录认证),下面的链接即获取code的链接(如果没有回调的话,redirect_uri默认为oob):https://openapi.baidu.com/oauth/2.0/authorize?client_id=注册应用时获得的API Key&redirect_uri=oob&response_type=code&scope=netdisk
参数:
client_id:必须参数,注册应用时获得的API Key。
response_type:必须参数,此值固定为“code”。
redirect_uri:必须参数,授权后要回调的URI,即接收Authorization Code的URI。如果用户在授权过程中取消授权,会回调该URI,并在URI末尾附上error=access_denied参数。对于无Web Server的应用,其值可以是“oob”,此时用户同意授权后,授权服务会将Authorization Code直接显示在响应页面的页面中及页面title中。非“oob”值的redirect_uri按照如下规则进行匹配:(1)如果开发者在“授权安全设置”中配置了“授权回调地址”,则redirect_uri必须与“授权回调地址”中的某一个相匹配;(2)如果未配置“授权回调地址”,redirect_uri所在域名必须与开发者注册应用时所提供的网站根域名列表或应用的站点地址(如果根域名列表没填写)的域名相匹配
获取code页面

获取code页面

通过Authorization Code获取Access Token
通过上面第一步获得Authorization Code后,便可以用其换取一个Access Token。获取方式是,应用在其服务端程序中发送请求(推荐使用POST)到 百度OAuth2.0授权服务的“https://openapi.baidu.com/oauth/2.0/token”地址上,并带上以下5个必须参数:
grant_type:必须参数,此值固定为“authorization_code”;
code:必须参数,通过上面第一步所获得的Authorization Code;
client_id:必须参数,应用的API Key;
client_secret:必须参数,'应用的Secret Key;
redirect_uri:必须参数,该值必须与获取Authorization Code时传递的“redirect_uri”保持一致。

例如:

https://openapi.baidu.com/oauth/2.0/token?
	grant_type=authorization_code&
	code=ANXxSNjwQDugOnqeikRMu2bKaXCdlLxn&
	client_id=Va5yQRHlA4Fq4eR3LT0vuXV4&
	client_secret=0rDSjzQ20XUj5itV7WRtznPQSzr5pVw2&
	redirect_uri=http%3A%2F%2Fwww.example.com%2Foauth_redirect
响应参数
access_token:要获取的Access Token;
expires_in:Access Token的有效期,以秒为单位;请参考“Access Token生命周期”
refresh_token:用于刷新Access Token 的 Refresh Token,所有应用都会返回该参数;(10年的有效期)
scope:Access Token最终的访问范围,即用户实际授予的权限列表(用户在授权页面时,有可能会取消掉某些请求的权限),关于权限的具体信息参考“权限列表”一节;
session_key:基于http调用Open API时所需要的Session Key,其有效期与Access Token一致;
session_secret:基于http调用Open API时计算参数签名用的签名密钥。

{
	"expires_in": 86400,
    "access_token": "1.a6b7dbd428f731035f771b8d15063f61.86400.1292922000-2346678-124328",
    "refresh_token": "2.385d55f8615fdfd9edb7c4b5ebdc3e39.604800.1293440400-2346678-124328",
    "scope": "basic email",
    "session_key": "ANXxSNjwQDugf8615OnqeikRMu2bKaXCdlLxn",
    "session_secret": "248APxvxjCZ0VEC43EYrvxqaK4oZExMB",
}
scope权限:
basic:用户基本权限,可以获取用户的基本信息 。
super_msg:往用户的百度首页上发送消息提醒,相关API任何应用都能使用,但要想将消息提醒在百度首页显示,需要第三方在注册应用时额外填写相关信息。
netdisk:获取用户在个人云存储中存放的数据。

到现在为止,我们已经做好了前期准备工作,后续则涉及文件的预上传、分片上传、创建文件,三步,文件才能上传成功。

#!/usr/bin/python3.7
# -*- coding: utf-8 -*-
# @Time    : 2022/1/27 17:07
# @Author  : maker
# @Email   : moyu_ji@163.com
# @File    : baiduwangpan.py
# @Software: PyCharm

import json, os, hashlib, requests
from urllib.parse import urlencode


class BaiDuWangPan():
    def __init__(self):
        self.access_token = ''
        self.refresh_token = ''
        self.app_name = ''
        self.app_key = ''  # Appkey
        self.secret_key = ''  # Secretkey
        self.precreate_api = 'https://pan.baidu.com/rest/2.0/xpan/file?'  # 预上传
        self.upload_api = 'https://d.pcs.baidu.com/rest/2.0/pcs/superfile2?'  # 分片上传
        self.create_api = 'https://pan.baidu.com/rest/2.0/xpan/file?'  # 创建文件
        self.query_file_url = 'http://pan.baidu.com/rest/2.0/xpan/multimedia?'  # 查询文件信息
        self.get_token_url = 'https://openapi.baidu.com/oauth/2.0/token?'  # 获取token

    def get_refresh_token(self):
        """
        使用Refresh Token刷新以获得新的Access Token
        :param refresh_token: 必须参数,用于刷新Access Token用的Refresh Token。注意一个Refresh Token只能被用来刷新一次;
        :return:
        """
        data = {
            "grant_type": "refresh_token",
            "refresh_token": self.refresh_token,
            "client_id": self.app_key,
            "client_secret": self.secret_key
        }
        response = requests.post(self.get_token_url, data)
        res_data = json.loads(response.text)
        return {
            "error": res_data.get("error", None),
            "error_description": res_data.get("error_description", ''),
            "access_token": res_data.get("access_token", ''),
            "refresh_token": res_data.get("refresh_token", ''),
            "session_key": res_data.get("session_key", ''),
            "session_secret": res_data.get("session_secret", ''),
        }

    def precreate(self, file_path):
        """
        预上传
        请求参数rtype=0时,如果云端存在同名文件,此次调用会失败。
        云端文件重命名策略:假设云端已有文件为test.txt,新的名称为test(1).txt1, 当发现已有目录 /dir 时, 新创建的目录命名为:/dir(1) 。
        content-md5和slice-md5都不为空时,接口会判断云端是否已存在相同文件,如果存在,返回的return_type=2,代表直接上传成功,无需请求后面的分片上传和创建文件接口。
        :param file_path: 文件路径
        :return:
        """
        remote_path = '/apps/' + self.app_name
        size = os.path.getsize(file_path)
        arr = file_path.split('/')
        for item in arr[1::]:
            remote_path = os.path.join(remote_path, item)
        block_list = []
        with open(file_path, 'rb') as f:
            while True:
                data = f.read(1024 * 1024 * 4)
                if not data:
                    break
                file_md5 = hashlib.md5(data).hexdigest()
                block_list.append(file_md5)
        params = {
            'method': 'precreate',
            'access_token': self.access_token,
        }
        data = {
            'path': remote_path,
            'size': size,
            'isdir': 0,
            'autoinit': 1,
            'block_list': json.dumps(block_list)
        }
        api = self.precreate_api + urlencode(params)
        response = requests.post(api, data=data)
        res_data = json.loads(response.content)
        errno = res_data.get('errno', 0)
        if errno:
            raise
        return res_data.get('uploadid', ''), remote_path, size, block_list

    def upload(self, remote_path, uploadid, partseq, file_data):
        """
        分片上传
        普通用户单个分片大小固定为4MB(文件大小如果小于4MB,无需切片,直接上传即可),单文件总大小上限为4G。
        普通会员用户单个分片大小上限为16MB,单文件总大小上限为10G。
        超级会员用户单个分片大小上限为32MB,单文件总大小上限为20G。
        :param remote_path: 上传后使用的文件绝对路径
        :param uploadid: precreate接口下发的uploadid
        :param partseq: 文件分片的位置序号,从0开始,参考precreate接口返回的block_list
        :param file_data: 上传的文件内容
        :return:
        """
        data = {}
        files = [
            ('file', file_data)
        ]
        params = {
            'method': 'upload',
            'access_token': self.access_token,
            'path': remote_path,
            'type': 'tmpfile',
            'uploadid': uploadid,
            'partseq': partseq
        }
        api = self.upload_api + urlencode(params)
        response = requests.post(api, data=data, files=files)
        res_data = json.loads(response.content)
        errno = res_data.get('errno', 0)
        if errno:
            raise
        md5 = res_data.get('md5', '')
        return md5

    def create(self, remote_path, size, block_list, uploadid):
        """
        创建文件
        :param remote_path: 上传后使用的文件绝对路径
        :param size: 文件大小B
        :param block_list: 文件各分片MD5的json串,MD5对应superfile2返回的md5,且要按照序号顺序排列
        :param uploadid: uploadid
        :return:
        """
        params = {
            'method': 'create',
            'access_token': self.access_token,
        }
        api = self.create_api + urlencode(params)
        data = {
            'path': remote_path,
            'size': size,
            'isdir': 0,
            'uploadid': uploadid,
            'block_list': json.dumps(block_list)
        }
        response = requests.post(api, data=data)
        res_data = json.loads(response.content)
        errno = res_data.get('errno', 0)
        if errno:
            raise
        else:
            fs_id = res_data.get("fs_id", '')
            md5 = res_data.get("md5", '')
            server_filename = res_data.get("server_filename", '')
            category = res_data.get("category", 0)
            path = res_data.get("path", '')
            ctime = res_data.get("ctime", '')
            isdir = res_data.get("isdir", '')
            return fs_id, md5, server_filename, category, path, isdir

    def finall_update_file(self, file_path):
        uploadid, remote_path, size, block_list = self.precreate(file_path)
        _block_list = []
        with open(file_path, 'rb') as f:
            i = 0
            while True:
                data = f.read(1024 * 1024 * 4)
                if not data:
                    break
                md5 = self.upload(remote_path, uploadid, i, data)
                _block_list.append(md5)
                i += 1
        fs_id, md5, server_filename, category, path, isdir = self.create(remote_path, size, _block_list, uploadid)

    def download_file(self, fs_id):
        """
        查询文件并下载
        先查询文件是否存在,若存在则返回文件下载地址(dlink)
        下载文件需要在下载地址拼上access_token
        :param fs_id: 文件id数组,数组中元素是uint64类型,数组大小上限是:100
        :return:
        """
        dlink = ''
        params = {
            "method": "filemetas",
            "access_token": self.access_token,
            "fsids": json.dumps([int(fs_id)]),
            "dlink": 1
        }
        api_url = self.query_file_url + urlencode(params)
        response = requests.get(api_url)
        res_data = json.loads(response.text)
        errmsg = res_data.get("errmsg", None)
        if errmsg == 'succ':
            res_list = res_data.get("list", [])
            if res_list:
                dlink = res_list[0].get('dlink', '')
            if dlink:
                return dlink + '&' + 'access_token={}'.format(self.access_token)
        else:
            raise

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值