Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.71/14: Рейтинг темы: голосов - 14, средняя оценка - 4.71
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
1
.NET 4.x

Сравнение производительности веб-парсинга разными способами

13.07.2012, 21:14. Просмотров 2663. Ответов 18
Метки нет (Все метки)

В процессе разработки очередной программы, цель которой отпарсить определенный список страниц, обработать и выдать результат, я задумался - а какой метод будет более быстрым, удобным или коротким? Решил сделать тестер и показать результаты эксперимента сюда. В качестве таймера я использовал QueryPerformanceCounter и QueryPerformanceFrequency из библиотеки Kernel32.dll. Вроде как по отзывам достаточно точно определяет затраченное время. Было сделано по 10 независимых тестов на каждый метод парсинга. Все времена указаны в секундах.

Цель эксперимента: вывести временные затраты при парсинге страницы.
Парсируемая страница: "https://www.google.ru/search?&q=Как+производить+замер+времени"
Необходимо: загрузить страницу в память, найти выданные ссылки, вывести на экран ссылки (название, описание и сам линк).

Методы:
1. Загружаем данные в виртуальный webbrowser, парсим через класс HtmlElement.
Плюсы: Приятный вид парсинга и загрузки данных, очень понятный и достаточно удобный и короткий.
Минусы: Необходимо создавать ивент для ожидания загрузки страницы в WebBrowser. Возможно на странице с каким-то мультимедийным контентом будет ещё дольше "висеть". Крайне мало настроек подключения (точнее их нет).

Среднее время на парсинг: 0,019997724;
Среднее время на инициализацию компонента и загрузку содержимого в память: 2,52409373;
Минимальное время на -//-: 2,140337;
Максимальное время на -//-: 3,079691;
Разница: 0,939354.


2. Загружаем данные через класс HTTPWebRequest в строку, парсим ручками.
Плюсы: Очень тонкие настройки соединения, GET/POST запросов, кукисов, всего чего только можно, в т.ч. кодировки.
Минусы: Ужасный вид кода парсинга ручками, очень тяжело работать в кривом коде HTML, большой код для парсинга загруженных данных. Возможно, если использовать RegEx, то вид парсинга будет более оптимальный и не такой большой (не занимался ещё RegEx'ом). Так же необходимо создавать дополнительные методы (функции) для очистки HTML кода от мусора (<em>/<br>/<b>) и подобных для вывода чистого текста. А так же очень ненадёжный вид и тяжело понимаемый.

Среднее время на парсинг: 0,018322276 (чуточку быстрее первого метода);
Среднее время на инициализацию компонента и загрузку содержимого в память: 0,38945552 (топ);
Минимальное время на -//-: 0,3817242;
Максимальное время на -//-: 0,3990794;
Разница: 0,0173552.


3. Загружаем данные через HtmlAgilityPack, парсим через методы HtmlAgilityPack
Плюсы: Весьма удобный и короткий вид загрузки данных, очень короткий код на парсинг, не надо ничего нового придумывать, есть немного настроек соединения.
Минусы: Очень непривычный и местами неудобный (а местами очень даже удобный) синтаксис (если позволите так сказать) парсинга. Чистый адрес ссылки мне так и не удалось вытащить, почему-то куча мусора приплюсовалась, но это лишь неприятная мелочь. Хотя я впервые знакомлюсь с HtmlAgilityPack, может быть он лучше и удобнее чем кажется.

Среднее время на парсинг: 0,006282992 (топ);
Среднее время на инициализацию компонента и загрузку содержимого в память: 0,44985426;
Минимальное время на -//-: 0,384168;
Максимальное время на -//-: 0,6345674;
Разница: 0,2503994.

ВЕРДИКТ: Первый метод хорош только видом парсинга. Ужасно долго по сравнению с другими методами происходит получение данных от сервера. Второй идеален для тонкой настройки подключения или цепочки соединений. Если найти хороший способ парсинга текста (обработки полученного кода), то, я думаю, это будет лучший способ для меня (надо бы RegEx почитать). Третий способ для эстетов - всё довольно аккуратно и чётко. Но лично для меня он: непривычный и неудобный.

Прошу Ваших комментариев!

P.S. Я не рассматривал возможность асинхронной загрузки данных.
P.P.S. К сообщению приложил архив с проектом.
3
Вложения
Тип файла: rar Parsing_Test.rar (158.6 Кб, 89 просмотров)
Лучшие ответы (1)
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
13.07.2012, 21:14
Ответы с готовыми решениями:

Сохранение информации разными способами
Здравствуйте! Недавно начал разбираться с изображениями. И у меня возник вопрос : Насколько...

Скорость парсинга ini файла разными методами
Ввиду моей большой лени самому неохота проверять данный вопрос, может кто сталкивался с этим?...

INSERT разными способами
Добрый вечер! Помогите решить ещё одну задачку. В ней сразу несколько проблем. есть таблица 1, в...

Сортировка массивов разными способами
У меня завтра зачёт с энономической информатики. И мне нужно запрограмировать следующие методы...

18
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
14.07.2012, 09:37  [ТС] 2
Кстати, надо будет попробовать сделать загрузку кода страницы через HTTPWebRequest, а потом загнать код в WebBrowser в надежде, что конкретно этот процесс не займёт времени. Тогда парсить будет очень легко (ну вот не понравился мне HtmlAgilityPack - Хочется обойтись родными библиотеками). Или всё-таки использовать Regex - как я убедился, он очень крут, но тяжело читаем (для меня пока что).
0
6223 / 3524 / 898
Регистрация: 28.10.2010
Сообщений: 5,926
14.07.2012, 10:50 3
WebBrowser еще скачивает картинки, CSS и JS
1
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
14.07.2012, 11:06  [ТС] 4
Цитата Сообщение от Петррр Посмотреть сообщение
WebBrowser еще скачивает картинки, CSS и JS
Тогда по идее должен иметься доступ к мультимедийному контенту (и скриптам), но это не нужно для парсинга в принципе. Всё это можно вытащить из кода. Спасибо.
0
Неадекват
1430 / 1184 / 229
Регистрация: 02.04.2010
Сообщений: 2,718
Записей в блоге: 2
14.07.2012, 12:13 5
А что мешает совместить второй и третий способ?
Как нибудь так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
HtmlDocument LoadPage(string URL)
        {
            HtmlDocument doc = new HtmlDocument();
            WebRequest request = WebRequest.Create(URL);
 
            using (var response = request.GetResponse())
            {
                using (var stream = response.GetResponseStream())
                {
                    doc.Load(stream);
                    return doc;
                }
            }
        }
а потом HtmlAgilityPack для парсинга:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public List<TypeRadio> GetTypeRadio()
        {
            List<TypeRadio> typeRadio = new List<TypeRadio>();
            string mainURL = ParseData.MainURL;
            HtmlDocument doc = LoadPage(mainURL);
            //string XPathTypeRadio = "/html/body/div[5]/div/div/div/div[3]/div/ul//a[@href]";
            foreach (HtmlNode node in doc.DocumentNode.SelectNodes(ParseData.XPathTypeRadio))
            {
                if (node.InnerText == string.Empty || failName.Contains(node.InnerText)) continue;
                HtmlAttribute ha = node.Attributes["href"];
                typeRadio.Add(new TypeRadio() { Name = node.InnerText, URL = CombineUri(mainURL, ha.Value) });
            }
            return typeRadio;
        }
1
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
14.07.2012, 12:28  [ТС] 6
Тоже как вариант, тем более что опыт показал самую шуструю обработку текста (из 3х вариантов) именно у Html Agility Pack. Просто привыкать к нему не хочется )). Надо будет регекс засечь таймером.
0
Неадекват
1430 / 1184 / 229
Регистрация: 02.04.2010
Сообщений: 2,718
Записей в блоге: 2
14.07.2012, 13:02 7
Нормально составленные регулярки пошустрее будут. Тем не менее зря вы так на HtmlAgilityPack косо смотрите, гибкий и удобный инструмент. Работа с регулярными выражениями - возня со строками. НАР - возня с объектами. ИМХО объекты удобнее.

