Giter VIP home page Giter VIP logo

bilibiliapidocs's Introduction

Bilibili API 第三方文档

绝大部分内容根据 http://docs.bilibili.cn/wiki 官方文档进行编写

哔哩哔哩开放平台为第三方提供了简便的合作模式,满足了网站、手机和平板电脑用户随时随地观看哔哩哔哩的需求。哔哩哔哩开放平台提供相关接口,以实现第三方WAP站和客户端等多种应用的接入。

API 分类

作者推荐番剧弹幕动态

视频/专题收藏、关注视频评论好友/悄悄关注/黑名单

历史记录批量获取排行信息(首页)获取视频排行信息

登录行为记录第三方登录登录

我的信息通知视频推荐搜索读取/创建专题

番剧专题搜索关键词标签用户信息获取视频信息

注意事项

关于滥用

以下这些,但并不限于以下这些行为都被视为滥用: a.短时间内大量操作API用于采集。 b.测试帐号及密码 c.其它没有提到的,但会给系统带来压力的请求行为。

禁止登录

如果账号尝试登录错误次数在 30 分钟内超过 5 次则会被禁止登录一个小时,写程序的时候特别是 客户端的时候必须判断是否登录成功,如果不成功必须马上停止尝试。

关于封禁

由于滥用 API 将会导致 API 被封禁,但并不影响在官方网站上的使用,封禁一定的时间后会自动解除,一般需要一个小时后才会解封,所以请小心操作,不要拿 API 进行大量测试

请求地址

请求的地址为 http://api.bilibili.com/ 开头的地址

请求方法

只支持 GET 方法请求数据,用其它方法会提示相关错误。

关于编码

请用 UTF-8 编码进行数据传输,返回的数据也是 UTF-8 编码的。

关于 UserAgent

请求的时候必须设置 UserAgent,如果不设置或者设置为不合法的(比如设置为浏览器的)也会导致账号被封禁 API。 UserAgent 的格式必须为:程序英文名称/版本 (联系邮箱) 比如:BiLiBiLi WP Client/1.0.0 ([email protected])

通用调用参数

字段 必选 类型 说明
type false string (json/xml/jsonp) 返回数据方式 默认json
appkey true string 应用appkey
ts true int 当前时间截 (UNIX TIMESTAMP)
sign true string 应用校验密匙
callback false string JSONp调用方式时回调函数名称
access_key false string 应用在用户申请登陆后获取到的access_key 可以此access_key访问需要用户权限的操作
platform false string (android/ios) 客户端平台适配及统计用

通用错误代码

代码 说明
-1 应用程序不存在或已被封禁
-2 Access key错误
-3 API校验密匙错误
-101 帐号未登陆
-102 帐号被封停
-103 积分不足
-104 硬币不足
-105 验证码错误
-106 帐号未激活
-107 帐号非正式会员或在适应期
-108 应用沒有存取相应功能的权限
-400 请求有误
-403 权限不足
-404 文档不存在
-500 服务器内部错误
-503 调用速度过快

签名(sign)生成方式

把接口所需所有参数拼接,如utk=xx&time=xx,按参数名称排序,最后再拼接上密钥App-Secret,做md5加密 (callback不需要参与sign校检)

代码示例

PHP 版本:

/**
 * @param $params array 参数列表
 * @param $key 加密密钥
 * @return array sign:加密校验串,params:参数拼接串
 */
 function get_sign($params, $key) {
  $_data = array();
  ksort($params);
  reset($params);
  foreach ($params as $k => $v) {
  // rawurlencode 返回的转义数字必须为大写( 如%2F )
  $_data[] = $k . '=' . rawurlencode($v);
  }
  $_sign = implode('&', $_data);
  return array(
    'sign' => strtolower(md5($_sign . $key)),
    'params' => $_sign,
  );
 }
 define("APP_SECRET","abcdef123456");
 get_sign(array("type"=>"json"),APP_SECRET);

JS 版本:

