提取网页的一部分显然得到了一些陈旧的信息; 2020 年 2 月开始的网络抓取似乎在 2021 年 7 月不再起作用。 嗯,这是预料之中的,因为 YouTube 没有理由不能更改格式化网页的 HTML。 @LNitz促使我重新审视并展示如何使用更官方的 API 来获取数据以及如何将该数据存储到数据表中。
JSL 中的注释将引导您前往可以获得 API 密钥的页面。 Google/YouTube 使用该密钥来限制您可以发出的请求数量。 构建这段代码显然使用了我每日配额的大约 500/10,000。 我已经运行了很多次了。 https://console.cloud.google.com/apis/dashboard 。
最初的问题是将 YouTube 视频的描述放入数据表中。 答案下载了(~500Kbyte)网页 HTML 和与关键字匹配的模式......似乎已经改变,因此匹配不再有效。 API 应该更加健壮。 它返回 JSON 数据,这比 HTML 更容易解析。 也小一些。 现在的问题是阅读 API 文档以弄清楚它是如何工作的。 幸运的是,大多数问题都可以通过谷歌搜索。
Video links, descriptions, thumbnails.Video links, descriptions, thumbnails.Video links, descriptions, thumbnails.Video links, descriptions, thumbnails.视频链接、描述、缩略图。
所附的 JSL 还将蓝色链接添加到 ID 列。
JSL 使用多个 API:
- 频道 - 查找我的频道和我的频道的上传列表 ID。
- playlistItems - 上传列表是一个播放列表。 从该播放列表中获取视频 ID。
- 视频 - 使用视频 ID 获取有关视频的信息。
YouTube 视频 ID 是图片中神秘的蓝色链接值。 JSL 有点愚蠢,因为存储在数据表中的信息可从播放列表项中获得,但您可能会从不在播放列表中的视频 ID 开始。
API 还有很多内容;几乎所有可以从 Web 界面执行的操作(上传、删除等)都可以通过 API 完成。
编辑: @bryan_boone发布了更多信息使用 HTTP 请求将 Google Drive 与 JMP 集成-- 与您可以从谷歌控制台获取的密钥相关。
非常感谢! @Craige_Hales ,
这非常有用。 我在此基础上制作了频道的播放列表列表以及这些播放列表中的视频列表。
当播放列表中有超过 50 个视频、超过 50 个播放列表、某些视频是私有的、缺乏描述或 YouTube 作品中存在各种扳手时,此任务可能会变得更加复杂。
我的脚本解决了其中一些问题以及更多问题,试图使其变得健壮。
//create a JMP data table with all playlists of https://www.youtube.com/c/JMPSoftwareFromSAS
// this script produces 2 data tables:
// 1) A list of playlists on a channel.
// 2) A list of videos from all the playlists.
Names Default To Here( 1 );
// in order to use API tools from google registration is required and an API key is needed.
// in order to get an api key.
// go to https://console.cloud.google.com/
// create new project (YouTube data api V3)
// create crdentials - YouTube data api V3 - Public data.
// Define Google API Key directly or in a separate file
apikey = "aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnoooppp"; // directly
// Define channel id for which to get the list of Playlists - get it from Youtube
// Open ("https://www.youtube.com/c/JMPSoftwareFromSAS");// JMP Software From SAS
// if channel id is not explicit search it at https://commentpicker.com/youtube-channel-id.php
Channelid = "UCPOQfD32PLMVdTg-38OwjCA";
// set up a data table for the list of playlists
dtplaylists = New Table( "JMP Software From SAS - list of playlists",
New Column( "Playlist name", Character, "Nominal" ),
New Column( "Playlist description", Character, "Nominal" ),
New Column( "Playlist link", Character, "Nominal" ),
New Column( "Playlistid", Character, "Nominal" ),
);
// this youtube api query brings a list of playlists from the channel using channelid and apikey
// if there are more than 50 playlists this needs to be in a loop
jsonplaylistslist = Parse JSON(
Open( "https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&channelId=" || Channelid || "&maxResults=50&key=" || apikey )
);
nplaylists = jsonplaylistslist["pageInfo"]["totalResults"];
Show( nplaylists );
resultsperpage = N Items( jsonplaylistslist["items"] );
Show( resultsperpage );
// extract the title, description and id of each list into the data table.
For( playlistspage = 1, playlistspage <= Ceiling( nplaylists / 50 ), playlistspage++,
For( iplaylists = 1, iplaylists <= resultsperpage, iplaylists++,
dtplaylists << add rows( 1 );
dtplaylists:Playlist name[N Rows()] = jsonplaylistslist["items"][iplaylists]["snippet"]["title"];
dtplaylists:Playlist description[N Rows()] = jsonplaylistslist["items"][iplaylists]["snippet"]["description"];
dtplaylists:Playlistid[N Rows()] = jsonplaylistslist["items"][iplaylists]["id"];
dtplaylists:Playlist link[N Rows()] = "https://www.youtube.com/playlist?list=" || jsonplaylistslist["items"][iplaylists]["id"];
playlistid = jsonplaylistslist["items"][iplaylists]["id"];
);
// inspect whether the list is complete or has a next page with more playlists
jsonplaylistslistkeys = jsonplaylistslist << get keys;
// if no nextPageToken exists - break the loop
If( Contains( jsonplaylistslistkeys, "nextPageToken" ) == 0,
Break()
);
// if nextPageToken is present get the next page
nextpagetoken = jsonplaylistslist["nextPageToken"];
jsonplaylistslist = Parse JSON(
Open(
"https://youtube.googleapis.com/youtube/v3/playlists?part=snippet&channelId=" || Channelid || "&maxResults=50&pageToken=" ||
nextpagetoken || "&key=" || apikey
)
);
// set the number of internal loops according to the number of items.
resultsperpage = N Items( jsonplaylistslist["items"] );
Show( resultsperpage );
);
// set up a data table to host the videos in all play lists
dt = New Table( "JMP Software From SAS - YouTube Playlist",
Set Cell Height( 100 ), // make cells taller to show description and picture
New Column( "Videoname", Character, "Nominal" ),
// new column for individual video tags
New Column( "Tags", Character, "Nominal", Set Display Width( 150 ) ),
New Column( "Playlist name", Character, "Nominal" ),
New Column( "Playlist description", Character, "Nominal" ),
New Column( "playlist link",
Character,
"Nominal",
Set Property(
"Event Handler",
Event Handler(
Click(JSL Quote(Function( {thisTable, thisColumn, iRow}, { },
Web( Char( thisTable:thisColumn[ iRow ] ) ); // open a web page
);) ),
Tip(JSL Quote(Function( {thisTable, thisColumn, iRow}, // 3 parameters
{ }, "Open " || Char( thisTable:thisColumn[ iRow ] ) || " in your browser."; // return the tool tip string
); ) ),
Color(JSL Quote(Function( {thisTable, thisColumn, iRow}, // 3 parameters
{ },
RGBColor("link"););
) )
)
),
),
New Column( "id",
Character,
"Nominal",
Set Property(
"Event Handler",
Event Handler( // use the column properties GUI to create this code
Click(JSL Quote(Function( {thisTable, thisColumn, ivids},
// add the youtube bit to the link...most of this was generated by the GUI
Web( Char( "https://youtu.be/"||thisTable:thisColumn[ ivids ] ) );
); ) ),
Tip(JSL Quote(Function( {thisTable, thisColumn, ivids},
"Open " || Char( thisTable:thisColumn[ ivids ] ) || " in your browser.";
); ) ),
Color(JSL Quote(Function( {thisTable, thisColumn, ivids},
RGBColor("link");
); ) )
)
),
),
// a column for the description text
New Column( "Description", character, "Nominal", Set Display Width( 350 ) ),
// a column for image link
New Column( "Image", Character, "Nominal", Set Display Width( 150 ) ),
);
// Here is where we start scraping the data per video
// this first youtube api query is to get the channel's uploads playlist ID
// Note: if there are less than 51 such videos this will suffice. otherwise a loop is required.
jsonChannel = Parse JSON(
Open( "https://youtube.googleapis.com/youtube/v3/channels?part=snippet,contentDetails,statistics&id=" || Channelid || "&key=" || apikey )
);
// extract channel description for later use
channel_description = jsonChannel["items"][1]["snippet"]["description"];
Show( channel_description );
For( ilist = 1, ilist <= nplaylists, ilist++,
// get the playlists one by one
currentplaylist = dtplaylists:Playlist id[ilist];
// this next api query gets the video IDs (and a lot of other info, which will be
// re-obtained below. You might have some other way to get video IDs, not from a playlist.)
jsonPlayList = Parse JSON(
Open( "https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&playlistId=" || currentplaylist || "&key=" || apikey )
);
//extract plylist length
playlistlenghth = jsonPlayList["pageInfo"]["totalResults"];
Show( playlistlenghth );
For( playlistpage = 1, playlistpage <= Ceiling( playlistlenghth / 50 ), playlistpage++,
// fill up the table
nvids = N Items( jsonPlayList["items"] );
Show( playlistpage, nvids );
// i presents video number
For( i = 1, i <= nvids, i += 1,
dt << add rows( 1 );
dt:Playlist name[N Row()] = dtplaylists:Playlist name[ilist];
dt:Playlist description[N Row()] = dtplaylists:Playlist description[ilist];
dt:playlist link[N Row()] = dtplaylists:Playlist link[ilist];
dt:id[N Row()] = jsonPlayList["items"][i]["snippet"]["resourceId"]["videoId"];
dt:Videoname[N Row()] = videotitle = jsonPlayList["items"][i]["snippet"]["title"];
Show( videotitle );
// string together the url for the api to get info about the video id on this row
json = Open( "https://www.googleapis.com/youtube/v3/videos?id=" || dt:id[N Row()] || "&key=" || apikey || "&part=snippet" );
// convert the JSON string to an associative array we can index with keys to extract parts of interest
jsonvideo = Parse JSON( json ); // make associative array
//extract the description itself and set it in the data table - Private vidos have no description...
If( jsonvideo["items"] != {},
If( Contains( jsonvideo["items"][1]["snippet"], "description" ) > 0,
dt:Description[N Row()] = jsonvideo["items"][1]["snippet"]["description"],
dt:Description[N Row()] = ""
)
);
// use the default / medium / high / maxres / medium / standard thumb nail url to fetch a pic via open()
dt:Image[N Row()] = "https://youtu.be/" || jsonPlayList["items"][i]["snippet"]["resourceId"]["videoId"];
// extract video tags
If( jsonvideo["items"] != {},
If( Contains( jsonvideo["items"][1]["snippet"], "tags" ) > 0,
dt:Tags[N Row()] = Substitute( Char( jsonvideo["items"][1]["snippet"]["tags"] ), "{\!"", "#", ", ", ", #", "}", "", "\!"", "" ),
dt:Tags[N Row()] = ""
)
);
Wait( 0 );
);
// inspect whether the list is complete or has a next page with more videos.
playlistkeys = jsonPlayList << get keys;
If( Contains( playlistkeys, "nextPageToken" ) == 0,
Break()
);
nextPage = jsonPlayList["nextPageToken"];
jsonPlayList = Parse JSON(
Open(
"https://www.googleapis.com/youtube/v3/playlistItems?part=snippet&maxResults=50&pageToken=" || nextPage || "&playlistId=" ||
currentplaylist || "&key=" || apikey
)
);
);
);
dt << Optimize Display;