Как извлечь данные с веб-сайта с помощью AngleSharp и LINQ?

Я пытаюсь извлечь цены с указанного ниже веб-сайта. Я использую AngleSharp для извлечения. На сайте цены указаны ниже (для примера):

<span class="c-price">650.00                            </span>

Я использую следующий код для извлечения.

using AngleSharp.Parser.Html;
using System.Net;
using System.Net.Http

//Make the request
var uri = "https://meadjohnson.world.tmall.com/search.htm?search=y&orderType=defaultSort&scene=taobao_shop";
var cancellationToken = new CancellationTokenSource();
var httpClient = new HttpClient();
var request = await httpClient.GetAsync(uri);
cancellationToken.Token.ThrowIfCancellationRequested();

//Get the response stream
var response = await request.Content.ReadAsStreamAsync();
cancellationToken.Token.ThrowIfCancellationRequested();

//Parse the stream
var parser = new HtmlParser();
var document = parser.Parse(response);

//Do something with LINQ
var pricesListItemsLinq = document.All
     .Where(m => m.LocalName == "span" && m.ClassList.Equals("c-price"));
Console.WriteLine(pricesListItemsLinq.Count());

Тем не менее, я не получаю никаких предметов, но они есть на сайте. Что я делаю не так? Если метод AngleSharp не рекомендуется, что мне следует использовать? И какой код мне использовать?


person inquisitive_one    schedule 06.09.2015    source источник
comment
Вместо этого вы можете попробовать document.QuerySelectorAll("span.c-price").   -  person Lucas Trzesniewski    schedule 06.09.2015
comment
Элементы, которые вы пытаетесь запросить, добавляются на страницу динамически. Вам нужно будет выполнить javascript на странице. Я не знаю, может ли AngleSharp это сделать.   -  person Jeff Mercado    schedule 06.09.2015
comment
@LucasTrzesniewski Я попробовал ваше предложение и до сих пор ничего не понял.   -  person inquisitive_one    schedule 07.09.2015
comment
У @JeffMercado AngleSharp, похоже, есть библиотека JS. Я добавил библиотеку и использовал следующее: var config = Configuration.Default.WithJavaScript(); var parser = new HtmlParser(config);. Мне все еще не везет. Есть предложения по альтернативам?   -  person inquisitive_one    schedule 07.09.2015
comment
Честно говоря, я не знаю ни одной библиотеки .NET, предназначенной только для разбора и выполнения сценариев изначально только ради запроса. Я думаю, что ваш единственный вариант здесь - загрузить его в браузере и очистить от него. Вероятно, вы могли бы использовать для этого что-то вроде Selenium.   -  person Jeff Mercado    schedule 07.09.2015


Ответы (1)


Я опаздываю на вечеринку, но я пытаюсь внести сюда немного здравого смысла.

Запрос статических веб-страниц

Для этого нам потребуется следующий набор инструментов/функционала:

  • HTTP-запросчик (для получения ресурсов, например HTML-документов, через HTTP), потенциально с уровнем SSL/TLS поверх (либо принимает все сертификаты, либо работает с хранилищем сертификатов/известными центрами сертификации)
  • парсер HTML
  • Запрашиваемое представление объектной модели проанализированного HTML-документа.
  • Возможно, дополнительно какое-то состояние куки и возможность переходить по ссылкам/формам публикации

AngleSharp дает нам все эти возможности (за исключением подключения к хранилищу сертификатов/известным центрам сертификации, поэтому для использования HTTPS мы необходимо выполнить некоторую дополнительную настройку, например, принять все сертификаты).

Мы начнем с создания конфигурации AngleSharp, которая определяет, какие возможности доступны для механизма просмотра. Этот движок представлен в виде «контекста просмотра», который можно рассматривать как вкладку без заголовка. На этой вкладке мы можем открыть новый документ (из локального источника, созданного источника или удаленного источника).

var config = Configuration.Default.WithDefaultLoader();
var context = BrowsingContext.New(config);
var document = await context.OpenAsync("http://example.com");

Получив документ, мы можем использовать селекторы запросов CSS для получения определенных элементов. Эти элементы можно использовать для сбора информации, которую мы ищем.

AngleSharp поддерживает LINQ (или IEnumerable в целом), однако имеет смысл предоставить полную мощность запросам, если это возможно.

Итак, вместо

var pricesListItemsLinq = document.All
    .Where(m => m.LocalName == "span" && m.ClassList.Equals("c-price"));

Мы пишем

var pricesListItemsLinq = document.QuerySelectorAll("span.c-price");

Это также гораздо более надежно (ClassList в любом случае является сложным объектом, предоставляющим доступ к списку классов, поэтому вы имели в виду либо ClassList.Contains, либо ClassName.Equals (последнее представляет собой строковое представление). Примечание: две версии не эквивалентны, потому что первый ищет класс в списке классов, в то время как последний ищет совпадение всей сериализации класса (таким образом, устанавливая некоторые дополнительные граничные условия для совпадения; это должен быть единственный класс).

Работа с динамическими страницами

Это гораздо сложнее. Основы такие же, как и раньше, но движок должен обеспечивать гораздо больше, чем просто ранее упомянутые требования. Кроме того, нам нужно

  • Движок JavaScript
  • Действительный CSSOM
  • Поддельное (или даже полностью вычисленное) дерево рендеринга
  • Гораздо больше интерфейсов DOM, которые можно найти в реальных браузерах (например, навигатор, полная история, веб-воркеры, ...) - список здесь безграничен.

Несмотря на то, что существует проект, который поставляет экспериментальный (и ограниченный) JS-движок только на C# для AngleSharp, последние два требования не могут быть полностью выполнены прямо сейчас. Кроме того, CSSOM может быть недостаточно полным для того или иного веб-приложения. Имейте в виду, что эти страницы потенциально предназначены для реальных браузеров. Они делают определенные предположения. Они могут даже требовать ввода данных пользователем (например, Google Captcha).

Короче говоря.

var config = Configuration.Default
    .WithDefaultLoader()
    .WithCss()
    .WithJavaScript(); // maybe even more
var context = BrowsingContext.New(config);

Задача, стоящая за await при открытии нового документа, эквивалентна событию load в DOM. Таким образом, он не сработает, когда документ будет загружен и проанализирован, а только после того, как все скрипты будут загружены (и потенциально запущены), в т.ч. ресурсы, которые необходимо загрузить.

Надеюсь это немного поможет!

person Florian Rappl    schedule 15.12.2017
comment
Спасибо за этот полезный пост! Я установил AngleSharp через браузер диспетчера пакетов nuget для проекта .net core 2, и мне также нужно было установить пакет AngleSharp.Scripting.JavaScript, чтобы указать WithJavaScript() - person kyle; 02.01.2018
comment
@Florian Rappl, как я могу сделать запрос POST, используя контекст? - person anatol; 04.04.2018
comment
С контекстом (я думаю, вы имеете в виду BrowsingContext) вы можете просто перемещаться по страницам. Такие переходы всегда являются GET-запросами. Отправка форм (т. е. POST) осуществляется через формы. Соответствующие загрузчики (или запросчики низкого уровня), конечно, могут вызывать любые запросы, включая POST-запросы. - person Florian Rappl; 04.04.2018