Парсеры. Пример парсера программы телепередач с Яндекса.

Не секрет, что для каждого сайта важен контент. Если это узкоспециализированный или комьюнити сайт, то контент для сайта пополняется администрацией и пользователями, однако, иногда требуется разместить информацию с других сайтов. Например, это может быть курс валюты, погода, гороскоп, новостная лента или телепрограмма… Как разместить подобную информацию у себя? Можно ли как-то автоматизировать процесс или каждый раз информацию придется забивать вручную?

Есть несколько способов.

1. Можно повесить информер.
Самый простой и самый «ленивый» способ — разместить на странице информер. Например, если это погода, то информер может быть либо с Яндекса, либо с Гисметео. Лично по моему мнению — это дурной тон и показывает на то, что программист либо ленивый, либо у него руки растут не как у всех :) Также вместе с информером часто цепляют и ссылку на сайт. По сути — это является банером. То есть, вы сознательно ставите у себя на странице рекламу Яндекса взамен на показ погоды…

2. Вносить информацию вручную.
Например, если это лента новостей или гороскоп, то можно раз в сутки заходить на какие-либо сайты, копировать оттуда информацию и вставлять ее к себе на страницу. Хм… По-моему накладно, особенно если это телепрограмма или курсы валют. Курс валют за день может поменяться несколько раз, а чтобы внести телепрограмму, потребуется довольно много времени. То есть, придется убивать по часу (а то и больше) в день, заполняя контент.

3. Автоматическое заполнение, парсер.
Этот способ является самым сложным (на мой взгляд) для реализации, однако самым простым и надежным в использовании методом. Основная идея парсера (в данном контексте можно также называть граббером) заключается в том, что в определенный момент времени запускается небольшой скрипт, который собирает информацию с нужных нам сайтов и заполнять контент автоматически. Быстро, весело и вкусно (с). Собственно об этом способе сейчас и пойдет речь.

Могут возникнуть вопросы: «А не является ли это плагиатом? А законно ли это?». Отвечу- это смотря как посмотреть и как вы ее будете использовать. Например, если это курс валюты — данная информация является законной и вполне свободно распространяется в интернете. Некоторые ресурсы, предоставляющие информеры, требуют только одного — ссылку на их сайт… Ну что ж, сделайте «Информация любезно предоставлена сайтом.ру». От вас не убудет, да и иногда это нужно для того, чтобы пользователь знал, что информация берется с доверенного сайта. А по поводу остального… Ну что могу сказать… Если официально — можно заключать договор, а если не официально, в манифесте хакера говорится, что информация должна распространяться свободно. В свою очередь, сразу замечу, что мы живем в стране, где на ЗАГСы подают в суд потому, что они использую болванки с маршем Мендильсона, а не лицензионные диски. Не удивлюсь, если скоро с уличных гитаристов начнут требовать мзду. Так что, думайте сами)

Небольшой FAQ.

Итак, для чего используют парсеры?
Причин может быть много, но на мой взгляд, основных — три:
1. Автоматическое наполнение сайта. Редко обновляемые сайты, как правило, быстро надоедают посетителям и сайты как-то нужно постоянно набивать информацией. Можно это, конечно, делать самому или нанять оператора, однако, если можно обойтись «малой кровью», почему бы этим не воспользоваться?
2. Показ актуальных данных. Например, это может быть прогноз погоды, который обновляется раз в сутки, а может быть индекс Доу-Джонса, который нужно обновлять довольно часто. Если его обновлять хотя бы раз в час, то делать это вручную уже очень накладно.
3. Сбор информации с нескольких сайтов в один. Например, если это новостные ленты, то почему бы не собрать все новости по данной категории с нескольких сайтов и поместить на один. Довольно удобно будет… Не нужно лазить по куче ресурсов, если все можно прочитать из одного места.

