/// <summary>
        /// Führt die Datensammlung durch.
        /// </summary>
        /// <param name="firstUrl">Das URL der ersten Webseite.</param>
        /// <param name="hops"> Eine Liste von Zergliedern für jedes erwartetes Hop.</param>
        /// <param name="contentParser">Injizierter Zerglieder für den Inhalt der am Ende der Hops erreichten Webseite.</param>
        /// <remarks>Denn das Herunterladen von Webseiten zeitintensiv sein kann, läuft es asynchron im Hintergrund, während die Ergebnisse schrittweise freigegeben werden.</remarks>
        /// <returns>Eine Liste mit den gesammelten Daten.</returns>
        /// <exception cref="ParserException" />
        public IEnumerable <DataType> CollectData(
            Uri firstUrl,
            IEnumerable <IHyperlinksParser> hops,
            IHypertextContentParser <DataType> contentParser)
        {
            var  contents            = new ConcurrentQueue <(Uri, string)>();
            Task contentDownloadTask = CollectContentRecursivelyAsync(firstUrl, hops, contents);

            bool downloading = true;

            while (downloading)
            {
                downloading = !contentDownloadTask.IsCompleted;

                while (contents.TryDequeue(out (Uri url, string hypertext)content))
                {
                    IEnumerable <DataType> parsedObjects =
                        WrapCall(() => contentParser.ParseContent(content.hypertext), content.url);

                    foreach (DataType obj in parsedObjects)
                    {
                        yield return(obj);
                    }

                    _log?.Info($"Die Zergliederung von {content.url} hat {parsedObjects.Count()} ergeben.");
                }
            }
        }
        /// <summary>
        /// Startet die Datensammlung asynchron.
        /// </summary>
        /// <param name="firstUrl">Das URL der ersten Webseite.</param>
        /// <param name="hops"> Eine Liste von Zergliedern für jedes erwartetes Hop.</param>
        /// <param name="contentParser">Injizierter Zerglieder für den Inhalt der am Ende der Hops erreichten Webseite.</param>
        /// <returns>Eine asynchrone Aufgabe, die die gesamten gesammelten Daten verspricht.</returns>
        /// <exception cref="ParserException" />
        public async Task <IEnumerable <DataType> > CollectDataAsync(
            Uri firstUrl,
            IEnumerable <IHyperlinksParser> hops,
            IHypertextContentParser <DataType> contentParser)
        {
            var contents = new ConcurrentQueue <(Uri, string)>();

            await CollectContentRecursivelyAsync(firstUrl, hops, contents);

            var collectedData = new List <DataType>();

            foreach ((Uri url, string hypertext) in contents)
            {
                IEnumerable <DataType> parsedObjects =
                    WrapCall(() => contentParser.ParseContent(hypertext), url);

                collectedData.AddRange(parsedObjects);

                _log?.Info($"Die Zergliederung von {url} hat {parsedObjects.Count()} ergeben.");
            }

            return(collectedData);
        }