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

fix(route): 通过在配置中预添加大量 Cookie 来绕过 Bilibili 反爬 #13365

Merged
merged 2 commits into from
Sep 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 34 additions & 9 deletions lib/v2/bilibili/cache.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,49 @@
const got = require('@/utils/got');
const utils = require('./utils');
const cheerio = require('cheerio');
const config = require('@/config').value;

module.exports = {
getCookie: (ctx) => {
if (Object.keys(config.bilibili.cookies).length > 0) {
Copy link
Collaborator

@TonyRL TonyRL Sep 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess getCookie needs an update. Maybe filling buvid3, b_nut, b_lsid, _uuid, buvid4 cookies when performing API requests will have less strict rate limit.

  1. buvid3 and b_nut will be provided by server when you visited the site first time, either https://www.bilibili.com/ or https://space.bilibili.com/1 will have them.
2. compute b_lsid and _uuid
// a
function randomHexStr(length) {
    let string = '';
    for (let r = 0; r < length; r++) {
        string += dec2HexUpper(16 * Math.random());
    }
    return padStringWithZeros(string, length);
}
// o
function dec2HexUpper(e) {
    return Math.ceil(e).toString(16).toUpperCase();
}
// s
function padStringWithZeros(string, length) {
    let padding = '';
    if (string.length < length) {
        for (let n = 0; n < length - string.length; n++) {
            padding += '0';
        }
    }
    return padding + string;
}
function lsid() {
    const e = Date.now().toString(16).toUpperCase();
    const lsid = randomHexStr(8) + '_' + e;
    return lsid;
}
function _uuid() {
    let e = randomHexStr(8);
    let t = randomHexStr(4);
    let r = randomHexStr(4);
    let n = randomHexStr(4);
    let o = randomHexStr(12);
    let i = Date.now();
    return e + '-' + t + '-' + r + '-' + n + '-' + o + padStringWithZeros((i % 100000).toString(), 5) + 'infoc';
}
  1. GET api.bilibili.com/x/frontend/finger/spi with cookies buvid3, b_nut, b_lsid, _uuid which will return buvid4 from data.b_4

  2. continuing typical api request like /x/space/wbi/acc/info or /x/space/wbi/arc/search with all 5 cookies

Note: The value of the cookie buvid_fp and fingerprint are basically the output of fingerprintjs

return config.bilibili.cookies[Object.keys(config.bilibili.cookies)[Math.floor(Math.random() * Object.keys(config.bilibili.cookies).length)]];
}
const key = 'bili-cookie';
return ctx.cache.tryGet(key, async () => {
// default Referer: https://www.bilibili.com is limited
// Bilibili return cookies with multiple set-cookie
const url = 'https://www.bilibili.com/';
const response = await got(url, {
headers: {
Referer: url,
},
});
const setCookies = response.headers['set-cookie'];
if (typeof setCookies === 'undefined') {
let response = await got('https://www.bilibili.com/');
const setCookie = response.headers['set-cookie'];
if (typeof setCookie === 'undefined') {
return '';
}
return setCookies.map((cookie) => cookie.split(';')[0]).join('; ');
const cookie = setCookie.map((cookie) => cookie.split(';')[0]);
cookie.push(['b_lsid', utils.lsid()].join('='));
cookie.push(['_uuid', utils._uuid()].join('='));
response = await got('https://api.bilibili.com/x/frontend/finger/spi', {
headers: {
Referer: 'https://www.bilibili.com/',
Cookie: cookie.join('; '),
}
});
cookie.push(['bvuid4', encodeURIComponent(response.data.data.b_4)].join('='));
const e = Math.floor(Date.now() / 1000);;
const hexsign = utils.hexsign(e);
await got('https://space.bilibili.com/1', {
headers: {
Referer: 'https://www.bilibili.com/',
Cookie: cookie.join('; '),
}
});
response = await got.post(`https://api.bilibili.com/bapis/bilibili.api.ticket.v1.Ticket/GenWebTicket?key_id=ec02&hexsign=${hexsign}&context[ts]=${e}&csrf=`, {
headers: {
Referer: 'https://space.bilibili.com/1',
Cookie: cookie.join('; '),
}
});
cookie.push(['bili_ticket', response.data.data.ticket].join('='));
cookie.push(['bili_ticket_expires', (parseInt(response.data.data.created_at) + parseInt(response.data.data.ttl)).toString()].join('='));
return cookie.join('; ');
});
},
getVerifyString: (ctx) => {
Expand Down
64 changes: 63 additions & 1 deletion lib/v2/bilibili/utils.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,71 @@
const md5 = require('@/utils/md5');
const CryptoJS = require('crypto-js');

function iframe(aid, page, bvid) {
return `<iframe src="https://player.bilibili.com/player.html?${bvid ? `bvid=${bvid}` : `aid=${aid}`}${
page ? `&page=${page}` : ''
}&high_quality=1&autoplay=0" width="650" height="477" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"></iframe>`;
}

const addVerifyInfo = (params, verifyString) => {
// a
function randomHexStr(length) {
let string = '';
for (let r = 0; r < length; r++) {
string += dec2HexUpper(16 * Math.random());
}
return padStringWithZeros(string, length);
}

// o
function dec2HexUpper(e) {
return Math.ceil(e).toString(16).toUpperCase();
}

// s
function padStringWithZeros(string, length) {
let padding = '';
if (string.length < length) {
for (let n = 0; n < length - string.length; n++) {
padding += '0';
}
}
return padding + string;
}

function lsid() {
const e = Date.now().toString(16).toUpperCase();
const lsid = randomHexStr(8) + '_' + e;
return lsid;
}

function _uuid() {
const e = randomHexStr(8);
const t = randomHexStr(4);
const r = randomHexStr(4);
const n = randomHexStr(4);
const o = randomHexStr(12);
const i = Date.now();
return e + '-' + t + '-' + r + '-' + n + '-' + o + padStringWithZeros((i % 100000).toString(), 5) + 'infoc';
}

// P
function shiftCharByOne(string) {
let shiftedStr = '';
for (let n = 0; n < string.length; n++) {
shiftedStr += String.fromCharCode(string.charCodeAt(n) - 1);
}
return shiftedStr;
}

// o
function hexsign(e) {
const n = 'YhxToH[2q';
const r = CryptoJS.HmacSHA256('ts'.concat(e), shiftCharByOne(n));
const o = CryptoJS.enc.Hex.stringify(r);
return o;
}

function addVerifyInfo(params, verifyString) {
const searchParams = new URLSearchParams(params);
searchParams.sort();
const verifyParam = searchParams.toString();
Expand All @@ -17,6 +76,9 @@ const addVerifyInfo = (params, verifyString) => {

module.exports = {
iframe,
lsid,
_uuid,
hexsign,
addVerifyInfo,
bvidTime: 1589990400,
};
6 changes: 1 addition & 5 deletions lib/v2/bilibili/video-all.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ const got = require('@/utils/got');
const cache = require('./cache');
const utils = require('./utils');
const { parseDate } = require('@/utils/parse-date');
const config = require('@/config');

module.exports = async (ctx) => {
const uid = ctx.params.uid;
Expand All @@ -29,21 +28,18 @@ module.exports = async (ctx) => {
const pageTotal = Math.ceil(response.data.data.page.count / response.data.data.page.ps);

const getPage = async (pageId) => {
// A hack way to skip limit
const userAgent = config.ua + `.${pageId}`;
const cookie = await cache.getCookie(ctx);
await got(`https://space.bilibili.com/${uid}/video?tid=0&page=${pageId}&keyword=&order=pubdate`, {
headers: {
Referer: `https://space.bilibili.com/${uid}/`,
Cookie: cookie,
'User-Agent': userAgent,
},
});
const params = utils.addVerifyInfo(`mid=${uid}&ps=30&tid=0&pn=${pageId}&keyword=&order=pubdate&platform=web&web_location=1550101&order_avoided=true`, verifyString);
return got(`https://api.bilibili.com/x/space/wbi/arc/search?${params}`, {
headers: {
Referer: `https://space.bilibili.com/${uid}/video?tid=0&page=${pageId}&keyword=&order=pubdate`,
Cookie: cookie,
'User-Agent': userAgent,
},
});
};
Expand Down
Loading