Выберите элементы, добавленные в DOM скриптом - PullRequest
0 голосов
/ 26 августа 2010

Я пытался получить тег <object> или <embed>, используя:

HtmlNode videoObjectNode = doc.DocumentNode.SelectSingleNode("//object");
HtmlNode videoEmbedNode = doc.DocumentNode.SelectSingleNode("//embed");

Это не похоже на работу.

Может кто-нибудь сказать, пожалуйстаКак получить эти теги и их InnerHtml?

Встроенное видео YouTube выглядит следующим образом:

    <embed height="385" width="640" type="application/x-shockwave-flash" 
src="http://s.ytimg.com/yt/swf/watch-vfl184368.swf" id="movie_player" flashvars="..." 
allowscriptaccess="always" allowfullscreen="true" bgcolor="#000000">

У меня сложилось впечатление, что JavaScript может помешать работе SWF-проигрывателя, надеюсь, что нет...

Приветствия

1 Ответ

3 голосов
/ 26 августа 2010

Обновление 2010-08-26 (в ответ на комментарий ОП) :

Я думаю, вы думаете об этом неправильно, Алекс.Предположим, я написал некоторый код C #, который выглядел так:

string codeBlock = "if (x == 1) Console.WriteLine(\"Hello, World!\");";

Теперь, если я написал синтаксический анализатор C #, должен ли он распознавать содержимое строкового литерала выше как код C # и выделять его (или что-то еще) кактакие? Нет , поскольку в контексте правильно сформированного файла C # этот текст представляет собой string, которому присваивается переменная codeBlock.

Аналогично в HTML дляСтраницы YouTube, элементы <object> и <embed> в действительности не являются элементами в контексте текущего HTML-документа.Они являются содержимым строковых значений, находящихся в коде JavaScript.

Фактически, если HtmlAgilityPack сделал проигнорировал этот факт и попытался распознать все части текста, которые мог будь HTML, с этими элементами все равно не получится, потому что, находясь внутри JavaScript, они в значительной степени экранированы \ символами (обратите внимание на ненадежный метод Unescape в коде, который я разместил, чтобы обойти эту проблему).

Я не говорю, что мое хакерское решение, приведенное ниже, является правильным способом решения этой проблемы;Я просто объясняю, почему получение этих элементов не так просто, как захват их с помощью HtmlAgilityPack.


YouTubeScraper

ОК, Алекс: вы просили об этом, таквот.Какой-то по-настоящему хакерский код для извлечения ваших драгоценных <object> и <embed> элементов из этого моря JavaScript.

class YouTubeScraper
{
    public HtmlNode FindObjectElement(string url)
    {
        HtmlNodeCollection scriptNodes = FindScriptNodes(url);

        for (int i = 0; i < scriptNodes.Count; ++i)
        {
            HtmlNode scriptNode = scriptNodes[i];

            string javascript = scriptNode.InnerHtml;

            int objectNodeLocation = javascript.IndexOf("<object");

            if (objectNodeLocation != -1)
            {
                string htmlStart = javascript.Substring(objectNodeLocation);

                int objectNodeEndLocation = htmlStart.IndexOf(">\" :");

                if (objectNodeEndLocation != -1)
                {
                    string finalEscapedHtml = htmlStart.Substring(0, objectNodeEndLocation + 1);

                    string unescaped = Unescape(finalEscapedHtml);

                    var objectDoc = new HtmlDocument();

                    objectDoc.LoadHtml(unescaped);

                    HtmlNode objectNode = objectDoc.GetElementbyId("movie_player");

                    return objectNode;
                }
            }
        }

        return null;
    }

