玩转 YouTube Data API v3(公共数据)和 Kigs 框架





5.00/5 (5投票s)
Kigs framework C++ Windows 项目,用于检索和显示给定 YouTube 频道订阅者所订阅的其他频道。
目录
引言
Kigs framework 是一个开源(MIT 许可证)、免费、C++、多用途和跨平台的框架,您可以在 https://github.com/Kigs-framework/kigs 找到它。
本文介绍的应用程序显示了订阅给定频道用户的频道订阅统计信息。
为此,应用程序首先检索给定 YouTube 频道的视频,然后检索每个视频的评论,并对每个评论的作者测试其订阅是否公开。
如果是,应用程序会检查作者是否订阅了给定的 YouTube 频道,如果是,则将作者订阅的其他频道添加到统计信息中。
在下图,我们可以看到,对于找到的大约 50 位 Kurzgesagt 订阅者,34% 订阅了 TED Ed,30% 订阅了 Mark Rober,30% 订阅了 VSauce。
背景
Code Project 上开始了一系列文章。您可以在这里查看第一篇文章
当然,您可以阅读本文,而无需先阅读 Kigs framework 介绍系列。
我们很乐意有希望参与 Kigs framework 的使用和改进的人加入我们。
Google API 密钥
要执行代码,您需要创建一个 Google API 密钥。您可以免费创建并使用一个(有限的)API 密钥。
在 https://console.developers.google.com 注册一个 Google 账户。
创建一个新项目,然后在“凭据”页面上,点击创建凭据 > API 密钥。
然后为“YouTube Data API v3”创建一个 API 密钥,平台选择“其他平台...”和公共数据。
构建代码
要构建代码,您需要从 GitHub 克隆框架并为 Windows 平台(Visual C++、CMake)设置开发环境。请查看 https://github.com/Kigs-framework/kigs/wiki/Getting-Started。
然后您可以克隆 publicKigsProjects 存储库( https://github.com/Kigs-framework/publicKigsProjects ),使 `kigs` 和 `publicKigsProjects` 文件夹位于同一级别。
然后执行(双击)`kigs\scripts\generateWinCMake.bat`。
浏览到生成的 `Build\solutionWinCMake\publicKigsProjects\projects\YoutubeAnalyser` 文件夹。
双击 `YoutubeAnalyser.sln` 在 Visual Studio 中打开解决方案。
在 Visual Studio 中,将 `YoutubeAnalyser` 设置为启动项目。选择 `StaticDebug` 或 `StaticRelease` 配置并构建。
执行代码
在执行代码之前,您需要编辑配置文件 `kigs\projects\YoutubeAnalyser\Data\launchParams.json`。
用文本编辑器打开它。您会看到
{
"GoogleKey" : "INSERT_YOUR_GOOGLE_KEY_HERE",
"ChannelID" : "INSERT_CHANNEL_ID_HERE",
"ValidUserCount" : 100,
"MaxChannelCount" : 40,
"ValidChannelPercent" : 0.02,
}
将 `INSERT_YOUR_GOOGLE_KEY_HERE` 替换为您之前生成的密钥。
然后去 YouTube,选择您最喜欢的频道之一(在本文中,我将使用“Kurzgesagt – In a Nutshell”)并查看 URL,https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q。
如果 URL 不包含频道 ID,您也可以查看页面源代码并搜索以“canonical”开头,看起来像频道 ID(以“UC”开头,后跟字母数字字符)的内容。
复制频道 ID `UCsXVk37bltHxD1rDPwtNM8Q` 并将 `INSERT_CHANNEL_ID_HERE` 替换为所需的频道 ID。
保存并关闭 `launchParams.json`。您现在可以在 Visual C++ 中执行该项目。
这是百分比视图,显示了其他频道的最高得分。
这是吸引/排斥视图。
深入代码
现在我们将看到 Kigs framework 如何用于做几件事情。
操作 JSON 文件
`YouTubeAnalyser` 类具有以下成员
// general application parameters
std::string mKey = "";
std::string mChannelName;
unsigned int mSubscribedUserCount = 100;
unsigned int mMaxChannelCount = 16;
int mMaxUserPerVideo = 0;
float mValidChannelPercent = 0.02f;
在应用程序启动时,我们需要检索存储在 `launchParams.json` 中的值。
这通过 `YoutubeAnalyser.cpp`(大约第 70 行)中的以下代码完成
JSonFileParser L_JsonParser;
CoreItemSP initP = L_JsonParser.Get_JsonDictionary("launchParams.json");
// retrieve parameters
mKey = "&key=" + (std::string)initP["GoogleKey"];
mChannelName = initP["ChannelID"];
auto SetMemberFromParam = [&](auto& x, const char* id) {
if (!initP[id].isNil()) x = initP[id];
};
SetMemberFromParam(mSubscribedUserCount, "ValidUserCount");
SetMemberFromParam(mMaxChannelCount, "MaxChannelCount");
SetMemberFromParam(mValidChannelPercent, "ValidChannelPercent");
SetMemberFromParam(mMaxUserPerVideo, "MaxUserPerVideo");
SetMemberFromParam(mUseKeyword, "UseKeyword");
如您所见,过程非常简单:创建一个 `JSonFileParser` 实例,以文件名作为参数调用 `Get_JsonDictionary`,并将结果存储在 `CoreItemSP initP` 实例中。
然后您可以使用 `isNil()` 方法搜索 `CoreItemSP` 实例上是否有可用的值,并通过 `initP["searched object"]` 访问它们。
更复杂的 JSON 数据访问在代码的其他部分完成(例如,大约在第 1430 行)
for (int i = 0; i < commentThreads->size(); i++)
{
CoreItemSP topComment = commentThreads[i]["snippet"]["topLevelComment"]["snippet"];
if (!topComment.isNil())
{
std::string authorID = topComment["authorChannelId"]["value"];
// ...
}
}
创建类似 JSON 的对象并保存它也很容易(大约在第 1170 行)
JSonFileParser L_JsonParser;
std::string filename = "Cache/" + mChannelName + "/videos/";
filename += videoID + "_videos.json";
CoreItemSP initP= CoreItemSP::getCoreMap();
CoreItemSP v = CoreItemSP::getCoreVector();
for (const auto& auth : mAuthorListToProcess)
{
v->set("", CoreItemSP::getCoreValue(auth));
}
initP->set("authors", v);
L_JsonParser.Export((CoreMap<std::string>*)initP.get(), filename);
HTTP GET 请求
公共 YouTube 数据通过执行 `GET` HTTP 请求(实际上是 HTTPS)来访问。
使用 Kigs framework,我们首先需要在应用程序初始化方法 `YoutubeAnalyser::ProtectedInit()` 中创建 `HTTPRequest` 模块。
CoreCreateModule(HTTPRequestModule, 0);
然后创建一个 `HTTPConnect` 实例并进行初始化
// ask for an instance of class HTTPConnect
mGoogleConnect = KigsCore::GetInstanceOf("googleConnect", "HTTPConnect");
// set HostName
mGoogleConnect->setValue("HostName", "www.googleapis.com");
// set connection type ( HTTP or HTTPS )
mGoogleConnect->setValue("Type", "HTTPS");
// set Port to default HTTPS port
mGoogleConnect->setValue("Port", "443");
// initialize instance with given parameters
mGoogleConnect->Init();
然后每次需要 HTTP `GET` 请求时,都会创建一个 `HTTPRequest` 实例
// encode request in URL
std::string url = "/youtube/v3/channels?part=snippet&id=";
std::string request = url + mChannelName + mKey;
// then ask HTTPConnect to create a HTTPRequest instance
// parameters are:
// - the request itself
// - the callback to call when results are received
// - the object on which callback is called
mAnswer = mGoogleConnect->retreiveGetAsyncRequest(request.c_str(), "getChannelID", this);
// initialize the instance to really send the request
mAnswer->Init();
Kigs framework 回调的声明如下(此处在 `YoutubeAnalyser.h` 中)
DECLARE_METHOD(getChannelID);
DECLARE_METHOD(getChannelPage);
DECLARE_METHOD(getVideoList);
// ...
COREMODIFIABLE_METHODS(getChannelID, getVideoList, getChannelPage);
然后回调的定义如下(此处在 `YoutubeAnalyser.cpp` 的大约第 1060 行)
DEFINE_METHOD(YoutubeAnalyser, getChannelID)
{
auto json=RetrieveJSON(sender);
if (!json.isNil())
{
CoreItemSP IDItem = json["items"][0]["id"];
// ...
显示结果
主屏幕使用 XML 文件 `YoutubeAnalyser\Data\assets\Screen_Main.xml` 定义。
该文件以这种方式开始
<?xml version="1.0" encoding="iso-8859-1"?>
<Inst N="sequencemain" T="DataDrivenSequence">
每次在数据驱动的 Kigs framework 应用程序中初始化新序列时,都会调用 `ProtectedInitSequence` 方法,并将序列名称作为参数。
在 `YoutubeAnalyser.cpp`(大约第 890 行)中,该方法定义如下
void YoutubeAnalyser::ProtectedInitSequence(const kstl::string& sequence)
{
if (sequence == "sequencemain")
{
mMainInterface = GetFirstInstanceByName("UIItem", "Interface");
}
}
序列名称“`sequencemain`”如前所述在 `Screen_Main.xml` 中定义。因此,在这里,我们将在框架所有实例化的对象中搜索名为“`Interface`”的 `UIItem` 类(所有 2D 元素的基础类)的实例,并将其存储在成员变量 `mMainInterface` 中。
在同一个 `Screen_Main.xml` 中,`Interface` `UIItem` 有一个(除其他外)名为“`thumbnail`”的 `UIImage` 子节点,它本身有一个名为“`ChannelName`”的 `UIText` 子节点
<Inst N="Interface" T="UIItem">
<Attr N="SizeY" V="800"/>
<Attr N="SizeX" V="1280"/>
<Inst N="thumbnail" T="UIImage">
<Attr N="Priority" V="48"/>
<Attr N="Dock" V="{0.5,0.5}"/>
<Attr N="Anchor" V="{0.5,0.5}"/>
<Inst N="ChannelName" T="UIText">
<Attr N="Priority" V="47"/>
<Attr N="Text" V="channelName"/>
<Attr N="Dock" V="{0.5,1.0}"/>
<Attr N="Anchor" V="{0.5,0.0}"/>
<Attr N="SizeX" V="-1"/>
<Attr N="SizeY" V="-1"/>
<Attr N="Font" V="Calibri.ttf"/>
<Attr N="FontSize" V="20"/>
<Attr N="MaxWidth" V="200"/>
</Inst>
</Inst>
</Inst>
在 `YoutubeAnalyser.cpp`(大约第 600 行)中,我们将设置频道的纹理和名称
if (mMainInterface)
{
// check if Channel texture was loaded
if (mChannelInfos.mThumb.mTexture && mMainInterface["thumbnail"])
{
// cast to UIImage smartpointer reference
const SP<UIImage>& tmp = mMainInterface["thumbnail"];
// texture was not already set ?
if (!tmp->HasTexture())
{
// add texture
tmp->addItem(mChannelInfos.mThumb.mTexture);
// set "Text" value on "ChannelName" instance son of "thumbnail" instance
// son of mMainInterface instance
mMainInterface["thumbnail"]["ChannelName"]("Text") = mChannelInfos.mName;
}
}
}
结论
免费的 Google API 密钥有限制,因此我们的应用程序每天只能进行有限数量的请求。但是所有请求都已缓存到文件中,因此当达到限制时,您可以关闭应用程序并在第二天重新执行以进一步操作。
在本文中,我们了解了 **Kigs framework** 的一些功能
- 操作 JSON 文件或对象
- 创建和操作框架类的实例
- XML 序列化,2D 显示
- ...
如果您喜欢本文,请随时评分,阅读 Kigs framework 介绍系列,并加入我们使用 Kigs framework,帮助我们使其保持活力并成长。
历史
- 2020 年 4 月 13 日:首次发布
- 2020 年 5 月 1 日:Kigs-framework GitHub 存储库已迁移
- 2020 年 7 月 9 日:修复了一些错误,改进了错误管理
- 2020 年 9 月 1 日:Kigs 存储库成员变量重命名后更新源代码
- 2020 年 9 月 14 日:更新源代码,文章中的行号和 publicKigsProjects 存储库
- 2021 年 2 月 24 日:更新源代码:圆角缩略图,新可视化