11<?php
2- // B站视频爬虫 - 简洁可用版(移除search相关代码)
3- header ('Content-Type: application/json; charset=utf-8 ' );
2+ /**
3+ * B站视频爬虫 - PHP 适配版 (道长重构)
4+ * 按照 BaseSpider 结构重写
5+ */
46
5- class BiliBiliSpider {
6- private $ extendDict = [];
7- private $ cookie = [];
8- private $ header = [
9- "User-Agent " => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.54 Safari/537.36 " ,
10- "Referer " => "https://www.bilibili.com "
11- ];
12-
13- public function __construct () {
14- $ this ->extendDict = $ this ->getExtendDict ();
15- $ this ->cookie = $ this ->getCookie ();
16- }
17-
18- private function getExtendDict () {
19- return [
20- 'cookie ' => $ this ->getConfigCookie (),
21- 'thread ' => '0 '
22- ];
23- }
7+ require_once __DIR__ . '/spider.php ' ;
8+
9+ class Spider extends BaseSpider {
2410
25- private function getConfigCookie () {
26- // 配置您的B站Cookie
27- return 'buvid3=xxxx; SESSDATA=xxxx; ' ;
11+ private $ cookie = [];
12+
13+ public function init ($ extend = '' ) {
14+ $ this ->headers ['Referer ' ] = "https://www.bilibili.com " ;
15+ // 配置初始 Cookie
16+ // 实际使用时,建议通过 ext 传入 cookie
17+ $ configCookie = 'buvid3=xxxx; SESSDATA=xxxx; ' ;
18+
19+ // 尝试从 extend 获取 cookie (假设 extend 是 JSON 字符串或直接是 cookie 字符串)
20+ // 这里简化处理:如果 extend 包含 SESSDATA,则认为是 cookie
21+ if (!empty ($ extend )) {
22+ if (strpos ($ extend , 'SESSDATA ' ) !== false ) {
23+ $ configCookie = $ extend ;
24+ } elseif (is_array ($ extend ) && isset ($ extend ['cookie ' ])) {
25+ $ configCookie = $ extend ['cookie ' ];
26+ } else {
27+ // 尝试解析 json
28+ $ json = json_decode ($ extend , true );
29+ if (isset ($ json ['cookie ' ])) {
30+ $ configCookie = $ json ['cookie ' ];
31+ }
32+ }
33+ }
34+
35+ $ this ->cookie = $ this ->parseCookie ($ configCookie );
2836 }
2937
30- private function getCookie () {
31- $ cookie = $ this ->extendDict ['cookie ' ] ?? '' ;
32- if (empty ($ cookie )) return [];
33-
38+ private function parseCookie ($ cookieStr ) {
39+ if (empty ($ cookieStr )) return [];
3440 $ cookies = [];
35- $ pairs = explode ('; ' , $ cookie );
41+ $ pairs = explode ('; ' , $ cookieStr );
3642 foreach ($ pairs as $ pair ) {
3743 $ pair = trim ($ pair );
3844 if (strpos ($ pair , '= ' ) !== false ) {
@@ -43,34 +49,6 @@ private function getCookie() {
4349 return $ cookies ;
4450 }
4551
46- private function httpRequest ($ url , $ params = []) {
47- $ ch = curl_init ();
48-
49- if (!empty ($ params )) {
50- $ url .= '? ' . http_build_query ($ params );
51- }
52-
53- $ headers = [];
54- foreach ($ this ->header as $ key => $ value ) {
55- $ headers [] = $ key . ': ' . $ value ;
56- }
57-
58- curl_setopt_array ($ ch , [
59- CURLOPT_URL => $ url ,
60- CURLOPT_RETURNTRANSFER => true ,
61- CURLOPT_TIMEOUT => 10 ,
62- CURLOPT_HTTPHEADER => $ headers ,
63- CURLOPT_COOKIE => $ this ->buildCookieString (),
64- CURLOPT_SSL_VERIFYPEER => false ,
65- CURLOPT_FOLLOWLOCATION => true
66- ]);
67-
68- $ response = curl_exec ($ ch );
69- curl_close ($ ch );
70-
71- return json_decode ($ response , true ) ?: [];
72- }
73-
7452 private function buildCookieString () {
7553 $ pairs = [];
7654 foreach ($ this ->cookie as $ name => $ value ) {
@@ -79,8 +57,18 @@ private function buildCookieString() {
7957 return implode ('; ' , $ pairs );
8058 }
8159
82- // homeContent - 首页分类
83- public function homeContent () {
60+ // 覆盖父类 fetch 以自动添加 cookie
61+ protected function fetch ($ url , $ options = [], $ headers = []) {
62+ if (!isset ($ options ['cookie ' ])) {
63+ $ cookieStr = $ this ->buildCookieString ();
64+ if (!empty ($ cookieStr )) {
65+ $ options ['cookie ' ] = $ cookieStr ;
66+ }
67+ }
68+ return parent ::fetch ($ url , $ options , $ headers );
69+ }
70+
71+ public function homeContent ($ filter = []) {
8472 $ classes = [
8573 ["type_id " => "沙雕仙逆 " , "type_name " => "傻屌仙逆 " ],
8674 ["type_id " => "沙雕动画 " , "type_name " => "沙雕动画 " ],
@@ -126,14 +114,12 @@ public function homeContent() {
126114 ["type_id " => "软件教程 " , "type_name " => "软件教程 " ],
127115 ["type_id " => "Windows " , "type_name " => "Windows " ]
128116 ];
129-
130117 return ['class ' => $ classes ];
131118 }
132-
133- // homeVideoContent - 首页推荐视频
119+
134120 public function homeVideoContent () {
135- $ url = 'https://api.bilibili.com/x/web-interface/popular ' ;
136- $ data = $ this ->httpRequest ($ url, [ ' ps ' => 20 , ' pn ' => 1 ] );
121+ $ url = 'https://api.bilibili.com/x/web-interface/popular?ps=20&pn=1 ' ;
122+ $ data = json_decode ( $ this ->fetch ($ url), true );
137123
138124 $ videos = [];
139125 if (isset ($ data ['data ' ]['list ' ])) {
@@ -146,22 +132,21 @@ public function homeVideoContent() {
146132 ];
147133 }
148134 }
149-
150135 return ['list ' => $ videos ];
151136 }
152-
153- // categoryContent - 分类内容(使用搜索API)
154- public function categoryContent ($ tid , $ page , $ filters = []) {
155- $ page = max (1 , intval ($ page ));
137+
138+ public function categoryContent ($ tid , $ pg = 1 , $ filter = [], $ extend = []) {
139+ $ page = max (1 , intval ($ pg ));
156140
157141 $ url = 'https://api.bilibili.com/x/web-interface/search/type ' ;
158142 $ params = [
159143 'search_type ' => 'video ' ,
160144 'keyword ' => $ tid ,
161145 'page ' => $ page
162146 ];
147+ $ url .= '? ' . http_build_query ($ params );
163148
164- $ data = $ this ->httpRequest ($ url, $ params );
149+ $ data = json_decode ( $ this ->fetch ($ url), true );
165150
166151 $ videos = [];
167152 if (isset ($ data ['data ' ]['result ' ])) {
@@ -180,19 +165,19 @@ public function categoryContent($tid, $page, $filters = []) {
180165 $ pageCount = $ data ['data ' ]['numPages ' ] ?? 1 ;
181166 $ total = $ data ['data ' ]['numResults ' ] ?? count ($ videos );
182167
183- return [
184- 'list ' => $ videos ,
185- 'page ' => $ page ,
186- 'pagecount ' => $ pageCount ,
187- 'limit ' => 20 ,
188- 'total ' => $ total
189- ];
168+ return $ this ->pageResult ($ videos , $ page , $ total , 20 );
190169 }
191-
192- // detailContent - 视频详情
193- public function detailContent ($ vid ) {
194- $ url = 'https://api.bilibili.com/x/web-interface/view ' ;
195- $ data = $ this ->httpRequest ($ url , ['aid ' => $ vid ]);
170+
171+ public function searchContent ($ key , $ quick = false , $ pg = 1 ) {
172+ return $ this ->categoryContent ($ key , $ pg );
173+ }
174+
175+ public function detailContent ($ ids ) {
176+ if (empty ($ ids )) return ['list ' => []];
177+ $ vid = $ ids [0 ];
178+
179+ $ url = 'https://api.bilibili.com/x/web-interface/view?aid= ' . $ vid ;
180+ $ data = json_decode ($ this ->fetch ($ url ), true );
196181
197182 if (!isset ($ data ['data ' ])) {
198183 return ['list ' => []];
@@ -204,7 +189,7 @@ public function detailContent($vid) {
204189 $ playUrl = '' ;
205190 foreach ($ video ['pages ' ] as $ index => $ page ) {
206191 $ part = $ page ['part ' ] ?: '第 ' . ($ index + 1 ) . '集 ' ;
207- $ duration = $ this -> formatDuration ( $ page [ ' duration ' ]);
192+ // 构造 playId: avid_cid
208193 $ playUrl .= "{$ part }\${$ vid }_ {$ page ['cid ' ]}# " ;
209194 }
210195
@@ -219,35 +204,34 @@ public function detailContent($vid) {
219204
220205 return ['list ' => [$ vod ]];
221206 }
222-
223- // playContent - 播放地址(高清优化)
224- public function playContent ($ vid ) {
225- if (strpos ($ vid , '_ ' ) !== false ) {
226- list ($ avid , $ cid ) = explode ('_ ' , $ vid );
207+
208+ public function playContent ($ flag , $ id , $ vipFlags = []) {
209+ if (strpos ($ id , '_ ' ) !== false ) {
210+ list ($ avid , $ cid ) = explode ('_ ' , $ id );
227211 } else {
228- return $ this -> errorResponse ( ' 无效的视频ID格式 ') ;
212+ return [ ' parse ' => 0 , ' url ' => '' , ' error ' => ' 无效的视频ID格式 '] ;
229213 }
230214
231- // 使用高质量参数
232215 $ url = 'https://api.bilibili.com/x/player/playurl ' ;
233216 $ params = [
234217 'avid ' => $ avid ,
235218 'cid ' => $ cid ,
236219 'qn ' => 112 , // 原画质量
237220 'fnval ' => 0 ,
238221 ];
222+ $ url .= '? ' . http_build_query ($ params );
239223
240- $ data = $ this ->httpRequest ($ url, $ params );
224+ $ data = json_decode ( $ this ->fetch ($ url), true );
241225
242226 if (!isset ($ data ['data ' ]) || $ data ['code ' ] !== 0 ) {
243- return $ this -> errorResponse ( ' 获取播放地址失败 ') ;
227+ return [ ' parse ' => 0 , ' url ' => '' , ' error ' => ' 获取播放地址失败 '] ;
244228 }
245229
246230 // 直接返回第一个播放地址
247231 if (isset ($ data ['data ' ]['durl ' ][0 ]['url ' ])) {
248232 $ playUrl = $ data ['data ' ]['durl ' ][0 ]['url ' ];
249233
250- $ headers = $ this ->header ;
234+ $ headers = $ this ->headers ;
251235 $ headers ['Referer ' ] = 'https://www.bilibili.com/video/av ' . $ avid ;
252236 $ headers ['Origin ' ] = 'https://www.bilibili.com ' ;
253237
@@ -259,9 +243,9 @@ public function playContent($vid) {
259243 ];
260244 }
261245
262- return $ this -> errorResponse ( ' 无法获取播放地址 ') ;
246+ return [ ' parse ' => 0 , ' url ' => '' , ' error ' => ' 无法获取播放地址 '] ;
263247 }
264-
248+
265249 // 工具函数
266250 private function formatDuration ($ seconds ) {
267251 if ($ seconds <= 0 ) return '00:00 ' ;
@@ -277,50 +261,6 @@ private function formatSearchDuration($duration) {
277261 }
278262 return '00:00 ' ;
279263 }
280-
281- private function errorResponse ($ message ) {
282- return [
283- 'parse ' => 0 ,
284- 'url ' => '' ,
285- 'error ' => $ message
286- ];
287- }
288264}
289265
290- // 主处理逻辑
291- $ ac = $ _GET ['ac ' ] ?? 'detail ' ;
292- $ t = $ _GET ['t ' ] ?? '' ;
293- $ pg = $ _GET ['pg ' ] ?? '1 ' ;
294- $ f = $ _GET ['f ' ] ?? '' ;
295- $ ids = $ _GET ['ids ' ] ?? '' ;
296- $ id = $ _GET ['id ' ] ?? '' ;
297-
298- $ spider = new BiliBiliSpider ();
299-
300- try {
301- switch ($ ac ) {
302- case 'detail ' :
303- if (!empty ($ ids )) {
304- echo json_encode ($ spider ->detailContent ($ ids ));
305- } elseif (!empty ($ t )) {
306- $ filters = !empty ($ f ) ? json_decode ($ f , true ) : [];
307- echo json_encode ($ spider ->categoryContent ($ t , $ pg , $ filters ));
308- } else {
309- $ result = $ spider ->homeContent ();
310- $ videoResult = $ spider ->homeVideoContent ();
311- $ result ['list ' ] = $ videoResult ['list ' ];
312- echo json_encode ($ result );
313- }
314- break ;
315-
316- case 'play ' :
317- echo json_encode ($ spider ->playContent ($ id ));
318- break ;
319-
320- default :
321- echo json_encode (['error ' => '未知操作: ' . $ ac ]);
322- }
323- } catch (Exception $ e ) {
324- echo json_encode (['error ' => $ e ->getMessage ()]);
325- }
326- ?>
266+ (new Spider ())->run ();
0 commit comments