В плюс HtmlAgilityPack также можно засчитать то, что он парсит страничку один раз. Для извлечения множества разнородной информации он подойдет лучше.
0
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
14.07.2012, 13:09  [ТС] 8
Ну так-то да, с объектами всегда проще и удобнее (всё-таки у нас объектно-ориентированная среда). И грамотнее. Но беда в том, что при попытке получить код ссылки при парсе гугла, допустим, текста "Привет! Как дела?", гугл даёт такую ссылку: "v k . c o m /privetkakdelagroup" (пробелы поставил чтоб линк не делался). Точно так же я получаю ссылку через HtmlElement и ручной парс (и, думаю, получу с регулярки так же). А вот HAP выдал мне такое: "http : // ww w.g oogl e.r u/u rl?sa=t&rct=j&q=&esrc=s&source=web&cd=2&ved=0CE0QFjAB&url=ht tp%3A%2F%2Fvk.com%2Fprivetkakdelagroup&ei=UTYBUIj0Ks_P4QS6mJ mlCA&usg=AFQjCNGKu_7uUwICKQFHTE-tvjjcb-HrFg". И теперь мне надо тогда отпарсить как-то вот этот кусок "url=http%3A%2F%2Fvk.com%2Fprivetkakdelagroup" (плюс, перекодировать url в понятный вид). Честно НЕ ЗНАЮ почему именно так HAP мне выдал ссылку, но я полчаса мучался и забил в итоге (цель была всё-таки получить время на получение результатов запроса). Хотя линк я добывал вроде правильно:
C#
1
lnk.URL = h3.ChildNodes[0].Attributes["href"].Value;
Разве нет? Поправьте меня если я не прав, и я буду только рад, если HAP выдаст мне человеческий линк в этом частном примере.
0
347 / 326 / 49
Регистрация: 12.12.2011
Сообщений: 563
14.07.2012, 13:32 9
HAP спарсил все правильно, вот как выглядит ссылка в гугле
http://puu.sh/Irm6
1
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
14.07.2012, 13:39  [ТС] 10
Да?
Значит я что-то не так сделал. Позже на досуге проверю. А заодно попробую соединить разные методы коннекта+парса. Заодно засеку по времени (QueryPerformance'ом и Stopwatch'ем). Дело-то плёвое, а интересно! И приведу примерный вид парса для оценки удобности на нескольких сайтах для разнообразия =). Планирую сделать программу для парса и сбора ответов с разных поисковых систем (т.к. надоело по 5-7 окон держать только с поисковиками), вот и исследую различные методы, т.к. в интернете свежей (да и не свежей) комплексной информации с учетом различных методов не нашел. Даже если она и есть - не бросать же дело, надо добивать, совершенствовать, выяснять )). Вечерком или завтра )
0
Неадекват
1430 / 1184 / 229
Регистрация: 02.04.2010
Сообщений: 2,718
Записей в блоге: 2
14.07.2012, 14:25 11
Yukikaze, все правильно сказал. Гугл собирает историю поиска, поэтому все ссылки с редиректом.
Вот примитив для извлечения прямых ссылок:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
using System;
using HtmlAgilityPack;
 
namespace testParse
{
    class Program
    {
        static void Main(string[] args)
        {
            HtmlDocument doc = new HtmlWeb().Load("https://www.google.ru/search?&q=Привет!+Как+дела?", "GET");
 
            foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//h3[@class='r']/a"))
            {
                HtmlAttribute ha = node.Attributes["href"];
                string link = string.Format("{0} - {1}\n", node.InnerText, ha.Value.Replace("/url?q=", "").Replace("amp;", ""));
                Console.WriteLine(link.Substring(0, link.IndexOf("&sa")));
            }
 
            Console.ReadKey(true);
        }
    }
}
1
347 / 326 / 49
Регистрация: 12.12.2011
Сообщений: 563
14.07.2012, 14:29 12
Лучший ответ Сообщение было отмечено как решение

