Пользователь
0,0
рейтинг
7 ноября 2012 в 18:01

Разработка → Работа с API Яндекс.Вебмастер из песочницы tutorial

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

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

Хотя документация по данному API на Яндексе существует, и даже снабжена примерами, лично я не смог «с разбегу» разобраться, что к чему. Поэтому, если вам также интересна эта тема, прошу под кат.

Начать свое знакомство с API Яндекс.Вебмастер нужно с регистрации на Яндексе. Когда мы уже авторизованы, мы можем зарегистрировать свое «Приложение». В дальнейшем оно нам потребуется, чтобы получать токены пользователей, сайты которых мы хотим мониторить. Зарегистрировать новое приложение можно здесь.

Немного про регистрацию.

  • Название приложения — пишите, что захотите, но лучше, чтобы пользователь, если вы его будете перенаправлять для авторизации, понимал, что это приложение действительно ваше, т.к. авторизовываясь через него, он отдает доступ к некоторым сервисам Яндекса.
  • Описание. По большей части, как мне кажется, это пункт для модераторов Яндекса. Я написал, что данное приложение буду использовать для собственных нужд в бэкофисе различных сайтов.
  • Права. Т.к. авторизация для всех сервисов API Яндекс может быть единой, перечислены они все. Для наших нужд по работе с API Вебмастера нужны следующие: «Добавлять сайты в сервис Яндекс.Вебмастер и получать информацию о статусе индексирования» и " Получать информацию о внешних ссылках на сайт (требуется идентификация)". Как и написано, для второго пункта требуется идентификация. О том, что это такое, чуть позже. Если вы хотите просто потестить сервис, можете пока не включать эту функцию.
  • Ссылку на иконку и ссылку на приложения можно пропустить.
  • Callback URI. Здесь нужно вписать тот адрес, на который Яндекс будет перебрасывать пользователя после авторизации. Именно на этом адресе мы будем получать токен. Токен, кстати говоря, выдается на 180 дней. Так что часто получать его не придется.
    Допустим, что урл, с которого мы отправляем пользователя на авторизацию — domen.ru/token, тогда вписываем в данное поле именно эту ссылку, чтобы получать токен тамже, откуда мы отправили пользователя.


Если вы не ставили галочку на втором пункте, то ваше приложение зарегистрировано и готово к использованию.
Список ваших приложений доступен здесь.
Зайдя в только что созданное, мы получаем «id приложения» и «пароль приложения», которые нам потребуются.

Если вы решили сразу получить доступ к ссылкам, т.е. отметили и второй пункт, вам нужно будет сделать следующее:
Скачать вот этот документ, распечатать его, заполнить, отсканировать и выслать на webmaster-api@yandex-team.ru. У меня приложение прошло модерацию через 4-5 дней.

Приступаем к работе.

Приведу пример на PHP, т.к. у меня все работает именно на нем.

$client_id = "здесь вставляем Id приложения";
$client_secret = "здесь пароль приложения";

// Если мы еще не получили разрешения от пользователя, отправляем его на страницу для его получения
// В урл мы также можем вставить переменную state, которую можем использовать для собственных нужд, я не стал
if (!isset($_GET["code"])) {
	Header("Location: https://oauth.yandex.ru/authorize?response_type=code&client_id=".$client_id);
	die();
	}

// Если пользователь нажимает "Разрешить" на странице подтверждения, он приходит обратно к нам
// $_Get["code"] будет содержать код для получения токена. Код действителен в течении часа.
// Теперь у нас есть разрешение и его код, можем отправлять запрос на токен.

$result=postKeys("https://oauth.yandex.ru/token",
	array(
		'grant_type'=> 'authorization_code', // тип авторизации
		'code'=> $_GET["code"], // наш полученный код
		'client_id'=>$client_id,
		'client_secret'=>$client_secret
		),
	array('Content-type: application/x-www-form-urlencoded')
	);

// отправляем запрос курлом

function postKeys($url,$peremen,$headers) {
	$post_arr=array();
	foreach ($peremen as $key=>$value) {
		$post_arr[]=$key."=".$value;
		}
	$data=implode('&',$post_arr);
	
	$handle=curl_init();
	curl_setopt($handle, CURLOPT_URL, $url);
	curl_setopt($handle, CURLOPT_HTTPHEADER, $headers);
	curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false);
	curl_setopt($handle, CURLOPT_POST, true);
	curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
	curl_setopt($handle, CURLOPT_POSTFIELDS, $data);
	$response=curl_exec($handle);
	$code=curl_getinfo($handle, CURLINFO_HTTP_CODE);
	return array("code"=>$code,"response"=>$response);
	}

