Yvision.kz
kk
Разное
Разное
399 773 постов42 подписчика
Всяко-разно
-8
02:05, 24 октября 2014

Функциональный граббинг сложных сайтов (yvision.kz)

На написание статьи меня натолкнула другая статья, пользователя @webПростой граббинг, сложных сайтов. C# (на примере Yvision.kz). В этой статье показано императивное решение задачи, я же задался вопросом - можно ли решить задачу, используя смешанные парадигмы, в частности функциональная (Linq) и императивная (ООП)?

Как оказалось - можно. Для этого я изначально поставил вопрос - что я хочу видеть в итоге? Есть сайт - Yvision, есть БД, куда надо будет сливать список статей, аналогично вышеуказанной статье, остается лишь мелочь - что же всё-таки мы будем сливать в БД?

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

   
  1. public class Yvision
  2. {
  3. /// <summary>
  4. /// Идентификатор статьи
  5. /// </summary>
  6. public string Id { get; set; }
  7. /// <summary>
  8. /// Наименование статьи
  9. /// </summary>
  10. public string Name { get; set; }
  11. /// <summary>
  12. /// Ссылка на статью
  13. /// </summary>
  14. public string Link { get; set; }
  15. /// <summary>
  16. /// Автор статьи
  17. /// </summary>
  18. public string Author { get; set; }
  19. /// <summary>
  20. /// Блог автора
  21. /// </summary>
  22. public string AuthorUrl { get; set; }
  23. /// <summary>
  24. /// Изображение статьи
  25. /// </summary>
  26. public string Image { get; set; }
  27. }

Дальше мне понадобился некий перечислитель, скрывающий методы работы с HtmlAgilityPack - так как программисту, пользующему библиотеку парсинга конкретно yvision.kz не всегда интересно знать как она устроена, главное, что она делает.

   
  1. public class YviEnumerable : IEnumerable<Yvision>
  2. {
  3. public YviEnumerable()
  4. {
  5. _yvisions = new HtmlWeb()
  6. .Load("http://www.yvision.kz")
  7. .DocumentNode
  8. //Выберем нужную нам ноду
  9. .SelectSingleNode("//div[@class=\"mainContent main_page\"]")
  10. //Выберем все дивы ноды
  11. .Descendants("div")
  12. //Где класс - статья
  13. .Where(d => d.Attributes.Contains("class") && d.Attributes["class"].Value.Contains("home_article1 big"))
  14. //Непосредственно трансформация данных
  15. .Select(node => new Yvision
  16. {
  17. //Id дива с которого берем данные
  18. Id = node.Id,
  19. //Url новости
  20. Link = _queryNodes(node, "hold").Element("a").Attributes["href"].Value,
  21. //Изображение
  22. Image = _queryNodes(node, "hold").ChildNodes.Elements("img").First().Attributes["src"].Value,
  23. //Наименование
  24. Name = _queryNodes(node, "text1").ChildNodes.Elements("a").First().InnerText,
  25. //Автор
  26. Author = _queryNodes(node, "author").Element("a").InnerText.Trim(),
  27. //Блог автора
  28. AuthorUrl = _queryNodes(node, "author").Element("a").Attributes["href"].Value
  29. });
  30. }
  31. /// <summary>
  32. /// Коллекция новостей Yvision
  33. /// </summary>
  34. private readonly IEnumerable<Yvision> _yvisions;
  35. public IEnumerable<Yvision> Yvisions
  36. {
  37. get { return _yvisions; }
  38. }
  39. /// <summary>
  40. /// Функция выборки единичного элемента с заданным классом
  41. /// </summary>
  42. private readonly Func<HtmlNode, string, HtmlNode> _queryNodes = (p, s) => p
  43. .Descendants("div")
  44. .First(d => d.Attributes.Contains("class") && d.Attributes["class"].Value.Contains(s));
  45. /// <summary>
  46. /// Возвращает перечислитель коллекции новостей
  47. /// </summary>
  48. /// <returns></returns>
  49. public IEnumerator<Yvision> GetEnumerator()
  50. {
  51. return _yvisions.GetEnumerator();
  52. }
  53. IEnumerator IEnumerable.GetEnumerator()
  54. {
  55. return GetEnumerator();
  56. }
  57. }

Собственно на этом всё. Дальнейших танцев с бубнами особо не требуется - кое-где необходимо переписать стандартные функции на чистые (выбрасывать исключения нам не нужно) и даже местами можно улучшить код.

Использование перечислителя.

   
  1. private static void Main()
  2. {
  3. Console.ForegroundColor = ConsoleColor.White;
  4. foreach (var item in new YviEnumerable())
  5. {
  6. Console.WriteLine("postId: {0}, name: {1}, url: {2}, image: {3}, author: {4}, authorUrl: {5}", item.Id,
  7. item.Name, item.Link,
  8. item.Image, item.Author, item.AuthorUrl);
  9. }
  10. Console.ReadLine();
  11. }

На этом всё, жду(или не жду:) без разницы) конструктивных комментариев.

Кстати результат исполнения программы:

Blog post image

-8
626
3