2013-12-06 1 views
0

В этой части HTML код:Получить атрибуты и продолжительность с использованием HTMLAgilityPack библиотеки

<div class="item"> 

    <div class="thumb"> 
     <a href="http://www.mp3crank.com/wolf-eyes/lower-demos-121866" rel="bookmark" lang="en" title="Wolf Eyes - Lower Demos album downloads"> 
     <img width="100" height="100" alt="Mp3 downloads Wolf Eyes - Lower Demos" title="Free mp3 downloads Wolf Eyes - Lower Demos" src="http://www.mp3crank.com/cover-album/Wolf-Eyes-–-Lower-Demos.jpg" /></a> 
    </div> 

    <div class="release"> 
     <h3>Wolf Eyes</h3> 
     <h4> 
     <a href="http://www.mp3crank.com/wolf-eyes/lower-demos-121866" title="Wolf Eyes - Lower Demos">Lower Demos</a> 
     </h4> 
     <script src="/ads/button.js"></script> 
    </div> 

    <div class="release-year"> 
     <p>Year</p> 
     <span>2013</span> 
    </div> 

    <div class="genre"> 
     <p>Genre</p> 
     <a href="http://www.mp3crank.com/genre/rock" rel="tag">Rock</a> 
     <a href="http://www.mp3crank.com/genre/pop" rel="tag">Pop</a> 
    </div> 

</div> 

Я знаю, как разобрать его другими способами, но я хотел бы получить эту информацию с помощью HTMLAgilityPack библиотеки:

Title : Wolf Eyes - Lower Demos 
Cover : http://www.mp3crank.com/cover-album/Wolf-Eyes-–-Lower-Demos.jpg 
Year : 2013 
Genres: Rock, Pop 
URL : http://www.mp3crank.com/wolf-eyes/lower-demos-121866 

Какие это HTML строки:

Title : title="Wolf Eyes - Lower Demos" 
Cover : src="http://www.mp3crank.com/cover-album/Wolf-Eyes-–-Lower-Demos.jpg" 
Year : <span>2013</span> 
Genre1: <a href="http://www.mp3crank.com/genre/rock" rel="tag">Rock</a> 
Genre2: <a href="http://www.mp3crank.com/genre/pop" rel="tag">Pop</a> 
URL : href="http://www.mp3crank.com/wolf-eyes/lower-demos-121866" 

Это то, что я пытаюсь, но я всегда получаю object reference not set исключение при попытке выбрать один узел, Извините, но я очень новичок в HTML, я пытался следовать инструкциям этого вопроса HtmlAgilityPack basic how to get title and link?

Public Class Form1 

    Private htmldoc As HtmlAgilityPack.HtmlDocument = New HtmlAgilityPack.HtmlDocument 
    Private htmlnodes As HtmlAgilityPack.HtmlNodeCollection = Nothing 

    Private Title As String = String.Empty 
    Private Cover As String = String.Empty 
    Private Genres As String() = {String.Empty} 
    Private Year As Integer = -0 
    Private URL as String = String.Empty 

    Private Sub Test() Handles MyBase.Shown 

     ' Load the html document. 
     htmldoc.LoadHtml(IO.File.ReadAllText("C:\source.html")) 

     ' Select the (10 items) nodes. 
     htmlnodes = htmldoc.DocumentNode.SelectNodes("//div[@class='item']") 

     ' Loop trough the nodes. 
     For Each node As HtmlAgilityPack.HtmlNode In htmlnodes 

      Title = node.SelectSingleNode("//div[@class='release']").Attributes("title").Value 
      Cover = node.SelectSingleNode("//div[@class='thumb']").Attributes("src").Value 
      Year = CInt(node.SelectSingleNode("//div[@class='release-year']").Attributes("span").Value) 
      Genres = ¿select multiple nodes? 
      URL = node.SelectSingleNode("//div[@class='release']").Attributes("href").Value 

     Next 

    End Sub 

End Class 

ответ

2

Ваша ошибка здесь, чтобы попытаться получить доступ к атрибуту childnode из того, который вы нашли.

Когда вы вызываете node.SelectSingleNode("//div[@class='release']"), вы получаете правильный div, но вызов .Attributes возвращает только атрибуты самого тега div, а не внутренние элементы HTML.

Можно написать запросы XPATH, которые выбирают подузел, например. //div[@class='release']/a - см. http://www.w3schools.com/xpath/xpath_syntax.asp для получения дополнительной информации о XPATH. Хотя примеры для XML, большинство принципов должны применяться к HTML-документу.

Другой подход - использовать дальнейшие вызовы XPATH на найденном вами узле. Я изменен код, чтобы заставить его работать, используя этот подход:

' Load the html document. 
htmldoc.LoadHtml(IO.File.ReadAllText("C:\source.html")) 

' Select the (10 items) nodes. 
htmlnodes = htmldoc.DocumentNode.SelectNodes("//div[@class='item']") 