// после получения ответа, проверяем на код 200, и если все хорошо, то у нас есть токен

if ($result["code"]==200) {
	$result["response"]=json_decode($result["response"],true);
	$token=$result["response"]["access_token"];
	echo $token;
	}else{
	echo "Какая-то фигня! Код: ".$result["code"];
	}

// Токен можно кинуть в базу, связав с пользователем, например, а за пару дней до конца токена напомнить, чтобы обновил



Итак, токен у нас есть. Теперь мы можем заняться получением информации о состоянии сайтов.
Я это покажу в несколько этапов, как-будто мы не имели доступа к какой-либо информации ранее.

$token="наш полученный токен";

// функция, для курления

function get_stat($url,$headers) {
	$handle=curl_init();
	curl_setopt($handle, CURLOPT_URL, $url);
	curl_setopt($handle, CURLOPT_HTTPHEADER, $headers);
	curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
	curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false);
	curl_setopt($handle, CURLOPT_RETURNTRANSFER, true);
	$response=curl_exec($handle);
	$code=curl_getinfo($handle, CURLINFO_HTTP_CODE);
	return array("code"=>$code,"response"=>$response);
	}

// при получении результатов, вы можете отслеживать код ответа по $result["code"]

// первый запрос - получение id пользователя по Яндексу.
// В ответ нам придет ссылка типа https://webmaster.yandex.ru/api/123456789, 123456789 - id пользователя
// Можете сохранить себе как сразу ссылку, так и id юзера отдельно

$result=get_stat('https://webmaster.yandex.ru/api/me',array('Authorization: OAuth '.$token));
$user_id=str_replace('https://webmaster.yandex.ru/api/','',$result["response"]);

// далее мы можем запросить сервисный документ, но в чем его смысл я так и не понял.
// возвращается ссылка вида: href="https://webmaster.yandex.ru/api/123456789/hosts, 123456789 - id пользователя
// поэтому получать его не будем, сразу запросим список сайтов

$result=get_stat('https://webmaster.yandex.ru/api/'.$user_id.'/hosts',array('Authorization: OAuth '.$token));
$xml=new SimpleXMLElement($result["response"]);
$hosts_xml=$xml->xpath("host");
$hosts=array();
foreach($hosts_xml as $host) {
	$hosts[(string)$host->name]=
		array(
			"name"=>(string)$host->name,
			"verification_state"=>(string)$host->verification->attributes()->state,
			"crawling_state"=>(string)$host->crawling->attributes()->state,
			"virused"=>(string)$host->virused,
			"last-access"=>(string)$host->{'last-access'},
			"tcy"=>(string)$host->tcy,
			"url-count"=>(string)$host->{'url-count'},
			"index-count"=>(string)$host->{'index-count'},
			"href"=>(string)$host->attributes()->href
			);
	}
unset($hosts_xml);
unset($xml);

/* в результате у нас имеется двумерный массив со всеми сайтами и частью их характеристик
ссылку для доступа к сайту также можно хранить целиком, для удобства в будущем
Array (
	[domen] => Array ( 
		[name] => domen - доменное имя
		[verification_state] => VERIFIED - статус подтверждения прав на управление доменом
		[crawling_state] => INDEXED - статус индексирования
		[virused] => false - наличие обнаруженных вирусов на сайте
		[last-access] => 2012-11-06T22:54:10 - последний доступ робота к сайту
		[tcy] => 150 - ТИЦ
		[url-count] => 7458 - количество забранных урлов
		[index-count] => 6131 - количество урлов в индексе
		[href] => https://webmaster.yandex.ru/api/id пользователя/hosts/id сайта - ссылка для доступа к статистике сайта
		)
	)
*/
// пробуем запросить полную статистику по сайту
// в ответе придет xml, которая уже содержит информацию, полученную до этого, так что берем только то, чего у нас нет

$site_href="https://webmaster.yandex.ru/api/654321/hosts/123456"; // 654321 - user_id, 123456 - site_id
$result=get_stat($site_href."/stats",array('Authorization: OAuth '.$token));
$xml=new SimpleXMLElement($result["response"]);
$errors=(string)$xml->{'url-errors'}; // количество страниц с ошибками
$internal-links=(string)$xml->{'internal-links-count'}; // количество внутренних ссылок
$links=(string)$xml->{'links-count'}; // количество внешних входящих ссылок
unset($xml);


