Одно из приемуществ всеобщего удешевления аппаратуры и интернета в том, что сбор информации из разных источников в интернете почти ничего не стоит и может производиться без особых проблем. Задача получения и обработки больших объемов данных является коммерчески превлекательной ввиду спроса на считывание («скрейпинг») веб-сайтов со стороны заказчиков (обычно это описывается термином ‘social media analysis’, т.е. анализ социальных медиа). Ну и в принципе это достаточно интересно – по крайней мере по сравнению с рутинной разработкой сайтов, отчетов, и т.д.
В этой статье я начну рассказ про то, как можно реализовать сбор и обработку данных с использованием платформы .Net. Было бы интересно послушать про то как делать то же самое в стеке Java, поэтому если кто-то хочет присоединиться к данной статье в качестве соавтора – милости прошу.
Все исходники находятся тут: http://bitbucket.org/nesteruk/datagatheringdemos

Итак, у нас пожалуй самая «размытая» из возможных задач – получение, обработка и хранение данных. Для чтого чтобы получить работующую систему, нам нужно знать

Давайте рассмотрим те источники данных, с которых нужно получать информацию:
Сразу хочу подчеркнуть, что веб-браузер не является единственным источником данных. Тем не менее, если работа с веб-сервисами или, скажем, использование API какой-то социальной платформы, является достаточно понятной задачей и не требует много телодвижений, разбор HTML является намного более сложной задачей. И HTML это не предел – порой приходится разбирать JavaScript или даже визуальную информацию с картинок (к пр. для обхода «капчи»).
Другой проблемой является то, что порой контент подгружается динамически через AJAX, что делает нуж��ым разного сорта ‘учет состояний’ для того чтобы получать контент именно тогда, когда он доступен.

Обработка данных – это самая трудоемкая и дорогостоящая (с точки зрения потенциального заказчика) операция. С одной стороны, может показаться что тот же HTML должен очень просто разбираться существующими средствами, но на самом деле это не так. Во-первых, HTML в большинстве случаев не является XHTML, иначе говоря сделав
Даже имея хорошо сформированные данные, у вас все равно будет много проблем – ведь любая более-менее сложная веб-страничк является проекцией многомерной структуры базы данных владельца на одномерное пространство. Восстановление связей и зависимостей является тем самым необходимой задачей для хранения полученной информации в реляционных БД.
Не следует забывать и про более «приземленный» процессинг данных, то есть некие трансформации или произвольные действия над полученными данными. Например, получив IP-адрес вам захочется узнать местоположение или наличие веб-сервера по этому адресу, что потребует дополнительных запросов. Или, скажем, при получении новых данных вам нужно постоянно пересчитывать движимое среднее (streaming OLAP).

Получив данные, их нужно где-то хранить. Вариантов храниния много – использование сериализации, текстовый файлов, а также объектно- и документно-ориентированных а также конечно реляционных баз данных. Выбор хранища в коммерческом заказе зависит скорее всего либо от заказчика («мы хотим MySQL») либо от финансовых предпочтений заказчика. В .Net-разработке базой «по умолчанию» является SQL Server Express. Если же вы делаете хранилище для себя, позволительно использовать все что угодно – будь то MongoDB, db4o или например SQL Server 2008R2 Datacenter Edition.
В большинстве случаев, хранилища данных не требуют особой сложности, т.к. пользователи просто проецируют базу в Excel (ну или SPSS, SAS, и т.п.) а дальше используют привычные методы для анализа. Варианты вроде SSAS (SQL Server Analysis Services) используются намного реже (ввиду минимального ценника в $7500 – см. тут), но знать о них тоже стоит.

Давайте посмотрим на минимальный кусочек кода, который поможет нам скачать и «распарсить» страницу. Для этих задач, мы воспользуемся двумя пакетами:
Вот минимальный пример того, как можно использовать два этих фреймворка вместе для того чтобы получить страничку с сайта:
В примере выше, мы получили страницу через WatiN, загрузили тело страницы в HTML Agility Pack, нашли первый элемент типа

Наверное для вас очевидно, что запись данных в какое-то хранилище не делается из консольного приложения. В большинстве случаев, для этого используется сервис (windows service). А то чем занимается сервис – это в большинстве случаев поллинг, то есть регулирное скачивание ресурса и обновление нашего представления о нем. Скачивание обычно происходит с интервалом раз в N минут/часов/дней.
Для хорошего поведения сервиса нужно еще несколько полезных фишек. Во-первых, полезно добавлять в сервисы возможность запуска из консоли. Это помогает при отладке.
Другая полезная фича – это саморегистрация, чтобы вместо использования
Класс установки использует мало знакомую сборку
Ну и последняя фича это конечно же использование логирования. Я использую библиотеку log4net, а для записывания логов в консоль можно использвать очень вкусную фичу под названием