    public HtmlNode FindEmbedElement(string url)
    {
        HtmlNodeCollection scriptNodes = FindScriptNodes(url);

        for (int i = 0; i < scriptNodes.Count; ++i)
        {
            HtmlNode scriptNode = scriptNodes[i];

            string javascript = scriptNode.InnerHtml;

            int approxEmbedNodeLocation = javascript.IndexOf("<\\/object>\" : \"<embed");

            if (approxEmbedNodeLocation != -1)
            {
                string htmlStart = javascript.Substring(approxEmbedNodeLocation + 15);

                int embedNodeEndLocation = htmlStart.IndexOf(">\";");

                if (embedNodeEndLocation != -1)
                {
                    string finalEscapedHtml = htmlStart.Substring(0, embedNodeEndLocation + 1);

                    string unescaped = Unescape(finalEscapedHtml);

                    var embedDoc = new HtmlDocument();

                    embedDoc.LoadHtml(unescaped);

                    HtmlNode videoEmbedNode = embedDoc.GetElementbyId("movie_player");

                    return videoEmbedNode;
                }
            }
        }

        return null;
    }

    protected HtmlNodeCollection FindScriptNodes(string url)
    {
        var doc = new HtmlDocument();

        WebRequest request = WebRequest.Create(url);
        using (var response = request.GetResponse())
        using (var stream = response.GetResponseStream())
        {
            doc.Load(stream);
        }

        HtmlNode root = doc.DocumentNode;
        HtmlNodeCollection scriptNodes = root.SelectNodes("//script");

        return scriptNodes;
    }

    static string Unescape(string htmlFromJavascript)
    {
        // The JavaScript has escaped all of its HTML using backslashes. We need
        // to reverse this.

        // DISCLAIMER: I am a TOTAL Regex n00b; I make no claims as to the robustness
        // of this code. If you could improve it, please, I beg of you to do so. Personally,
        // I tested it on a grand total of three inputs. It worked for those, at least.
        return Regex.Replace(htmlFromJavascript, @"\\(.)", UnescapeFromBeginning);
    }

    static string UnescapeFromBeginning(Match match)
    {
        string text = match.ToString();

        if (text.StartsWith("\\"))
        {
            return text.Substring(1);
        }

        return text;
    }
}

И, если вам интересно, вот небольшая демонстрация, которую я собрал вместе (супер, Я знаю):

class Program
{
    static void Main(string[] args)
    {
        var scraper = new YouTubeScraper();

        HtmlNode davidAfterDentistEmbedNode = scraper.FindEmbedElement("http://www.youtube.com/watch?v=txqiwrbYGrs");
        Console.WriteLine("David After Dentist:");
        Console.WriteLine(davidAfterDentistEmbedNode.OuterHtml);
        Console.WriteLine();

        HtmlNode drunkHistoryObjectNode = scraper.FindObjectElement("http://www.youtube.com/watch?v=jL68NyCSi8o");
        Console.WriteLine("Drunk History:");
        Console.WriteLine(drunkHistoryObjectNode.OuterHtml);
        Console.WriteLine();

        HtmlNode jessicaDailyAffirmationEmbedNode = scraper.FindEmbedElement("http://www.youtube.com/watch?v=qR3rK0kZFkg");
        Console.WriteLine("Jessica's Daily Affirmation:");
        Console.WriteLine(jessicaDailyAffirmationEmbedNode.OuterHtml);
        Console.WriteLine();

        HtmlNode jazzerciseObjectNode = scraper.FindObjectElement("http://www.youtube.com/watch?v=VGOO8ZhWFR4");
        Console.WriteLine("Jazzercise - Move your Boogie Body:");
        Console.WriteLine(jazzerciseObjectNode.OuterHtml);
        Console.WriteLine();

        Console.Write("Finished! Hit Enter to quit.");
        Console.ReadLine();
    }
}

Оригинальный ответ

Почему бы не попробовать вместо этого использовать идентификатор элемента?

HtmlNode videoEmbedNode = doc.GetElementbyId("movie_player");

Обновить : О человек, вы ищете HTML-теги, которые сами по себе в JavaScript?Именно поэтому это не работает.(Они на самом деле не являются тегами, которые нужно анализировать с точки зрения HtmlAgilityPack; весь этот JavaScript на самом деле представляет собой одну большую строку внутри тега <script>.) Может быть, есть какой-то способ, которым вы можете анализировать внутренний текст самого тега <script> как HTML и перейдите оттуда.

...