Также, мы можем получить информацию по проиндексированным и исключенным страницам, получив урлы, но, к сожалению, данные отдаются только «за прошедшую неделю», а это, можно сказать, отсутствие данных.
Получить эти данные вы сможете по запросам:

$result=curlGet($site_href."/indexed",array('Authorization: OAuth '.$token));
/*
Ответ формата:
<host>
  <index-count>238</index-count>
  <last-week-index-urls>
    <url>http://example.com/page1.html</url>
    <url>http://example.com/page2.html</url>
  </last-week-index-urls>
</host>
*/
$result=curlGet($site_href."/excluded",array('Authorization: OAuth '.$token));
/*
Ответ формата:
<host>
  <url-errors count="12">
    <url-errors-with-code code=”404”>
      <count>12</count>
      <severity>ERROR</severity>
    </url-errors-with-code>
  </url-errors>
</host>*/

Статусы и расшифровки ошибок смотреть здесь.

Также можно получать данные по внешним ссылкам на сайт, но и они максимально доступны в период «за неделю».

$result=curlGet($site_href."/links",array('Authorization: OAuth '.$token));
/*
Ответ формата:
<host>
  <links-count>1436</links-count>
  <last-week-links>
    <url>http://example1.com/page1.html</url>
    <url>http://example2.com/page2.html</url>
  </last-week-links>
</host>*/


Еще Яндекс отдает данные «о популярных запросах», но практика показывает, что по позициям там расхождение очень часто с действительным положением, и данные устаревшие. Так что я не стал даже заморачиваться на этот счет.

Самое главное, что я делаю — собираю раз в 12 часов статистику по всем своим сайтам. Все данные хранятся в базе, а следовательно я имею возможность не только видеть текущее положение дел, но и проводить анализ изменений.

И еще, чтобы не получать каждый раз все данные по сайту заново, лучше хранить ссылки на них в базе. Я имею ввиду $site_url. Если вдруг в API что-нибудь изменится, вы всегда сможете обновить эти ссылки.