<script type="text/javascript" src="http://static.hdslb.com/js/md5.js">/script>
 function get_sign(params, key)
 {
 	var s_keys = [];
 	for (var i in params)
 	{
 		s_keys.push(i);
 	}
 	s_keys.sort();
 	var data = "";
 	for (var i = 0; i < s_keys.length; i++)
 	{
 		// encodeURIComponent 返回的转义数字必须为大写( 如 %2F )
 		data+=(data ? "&" : "")+s_keys[i]+"="+encodeURIComponent(params[s_keys[i]]);
 	}
 	return {
 		"sign":hex_md5(data+key),
 		"params":data
 	};
 }

bilibiliapidocs's People

Contributors

fython avatar qixingchen avatar yinzo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bilibiliapidocs's Issues

网址无法访问

发现api.bilibili.cn和api.bilibili.com无法访问:访问超时,是这个网站关了还是需要全局代理?

如何获取直播、点播地址?

目前根据URL分析 直播地址

http://live-play.acgvideo.com/live/856/live_14837663_7724658.flv
其中856猜测为服务随机数,14837663为视频mid,但最后7724658部分不清楚是什么加密还是什么

点播地址
http://cn-shcy3-dx.acgvideo.com/vg12/f/9a/3215566hd.mp4?expires=1457420100&ssig=Xr96D4B9sG2ZD2W99rOHng&oi=1961670062&appkey=f3bb208b3d081dc8&or=3026306825&rate=0
ssig是通过什么方式加密的?看22位可能是md5(base64),但参数试了几次不太对,不知道是加密方式搞错了,还是加密用数据不对...

是否应该考虑把cn换成com?

我最近也在看关于bilibili的API,发现有些API地址(CONST.typeid.md)可以直接用.com了
bilibili也不知道什么时候从.cn变成.com
并且.com支持https,需不需要把部分.cn换成.com?

bilibili

// ==UserScript==
// @name Bilibili 港澳台
// @namespace http://kghost.info/
// @Version 1.3.6
// @description: Remove area restriction
// @description:zh-CN 解除区域限制 (修正大会员限制,添加国际友人看国内功能)
// @supportURL https://github.com/kghost/bilibili-area-limit
// @author zealot0630
// @include https://.bilibili.com/
// @run-at document-start
// @description Bilibili 港澳台, 解除区域限制 (修正大会员限制,添加国际友人看国内功能)
// @grant GM_notification
// @grant GM_cookie
// @grant GM.setValue
// @grant GM.getValue
// ==/UserScript==

