更新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并去从那里。
我得到一个错误的代码:'HtmlAgilityPack.HtmlDocument'不包含'GetElementById'的定义,也没有扩展方法'GetElementById'' – Alex 2010-08-25 23:32:58
@AlexW:它看起来像'b'应该是小写?尝试一下('GetElementbyId'),看看你是否有幸运。 – 2010-08-25 23:44:10
@Dan Tao - 仍然没有抓取任何'videoEmbedNode == null;' – Alex 2010-08-25 23:50:00