Обновление 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 и перейдите оттуда.