Używam API YouTube, aby pobrać wszystkie tytuły w liście odtwarzania.

Udało mi się odzyskać tekst JSON z interfejsu API i znaleźć właściwe elementy Ten post:

Dim wc As New Net.WebClient
Dim incstr As IO.Stream = Nothing
wc.Encoding = Encoding.UTF8
incstr = wc.OpenRead("https://www.googleapis.com/youtube/v3/playlistItems?part=contentDetails%2Csnippet&maxResults=50&playlistId=PL4_Dx88dpu7cEY_cBjTZFFM1tVKF5Plsx&key=yourkey")

Using rd As New IO.StreamReader(incstr)
    RichTextBox1.Text = rd.ReadToEnd
End Using
Dim strBuf As String = (richtextbox1.text)

Dim myjobj As New Json.Linq.JObject
myjobj = Json.Linq.JObject.Parse(strBuf)

dim titleText = JObject.Parse(json)("title")("runs")(0)("text")

Ale niestety, ten kod generuje na ostatnim wierszu, taki jak: System.argumentException: "Nie może konwertować obiektu do ciągów"

Ponadto API odzyskuje tylko maksymalnie 50 elementów z listy odtwarzania, więc @jimi sugeruje:

Jeśli masz więcej niż 50 elementów w listy odtwarzania, wróciłeś do wpisu nextPageToken w głównym obiekcie klasy. Możesz użyć tego tokena do zapytania o następną stronę, ustawiając pageToken=[The Provided Token String] Query Tuple.

Widzę na początku tekstu JSON:

"Nextpageneoken": "Cgqqaa",
"Prevpagetoken": "CDIQAQ",

Ale nie wiem, jak je złapać.

Tak więc, ponieważ limity są w tym czasie 50 utworów, jak mogę odzyskać wszystkie utwory jednocześnie za pomocą tokena następnego strony?

3
34234234 26 październik 2020, 20:28

1 odpowiedź

Najlepsza odpowiedź

API Google wykorzystuje formę paginacji, gdy żądany jest liście odtwarzania (dotyczy to również innych typów odpowiedzi API).
Maksymalna liczba wpisów na stronę odpowiedzi wynosi 50.

► Format odpowiedzi jest obiektem JSON, który zawiera opis odpowiedzi zapytania, w tym żądane żądane elementy i elementy zawarte w bieżącej odpowiedzi.

► Jeśli zapytanie zawiera więcej elementów niż maksymalna strona na stronie, właściwość nextPageToken jest ustawiona na wartość ciągową reprezentującą żeton strony, który można użyć do zapytania wyników następnej strony (ty Może zobaczyć, jak działa w kodzie, w metodzie LoadPlaylistAsync() klasy YouTubePlayList).
Gdy nie są dostępne żadne inne strony, nextPageToken jest ustawiony na null (Nothing).
Sprawdzanie tej wartości tokena, możemy ustalić, czy zawartość listy odtwarzania jest zakończona.

API można wywołać za pomocą OAuth 2.0 z kluczem dostępu, który wymaga biblioteki klientów API Google lub za pomocą prostego HTTTP Get with URI skomponowane jako FormurlenCodedContent, standardowe kweuty URL Kto |

Tutaj używam tej ostatniej metody, ponieważ jest to całkiem uproszczone i działa całkiem dobrze.
Jeśli masz Google SDK, możesz podążać za przykładem w PlaylistItems Lista Dokumentacja.

Używam struktury klasy (model) do opuszczania odpowiedzi JSON: Jest to dość prostsze, aby obsługiwać zawartość za pomocą standardowych klas .NET, gdzie wartości właściwości JSON zostały już przekonwertowane na silnie wpisane wartości we właściwym formacie.
. Klasa pomocnika, {x0}} , zawiera model klasy i metody potrzebne do opuszczania JSON lub serializację obiektów klasy z powrotem do łańcucha JSON.

Wykonuje również żądanie HTTP, używając statycznego httpclient i obsługuje paginację odpowiedzi API, w razie potrzeby.
Klasa wykorzystuje metody asynchroniczne, które zapewnia klasę HTTPClient.

► Klasa jest zainicjowana przekazując klucz dostępu wymagany do zapytania API.
Jeśli klucz nie jest jeszcze dostępny, postępuj zgodnie z instrukcjami, aby utworzyć jeden (jest wolny) w Odniesienie API Strona dokumentacji, która wyśle Ci do okienka dostępu do API Console Console.