' Loop through the nodes. 
For Each node As HtmlAgilityPack.HtmlNode In htmlnodes 

    Dim releaseNode = node.SelectSingleNode(".//div[@class='release']") 
    'Assumes we find the node and it has a a-tag 
    Title = releaseNode.SelectSingleNode(".//a").Attributes("title").Value 
    URL = releaseNode.SelectSingleNode(".//a").Attributes("href").Value 

    Dim thumbNode = node.SelectSingleNode(".//div[@class='thumb']") 
    Cover = thumbNode.SelectSingleNode(".//img").Attributes("src").Value 

    Dim releaseYearNode = node.SelectSingleNode(".//div[@class='release-year']") 
    Year = CInt(releaseYearNode.SelectSingleNode(".//span").InnerText) 

    Dim genreNode = node.SelectSingleNode(".//div[@class='genre']") 
    Dim genreLinks = genreNode.SelectNodes(".//a") 
    Genres = (From n In genreLinks Select n.InnerText).ToArray() 

    Console.WriteLine("Title : {0}", Title) 
    Console.WriteLine("Cover : {0}", Cover) 
    Console.WriteLine("Year : {0}", Year) 
    Console.WriteLine("Genres: {0}", String.Join(",", Genres)) 
    Console.WriteLine("URL : {0}", URL) 

Next 

Обратите внимание, что в этом коде мы предполагаем, что документ правильно формируется и что каждый узел/элемент/атрибут существует и является правильным. Возможно, вы захотите добавить к ней много ошибок, например, If someNode Is Nothing Then ....

Edit: Я выше немного изменен код, чтобы убедиться, что каждый .SelectSingleNode использует «.//» префикс - это гарантирует, что работает, если есть несколько „предмет“ узлов, в противном случае он выбирает первый матч из документа не текущий узел.

Если вы хотите более короткое решение XPATH, здесь тот же код, использующий этот подход:

' Load the html document. 
htmldoc.LoadHtml(IO.File.ReadAllText("C:\source.html")) 

' Select the (10 items) nodes. 
htmlnodes = htmldoc.DocumentNode.SelectNodes("//div[@class='item']") 

' Loop through the nodes. 
For Each node As HtmlAgilityPack.HtmlNode In htmlnodes 

    Title = node.SelectSingleNode(".//div[@class='release']/h4/a[@title]").Attributes("title").Value 
    URL = node.SelectSingleNode(".//div[@class='release']/h4/a[@href]").Attributes("href").Value 

    Cover = node.SelectSingleNode(".//div[@class='thumb']/a/img[@src]").Attributes("src").Value 

    Year = CInt(node.SelectSingleNode(".//div[@class='release-year']/span").InnerText) 

    Dim genreLinks = node.SelectNodes(".//div[@class='genre']/a") 
    Genres = (From n In genreLinks Select n.InnerText).ToArray() 

    Console.WriteLine("Title : {0}", Title) 
    Console.WriteLine("Cover : {0}", Cover) 
    Console.WriteLine("Year : {0}", Year) 
    Console.WriteLine("Genres: {0}", String.Join(",", Genres)) 
    Console.WriteLine("URL : {0}", URL) 
    Console.WriteLine() 

Next 
+0

Спасибо, то, если я понимаю, хороший второй код безопаснее и быстрее? который я должен использовать? У меня есть 10 элементов/блоков я имею в виду 10 названий/обложек/URL-адресов для извлечения – ElektroStudios

+1

Я не уверен, что было бы лучше. Второй пример требует хорошего знания xpath и немного более тесно связан с структурой HTML. Например, название изменяется с тега h4 на h5, второй пример будет неудачным, но первый будет работать. – Quango

+1

Ох .. если скорость - проблема, я бы предложил несколько экспериментов по синхронизации - попробуйте создать большой html-документ и используйте класс StopWatch (в System.Diagnostics), чтобы время, чтобы увидеть, что быстрее. – Quango

1

Вы не были так далеко от решения. Две важные замечания:

  • // - это рекурсивный вызов. Он может оказывать значительное влияние на производительность, а также может выбирать узлы, которые вам не нужны, поэтому я предлагаю вам использовать его только тогда, когда иерархия является глубокой или сложной или переменной, и вы не хотите указывать весь путь.
  • Существует полезный вспомогательный метод на XmlNode с именем GetAttributeValue, который вы получите атрибут, даже если он не существует (вам нужно указать значение по умолчанию).

Вот пример, который, кажется, работает:

' select the base/parent DIV (here we use a discriminant CLASS attribute) 
' all select calls below will use this DIV element as a starting point 
Dim node As HtmlNode = htmldoc.DocumentNode.SelectNodes("//div[@class='item']") 

' get to the A tag which is a child or grand child (//) of a 'release' DIV 
Console.WriteLine(("Title :" & node.SelectSingleNode("div[@class='release']//a").GetAttributeValue("title", CStr(Nothing)))) 

' get to the IMG tag which is a child or grand child (//) of a 'thumb' DIV 
Console.WriteLine(("Cover :" & node.SelectSingleNode("div[@class='thumb']//img").GetAttributeValue("src", CStr(Nothing)))) 

' get to the SPAN tag which is a child or grand child (//) of a 'release-year' DIV 
Console.WriteLine(("Year :" & node.SelectSingleNode("div[@class='release-year']//span").InnerText)) 

' get all A elements which are child or grand child(//) of a 'genre' DIV 
Dim nodes As HtmlNodeCollection = node.SelectNodes("div[@class='genre']//a") 
Dim i As Integer 
For i = 0 To nodes.Count - 1 
    Console.WriteLine(String.Concat(New Object() { "Genre", (i + 1), ":", nodes.Item(i).InnerText })) 
Next i 

' get to the A tag which is a child or grand child (//) of a 'release' DIV 
Console.WriteLine(("Url :" & node.SelectSingleNode("div[@class='release']//a").GetAttributeValue("href", CStr(Nothing)))) 
+0

Большое спасибо, «GetAttributeValue» сохранит меня для пустых атрибутов. – ElektroStudios

Смежные вопросы