На первый раз достаточно информации. К концу хочу напомнить несколько простых правил:
Кстати о птичках… вместо сервиса можно в принципе сделать EXE и запускать его через sheduler. Но это как-то неопрятно.
Спасибо за внимание. Продолжение следует :)
В этой статье я начну рассказ про то, как можно реализовать сбор и обработку данных с использованием платформы .Net. Было бы интересно послушать про то как делать то же самое в стеке Java, поэтому если кто-то хочет присоединиться к данной статье в качестве соавтора – милости прошу.
Все исходники находятся тут: http://bitbucket.org/nesteruk/datagatheringdemos

Итак, у нас пожалуй самая «размытая» из возможных задач – получение, обработка и хранение данных. Для чтого чтобы получить работующую систему, нам нужно знать
- Где находятся данные и как к ним правильно обращаться
- Как обработать данные чтобы получить только то, что нужно
- Где и как хранить данные

Давайте рассмотрим те источники данных, с которых нужно получать информацию:
- Форумы
- Блоги
- Новостные сайты
- Каталоги, листинги
- Публичные веб-сервисы
- Прикладное ПО
Сразу хочу подчеркнуть, что веб-браузер не является единственным источником данных. Тем не менее, если работа с веб-сервисами или, скажем, использование API какой-то социальной платформы, является достаточно понятной задачей и не требует много телодвижений, разбор HTML является намного более сложной задачей. И HTML это не предел – порой приходится разбирать JavaScript или даже визуальную информацию с картинок (к пр. для обхода «капчи»).
Другой проблемой является то, что порой контент подгружается динамически через AJAX, что делает нуж��ым разного сорта ‘учет состояний’ для того чтобы получать контент именно тогда, когда он доступен.

Обработка данных – это самая трудоемкая и дорогостоящая (с точки зрения потенциального заказчика) операция. С одной стороны, может показаться что тот же HTML должен очень просто разбираться существующими средствами, но на самом деле это не так. Во-первых, HTML в большинстве случаев не является XHTML, иначе говоря сделав
XElement.Parse()
вы попросту получите исключение. Поэтому нужно как минимум иметь возможность «корректировать» плохо написаный HTML.Даже имея хорошо сформированные данные, у вас все равно будет много проблем – ведь любая более-менее сложная веб-страничк является проекцией многомерной структуры базы данных владельца на одномерное пространство. Восстановление связей и зависимостей является тем самым необходимой задачей для хранения полученной информации в реляционных БД.
Не следует забывать и про более «приземленный» процессинг данных, то есть некие трансформации или произвольные действия над полученными данными. Например, получив IP-адрес вам захочется узнать местоположение или наличие веб-сервера по этому адресу, что потребует дополнительных запросов. Или, скажем, при получении новых данных вам нужно постоянно пересчитывать движимое среднее (streaming OLAP).

Получив данные, их нужно где-то хранить. Вариантов храниния много – использование сериализации, текстовый файлов, а также объектно- и документно-ориентированных а также конечно реляционных баз данных. Выбор хранища в коммерческом заказе зависит скорее всего либо от заказчика («мы хотим MySQL») либо от финансовых предпочтений заказчика. В .Net-разработке базой «по умолчанию» является SQL Server Express. Если же вы делаете хранилище для себя, позволительно использовать все что угодно – будь то MongoDB, db4o или например SQL Server 2008R2 Datacenter Edition.
В большинстве случаев, хранилища данных не требуют особой сложности, т.к. пользователи просто проецируют базу в Excel (ну или SPSS, SAS, и т.п.) а дальше используют привычные методы для анализа. Варианты вроде SSAS (SQL Server Analysis Services) используются намного реже (ввиду минимального ценника в $7500 – см. тут), но знать о них тоже стоит.

