Главная > Coding, SEO, Блог > Seoscan — анализ сайта

Seoscan — анализ сайта

Решил наконец то основательно взяться за продвижение, ибо хочется почувствовать уже, что сайт создается не только для самоутверждения и собственного удовольствия, но и для общественности. Справедливости ради стоит отметить, что некоторая аудитория у сайта уже есть. По последним данным анализа логов посещаемость сайта составляет в среднем 160 уникальных посетителей в сутки. Это неплохой показатель, учитывая, что целенаправленного продвижения не осуществлялось, следовательно, объяснение может быть одно — тематичность статей. Анализ поисковых запросов показывает, что материал в блоге получается довольно таки уникальным, что подтверждает мою стратегию, согласно которой я публикую статьи на темы, которые практически не освещены в интернете. Прежде чем взяться за продвижение, я создал на сайте инструмент для оперативного анализа СЕО-состояния сайта, и нарек его SeoScan. Для чего же он мне понадобился…
… если в интернете полно такого добра? Во-первых, моя склонность к творчеству — люблю что-то сделать и потом пользоваться этим. Это дает ряд преимуществ, например, если пользуешься чужим продуктом, и чего-то тебе не хватает, то нельзя взять, и добавить это. В случае же со своим продуктом ты можешь делать что хочешь. Так же получилось и с SeoScan — я изучил несколько подобных сервисов, взял от них лучшие идеи, и реализовал в своем проекте. Во-вторых, при реализации получил много новых знаний и навыков. В-третьих, своему продукту все-таки как-то больше доверяешь — яркий пример: один из изучаемых сервисов показал, что мой домен присутствует в каталоге DMOZ, а это не так. Ну и в-четвертых — дополнительный полезный функционал на сайте (ознакомиться можно тут).
Что же полезного я узнал, разрабатывая сеоскан? Пришось основательно заняться парсингом всего и вся. В первую очередь это конечно касается отределения Google PR и Яндекс тИЦ. Начну со второго, поскольку яндекс оказался более демократичным в предоставлении данных. Все нужные данные можно получить через xml-интерфейс Яндекс-тулбара. Что-то удалось нагуглить, что-то отреверсить из того-же тулбара. В общем, код получения Яндекс-ТИЦ на php очень прост:

    function getTCY($url) {
       $xml = $this->parent->getURL('http://bar-navig.yandex.ru/u?ver=2&show=32&url='.$url);
       return $xml ? (int) substr(strstr($xml, 'value="'), 7) : false;
    }

$this->parent->getURL — особенность моей ZendFramework реализации. На самом деле это функция из родительского класса для получения контента по указанному URL (о ней чуть ниже).

Не сложнее и функция получения Alexa Rank на php:

    function getAlexaRank($url) {
      $file = $this->parent->getURL('http://data.alexa.com/data?cli=10&dat=snbamz&url='.$url);
      if (!$file)  return false;
      return simplexml_load_string($file);
    }

Вот с GooglePR пришлось повозиться. Во-первых, гугл почему-то не хочет официально делиться своими данными. Алгоритм вычисления PageRank не документирован, поэтому честным программерам пришлось реверсить Google Toolbar, в результате чего удалось выяснить, что для того, чтобы получить страницу с кодом, нужно сформировать http-запрос, и помимо параметра с интересующим доменом передать некую контрольную сумму. Алгоритм отреверсили, и в сети его можно легко найти. Нашел, интегрировал, протестрировал — круто пашет… на домашнем тестовом сервере. Перенес на продакшн — не пашет. Стал искать. Выяснилось внезапно, что на проде оказывается 64-битная система (:. Таким образом, алгоритм подсчета контрольной суммы, расчитаный на 32x архитектуру, работает некорректно в связи с арифметическим переполнением. Снова принялся за поиски. Долго искал, много пробовал — все тщетно. В итоге все-таки удалось по крохам собрать 64-битную версию алгоритма определения Google PR для php. Вот она:

    function strToNum($str, $check, $magic) {
	$int32Unit = 4294967296;  // 2^32
 
	$length = strlen($str);
	for ($i = 0; $i < $length; $i++) {
              $check *= $magic;
              /**
                * Если выпадаем за граници инта (обычно +/- 2.15e+9 = 2^31), то
                * получим undefined, читать ниже по ссылке:
                * http://www.php.net/manual/en/language.types.integer.php
                * потому танцуем с бубном
              */
               if ($check >= $int32Unit) {
			$check = ($check - $int32Unit * (int) ($check / $int32Unit));
			//if the check less than -2^31
			$check = ($check < -2147483648) ? ($check + $int32Unit) : $check;
               }
               $check += ord($str{$i});
        }
        return $check;
   }
/**
  * Получаем хеш URL-а
  * @param	string	$string
  * @return	integer
*/
    function hashUrl($string) {
        $check1 = $this->strToNum($string, 0x1505, 0x21);
	$check2 = $this->strToNum($string, 0, 0x1003F);
 
	$check1 >>= 2;
	$check1 = (($check1 >> 4) & 0x3FFFFC0 ) | ($check1 & 0x3F);
	$check1 = (($check1 >> 4) & 0x3FFC00 ) | ($check1 & 0x3FF);
	$check1 = (($check1 >> 4) & 0x3C000 ) | ($check1 & 0x3FFF);
 
	$T1 = (((($check1 & 0x3C0) << 4) | ($check1 & 0x3C)) <<2 ) | ($check2 & 0xF0F );
	$T2 = (((($check1 & 0xFFFFC000) << 4) | ($check1 & 0x3C00)) << 0xA) | ($check2 & 0xF0F0000 );
        return ($T1 | $T2);
     }
/**
  * Получаем чексум URL-а
  * @param	integer	$Hashnum	хеш URL-а
  * @return	integer
*/
    function checkHash($hashNum) {
      $checkByte = 0;
      $flag = 0;
      $hashStr = sprintf('%u', $hashNum) ;
      $length = strlen($hashStr);
      for ($i = $length - 1;  $i >= 0;  $i --) {
		$re = $hashStr{$i};
		if (1 === ($flag % 2)) {
			$re += $re;
			$re = (int)($re / 10) + ($re % 10);
		}
		$checkByte += $re;
		$flag ++;
	}
 
	$checkByte %= 10;
	if (0 !== $checkByte) {
		$checkByte = 10 - $checkByte;
		if (1 === ($flag % 2) ) {
			if (1 === ($checkByte % 2)) {
				$checkByte += 9;
			}
			$checkByte >>= 1;
		}
	}
 
	return '7' . $checkByte . $hashStr;
    }
 
/**
 * Получаем PR с одного из сайтов гугла "закосив" под мозиллу
 * @param	string	$url	URL страницы
 */
function getPageRank($url) {
	if ( ! preg_match('/^(http:\/\/)(.*)/i', $url)) {
		$url = 'http://' . $url;
	}
	$googlehost = 'toolbarqueries.google.com';
	$googleua = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.6) Gecko/20060728 Firefox/1.5';
 
	$ch = $this->checkHash($this->hashUrl($url));
	/**
	 * Используя сокеты, вероятность бана снижается до 0,
	 * темболее что едим только 30 первых символов ;)
	 */
	$fp = fsockopen($googlehost, 80, $errno, $errstr, 30);
	if ($fp) {
		$out = "GET /search?client=navclient-auto&ch=$ch&features=Rank&q=info:$url HTTP/1.1\r\n";
		$out .= "User-Agent: $googleua\r\n";
		$out .= "Host: $googlehost\r\n";
		$out .= "Connection: Close\r\n\r\n";
 
		fwrite($fp, $out);
 
		while ( ! feof($fp)) {
			$data = fgets($fp, 128);
			// ПР идёт в строке Rank_1:1:Х где Х наш ПР
			$pos = strpos($data, "Rank_");
			if($pos === false){} else{
				fclose($fp);
				$pr=substr($data, $pos + 9);
				$pr=trim($pr);
				$pr=str_replace("\n",'',$pr);
				return $pr ? $pr : '0';
			}
		}
		fclose($fp);
	}
}

Стыдно признаться, но я по сей день не приучил себя к использованию регулярных выражений, а ведь это МОЩЬ! Я четко осознаю факт. Чтобы устранить данный недостаток, необходима постоянная практика, поэтому парсинг данных в задаче СЕО-анализа — отличная возможность потренироваться в регулярках. Первая задача для регулярок — определение наличия сайта в каталогах DMOZ и YACA (яндекс-каталог).

    function getDMOZ($domain) {
      $url='http://www.dmoz.org/search?q='.$domain;
      $xml = $this->parent->getURL($url);
      if (!$xml) return 'n/a';
      $href = 'http://search.dmoz.org/cgi-bin/search?search='.$domain;
      $p=strpos($xml,'Open Directory Categories');
      if ($p===false) $p=strpos($xml,'Open Directory Sites');
      if ($p===false) return
        array('href'=>$href, 'txt' => "Нет");
      $xml = substr($xml, $p, strlen($xml) - $p);
      $regex_pattern = "/(.*?)<\/a>/i";
      preg_match_all($regex_pattern,$xml,$links);
      if (count($links[0])==0) return 'n/a';
      $ar = explode(':', strip_tags($links[4][0]));
      return
        array('href'=>$href, 'txt' => $ar[count($ar)-2].':'.$ar[count($ar)-1]);
    }

Вообще, для парсинга html-документов существует очень мощное средство в php — класс DOMDocument. На продакшн-сервере этого класса не оказалось, пришлось его устанавливать из портов. Напомню, на проде у меня на данный момент стоит FreeBSD. Компиляция порта php5-dom завершалась с ошибкой

Error: shared library "Aiksaurus-1.2.0" does not exist

Долго гуглил, но безрезультатно. Но проблема сама не уйдет, поэтому решил спросить у FreeBSD-комьюнити. Прикольный форум, чувствуется профессиональный подход, модерация на высшем уровне, правят даже орфографические ошибки. В общем, посоветовали мне там обновить дерево портов. Это оказалось не очень сложно, все что нужно — утилита csup.

Что еще интересного? Пожалуй вот еще что.  Попытавшись запустить анализ для сайта http://google.com наткнулся на то, что сервер вернул 301 Redirect — таким образом гугл перенаправляет пользователя на локализованую версию сайта для его региона (в нашем случае google.ru). Естетственно, никому не нужен анализ такого контента. Как же достичь целевой страницы? С помощью cURL это делается легко, нужно всего лишь добавить заголовок

  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);

Однако я стлокнулся с одним нюансом. Если в настройках php установлен safe_mode = On или переменная open_basedir, то опция CURLOPT_FOLLOWLOCATION не будет иметь эффекта. Что же тогда делать? Ответ прост — воспроизвести логику CURLOPT_FOLLOWLOCATION. Для этого нужно написать одну рекурсивную функцию. Вот она:

  private $curl_loops = 0;
  private $curl_max_loops = 20;
  function curl_redir_exec($ch)  {
    if ($this->curl_loops++ >= $this->curl_max_loops)   {
       $this->curl_loops = 0;
       return FALSE;
    }
    curl_setopt($ch, CURLOPT_HEADER, true);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    $data = curl_exec($ch);
    list($header, $data) = explode("\n\r", $data, 2);
    $this->http_hdr = explode("\n", $header);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    if ($http_code == 301 || $http_code == 302) {
      $matches = array();
      preg_match('/Location:(.*?)\n/', $header, $matches);
      $url = @parse_url(trim(array_pop($matches)));
      if (!$url)   {
          //couldn't process the url to redirect to
        $this->curl_loops = 0;
        return $data;
      }
      $last_url = parse_url(curl_getinfo($ch, CURLINFO_EFFECTIVE_URL));
      if (!$url['scheme'])
          $url['scheme'] = $last_url['scheme'];
      if (!$url['host'])
          $url['host'] = $last_url['host'];
      if (!$url['path'])
          $url['path'] = $last_url['path'];
      $new_url = $url['scheme'] . '://' . $url['host'] . $url['path'] . ($url['query']?'?'.$url['query']:'');
      $this->final_url = $new_url;
      curl_setopt($ch, CURLOPT_URL, $new_url);
      error_log('Redirecting to', $new_url);
      return $this->curl_redir_exec($ch);
    } else {
      $this->curl_loops=0;
      return $data;
    }
  }  
 
  function getUrl($url) {
    $this->curl_loops = 0;
    $this->curl_max_loops = 20;
    $this->final_url = $url;
    $ch = curl_init($url);
    $cont = $this->curl_redir_exec($ch);
    curl_close($ch);
    return $cont;
  }
Categories: Coding, SEO, Блог Tags: , , , ,
  1. Dimas
    8 June 2011 в 09:06 | #1

    Интересно, а когда начнётся размещение на страницах блога контекстной либо иной рекламы….
    Так сказать монетизация сайта….

  2. 8 June 2011 в 10:31 | #2

    Это хороший вопрос (:
    Я думал об этом, и, в принципе, прихожу к выводу, что ничего уже сейчас не мешает этого делать. Аудитория какая-то есть, пусть случайная, но все же для контекстной рекламы должно хватить. В Яндекс-Директ пока рановато, ибо у них есть странная политика — в контекстной рекламной сети могут участвовать только сайты с посещаемостью не ниже 300 за последний месяц. У Google AdSense нет таких ограничений, поэтому можно начать с нее. Было бы прикольно, если контекстной рекламой хотя бы окупался хостинг.

  3. Dimas
    9 June 2011 в 10:50 | #3

    Google AdSense это то что нужно сейчас.
    Пора набить на этом деле руку, а за одно и нас просвятить, что да как.
    В сети сейчас много различных статей на тему о монетизации и хочется проверить эту информацию на живом примере.

    Может я ошибаюсь, но Ваша статья о тв программе произвела буквально фурор на блоге. Было очень много отзывов в том числе и от меня. Не хотите продолжить усовершенствовать этот скрипт? Пара предложений в отзывах прозвучало.

    Хотелось бы почитать статьи в Вашем исполнении с примерами на тему о Яндекс.Картах Google.Map.
    Хотя мне кажется было бы интересней о OpenStreetMap так сказать о свободной карте со свободной лицензией. А то эти монополисты, я про Яндекс, совсем уже зажрались и хотят прибрать всё к своим рукам.

  4. 9 June 2011 в 22:26 | #4

    Начал изучать современные методы продвижения (последний раз СЕО занимался года два назад), наткнулся на одну интересную идейку. Сейчас занимаюсь ее реализацией. Как закончу, напишу статью и после этого займусь интеграцией Google AdSense. Просто не могу себя остановить, когда делаю что-то творческое))
    По поводу тв-скрипта… Да, можно развивать эту идею. Можно, например, оформить его, как плагин к разным популярным CMS (wordpress, drupal, joomla). Естественно, неплохо бы для начала облагородить по-всякому, например, добавить анонсы, да и вообще, причесать код. Извечная проблема — времени катастрофически не хватает…(((
    Эх… в наш век информатизации существует столько интересных вещей, которыми хотелось бы заняться. Жаль, что нельзя разорваться))
    Про геосервисы — несомненно интересная тема, я думаю, что у OpenStreetMap хорошие перспективы. Но лично у меня пока не возникает никаких идей по поводу того, что бы можно замутить полезного на сайте с использованием служб геолокации. По этому поводу только одна мысль мне недавно пришла в голову, во время поездки в Амстердам :)

  5. panteleich
    29 June 2011 в 15:35 | #5

    заметил в в Вашем сео-анализаторе некоторое противоречие. он определяет тИЦ и GooglePR домена. для тИЦ это может быть и не критично — он присваивается всему домену, но для PageRank — весьма. PR расчитывается на конкретную страницу. так что, может быть имеет смысл над этим подумать…

  6. 1 July 2011 в 14:24 | #6

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

  7. Андрей
    14 July 2013 в 20:37 | #7

    Здравствуйте. А не могли бы Вы скинуть скрипт seoscan полностью. Заранее благодарен.

Подписаться на комментарии по RSS