croyoux's blog

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 提供)

在完成登录后,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 从 WebKitWebContextWebKitWebsiteDataManager 转移到了 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>");
Tags: