Зачем?
У меня возникла необходимость построить графики статистики игроков c iccup.com
Когда разбирался с библиотекой искал на хабре, но ничего подходящего мне не нашел.
Поэтому попутно написал эту статью.
Технические детали
Никакого API так нету, и не будет в ближайшие время. Поэтому выбор способов получения данных не велик, придется парсить страницы.
Я решил делать это с помощью библиотекой htmlagilitypack. Она довольно проста и удобна. XPath поиск занимает около 100мс.
Реализация
Я решил не придумывать велосипеды, и сделал все максимально просто.
Скачиваем страницу с помощью класса WebClient, и с помощью библиотеки htmlagilitypack и xpath выражений парсим ее.
Собственно код
Основная функция
Тут мы читаем файл и вызываем парсинг
private static void Main(string[] args)
{
//Create timer
Stopwatch sw = Stopwatch.StartNew(); // Таймер я использую для измерения производительности
//Copyright
Console.WriteLine("iCCup Fetcher");
Console.WriteLine();
//Read
var lines = new List<string>(); // Тут мы читаем файлик в список.
using (var r = new StreamReader("users.txt"))
{
string line;
while ((line = r.ReadLine()) != null)
{
lines.Add(line);
}
}
//Write time taken
sw.Stop();
Console.WriteLine("Инициальзация заняла: {0}ms", sw.Elapsed.TotalMilliseconds);
Console.WriteLine();
//Write stuff
foreach (string line in lines)
{
Console.WriteLine(GetStats(line)); // Тут для каждого аккаунта вызывается функция которая собственно все и делает.
}
//Stop at end
Console.WriteLine("Нажмите любую кнопку чтобы выйти...");
Console.ReadKey();
}
GetStats()
Тут самое интересное, использование библиотеки, и парсинг.
SelectNodes() возвращает коллекцию нод подходящих под переданное xpath выражение. В данном случае элемент всегда один, поэтому [0] жестко вшито.
private static string GetStats(string nickname)
{
var doc = new HtmlDocument();
var wc = new WebClient();
var sb = new StringBuilder();
var sw = Stopwatch.StartNew();
string page = wc.DownloadString("http://ru.iccup.com/dota/gamingprofile/" + nickname + ".html");
//Select team-profile or not
int profileType = page.Contains(@"<div class=""profile-team"">") ? 1 : 0; // Тут решение примитивной, но важной проблемы. Если не учитывать это, то будут либо NullRefenceException, либо статистика будет не верной.
//0 - Generic
//1 - With team profile
doc.LoadHtml(page); //Загружаем страницу из строки
switch (profileType) //Тут нам
{
case 0:
//Игрок
sb.Append("Игрок: ");
sb.Append(nickname);
sb.AppendLine();
//П-П-Л
sb.Append("Игры: ");
sb.Append(
doc.DocumentNode.SelectNodes(@"//*[@id=""profile""]/div[4]/div[2]/div[4]/div[2]")[0].InnerText); //Тут мы получаем текст из отдельного элемента найденного по его XPATH. Про нахождение XPATH чуть позже.
sb.AppendLine();
//Процент
sb.Append("Процент: ");
sb.Append(
doc.DocumentNode.SelectNodes(@"//*[@id=""profile""]/div[4]/div[2]/div[9]/div[2]")[0].InnerText);
sb.AppendLine();
//Очки
sb.Append("Очки: ");
sb.Append(
doc.DocumentNode.SelectNodes(@"//*[@id=""profile""]/div[4]/div[2]/div[3]/div[2]")[0].InnerText);
sb.AppendLine();
break;
case 1:
//Игрок
sb.Append("Игрок: ");
sb.Append(nickname);
sb.AppendLine();
//П-П-Л
sb.Append("Игры: ");
sb.Append(
doc.DocumentNode.SelectNodes(@"//*[@id=""profile""]/div[5]/div[2]/div[4]/div[2]")[0].InnerText);
sb.AppendLine();
//Процент
sb.Append("Процент: ");
sb.Append(
doc.DocumentNode.SelectNodes(@"//*[@id=""profile""]/div[5]/div[2]/div[9]/div[2]")[0].InnerText);
sb.AppendLine();
//Очки
sb.Append("Очки: ");
sb.Append(
doc.DocumentNode.SelectNodes(@"//*[@id=""profile""]/div[5]/div[2]/div[3]/div[2]")[0].InnerText);
sb.AppendLine();
break;
}
sb.Append("Затраченно времени: " + sw.Elapsed.TotalMilliseconds.ToString() + "ms");
sb.AppendLine();
return sb.ToString();
}
XPATH
Здесь все очень просто в любом WebKit браузере.
Иногда HAP не признает выражения, пока такое поведение выявил только с таблицами.
Заключение
Это быстрый и удобный способ быстро собирать данные с сайтов без сложной и/или не валидной табличной верстки. Надеюсь эта статья поможет в написании легких и быстрых парсеров.
Автор: l0cal