Extrahieren eines Abschnitts einer Webseite hat anscheinend veraltete Informationen; das Web Scraping vom Februar 2020 scheint im Juli 2021 nicht mehr zu funktionieren. Nun, das wird erwartet, weil es keinen Grund gibt, warum YouTube den HTML-Code, der die Webseite formatiert, nicht ändern kann. @LNitz veranlasste mich, einen weiteren Blick darauf zu werfen und zu zeigen, wie man eine offiziellere API verwendet, um die Daten abzurufen und wie man diese Daten in einer Datentabelle speichert.
Die Kommentare in der JSL verweisen Sie auf eine Seite, auf der Sie einen API-Schlüssel erhalten können. Google/YouTube verwenden den Schlüssel, um die Anzahl der Anfragen einzuschränken, die Sie stellen können. Das Erstellen dieses Codes hat anscheinend ungefähr 500/10.000 meiner täglichen Quote verbraucht. Ich habe es viel laufen lassen. https://console.cloud.google.com/apis/dashboard .
Die ursprüngliche Frage betraf das Einfügen von Beschreibungen von YouTube-Videos in eine Datentabelle. Die Antwort hat den (~500 KByte) Webseiten-HTML und das Muster heruntergeladen, das mit Schlüsselwörtern abgeglichen wurde ... die sich anscheinend geändert haben, sodass der Abgleich nicht mehr funktioniert. Die API sollte robuster sein. Es gibt JSON-Daten zurück, die viel einfacher zu analysieren sind als HTML. Auch kleiner. Das Problem besteht nun darin, API-Dokumente zu lesen, um herauszufinden, wie es funktioniert. Glücklicherweise lassen sich die meisten Fragen googeln.
Video links, descriptions, thumbnails.Videolinks, Beschreibungen, Vorschaubilder.
JSL fügt im Anhang auch die blauen Links zur ID-Spalte hinzu.
Die JSL verwendet mehrere APIs:
- Kanäle - finde meinen Kanal und die Upload-Listen-ID meines Kanals.
- playlistItems - Die Upload-Liste ist eine Wiedergabeliste. Rufen Sie die Video-IDs aus dieser Wiedergabeliste ab.
- videos - erhalten Sie Informationen über ein Video anhand seiner ID.
YouTube-Video-IDs sind die kryptischen blauen Linkwerte im Bild. Die JSL ist ein bisschen albern, weil die in der Datentabelle gespeicherten Informationen aus den Playlist-Elementen verfügbar waren, aber Sie könnten mit Video-IDs beginnen, die nicht in einer Playlist enthalten sind.
Die API hat noch viel mehr zu bieten; So ziemlich alles, was Sie über die Weboberfläche tun können (Hochladen, Löschen usw.), kann über die API erfolgen.
Danke sehr! @Craige_Hales ,
Das ist sehr nützlich. Ich habe darauf aufgebaut, um eine Liste von Wiedergabelisten eines Kanals und eine Liste von Videos in diesen Wiedergabelisten zu erstellen.
Diese Aufgabe kann komplizierter werden, wenn es mehr als 50 Videos in einer Playlist gibt, mehr als 50 Playlists, einige Videos privat sind, keine Beschreibung oder alle möglichen Spanner in den Werken von YouTube sind.
Mein Skript behandelt einige dieser Probleme und mehr, um es robust zu machen.
//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;