// (function(modules) { // webpackBootstrap
/
/ // The module cache
// var installedModules = {};
/
/
// // The require function
/
/ function webpack_require(moduleId) {
//
/
/ // Check if module is in cache
// if(installedModules[moduleId]) {
/
/ return installedModules[moduleId].exports;
// }
/
/ // Create a new module (and put it into the cache)
// var module = installedModules[moduleId] = {
/
/ i: moduleId,
// l: false,
/
/ exports: {}
// };
/
/
// // Execute the module function
/
/ modules[moduleId].call(module.exports, module, module.exports, webpack_require);
//
/
/ // Flag the module as loaded
// module.l = true;
/
/
// // Return the exports of the module
/
/ return module.exports;
// }
/
/
//
/
/ // expose the modules object (webpack_modules)
// webpack_require.m = modules;
/
/
// // expose the module cache
/
/ webpack_require.c = installedModules;
//
/
/ // define getter function for harmony exports
// webpack_require.d = function(exports, name, getter) {
/
/ if(!webpack_require.o(exports, name)) {
// Object.defineProperty(exports, name, { enumerable: true, get: getter });
/
/ }
// };
/
/
// // define __esModule on exports
/
/ webpack_require.r = function(exports) {
// if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/
/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
// }
/
/ Object.defineProperty(exports, '__esModule', { value: true });
// };
/
/
// // create a fake namespace object
/
/ // mode & 1: value is a module id, require it
// // mode & 2: merge all properties of value into the ns
/
/ // mode & 4: return value when already ns object
// // mode & 8|1: behave like require
/
/ webpack_require.t = function(value, mode) {
// if(mode & 1) value = webpack_require(value);
/
/ if(mode & 8) return value;
// if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/
/ var ns = Object.create(null);
// webpack_require.r(ns);
/
/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
// if(mode & 2 && typeof value != 'string') for(var key in value) webpack_require.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/
/ return ns;
// };
/
/
// // getDefaultExport function for compatibility with non-harmony modules
/
/ webpack_require.n = function(module) {
// var getter = module && module.__esModule ?
/
/ function getDefault() { return module['default']; } :
// function getModuleExports() { return module; };
/
/ webpack_require.d(getter, 'a', getter);
// return getter;
/
/ };
//
/
/ // Object.prototype.hasOwnProperty.call
// webpack_require.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/
/
// // webpack_public_path
/
/ webpack_require.p = "";
//
/
/
// // Load entry module and return exports
/
/ return webpack_require(webpack_require.s = "./src/main.js");
// })
/
************************************************************/
/
/ ({

// "./src/main.js":
/
!
!
!
./src/main.js !
*
*/
/! no exports provided /
/
/ (function(module, webpack_exports, webpack_require) {

"use strict";
webpack_require.r(webpack_exports);
/* harmony import / var url__WEBPACK_IMPORTED_MODULE_0_ = webpack_require(/! ./url */ "./src/url.js");

(function(XMLHttpRequest) {
class ClassHandler {
constructor(proxy) {
this.proxy = proxy;
}

construct(target, args) {
  const obj = new target(...args);
  return new Proxy(obj, new this.proxy(obj));
}

}

const ProxyGetTarget = Symbol('ProxyGetTarget');
const ProxyGetHandler = Symbol('ProxyGetHandler');
class ObjectHandler {
constructor(target) {
this.target = target;
}

get(target, prop, receiver) {
  if (target.hasOwnProperty(prop)) {
    return Reflect.get(target, prop, receiver);
  } else if (prop == ProxyGetTarget) {
    return target;
  } else if (prop == ProxyGetHandler) {
    return this;
  } else {
    const value = target[prop];
    if (typeof value == 'function')
      return new Proxy(value, new FunctionHandler(value));
    return value;
  }
}

set(target, prop, value) {
  return Reflect.set(target, prop, value);
}

}

class FunctionHandlerBase extends ObjectHandler {
apply(target, thisArg, argumentsList) {
const realTarget = thisArg[ProxyGetTarget];
if (!realTarget) throw new Error('illegal invocations');
return this.call(this.target, thisArg, realTarget, argumentsList);
}
}

class FunctionHandler extends FunctionHandlerBase {
call(fn, proxy, target, argumentsList) {
return fn.apply(target, argumentsList);
}
}

class EventTargetHandler extends ObjectHandler {
constructor(target) {
super(target);
this.listeners = {};
}

getListeners(event) {
  if (!this.listeners.hasOwnProperty(event))
    this.listeners[event] = new Map();
  return this.listeners[event];
}

get(target, prop, receiver) {
  if (prop === 'addEventListener') {
    return new Proxy(
      target.addEventListener,
      new this.addEventListener(target.addEventListener)
    );
  } else if (prop === 'removeEventListener') {
    return new Proxy(
      target.removeEventListener,
      new this.removeEventListener(target.removeEventListener)
    );
  } else return super.get(target, prop, receiver);
}

}

EventTargetHandler.prototype.addEventListener = class extends FunctionHandlerBase {
call(fn, proxy, realTarget, argumentsList) {
const event = argumentsList[0];
const listener = argumentsList[1];
const bridge = listener.bind(proxy);
argumentsList[1] = bridge;
proxy[ProxyGetHandler].getListeners(event).set(listener, bridge);
return fn.apply(realTarget, argumentsList);
}
};

EventTargetHandler.prototype.removeEventListener = class extends FunctionHandlerBase {
call(fn, proxy, realTarget, argumentsList) {
const event = argumentsList[0];
const listener = argumentsList[1];
const cache = proxy[ProxyGetHandler].getListeners(event);
if (cache.has(listener)) {
argumentsList[1] = cache.get(listener);
cache.delete(listener);
}
return fn.apply(realTarget, argumentsList);
}
};

class XhrHandler extends EventTargetHandler {
constructor(target) {
super(target);
this.overrideResponse = false;
this.overrideResponseValue = null;
}

get(target, prop, receiver) {
  if (prop === 'open') {
    return new Proxy(target.open, new this.open(target.open));
  } else if (prop === 'send') {
    return new Proxy(target.send, new this.send(target.send));
  } else if (prop === 'response' && this.overrideResponse) {
    console.log('BAL: Return hooked area limit');
    return this.overrideResponseValue;
  } else if (prop === 'responseText' && this.overrideResponse) {
    console.log('BAL: Return hooked area limit');
    return this.overrideResponseValue;
  } else {
    return super.get(target, prop, receiver);
  }
}

}

const showTamperMonkeyUpdate = () => {
GM.getValue('area__limit', 0).then(last => {
if (last > new Date().getTime() - 86400000) return;
if (
confirm(
'Bilibili 港澳台: 无法获取播放文件信息,如果已开通大会员,请升级油猴到BETA版本'
)
) {
window.open(
'https://chrome.google.com/webstore/detail/tampermonkey-beta/gcalenpjmijncebpfijmoaglllgpjagf',
'_blank'
);
} else {
GM.setValue('area__limit', new Date().getTime());
}
});
};

let limited = false;
XhrHandler.prototype.open = class extends FunctionHandlerBase {
call(fn, proxy, realTarget, argumentsList) {
const method = argumentsList[0];
const url = argumentsList[1];

  if (method === 'GET') {
    if (limited && url.match(_url__WEBPACK_IMPORTED_MODULE_0__["url_play"])) {
      for (const [regs, to] of _url__WEBPACK_IMPORTED_MODULE_0__["url_replace_to"]) {
        function any() {
          for (const reg of regs) {
            if (document.title.match(reg)) return true;
          }
          return false;
        }
        if (any()) {
          argumentsList[1] = url.replace(_url__WEBPACK_IMPORTED_MODULE_0__["url_api_replace"], to.api);
          realTarget.hookCookie = true;
          console.log(`BAL: playurl via proxy ${to.api}.`);
          break;
        }
      }
    } else if (
      (function() {
        for (const status of _url__WEBPACK_IMPORTED_MODULE_0__["url_status"]) {
          if (url.match(status)) return true;
        }
      })()
    ) {
      realTarget.addEventListener('readystatechange', () => {
        if (realTarget.readyState === 4 && realTarget.status === 200) {
          const status = JSON.parse(realTarget.response);
          if (status && status.result && status.result.area_limit === 1) {
            status.result.area_limit = 0;
            limited = true;
            console.log('BAL: Hook area limit');
            proxy[ProxyGetHandler].overrideResponse = true;
            proxy[ProxyGetHandler].overrideResponseValue = JSON.stringify(
              status
            );
          }
        }
      });
    }
  }
  return fn.apply(realTarget, argumentsList);
}

};

XhrHandler.prototype.send = class extends FunctionHandlerBase {
call(fn, proxy, realTarget, argumentsList) {
if (realTarget.hookCookie) {
GM_cookie.list(
{ domain: '.bilibili.com', name: 'SESSDATA' },
(cookies, error) => {
if (error) {
console.log('BAL: Error fetch cookie, not login');
realTarget.addEventListener('readystatechange', () => {
if (realTarget.readyState === 4 && realTarget.status === 200) {
const status = JSON.parse(realTarget.response);
if (status.code == -10403) showTamperMonkeyUpdate();
}
});
fn.apply(realTarget, argumentsList);
} else {
console.log(BAL: Get Cookie ${cookies});
realTarget.setRequestHeader('X-Cookie', cookies[0].value);
fn.apply(realTarget, argumentsList);
}
}
);
} else {
fn.apply(realTarget, argumentsList);
}
}
};

unsafeWindow.XMLHttpRequest = new Proxy(
XMLHttpRequest,
new ClassHandler(XhrHandler)
);

(() => {
var info = undefined;
Object.defineProperty(unsafeWindow, 'PGC_USERSTATE', {
configurable: true,
get: () => info,
set: v => {
if (v.area_limit == 1) {
console.log('BAL: modify area_limit = 0');
limited = true;
v.area_limit = 0;
}
info = v;
},
});
})();

window.addEventListener('load', () => {
if (document.querySelector('div.error-body')) {
// try load via proxy
console.log('BAL: Load failed, try use proxy');
const avid = //av(\d*)/gm.exec(window.location.pathname)[1];
for (const [u, loc] of url__WEBPACK_IMPORTED_MODULE_0_["url_replace_to"]) {
const detail = loc.api + 'x/web-interface/view/detail?aid=' + avid;
const xhr = new unsafeWindow.XMLHttpRequest();
xhr.open('GET', detail);
xhr.hookCookie = true;
xhr.onreadystatechange = function() {
if (this.readyState === xhr.DONE && this.status === 200) {
const r = JSON.parse(this.responseText).data.View.redirect_url;
console.log(BAL: Redirected to ${r}.);
window.location = r;
}
};
xhr.send();
}
}
});
})(XMLHttpRequest);

/***/ }),

// "./src/url.js":
/
!
****************!
!
./src/url.js !
*
*************/
/
! exports provided: url_status, url_play, url_api_replace, url_replace_to /
/
/ (function(module, webpack_exports, webpack_require) {

"use strict";
webpack_require.r(webpack_exports);
/* harmony export (binding) / webpack_require.d(webpack_exports, "url_status", function() { return url_status; });
/
harmony export (binding) / webpack_require.d(webpack_exports, "url_play", function() { return url_play; });
/
harmony export (binding) / webpack_require.d(webpack_exports, "url_api_replace", function() { return url_api_replace; });
/
harmony export (binding) / webpack_require.d(webpack_exports, "url_replace_to", function() { return url_replace_to; });
const url_status = [
/^https://bangumi.bilibili.com/view/web_api/season/user/status?.
/,
/^https://api.bilibili.com/pgc/view/web/season/user/status?.*/,
];

const url_play = /^https://api.bilibili.com/pgc/player/web/playurl?.*/;

const url_api_replace = /^https://api.bilibili.com//;

const url_replace_to = [
[
// TW
[/僅.*台/],
{
api: 'https://bilibili-tw-api.kghost.info/',
},
],
[
// HK
[/僅.港/],
{
api: 'https://bilibili-hk-api.kghost.info/',
},
],
[
// SG
[/仅限东南亚/],
{
api: 'https://bilibili-sg-api.kghost.info/',
},
],
[
// CN
[/^((?!僅).)
$/],
{
api: 'https://bilibili-cn-api.kghost.info/',
},
],
];

/***/ })

/******/ });
//# sourceMappingURL=bundle.js.map

JS 版本 get_sign 函数的 key 参数是什么?APP KEY?

fython 你好~

我想做的事情是根据视频地址拿到 interface 地址。

根据

http://www.bilibili.com/video/av4371609/

获取到

http://interface.bilibili.com/playurl?cid=7076936&player=1&ts=1460728450&sign=705c096777c7ed41d5cee161913c35be




思路是先手抓一个,然后去用程序跑,看看地址是否一样。
碰到的问题是根据文档里的代码,没成功,想问问咋回事。如题。

手抓的:
http://www.bilibili.com/video/av4371609/
cid 7076936
http://interface.bilibili.com/playurl?cid=7076936&player=1&ts=1460728450&sign=705c096777c7ed41d5cee161913c35be


### 程序
<script type="text/javascript" src="http://static.hdslb.com/js/md5.js"></script>
<script type="text/javascript">

    function get_sign(params, key)
 {
    var s_keys = [];
    for (var i in params)
    {
        s_keys.push(i);
    }
    s_keys.sort();
    var data = "";
    for (var i = 0; i < s_keys.length; i++)
    {
        // encodeURIComponent 返回的转义数字必须为大写( 如 %2F )
        data+=(data ? "&" : "")+s_keys[i]+"="+encodeURIComponent(params[s_keys[i]]);
    }
    return {
        "sign":hex_md5(data+key),
        "params":data
    };
 }

  p = {
    "cid" : "7076936",
    'player' : '1',
    'ts': '1460728588'
  };
/*
# cid = 7076936
# player = 1
# ts = 1460728588
*/
 a = get_sign(p, '85eb6835b0a1034e');
 console.log(a);  

</script>

然后 console(a) 的结果:
// Object { sign: "3a85837f235a317906016f1f6b86dd37", params: "cid=7076936&player=1&ts=1460728588" }

结果应该是 705c096777c7ed41d5cee161913c35be // 因为相同时间戳的那个手抓结果就是这个
但是程序是 3a85837f235a317906016f1f6b86dd37

APPKEY 85eb6835b0a1034e 来源网上的一篇博客。

非常感谢~

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.