Давайте посмотрим на минимальный кусочек кода, который поможет нам скачать и «распарсить» страницу. Для этих задач, мы воспользуемся двумя пакетами:
- WatiN – это библиотека для тестирования веб-интерфейсов. Ее хорошо использовать для автоматизированного нажатия кнопочек, выбора элементов из списка, и подобных вещей. WatiN также предоставляет объектную модель заполученной страницы, но я бы ей не пользовался. Причина в целом одна – WatiN нестабильная и весьма капризная библиотека, которую нужно с опаской использовать (только в 32-битном режиме!) для управления браузером.
- HTML Agility Pack – библиотека для разбора HTML. С��м HTML можно взять из WatiN, загрузить, и даже если он плохо сформирован, Agility Pack позволит делать в нем поиски и выборки с помощью XPath.
Вот минимальный пример того, как можно использовать два этих фреймворка вместе для того чтобы получить страничку с сайта:
[STAThread]<br/>
static void Main()<br/>
{<br/>
using (var browser = new IE("http://www.pokemon.com"))<br/>
{<br/>
var doc = new HtmlDocument();<br/>
doc.LoadHtml(browser.Body.OuterHtml);<br/>
var h1 = doc.DocumentNode.SelectNodes("//h3").First();<br/>
Console.WriteLine(h1.InnerText);<br/>
}<br/>
Console.ReadKey();<br/>
}<br/>
В примере выше, мы получили страницу через WatiN, загрузили тело страницы в HTML Agility Pack, нашли первый элемент типа
H3
и выписали в консоль его содержание.
Наверное для вас очевидно, что запись данных в какое-то хранилище не делается из консольного приложения. В большинстве случаев, для этого используется сервис (windows service). А то чем занимается сервис – это в большинстве случаев поллинг, то есть регулирное скачивание ресурса и обновление нашего представления о нем. Скачивание обычно происходит с интервалом раз в N минут/часов/дней.
public partial class PollingService : ServiceBase<br/>
{<br/>
private readonly Thread workerThread;<br/>
public PollingService()<br/>
{<br/>
InitializeComponent();<br/>
workerThread = new Thread(DoWork);<br/>
workerThread.SetApartmentState(ApartmentState.STA);<br/>
}<br/>
protected override void OnStart(string[] args)<br/>
{<br/>
workerThread.Start();<br/>
}<br/>
protected override void OnStop()<br/>
{<br/>
workerThread.Abort();<br/>
}<br/>
private static void DoWork()<br/>
{<br/>
while (true)<br/>
{<br/>
log.Info("Doing work⋮");<br/>
// do some work, then
Thread.Sleep(1000);<br/>
}<br/>
}<br/>
}<br/>
Для хорошего поведения сервиса нужно еще несколько полезных фишек. Во-первых, полезно добавлять в сервисы возможность запуска из консоли. Это помогает при отладке.
var service = new PollingService();<br/>
ServiceBase[] servicesToRun = new ServiceBase[] { service };<br/>
<br/>
if (Environment.UserInteractive)<br/>
{<br/>
Console.CancelKeyPress += (x, y) => service.Stop();<br/>
service.Start();<br/>
Console.WriteLine("Running service, press a key to stop");<br/>
Console.ReadKey();<br/>
service.Stop();<br/>
Console.WriteLine("Service stopped. Goodbye.");<br/>
}<br/>
else<br/>
{<br/>
ServiceBase.Run(servicesToRun);<br/>
}<br/>
Другая полезная фича – это саморегистрация, чтобы вместо использования
installutil
можно было установить сервис через myservice /i
. Для этого существует отдельный класс…
class ServiceInstallerUtility<br/>
{<br/>
private static readonly ILog log = <br/>
LogManager.GetLogger(typeof(Program));<br/>
private static readonly string exePath = <br/>
Assembly.GetExecutingAssembly().Location;<br/>
public static bool Install()<br/>
{<br/>
try { ManagedInstallerClass.InstallHelper(new[] { exePath }); }<br/>
catch { return false; }<br/>
return true;<br/>
}<br/>
public static bool Uninstall()<br/>
{<br/>
try { ManagedInstallerClass.InstallHelper(new[] { "/u", exePath }); }<br/>
catch { return false; }<br/>
return true;<br/>
}<br/>
}<br/>
Класс установки использует мало знакомую сборку
System.Configuration.Install
. Используется она прямо из Main()
:
if (args != null && args.Length == 1 && args[0].Length > 1<br/>
&& (args[0][0] == '-' || args[0][0] == '/'))<br/>
{<br/>
switch (args[0].Substring(1).ToLower())<br/>
{<br/>
case "install":<br/>
case "i":<br/>
if (!ServiceInstallerUtility.Install())<br/>
Console.WriteLine("Failed to install service");<br/>
break;<br/>
case "uninstall":<br/>
case "u":<br/>
if (!ServiceInstallerUtility.Uninstall())<br/>
Console.WriteLine("Failed to uninstall service");<br/>
break;<br/>
default:<br/>
Console.WriteLine("Unrecognized parameters.");<br/>
break;<br/>
}<br/>
}<br/>
Ну и последняя фича это конечно же использование логирования. Я использую библиотеку log4net, а для записывания логов в консоль можно использвать очень вкусную фичу под названием
ColoredConsoleAppender
. Сам процесс логирования примитивен.
На первый раз достаточно информации. К концу хочу напомнить несколько простых правил:
- Запуск IE требует single-thread apartment; я правда использую FireFox т.к. мне нравится FireBug
- WatiN следует исполнять в 32-битной программе (x86)
- Поллинг, приведенный выше неидеален, т.к. не учитывает тот факт, что сам по себе WatiN протормаживает и парсинг HTML – тоже операция небыстрая
Кстати о птичках… вместо сервиса можно в принципе сделать EXE и запускать его через sheduler. Но это как-то неопрятно.
Спасибо за внимание. Продолжение следует :)