Что из себя представляет парсер на php?
Это небольшой скрипт, которые при запросе заходит на определенную страницу в интернете, собирает с нее информацию, выбирает оттуда нужные данные (парсит) и сохраняет в удобном для нас виде для дальнейшего использования.

С каких страниц лучше брать информацию?
Вообще можно брать с любых, однако тут есть определенная грань. Лучше всего, конечно, брать информацию из XML, RSS или JSON файлов, если подобные формы есть. Если же нет, попробовать найти страницу для печати. Если и ее нет, ну что ж, тогда тяните информацию с полной странице. Объясню почему: во-первых, чем меньше лишней информации, тем быстрее будет работать парсер, а во-вторых, согласитесь, гораздо удобнее парсить информацию с типизированных файлов, в конце концов, на HTML страницах могут поменять дизайн или какой-либо тег, и парсер окажется нерабочим.

Приступим к технической реализации.

Допустим, мы хотим, чтобы у нас на сайте была программа передач определенных каналов. Ну, например, мы живем в Воронеже, почему бы нам не поместить на сайт телепрограмму местных каналов? Я решил, что сделаю это на примере Яндекса.

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

$day=12345; // Номер дня
$hour=5; // Час, с которого программа начинается и заканчивается. Лично я использую 5 часов. То есть, день у меня начинается с пяти утра. 
$period=24; // Период, за который выдается программа. 
$channel="1,717,434,4,5,235,104,255,591"; // Перечень каналов через запятую. Думаю, с этим проблем возникнуть не должно.
$url="http://tv.yandex.ru/index.xml?day=$day&hour=$hour&period=$period&channel=$channel&mode=print";


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

function sock_get($path,$host){
  $req="GET ".$path." HTTP/1.0\r\n";
  $req.="Host: ".$host."\r\n";
  $req.="User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; ru:1.7a) Gecko/20040219\r\n";
  $req.="Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1\r\n";
  $req.="Accept-Language: en-us,en;q=0.7,ru;q=0.3\r\nAccept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7\r\n";
  $req.="Keep-Alive: 300\r\n";
  $req.="Connection: Keep-Alive\r\n\r\n";
  $soc = fsockopen($host,80, $errno, $errstr, 30);

  if($soc>0){
  	@fputs($soc,$req);
    $i=0;
    $res=array();
   while(!feof($soc)){
    $i++;
    $res[$i]=fgets($soc);
   }
   fclose($soc);
   if(!preg_match("/200/",$res[1]))return false;
   return $res;
  }else{
   return false;
   die("Cannot connect to server.");
  };
 }

Почему именно сокет? Привык как-то, да и cURL не всегда у хостера установлен.

Теперь внимание, основная функция. Из полученного массива строк мы выберем все про телепередачи и на выходе получим массив.

function parse_tv_prog_list($data){
  if(!$data)return array();
  $prog_list=array();
  $day=floor(time()/86400);
  for($i=1;$i<count($data);$i++){
   // Если потребуется, переведите строку в нужную кодировку. По умолчанию, Яндекс отдает в UTF-8
   // $data[$i]=utf_to_win($data[$i]); 
   if(strpos($data[$i],'table width="100%" border="0" cellspacing="0" cellpadding="10" class="channel">')){
		$channel_id='';
		$channel_name='';
                // Далее смотрим в HTML код и с помощью RegEx достаем информацию
   		if(preg_match('/<img src="\/i\/logo\/([0-9]+).gif" border="0" alt="[^"]+">
<b>([^<]+)<\/b>

/',$data[$i+2],$m))
   			{$channel_id=$m[1];$channel_name=$m[2];$i+=2;} // Сперва выбираем канал
		else die("<b>!!!БЫЛА СМЕНА ДИЗАЙНА!!!</b>");
		if(!empty($channel_id)){
			$i+=3;
			$sch=0; 
   			Далее парсим программы по отдельности.
			while (strpos($data[$i],"/td>")==0 ){
				if(preg_match("/<div>([0-9]{1,2}\:[0-9]{1,2})".chr(160)."(<b>){0,1}([^<]+)<\/(b|div)>/",$data[$i],$m)){
					if($sch==0){
						if($m[1]=='5:00')  $prog_list[]=array('channel_id'=>$channel_id,'channel_name'=>$channel_name,'time'=>$m[1],'prog_name'=>$m[3],'num'=>$sch);
					}else {
						$prog_list[]=array('channel_id'=>$channel_id,'channel_name'=>$channel_name,'time'=>$m[1],'prog_name'=>$m[3],'num'=>$sch);
					}
					$sch++;
				}
				
				$i++;
			}
			
		}
   }
  }

  return($prog_list);
 } 

Обращаю внимание на то, что я тут использовал дополнительный счетчик $sch. Так как программы у нас начинаются с пяти утра, то по дате их упорядочить не получиться. А вот по счетчику — уже другое дело. Проверить массив можно оператором print_r($array).

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

$hour=5; 
$period=24;
$channel="1,717,434,4,5,235,104,255,591";

for($x=0;$x<7;$x++){
    $day = floor((time() + $x * (24*60*60))/86400); // Вычисляем день
    

    $data=parse_tv_prog_list(sock_get('/?day='.$day.'&hour='.$hour.'&period='.$period.'&channel='.$channel.'&mode=print','tv.yandex.ru')); // Парсим
	if(!empty($data)){
		// Удаляем старую программу за этот день
		$mysql->delete('tv',"day='$day'");
		$cur_hour=0;
		$cur_day=0;
		$cur_channel=0;
		for($i=0;$i<count($data);$i++){
			
			//Так как у нас смещение в пять часов, делаем корректировку перехода на другую дату в полночь
			$p_hour=intval(substr($data[$i]['time'],0,strpos($data[$i]['time'],':')));
			if($data[$i]['channel_id']!=$cur_channel){
				$cur_channel=$data[$i]['channel_id'];
				$cur_hour=$p_hour;
				$cur_day=$day;
			}else{
				if($cur_hour>$p_hour) $cur_day++;
				$cur_hour=$p_hour;
			}
			//------------------------------
			$mysql->insert('tv',array(
				'channel_id'	=>	$data[$i]['channel_id'],
				'channel_name'	=>	$data[$i]['channel_name'],
				'prog_name'		=>	$data[$i]['prog_name'],
				'day'			=>	$day,
				'time_hours'	=>	substr($data[$i]['time'],0,strpos($data[$i]['time'],':')),
				'time_minutes'	=>	substr($data[$i]['time'],strpos($data[$i]['time'],':')+1),
				'num'		=>	$data[$i]['num'],
			));
		}
		echo 'Программа на '.date('Y-m-d',$day*24*60*60).'. Вкатилось программ:'.count($data).'
';
	}else echo 'Ошибка вкатки. Значение пусто. Дата: '.date('Y-m-d',$day*24*60*60).'
';
}

Тут я использовал свой класс для работы mysql. Ссылка на статью.

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

На этом все, до новых статей)
  • 0
  • 20 декабря 2009, 01:44
  • AgentSIB

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

RSS свернуть / развернуть
+
0
Спасибо за статью, очень заинтересовала. Но с реализацией возникли проблемы. Ход мыслей я уловил, но при запуске кода высвечивается ошибка, может что-то делаю не так, объясните?
avatar

Alexandr78

  • 28 мая 2011, 13:58
+
0
Этот код писался давно и в качетсве примера. Насколько я знаю уже несколько раз поменялись в дизайне некоторые вещи, так что втягивать уже скрипт ничего не будет. Однако на основе этой идее можно реализовать более стабильный парсер.

simplehtmldom.sourceforge.net/ — вот очень хорошее решение, которое поможет минимизировать зависимость от дизайна. Однако будьте с ним осторожны, ибо памяти пожирает немеренно.
avatar

AgentSIB

  • 28 мая 2011, 14:15

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