WebKitGtk 实现 BiliBili 登录
最近在研究 BiliBili 的 api, 想做一个以视频为音源的播放器;其中的一个功能允许导入收藏夹的视频
主要有两个 api
1通过 mid 获取所有收藏夹信息
2https://api.bilibili.com/x/v3/fav/folder/created/list-all?up_mid=<mid>
3
4通过 media_id 获取指定收藏夹信息
5https://api.bilibili.com/x/v3/fav/resource/list?media_id=<media_id>&pn=<页数>&ps=<每页显示的视频个数>
但是光有 mid 还不够;这两个 api 都需要设置特定的 Cookie 进行认证,所以只能在请求前先实现登录
登录
BiliBili 主要支持短信登录
,密码登录
和二维码登录
;除了二维码登录
外,其余两种都需要在登录前进行人机验证(由 geetest 提供)
Cookie
在完成登录后,BiliBili 将设置几个 Cookie
1HTTP/1.1 200 OK
2Date: Mon, 13 Jul 2020 09:57:33 GMT
3Content-Type: application/json;charset=UTF-8
4Content-Length: 78
5Connection: keep-alive
6Server: Apache-Coyote/1.1
7Set-Cookie: DedeUserID=***; Domain=.bilibili.com; Expires=Sat, 18-Jul-2020 09:57:57 GMT; Path=/
8Set-Cookie: DedeUserID__ckMd5=***; Domain=.bilibili.com; Expires=Sat, 18-Jul-2020 09:57:57 GMT; Path=/
9Set-Cookie: SESSDATA=***; Domain=.bilibili.com; Expires=Sat, 18-Jul-2020 09:57:57 GMT; Path=/; HttpOnly
10Set-Cookie: bili_jct=***; Domain=.bilibili.com; Expires=Sat, 18-Jul-2020 09:57:57 GMT; Path=/
11Set-Cookie: sid=***; Domain=.bilibili.com; Expires=Sat, 18-Jul-2020 09:57:57 GMT; Path=/
12Expires: Mon, 13 Jul 2020 09:57:32 GMT
13Cache-Control: no-cache
14X-Cache-Webcdn: BYPASS from jd-sxhz-dx-w-01
示例来自 BAC Document
其中 SESSDATA
就是我们需要在请求中设置的 Cookie
WebKitGtk
如果加上人机验证,实现起来太复杂,不如用 WebKitGtk
打开登录界面,登录完成后读取设置的 cookie
WebKitGtk 能很轻松地和 Gtk 组合在一起
1GtkWidget *login_webkit;
2
3login_webkit = webkit_web_view_new();
4webkit_web_view_load_uri(WEBKIT_WEB_VIEW(login_webkit), "https://passport.bilibili.com/login");
5gtk_widget_set_size_request(login_webkit, 1300, 800);
新建一个 web_view
并打开登录界面
我们还可以创建一个按钮,在登录完成时被按下,调用回调函数读取 Cookie
1GtkWidget *btn_next;
2
3btn_next = gtk_button_new_with_label("下一步");
4g_signal_connect(btn_finish_login, "clicked", G_CALLBACK(load_changed), NULL);
读取 Cookie
1void get_cookie(WebKitCookieManager *cookie_mgr, GAsyncResult *res)
2{
3 GList *cookit_ls = webkit_cookie_manager_get_cookies_finish(cookie_mgr, res, NULL);
4
5 for (GList *cookie_l = cookit_ls; cookie_l != NULL; cookie_l = cookie_l->next) {
6 SoupCookie *soup_cookie = (SoupCookie *)cookie_l->data;
7 if (!strcmp(soup_cookie_get_name(soup_cookie), "DedeUserID")) {
8 const char *value = soup_cookie_get_value(soup_cookie);
9 account->DedeUserID = strdup(value);
10 continue;
11 }
12 if (!strcmp(soup_cookie_get_name(soup_cookie), "DedeUserID__ckMd5")) {
13 const char *value = soup_cookie_get_value(soup_cookie);
14 account->DedeUserID__ckMd5 = strdup(value);
15 continue;
16 }
17 if (!strcmp(soup_cookie_get_name(soup_cookie), "SESSDATA")) {
18 const char *value = soup_cookie_get_value(soup_cookie);
19 account->SESSDATA = strdup(value);
20 continue;
21 }
22 if (!strcmp(soup_cookie_get_name(soup_cookie), "bili_jct")) {
23 const char *value = soup_cookie_get_value(soup_cookie);
24 account->bili_jct = strdup(value);
25 continue;
26 }
27 if (!strcmp(soup_cookie_get_name(soup_cookie), "sid")) {
28 const char *value = soup_cookie_get_value(soup_cookie);
29 account->sid = strdup(value);
30 continue;
31 }
32 }
33 g_list_free(cookit_ls);
34
35 api_get_basic_info_net();
36}
37
38void load_changed(GtkWidget *widget)
39{
40 WebKitNetworkSession *net_session = webkit_network_session_get_default();
41 WebKitCookieManager *cookie_mgr = webkit_network_session_get_cookie_manager(net_session);
42
43 webkit_cookie_manager_get_all_cookies(cookie_mgr, NULL, (GAsyncReadyCallback)get_cookie, NULL);
44}
All the networking APIs have been moved from WebKitWebContext and WebKitWebsiteDataManager to the new class WebKitNetworkSession
新版的 WebKitGtk
将所有 API 从 WebKitWebContext
和 WebKitWebsiteDataManager
转移到了 WebKitNetworkSession
,现在我们只需要
通过webkit_network_session_get_default()
获取一个全局持久会话就能得到cookie_manager
发送请求
最后添加得到的 Cookie 即可获取收藏夹信息
curl
1curl https://api.bilibili.com/x/v3/fav/folder/created/list-all?up_mid=<mid> -b "SESSDATA=<SESSDATA>"
设置 Cookie(libcurl)
1curl_easy_setopt(easy_handle, CURLOPT_COOKIE, "SESSDATA=<SESSDATA>");