Aby załadować listę odtwarzania, przekaż token odtwarzania do metody {x0}} .
Ta metoda zwraca obiekty PlayList, które zawiera wszystkie informacje związane z samym zapytaniem API i wszystkimi wynikowymi elementami, w obiektach {x2}}.
.. Wszystkie zadania dotyczące zadań Deserialization i HTTP są obsługiwane wewnętrznie.


Na przykład, w programie Button.Click, zainicjuj klasę pomocnika przechodzącą klawisz, wywołaj metodę LoadPlaylistAsync(), określając token listy odtwarzania do pobrania, a następnie pozbyć się obiektu klasy {x2}} i po prostu Użyj obiektu zwróconego klas dla tego, co jest potrzebne:

Używam tokenu listy odtwarzania, które podałeś w poprzednie pytanie

Private Async Sub btnLoadPlaylist_Click(sender As Object, e As EventArgs) Handles btnLoadPlaylist.Click
    ' Initialize with the Google API Key
    Dim playListHelper As New YouTubePlayList("AIzaSy---------------------------------")
    ' Load the Playlist passing the playListId
    Dim playList = Await playListHelper.LoadPlaylistAsync("PL4_Dx88dpu7epfH6ybwqJpf9uL2tAl368")
    ' Dispose of the class object, to close and dispose of the HttpClient object
    playListHelper.Dispose()

    For Each item As YouTubePlayList.PlayListEntry In playList.Items
        Dim itemTitle = item.ItemContent.Title
        Dim itemChannel = item.ItemContent.ChannelTitle
        Dim videoAddress = "https://www.youtube.com/watch?v=" & item.ItemContent.ResourceId.VideoId
    Next
End Sub

Przykładowa funkcjonalność obiektu klasy w pracy:

Google PlayList handler

Google Playlist Downloader Class :

Wymaga JSON.NET (Newtonsoft.Json) 12.0.3+

Imports System
Imports System.Collections.Generic
Imports System.Globalization
Imports System.Net.Http
Imports System.Threading.Tasks
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Converters