Решение

Ну и я тогда приколюху выложу
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using HtmlAgilityPack;
 
namespace GoogleParser
{
    class Program
    {
        static void Main(string[] args)
        {
            string str;
            while ((str = Console.ReadLine()) != "exit")
            {
                Console.Clear();
                foreach (string s in Parse(str))
                {
                    Console.WriteLine(s);
                }
            }
        }
 
        private static IEnumerable<string> Parse(string str)
        {
 
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.google.ru/search?oe=UTF-8&q=" + str);
            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            HtmlDocument doc = new HtmlDocument();
            doc.Load(response.GetResponseStream(), Encoding.UTF8);
            HtmlNode[] nodes = doc.DocumentNode.SelectNodes("//h3[@class='r']/a").ToArray();
            foreach (HtmlNode node in nodes)
            {
                string name = node.InnerText;
                string url = WebUtility.HtmlDecode(node.GetAttributeValue("href", "n/a"));
                int start = url.IndexOf("q=") + 2;
                int end = url.IndexOf('&', start);
                string fine = url.Substring(start, end - start);
                yield return name + "\r\n" + fine;
            }
        }
    }
}
3
Неадекват
1430 / 1184 / 229
Регистрация: 02.04.2010
Сообщений: 2,718
Записей в блоге: 2
14.07.2012, 15:17 13
Генератор? Я думал ими никто уже не пользуется с появления LINQ
0
347 / 326 / 49
Регистрация: 12.12.2011
Сообщений: 563
14.07.2012, 16:04 14
freeba, в LINQ нет души
0
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
15.07.2012, 00:55  [ТС] 15
Цитата Сообщение от Yukikaze Посмотреть сообщение
в LINQ нет души
Это почему ещё?

freeba, Я делать так не стал, т.к. боялся что амперсанд (amp может быть и в самой ссылке и в ссылке редиректа. Но я не заострял внимание на этом. Щас займусь.

Цитата Сообщение от Yukikaze Посмотреть сообщение
WebUtility.HtmlDecode
Вот до него-то я и не успел дойти ещё . Мне кажется, что неправильно от "q=" до первого "&" выбирать. Есть ли гарантия что символ "&" не будет относиться к той ссылке которую нужно получить? То есть, может быть, в получаемой ссылке тоже есть всякие "php" плюшки, которые не желательно выкидывать?
0
Неадекват
1430 / 1184 / 229
Регистрация: 02.04.2010
Сообщений: 2,718
Записей в блоге: 2
15.07.2012, 01:16 16

Не по теме:

Цитата Сообщение от Yukikaze Посмотреть сообщение
в LINQ нет души
+1. Функциональная ересь в царстве ООП. Тысячи православных программистов поддались искушению и продали генераторы в обмен на кавайный синтаксис и кажущуюся простоту. Ждем судного дня ибо расставит он все по местам своим.



Цитата Сообщение от Mans7 Посмотреть сообщение
т.к. боялся что амперсанд (amp может быть и в самой ссылке и в ссылке редиректа.
Действительно, не подумал. Тогда меняем Replace("amp;", "") на Replace("amp;", "&").
0
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
15.07.2012, 01:31  [ТС] 17
И-так. Делаем Френкенштейна:
1) Соединим 1 и 2 способы. Загружаем данные через HttpWebRequest, стринг с кодом загоняем в webBrowser.DocumentText. Итого: ничего хорошего. Даже при отключении появления ошибок о скриптах, он все равно ругается на всякие xjs скрипты. При этом, парсинг не проходит. Fail.
2) Будем соединять 2 и 3 способы. Загружаем данные через HttpWebRequest, ответ напрямую получает HAP. Как и ожидалось - лучшая скорость загрузки страницы, лучшая скорость парса.

