准备

你需要一个阿里云的账号并完成实名认证

开始

创建服务

  1. 首先进入阿里云函数计算,点击侧边栏服务和函数
  2. 选择当前区域为中国(香港)
  3. 创建服务,名称随意,剩下的默认即可

创建函数

  1. 进入刚刚创建的服务,点击左上创建函数
  2. 选择使用标准的 Runtime 从零创建,函数名称随意,请求处理程序类型选择处理 HTTP 请求
  3. 运行环境选择 PHP7.2,代码上传方式默认即可
  4. 在高级配置里选择弹性实例,内存规格 128MB,执行超时时间 15 秒,别的无需修改,直接创建即可

编辑函数

进入函数详情页,将下面代码替换原有的代码,然后点部署函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<?php

use RingCentral\Psr7\Response;
/*
To enable the initializer feature (https://help.aliyun.com/document_detail/89029.html)
please implement the initializer function as below:
function initializer($context) {
echo 'initializing' . PHP_EOL;
}
*/

function handler($request, $context): Response
{
/*
$body = $request->getBody()->getContents();
$queries = $request->getQueryParams();
$method = $request->getMethod();
$headers = $request->getHeaders();
$path = $request->getAttribute('path');
$requestURI = $request->getAttribute('requestURI');
$clientIP = $request->getAttribute('clientIP');
*/
/* Config */
$upstream_pc_url = 'https://api.bilibili.com/pgc/player/web/playurl';
$upstream_app_url = 'https://api.bilibili.com/pgc/player/api/playurl';
$upstream_pc_search_url = 'https://api.bilibili.com/x/web-interface/search/type';
$timeout = 5; // seconds


/* Read incoming request */
$request_method = $request->getMethod();
$request_query = substr(stristr($request->getAttribute("requestURI"), '?'),1);
//$request->getHeaderLine('referer')会被阿里云替换成云函url
//$req_referer = $request->getHeaderLine('referer');
$req_referer = "https://www.bilibili.com";
$request_headers = $request->getHeaders();
$request_body = $request->getBody()->getContents();
$request_uri = $request->getAttribute('requestURI');



/* Forward request */
$ch = curl_init();

//处理请求相关header
$request_headers = array_remove_by_key($request_headers,'X-Forwarded-Proto');
$request_headers = array_remove_by_key($request_headers,'Host');
$request_headers = array_remove_by_key($request_headers,'Referer');
//配置body压缩方式
$request_headers = array_remove_by_key($request_headers,'Accept-Encoding');
curl_setopt($ch, CURLOPT_ENCODING, "identity");//好像b站只有br压缩

$headers = array();
foreach ($request_headers as $key => $value) {
$headers[] = $key . ": " .implode($value);
}

//判断请求接口
if(substr_count($request_uri,'/search/type')!=0){
$url = $upstream_pc_search_url . '?' .$request_query;
curl_setopt($ch, CURLOPT_REFERER, $req_referer);
}elseif (substr_count($request_uri,'playurl')!=0){
//判断使用的那个pc还是app接口
if(substr_count($request_query,'platform=android')!=0){
$url = $upstream_app_url . '?' .$request_query;
curl_setopt($ch, CURLOPT_USERAGENT, 'Bilibili Freedoooooom/MarkII');
}else{
$url = $upstream_pc_url . '?' .$request_query;
curl_setopt($ch, CURLOPT_REFERER, $req_referer);
}
}else{
$header['Content-Type'] = 'text/plain';
return new Response(
502,
$header,
'Failed to match interface.'
);
}
//curl配置
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request_method);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, $request_body);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
$header = array();

if ($response === false) {
$header['Content-Type'] = 'text/plain';
return new Response(
502,
$header,
'Upstream host did not respond.'
);
} else {
$header_length = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$response_headers = explode("\n", substr($response, 0, $header_length));
$response_body = substr($response, $header_length);
//处理返回相关header
foreach ($response_headers as $n => $response_header) {
//配置返回的body压缩方式
if (strpos($response_header, "Content-Encoding") !== false) {
$response_headers[$n] = "Content-Encoding: identity\n";
}
//删除B站返回的Content-Length,防止浏览器只读取Content-Length长度的数据,造成json不完整
if (strpos($response_header, "Content-Length") !== false) {
unset($response_headers[$n]);
}
//阿里云函数好像会自己添加Access-Control-Allow-Credentials头,删除b站返回的
if (strpos($response_header, "Access-Control-Allow-Credentials") !== false) {
unset($response_headers[$n]);
}
}
unset($response_header);

//response_headers数组转成key=>value形式
foreach ($response_headers as $header_string) {
$header_tmp = explode(': ', $header_string, 2);
if (count($header_tmp) == 2) {
$header[$header_tmp[0]] = trim($header_tmp[1]);
}
}

curl_close($ch);
// 这行用于调试请求信息
// return new Response(200, array(), json_encode(array('header' => $header, 'body' => $response_body, 'url' => $url, 'response'=>$response, 'curl_headers'=>$curl_response_headers)));
return new Response(
200,
$header,
$response_body
);
}
}

/*tool*/
//某个字符串在另一个字符串第N此出现的下标
function str_n_pos($str, $find, $n)
{
$pos_val = 0;
for ($i = 1; $i <= $n; $i++) {
$pos = strpos($str, $find);
$str = substr($str, $pos + 1);
$pos_val = $pos + $pos_val + 1;
}
$count = $pos_val - 1;
return $count;
}

function array_remove_by_key($arr, $key)
{
if(!array_key_exists($key, $arr)){
return $arr;
}
$keys = array_keys($arr);
$index = array_search($key, $keys);
if($index !== FALSE){
array_splice($arr, $index, 1);
}

return $arr;
}

查看访问地址

在函数详情页点击触发器管理,其中的公网访问地址就是代理地址

自定义域名

在云函数首页点击高级功能 - 域名管理 - 添加自定义域名

输入你要的域名,然后在路由配置里选择对应的函数即可(https 和 cdn 加速按照个人需求开启)

如何使用

网页端

安装解除 b 站区域限制油猴脚本,在参数设置里即可使用刚刚创建的公网访问地址或者自定义域名

手机端

使用哔哩漫游解锁番剧限制,自定义解析服务器

本文摘抄自梦璃雨落
由于阿里云函数不稳定,需要不断更新函数,所以摘抄自用,侵删