Public Class YouTubePlayList
    Implements IDisposable

    Private m_Json As String = String.Empty
    Private m_KeyToken As String = String.Empty
    Private m_APIUrl As String = "https://www.googleapis.com/youtube/v3/playlistItems?"
    Private Shared client As HttpClient = Nothing

    Public Sub New(key As String)
        Me.New(key, String.Empty)
    End Sub

    Public Sub New(key As String, json As String)
        m_KeyToken = key
        If Not String.IsNullOrEmpty(json) Then
            m_Json = json
        End If
        client = New HttpClient()
    End Sub

    Public Function Deserialize() As PlayList
        Return Deserialize(m_Json)
    End Function

    Public Function Deserialize(json As String) As PlayList
        If String.IsNullOrEmpty(json) Then Return Nothing
        Return JsonConvert.DeserializeObject(Of PlayList)(json, ConverterSettings.Settings)
    End Function

    Public Function Serialize(root As PlayList) As String
        Return JsonConvert.SerializeObject(root, ConverterSettings.Settings)
    End Function

    Friend NotInheritable Class ConverterSettings
        Public Shared ReadOnly Settings As New JsonSerializerSettings() With {
            .MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            .DateParseHandling = DateParseHandling.None,
            .Converters = {New IsoDateTimeConverter() With {
                .DateTimeStyles = DateTimeStyles.AssumeUniversal
            }}
        }
    End Class

    Public Async Function LoadPlaylistAsync(playListToken As String) As Task(Of PlayList)
        Dim resultPlayList As PlayList = Nothing
        Dim nextPageToken As String = String.Empty
        Dim listItemsRead As Integer = 0
        Dim resultsPerPage As Integer = 50

        While nextPageToken IsNot Nothing
            Dim queryUrl = Await GetRequestUrl(playListToken, nextPageToken, resultsPerPage)
            Using response = Await client.GetAsync(queryUrl)
                If response.IsSuccessStatusCode Then
                    Dim jsonResponse = Await response.Content.ReadAsStringAsync()
                    Dim playList = Deserialize(jsonResponse)

                    listItemsRead += playList.Items.Count

                    If resultPlayList Is Nothing Then
                        resultPlayList = playList
                    Else
                        resultPlayList.Items.AddRange(playList.Items)
                    End If
                    nextPageToken = playList.NextPageToken
                    resultPlayList.PageInfo.ResultsPerPage = listItemsRead
                End If
            End Using
        End While
        Return resultPlayList
    End Function

    Private Async Function GetRequestUrl(playListToken As String, nextPageToken As String, resultsPerPage As Integer) As Task(Of String)
        Dim content = New FormUrlEncodedContent(New KeyValuePair(Of String, String)() {
            New KeyValuePair(Of String, String)("part", "contentDetails,snippet,id"),
            New KeyValuePair(Of String, String)("maxResults", resultsPerPage.ToString()),
            New KeyValuePair(Of String, String)("pageToken", nextPageToken),
            New KeyValuePair(Of String, String)("playlistId", playListToken),
            New KeyValuePair(Of String, String)("key", m_KeyToken)
        })
        Return m_APIUrl & Await content.ReadAsStringAsync()
    End Function


    Public Class PlayList
        <JsonProperty("kind")>
        Public Property Kind As String
        <JsonProperty("etag")>
        Public Property Etag As String
        <JsonProperty("nextPageToken")>
        Public Property NextPageToken As String
        <JsonProperty("prevPageToken")>
        Public Property PrevPageToken As String
        <JsonProperty("items")>
        Public Property Items As List(Of PlayListEntry)
        <JsonProperty("pageInfo")>
        Public Property PageInfo As PageInfo
    End Class

    Public Class PlayListEntry
        <JsonProperty("kind")>
        Public Property Kind As String
        <JsonProperty("etag")>
        Public Property Etag As String
        <JsonProperty("id")>
        Public Property Id As String
        <JsonProperty("snippet")>
        Public Property ItemContent As Content
        <JsonProperty("contentDetails")>
        Public Property ContentDetails As ContentDetails
    End Class

    Public Class ContentDetails
        <JsonProperty("videoId")>
        Public Property VideoId As String
        <JsonProperty("videoPublishedAt")>
        Public Property VideoPublishedAt As DateTimeOffset
    End Class

    Public Class Content
        <JsonProperty("publishedAt")>
        Public Property PublishedAt As DateTimeOffset
        <JsonProperty("channelId")>
        Public Property ChannelId As String
        <JsonProperty("title")>
        Public Property Title As String
        <JsonProperty("description")>
        Public Property Description As String
        <JsonProperty("thumbnails")>
        Public Property Thumbnails As Thumbnails
        <JsonProperty("channelTitle")>
        Public Property ChannelTitle As String
        <JsonProperty("playlistId")>
        Public Property PlaylistId As String
        <JsonProperty("position")>
        Public Property Position As Long
        <JsonProperty("resourceId")>
        Public Property ResourceId As ResourceId
    End Class

    Public Class ResourceId
        <JsonProperty("kind")>
        Public Property Kind As String
        <JsonProperty("videoId")>
        Public Property VideoId As String
    End Class

    Public Class Thumbnails
        <JsonProperty("default")>
        Public Property DefaultImage As ImageDescriptor
        <JsonProperty("medium")>
        Public Property Medium As ImageDescriptor
        <JsonProperty("high")>
        Public Property High As ImageDescriptor
        <JsonProperty("standard", NullValueHandling:=NullValueHandling.Ignore)>
        Public Property Standard As ImageDescriptor
        <JsonProperty("maxres", NullValueHandling:=NullValueHandling.Ignore)>
        Public Property MaxResolution As ImageDescriptor
    End Class

    Public Class ImageDescriptor
        <JsonProperty("url")>
        Public Property Url As Uri
        <JsonProperty("width")>
        Public Property Width As Integer
        <JsonProperty("height")>
        Public Property Height As Integer
    End Class

    Public Class PageInfo
        <JsonProperty("totalResults")>
        Public Property TotalResults As Integer
        <JsonProperty("resultsPerPage")>
        Public Property ResultsPerPage As Integer
    End Class

    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    Protected Sub Dispose(disposing As Boolean)
        If disposing Then
            client?.CancelPendingRequests()
            client?.Dispose()
            client = Nothing
        End If
    End Sub
End Class

C # wersja :

