Webページのセクションを抽出する明らかにいくつかの古い情報を持っています。 2020年2月からのWebスクレイピングは、2021年7月には機能しなくなったようです。 YouTubeがWebページをフォーマットするHTMLを変更できない理由がないので、それは予想されることです。 @LNitz別の見方をして、より公式なAPIを使用してデータを取得する方法と、そのデータをデータテーブルに保存する方法を示すように促されました。
JSLのコメントは、APIキーを取得できるページを示しています。 Google / YouTubeは、キーを使用して、作成できるリクエストの数を制限します。 このコードを作成すると、1日の割り当ての約500/10,000が使用されたようです。 私はそれをたくさん実行しました。 https://console.cloud.google.com/apis/dashboard 。
元々の質問は、YouTube動画の説明をデータテーブルに取り込むことについてでした。 回答は、キーワードに一致する(〜500Kbyte)WebページのHTMLとパターンをダウンロードしました...変更されたように見えるため、一致は機能しなくなりました。 APIはより堅牢である必要があります。 JSONデータを返します。これはHTMLよりもはるかに簡単に解析できます。 小さすぎます。 問題は、APIドキュメントを読んでそれがどのように機能するかを理解することです。 幸いなことに、ほとんどの質問はグーグルで検索できます。
Video links, descriptions, thumbnails.ビデオリンク、説明、サムネイル。
添付のJSLは、ID列に青いリンクも追加します。
JSLはいくつかのAPIを使用します。
- チャンネル-自分のチャンネルと自分のチャンネルのアップロードリストIDを見つけます。
- playlistItems-アップロードリストはプレイリストです。 その再生リストからビデオIDを取得します。
- ビデオ-IDを使用してビデオに関する情報を取得します。
YouTubeのビデオIDは、写真の不可解な青いリンク値です。 データテーブルに保存されている情報がプレイリストアイテムから利用可能であったため、JSLは少しばかげていますが、プレイリストにないビデオIDから始めている可能性があります。
APIにはまだまだたくさんあります。 Webインターフェイスから実行できるほとんどすべてのこと(アップロード、削除など)は、APIを介して実行できます。
どうもありがとうございます! @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;