Yukikaze, скажите, а что даёт "n/a"? И ещё, я укоротил этот кусок:
Цитата Сообщение от Yukikaze Посмотреть сообщение
C#
1
2
3
4
string url = WebUtility.HtmlDecode(node.GetAttributeValue("href", "n/a"));
int start = url.IndexOf("q=") + 2;
int end = url.IndexOf('&', start);
string fine = url.Substring(start, end - start);
до
C#
1
lnk.URL = WebUtility.HtmlDecode(node.GetAttributeValue("href","n/a"));
И на выходе получаю готовую ссылку без всяких танцев с бубном над ней =) Я не заметил что у GetAttributeValue есть 3 конструктора, видел только булевый. Здорово!

Замерил время выполнения StopWatch'ем. Получил:
Среднее время соединения - 462,8 мс (миллисекунд)
Среднее время парса - 6,6 мс

Так же заметил разницу между замерами QueryPerformance и StopWatch. Разницу я наблюдал 0-5%.
0
347 / 326 / 49
Регистрация: 12.12.2011
Сообщений: 563
15.07.2012, 01:39 18
Mans7, у вас какой-то бракованый гугл, дает чистые ссылки, без редиректа
0
61 / 61 / 14
Регистрация: 05.08.2011
Сообщений: 323
Записей в блоге: 5
15.07.2012, 03:56  [ТС] 19
Ну, какой есть )
А теперь ещё фишка - сделал то чего боялся и не хотел. Парсинг RegEx'ом . Паттерн получился такой:
C#
1
string pattern = "<h3 class=.r.><a *href=\"(.+?)\"[^>]*>(.+?)</a>.*<span *class=.st.>(.+?)</span>";
Код очень короткий, время парса 3 мс (0,00349404). Но, блин, регулярку составить трудно =(
Я составлять регулярки не умею толком, так что наверняка её можно слегка упростить.

Добавлено через 10 минут
Из вышесказанного можно сказать что парсинг регулярками имеет:
Плюсы:
- очень производительный (быстрый);
- имеет короткий код;
- не нужно загружать сторонние библиотеки.
Минусы:
- почти нечитабельный (нужно глаз набивать);
- индивидуальный под каждую страницу;
- опять-таки местами где надо вывести чистый текст надо чистить код от мусора (<b><br><em>...), но можно Replace сделать;
- в случае изменения кода на странице, скорее всего, регулярку тоже надо будет корректировать.

Добавлено через 2 минуты
Позже, если ничего нового не придумаю, подведу общие итоги всех этих исследований. Хотя надо что-нибудь ещё придумать, хочется по-глубже залезть в вопросы парсинга.

Добавлено через 26 минут
А теперь заценим асинхронную загрузку кода страницы.
C#
1
2
3
4
5
6
7
8
9
10
11
        public void GetPerfomanceTime_5()
        {
            WebClient wc = new WebClient();
            wc.DownloadStringAsync(new Uri("https://www.google.ru/search?q=Как+производить+замер+времени"));
            wc.DownloadStringCompleted += new DownloadStringCompletedEventHandler(downld); 
        }
 
        private void downld(object ob, DownloadStringCompletedEventArgs e)
        {
            string result = e.Result;
        }
На выходе получаем ту же строчку, без заморочек с GET\POST - хотя их можно настроить без проблем.
Средняя скорость получения данных StopWatch: 453 мс
Средняя скорость получения данных QueryPerformance: 0,446738

Для единичной загрузки страницы скорость меньше чем при использовании HttpWebRequest/Response. А вот если делать это в потоках, то, возможно эффект будет более положительным для Async. Плюс, насколько я заметил, основной поток не подвисает во время получения ответа. То есть получен тот же мини-тред.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
15.07.2012, 03:56

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

Интегрирование функции разными способами
Дали задание в университете, мол, реализуйте интегрирование функции вида f(x) методами...

Определенные интегралы разными способами
Столкнулся с трудностью применения функций quad и quad8. Задание обстоит так: Найдите...

Сортировка массивов разными способами
1.Реализовать алгоритм сортировки прямым включением в виде функции 2.Реализовать алгоритм...

Реализация стек разными способами
Требуется реализовать стек разными способами (требуется три программы): 1) Стек...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
19
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2021, vBulletin Solutions, Inc.