using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public class YouTubePlayList : IDisposable
{
    private string m_Json = string.Empty;
    private string m_KeyToken = string.Empty;
    private string m_APIUrl = "https://www.googleapis.com/youtube/v3/playlistItems?";
    private static HttpClient client = null;

    public YouTubePlayList(string key) : this(key, string.Empty) { }
    public YouTubePlayList(string key, string json)
    {
        m_KeyToken = key;
        if (!string.IsNullOrEmpty(json)) {
            m_Json = json;
        }
        client = new HttpClient();
    }

    public PlayList Deserialize() => Deserialize(m_Json);

    public PlayList Deserialize(string json)
    {
        if (string.IsNullOrEmpty(json)) return null;
        return JsonConvert.DeserializeObject<PlayList>(json, ConverterSettings.Settings);
    }

    public string Serialize(PlayList root) =>
        JsonConvert.SerializeObject(root, ConverterSettings.Settings);

    internal sealed class ConverterSettings
    {
        public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings {
            MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
            DateParseHandling = DateParseHandling.None,
            Converters = {
                new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
            }
        };
    }

    public async Task<PlayList> LoadPlaylistAsync(string playListToken)
    {
        PlayList resultPlayList = null;
        string nextPageToken = string.Empty;
        int listItemsRead = 0;
        int resultsPerPage = 50;

        while (nextPageToken != null) {
            string queryUrl = await GetRequestUrl(playListToken, nextPageToken, resultsPerPage);
            using (var response = await client.GetAsync(queryUrl)) {
                if (response.IsSuccessStatusCode) {
                    string jsonResponse = await response.Content.ReadAsStringAsync();
                    var playList = Deserialize(jsonResponse);
                    listItemsRead += playList.Items.Count;
                    if (resultPlayList == null) {
                        resultPlayList = playList;
                    }
                    else {
                        resultPlayList.Items.AddRange(playList.Items);
                    }
                    nextPageToken = playList.NextPageToken;
                    resultPlayList.PageInfo.ResultsPerPage = listItemsRead;
                }
            }
        }
        return resultPlayList;
    }
    private async Task<string> GetRequestUrl(string playListToken, string nextPageToken, int resultsPerPage)
    {
        var content = new FormUrlEncodedContent(new KeyValuePair<string, string>[] {
            new KeyValuePair<string, string>("part", "contentDetails,snippet,id"),
            new KeyValuePair<string, string>("maxResults", resultsPerPage.ToString()),
            new KeyValuePair<string, string>("pageToken", nextPageToken),
            new KeyValuePair<string, string>("playlistId", playListToken),
            new KeyValuePair<string, string>("key", m_KeyToken)
        });
        return m_APIUrl + await content.ReadAsStringAsync();
    }

    public class PlayList
    {
        public string Kind { get; set; }
        public string Etag { get; set; }
        public string NextPageToken { get; set; }
        public string PrevPageToken { get; set; }
        public List<PlayListEntry> Items { get; set; }
        public PageInfo PageInfo { get; set; }
    }

    public class PlayListEntry
    {
        public string Kind { get; set; }
        public string Etag { get; set; }
        public string Id { get; set; }
        [JsonProperty("snippet")]
        public Content ItemContent { get; set; }
        public ContentDetails ContentDetails { get; set; }
    }
    public class ContentDetails
    {
        public string VideoId { get; set; }
        public DateTimeOffset VideoPublishedAt { get; set; }
    }

    public class Content
    {
        public DateTimeOffset PublishedAt { get; set; }
        public string ChannelId { get; set; }
        public string Title { get; set; }
        public string Description { get; set; }
        public Thumbnails Thumbnails { get; set; }
        public string ChannelTitle { get; set; }
        public string PlaylistId { get; set; }
        public long Position { get; set; }
        public ResourceId ResourceId { get; set; }
    }

    public class ResourceId
    {
        public string Kind { get; set; }
        public string VideoId { get; set; }
    }

    public class Thumbnails
    {
        [JsonProperty("default")]
        public ImageDescriptor DefaultImage { get; set; }
        public ImageDescriptor Medium { get; set; }
        public ImageDescriptor High { get; set; }
        [JsonProperty("standard", NullValueHandling = NullValueHandling.Ignore)]
        public ImageDescriptor Standard { get; set; }
        [JsonProperty("maxres", NullValueHandling = NullValueHandling.Ignore)]
        public ImageDescriptor MaxResolution { get; set; }
    }

    public class ImageDescriptor
    {
        public Uri Url { get; set; }
        public int Width { get; set; }
        public int Height { get; set; }
    }

    public class PageInfo
    {
        public int TotalResults { get; set; }
        public int ResultsPerPage { get; set; }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected void Dispose(bool disposing)
    {
        if (disposing) {
            client?.CancelPendingRequests();
            client?.Dispose();
            client = null;
        }
    }
}
3
Jimi 28 grudzień 2020, 10:02