2010-08-25 78 views
0

我一直想要么一个<object><embed>标签使用得到添加到DOM。选择元素通过脚本

任何人都可以请告诉我如何获得这些标签和他们的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播放器,希望不是......

干杯

回答

3

更新2010 -08-26(回应OP的评论)

我想你是在错误地思考它,Alex。假设我写了看起来像这样一些C#代码:

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

现在,如果我写了一个C#编译器,它应该认识到字符串字面上面的内容为C#代码,并突出显示它(或其他)这样? ,因为在格式良好的C#文件的上下文中,该文本表示string变量正在分配给codeBlock变量。

同样,在YouTube上的页面HTML中,<object><embed>元素是不是真的在当前的HTML文件的情况下,在所有元素。它们是驻留在JavaScript代码中的字符串值的内容。

事实上,如果HtmlAgilityPack没有无视这一事实,并试图识别文本可能是HTML的所有部分,但它仍然无法与这些元素,因为作为内部的JavaScript,他们正在大量成功逃脱与\字符(请注意在我发布的代码中解决此问题的稳健的Unescape方法)。

我不是说我下面的hacky解决方案是解决这个问题的正确方法;我只是解释为什么获得这些元素并不像用HtmlAgilityPack来抓住它们那么简单。


YouTubeScraper

OK,亚历克斯:你自找的,所以在这儿呢。一些真正的hacky代码从JavaScript的海洋中提取您珍贵的<object><embed>元素。

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(); 
    } 
} 

原来的答案

为什么不尝试使用元素的ID代替?

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

更新:哦,那你是寻找那些本身 JavaScript的HTML标签?这绝对是为什么这不起作用。 (从HtmlAgilityPack的角度来看,它们并不是真正的标签;所有这些JavaScript实际上都是<script>标签中的一个大字符串。)也许有一些方法可以将<script>标签的内部文本本身解析为HTML并去从那里。

+0

我得到一个错误的代码:'HtmlAgilityPack.HtmlDocument'不包含'GetElementById'的定义,也没有扩展方法'GetElementById'' – Alex 2010-08-25 23:32:58

+0

@AlexW:它看起来像'b'应该是小写?尝试一下('GetElementbyId'),看看你是否有幸运。 – 2010-08-25 23:44:10

+0

@Dan Tao - 仍然没有抓取任何'videoEmbedNode == null;' – Alex 2010-08-25 23:50:00

相关问题