Строим график по статистике хаброюзера

По карме и рейтингу сложно понять какие блоги более интересны пользователю, и как в среднем другие пользователи оценивают его комментарии. Я решил реализовать возможность построить круговые диаграммы по обоим пунктам.
В скрипте используются:
- XPath
- Регулярные выражения
- Google Charts
Итак, привожу класс который обрабатывает страницы хабра с комментариями пользователя:
class Parser
{
private $_sumPages;
private $_username;
private $_myMarks;
private $_uniqName;
public function __construct()
{
$this->_uniqName = time().rand().".png";
}
public function setUsername($un)
{
$this->_username = $un;
return $this;
}
public function getNumPages()
{
if(!empty($this->_username))
{
$page = file_get_contents("http://".$this->_username.".habrahabr.ru/comments");
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->strictErrorChecking = false;
$dom->recover = true;
$dom->loadHTML($page);
}
else
{
return false;
}
$xpath = new DOMXPath($dom);
$pages = $xpath->query('//ul[@id="nav-pages"]/li[last()]//a/@href');
foreach($pages as $page)
{
$lastPage = $page->nodeValue;
preg_match('/([0-9]+)/is', $lastPage, $sumPages);
$this->_sumPages = $sumPages[0];
}
if(empty($this->_sumPages))
{
$this->_sumPages = 1;
}
return $this;
}
В конструкторе создаем уникальное имя для текущей диаграммы. Метод setUsername задает свойства $_username чтобы в дальнейшем класс мог его использовать. Метод getNumPages получает страничку с комментариями пользователя имя которого хранится в $_username, создает объект DOMDocument в который передается контент загруженной странички. Далее происходит XPath запрос который возвращает объект типа DOMNodeList, и с помощью итератора происходит перебор полученных элементов(на самом деле элемент только один) и на выходе получаем URL последней странички с комментариями в виде /comments/page7/, через рег.выр. вытаскиваем число — номер последней страницы и сохраняем в переменной $this->_sumPages.
public function getAllMarks()
{
$marks = array();
if($this->_sumPages != 0)
{
for($i = 1; $i <= $this->_sumPages; $i++)
{
$page = file_get_contents("http://".$this->_username.".habrahabr.ru/comments/page".$i."/");
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->strictErrorChecking = false;
$dom->recover = true;
$dom->loadHTML($page);
$xpath = new DOMXPath($dom);
$marks = $xpath->query('//ul[@class="vote"]/li');
// $myMarks = array();
foreach($marks as $mark)
{
$newMark = str_replace("–", "-", $mark->nodeValue);
if($newMark <= -5)
{
$myMarks["<=%20-5"]++;
}
else
{
if($newMark < 0)
{
$myMarks[">%20-5%20and%20<%200"]++;
}
else
{
if($newMark > 0 && $newMark <= 5)
{
$myMarks[">%200%20and%20<=%205"]++;
}
else
{
if($newMark > 5)
{
$myMarks[">%205"]++;
}
else
{
$myMarks["0"]++;
}
}
}
}
}
}
$this->_myMarks = $myMarks;
return $this;
}
}
Этот метод идет по всем страницам, количество которых мы получили с помощью метода getNumPages, и XPath запросом '//ul[@class=«vote»]/li' вытаскивает оценки за каждый комментарий который сделал пользователь. Далее все оценки пишутся в хэш и ключами которого являются диапазоны в которые эти оценки входят. Пробелы заменены на %20 потому что эти ключи будут участвовать в формировании URL'а. На выходе мы получаем хэш где ключи это диапазоны, а значения — количество оценок которые входят в этот диапазон.
public function getAllCats()
{
if($this->_sumPages != 0)
{
$allCats = array();
for($i = 1; $i <= $this->_sumPages; $i++)
{
$page = file_get_contents("http://".$this->_username.".habrahabr.ru/comments/page".$i."/");
$dom = new DOMDocument();
$dom->preserveWhiteSpace = false;
$dom->strictErrorChecking = false;
$dom->recover = true;
$dom->loadHTML($page);
$xpath = new DOMXPath($dom);
$cats = $xpath->query("//a[@class='where']");
foreach($cats as $cat)
{
$allCats[] = $cat->nodeValue;
}
}
$newCats = array();
$sumCats = count($allCats);
foreach($allCats as $cat)
{
$newCats[$cat]++;
}
foreach($newCats as $key => $value)
{
$newCats[$key] = $value/$sumCats*100;
if($newCats[$key] < 5)
{
$newCats["Другие разделы"] += $newCats[$key];
unset($newCats[$key]);
}
}
$this->_myCats = $newCats;
return $this;
}
}
Этот метод схож с предыдущим методом, только здесь другим XPath запросом вытаскиваются все блоги в которые комментировал пользователь, далее создается хэш в котором в качестве ключей выступают блоги, а значение — количество комментариев в этот раздел, потом происходит проверка сколько процентов от общего количества комментариев приходится на текущий блог и если <5%, то этот блог удаляется их хэша а его процент прибавляется к «Другим блогам», так сделано потому что если у пользователя огромное количество комментариев на диаграмме получится каша, а цель у нас получить основные блоги куда комментировал пользователь. В конце получаем хэш с блогами в качестве ключа и процентом комментариев в качестве значения.
public function getChart($type)
{
switch($type)
{
case "marks":
$strMarks = join(",", $this->_myMarks);
$strKeys = join("|", array_keys($this->_myMarks));
break;
case "cats":
$keys = array_keys($this->_myCats);
foreach($keys as $key => $value)
{
$keys[$key] = str_replace(" ", "%20", $keys[$key]);
}
$strMarks = join(",", $this->_myCats);
$strKeys = join("|", $keys);
break;
}
$chart = file_get_contents("http://chart.apis.google.com/chart?cht=p3&chd=t:".$strMarks."&chs=850x300&chl=".$strKeys);
if($f = file_put_contents("charts/".$this->_uniqName, $chart))
{
return($this->_uniqName);
}
}
}
Последний метод обрабатывает хэш, в зависимости от того диаграмму чего мы хотим получить мы обрабатываем разные хэши. Обрабатываем их для того чтобы можно было подставить их значения и ключи в запрос к Google Charts. Далее делаем запрос к Google Charts и получаем PNG картинку, и в конце пишем ее на диск с именем которое было сгенерировано в конструкторе класса. Вот собственно и весь класс, используется он так:
$parser = new Parser();
print $parser->setUsername('username')->getNumPages()->getAllMarks()->getChart("marks"); //получаем диаграмму пользователя username с оценками его комментариев.
$parser2 = new Parser();
print $parser2->setUsername('username')->getNumPages()->getAllCats()->getChart("cats"); //получаем диаграмму пользователя username с его комментариями в блоги.
Работающая версия есть тут: habr.zakatnov.ru
Скачать исходники с уже прикрученной мордой можно тут: habr.zakatnov.ru/parser.rar
P.S.:
Почитать о XPath можно тут — ru.wikipedia.org/wiki/Xpath
Почитать о Google Charts можно тут — code.google.com/intl/ru-RU/apis/charttools/
P.P.S:
Это мой первый пост на хабр, не судите строго, жду объективной критики.
UPDATE:
Обновил немного код, теперь диаграммы отдаются в таком виде:

Справа теперь появилась легенда, в оценках комментариев это количество комментариев в каждом диапазоне. В блогах процентное отношение количества комментариев по блогам.



комментарии (41)