Надеюсь, что эта информация была для вас интересной и полезной!
Автоматизируйте свою работу. Чем меньше мы заняты волокитой, тем больше у нас времени на творчество. И наших близких!
Дмитрий @Bigbad
карма
15,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (8)

  • +2
    curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, false);
    

    Вот за это я и люблю все руководства.
    Ребят, не нужно плевать на безопасность, а?
    • +3
      Не претендую на истину в последней инстанции, но как-то так, с проверками хоста и прочими плюшками, возможно, будет лучше:
      /**
       * Yandex API wrapper.
       * - First of all you need to register new console application @link https://oauth.yandex.ru/client/new
       *   with redirect_url https://oauth.yandex.ru/verification_code
       * - Next - take <access_code>:
       *   https://oauth.yandex.ru/authorize?response_type=code&client_id=<client_id>
       * - Use $this->getTokenByCode() to obtain access_token
       */
      class YandexApiBase
      {
          private $client_id;
          private $client_secret;
      
          public $access_token;
      
          public function __construct($token, $id=null, $secret=null)
          {
              $this->client_id = $id;
              $this->client_secret = $secret;
              $this->access_token = $token;
          }
      
          /**
           * @param string $code
           * @throws YandexApiException on fail
           */
          public function getTokenByCode($code)
          {
              $data = self::rawRequest('POST', 'https://oauth.yandex.ru/token', array(
                  'grant_type'=>'authorization_code',
                  'code'=>$code,
                  'client_id'=>$this->client_id,
                  'client_secret'=>$this->client_secret,
              ));
              if (!$data)
                  throw new YandexApiException("Can't take access_token by application code");
              $data = json_decode($data);
              if (isset($data['error']))
                  throw new YandexApiException("Shit happens: {$data['error']}");
              if (!isset($data['access_token']))
                  throw new YandexApiException("No errors, but token not send: ".CVarDumper::dumpAsString($data));
              return $data;
      
          }
      
          protected function request($method='GET', $url, $options = array())
          {
              $url .= (strpos($url, '?')===false ? '?' : '&') . http_build_query(array('oauth_token'=>$this->access_token));
              return json_decode(self::rawRequest($method, $url, $options), true);
          }
      
          /**
           * Send request with token
           * @param $url
           * @param array $options
           * @param string $method
           */
          protected static function rawRequest($method='GET', $url, $options = array())
          {
              //Default options for all requests
              $curlOpt = array(
                  CURLOPT_RETURNTRANSFER => 1,
                  CURLOPT_FOLLOWLOCATION => 1,
                  CURLOPT_MAXREDIRS => 3,
      
                  CURLOPT_CONNECTTIMEOUT => 10,
                  CURLOPT_TIMEOUT => 20,
      
                  CURLOPT_SSL_VERIFYPEER => true,
                  CURLOPT_SSL_VERIFYHOST => 2,
                  CURLOPT_CAPATH => dirname(__FILE__).'/cert',
                  CURLOPT_CAINFO => dirname(__FILE__).'/cert/solid-cert.crt',
              );
      
              switch (strtoupper($method)){
                  case 'DELETE':
                      $curlOpt[CURLOPT_CUSTOMREQUEST] = "DELETE";
                  case 'GET':
                      if (!empty($options))
                          $url .= (strpos($url, '?')===false ? '?' : '&') . http_build_query($options);
                  break;
                  case 'PUT':
                      $body = http_build_query($options);
                      $fp = fopen('php://temp/maxmemory:256000', 'w');
                      if (!$fp)
                          throw new YandexApiException('Could not open temp memory data');
                      fwrite($fp, $body);
                      fseek($fp, 0);
                      $curlOpt[CURLOPT_PUT] = 1;
                      $curlOpt[CURLOPT_BINARYTRANSFER] = 1;
                      $curlOpt[CURLOPT_INFILE] = $fp; // file pointer
                      $curlOpt[CURLOPT_INFILESIZE] = strlen($body);
                  break;
                  case 'POST':
                      $curlOpt[CURLOPT_HTTPHEADER] = array('Content-Type: application/x-www-form-urlencoded; charset=UTF-8;');
                      $curlOpt[CURLOPT_POST] = true;
                      $curlOpt[CURLOPT_POSTFIELDS] = http_build_query($options);
                  break;
                  default:
                      throw new YandexApiException("Unsupported request method '$method'");
              }
      
              $curl = curl_init($url);
              curl_setopt_array($curl, $curlOpt);
              $return = curl_exec($curl);
              $err_no = curl_errno($curl);
              if ($err_no === 0) {
                  curl_close($curl);
                  return $return;
              } else {
                  $err_msg = curl_error($curl);
                  curl_close($curl);
                  throw new YandexApiException($err_msg, $err_no);
              }
          }
      
      }
      
      class YandexApiException extends Exception {}
      


      И совсем для ленивых (кто не хочет выдирать сертификаты с сайтов Яндекса):
      -----BEGIN CERTIFICATE-----
      MIICWjCCAcMCAgGlMA0GCSqGSIb3DQEBBAUAMHUxCzAJBgNVBAYTAlVTMRgwFgYD
      VQQKEw9HVEUgQ29ycG9yYXRpb24xJzAlBgNVBAsTHkdURSBDeWJlclRydXN0IFNv
      bHV0aW9ucywgSW5jLjEjMCEGA1UEAxMaR1RFIEN5YmVyVHJ1c3QgR2xvYmFsIFJv
      b3QwHhcNOTgwODEzMDAyOTAwWhcNMTgwODEzMjM1OTAwWjB1MQswCQYDVQQGEwJV
      UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
      cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
      b2JhbCBSb290MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVD6C28FCc6HrH
      iM3dFw4usJTQGz0O9pTAipTHBsiQl8i4ZBp6fmw8U+E3KHNgf7KXUwefU/ltWJTS
      r41tiGeA5u2ylc9yMcqlHHK6XALnZELn+aks1joNrI1CqiQBOeacPwGFVw1Yh0X4
      04Wqk2kmhXBIgD8SFcd5tB8FLztimQIDAQABMA0GCSqGSIb3DQEBBAUAA4GBAG3r
      GwnpXtlR22ciYaQqPEh346B8pt5zohQDhT37qw4wxYMWM4ETCJ57NE7fQMh017l9
      3PR2VX2bY1QY6fDq81yx2YtCHrnAlU66+tXifPVoYb+O7AWXX1uw16OFNMQkpw0P
      lZPvy5TYnh+dXIVtx6quTx8itc2VrbqnzPmrC3p/
      -----END CERTIFICATE-----
      -----BEGIN CERTIFICATE-----
      MIIEPjCCA6egAwIBAgIEBycT9TANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV
      UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU
      cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds
      b2JhbCBSb290MB4XDTA3MDExNzE1MTYyMFoXDTE0MDExNzE1MTU0NlowWzESMBAG
      CgmSJomT8ixkARkWAnJ1MRYwFAYKCZImiZPyLGQBGRYGeWFuZGV4MRIwEAYKCZIm
      iZPyLGQBGRYCbGQxGTAXBgNVBAMTEFlhbmRleEV4dGVybmFsQ0EwggEiMA0GCSqG
      SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDOf3icODyZl7NcIpHKuJQ5ol5X/roVHJiG
      0s2wu3vXYmyEYTFQLGM1qr/yjOAKnNtwpQMr98+quO7SWs8TvtxT+mf75lptRuD2
      JawD2Vrkqq/gv92L0lyg6vfmWgovWhGftKjy6S8LPTGhsypfPEvCjBzG3IcyIlUP
      S/4VIvk5hXLNFlvR9iPjMZ6Pfs1MfU+GwudBWkG4HefSTcrsJV4j/l/eORIkCc36
      yWWTJrCUTTigx50qeRjiH6Aq8UxEhaNNU6GROgEQyarDT0n78Zu4v8/S6bRBhL+q
      yDMTUDuXzLseDNr5i1w8g6NZ9XbvmMF4fl5SGAKKNtLFxfeDqsoXAgMBAAGjggFv
      MIIBazASBgNVHRMBAf8ECDAGAQH/AgEBMFMGA1UdIARMMEowSAYJKwYBBAGxPgEA
      MDswOQYIKwYBBQUHAgEWLWh0dHA6Ly93d3cucHVibGljLXRydXN0LmNvbS9DUFMv
      T21uaVJvb3QuaHRtbDAOBgNVHQ8BAf8EBAMCAYYwgYkGA1UdIwSBgTB/oXmkdzB1
      MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQL
      Ex5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBD
      eWJlclRydXN0IEdsb2JhbCBSb290ggIBpTBFBgNVHR8EPjA8MDqgOKA2hjRodHRw
      Oi8vd3d3LnB1YmxpYy10cnVzdC5jb20vY2dpLWJpbi9DUkwvMjAxOC9jZHAuY3Js
      MB0GA1UdDgQWBBTbQScwTxr1Wz6EVsjshZizUSwtJzANBgkqhkiG9w0BAQUFAAOB
      gQAZuNLEObDlHdW3QJbokq5ANrTp9/WLLdRONjFK0tPkHq5FjeyX4GgPVvAUTuQa
      ydC35nz7H+1SGZBp9F+pT9YnaNH6lKl7o8mXPOCznQYeIvGCgI4L1uv37QtBvbri
      B/I8h+FY/43FMjAnk9ciR1xgbARK4bUKZaPd9MdU+/TY7w==
      -----END CERTIFICATE-----
      
      • 0
        Чтобы не морочиться с сертификатами, можно использовать Automatically converted CA Certs from mozilla.org.
        • 0
          Качаем cacert.pem и скармливаем курлу через опцию CURLOPT_CAINFO.
      • 0
        А чем Вы сертификаты выдирали?
        • +1
          FireFox + блокнот =)
          Заходим на страницу oauth.yandex.ru/, открываем список сертификатов, сохраняем в файлики, файлы склеиваем.
          Либо поиграться с вытаскиванием через код:
              public function actionIndex($domain){
                  $context = stream_context_create (array("ssl" => array("capture_peer_cert" => true)));
                  $run = stream_socket_client("ssl://$domain:443", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context);
                  $cont = stream_context_get_params($run);
                  if (isset($cont["options"]["ssl"]["peer_certificate"])) {
                      openssl_x509_export($cont["options"]["ssl"]["peer_certificate"], $output);
                      return $output;
                  } else {
                      return false;
                  }
              }
          

          Но так достается только OAuth сертификат, дальше копать в сторону автоматического выдергивания всех не стал. быстрее FireFox + блокнот =)
          • 0
            Спасибо за код. Я как-то пробовал получать сертификаты при помощи openssl в консоли, но иногда этот способ подводил или у меня руки кривые :)
    • 0
      У меня это происходило из бэкофиса сайта, по этой причине не видел смысла в значении «1» или «2» для CURLOPT_SSL_VERIFYHOST и true для CURLOPT_SSL_VERIFYPEER.
      Но если делать это, как сервис «для всех», то согласен, что лучше подстраховаться.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.