Пакет сео-плюшек для ленивых менеджеров



В который раз убеждаюсь, что у программистов мысли рождаются одинаково) Честно, не этот пост послужил отправной точкой для создания нижеследующего пакета) Реально параллельно мысли шли)

Вообщем решил собрать по сусекам все что у меня есть полезного и нужного для СЕО оптимизации и объединить в один пакет.

Что мы умеем?

  • Проставлять alt длля картинок согласно шаблону, с порядковым номеро
  • Проставлять title для ссылок согласно содержанию, а если там пустота — ставить свой текст по шаблону, также с порядковым номером
  • Вытягивать код в одну строку (спасибо Agel_Nash), но оставлять нетронутым тэг pre (лично я не пользовался режением Евгения, именно из-за этого)
  • Убирать внешние ссылки, но оставлять возможность перехода по ним (не забываем в роботе прописать Disallow: /exit.php)
  • Убирать циклиечские ссылки (т.е.ссылки на самих себя) — где-то слышал, что это не зер гуд
  • Формировать ключевики из тэгов h1-h6,b,i,strong и докидывать получившиеся к основным

В принципе и не так много, но весьма полезно.

Минусы решения:
  • Т.к. идет постобработка, то увеличивается время генерации страницы. Хотя я разницы особо и не заметил, ибо там простые, по-сути, математические действия

Плюсы:
  • Установил и оно работает
  • Работает как со старыми, так и с новыми документами, не нужно ничего пересохранять
  • Смотрит не только в поле контента, а по всей странице. Поэтому дополняет «чанки, шаблоны и прочие телевизоры)»
  • Делает MODX реально лучшим движком для СЕО)



Установка:
Качаем отсель библиотеку simple_html_dom и кидаеи ее в папочку /assets/lib/ (нам нужен тольк один файлик — simple_html_dom.php), все остальное примеры и доки.
Создаем плагин, называем SEOPack, ставим события OnWebPagePrerender, OnPageNotFound
Вставляем нижеследующий код:

$error_page = 'exit.php'; //Уникальное название страницы, куда первоначально будем делать редирект при внешнем ресурсе 
	
	$e =&$modx->Event;
	
	if (!function_exists('compress_html')) { 
		function compress_html($compress)
		{
			// Подсмотренно у Agel Nash
			$compress = str_replace("\n", '', $compress);
			$compress = str_replace("\s", '', $compress);
			$compress = str_replace("\r", '', $compress);
			$compress = str_replace("\t", '', $compress);
			$compress = preg_replace('/(?:(?<=\>)|(?<=\/\>))\s+(?=\<\/?)/', '', $compress);
			$compress = preg_replace('/[\t\r]\s+/', ' ', $compress);
			$compress = preg_replace('/<!(--)([^\[|\|])^(<!-->.*<!--.*-->)/', '', $compress);
			$compress = preg_replace('/\/\*.*?\*\//', '', $compress);
			return preg_replace("#\\s+#ism"," ",$compress);
		}
	}
	
	if ($e->name=='OnWebPagePrerender')
	{
		
		
		
		$content = $modx->Event->params['documentOutput'];
		
		require_once MODX_BASE_PATH.'assets/lib/simple_html_dom.php';
		$html = new simple_html_dom();
		$html->load($content, false, null, -1, -1, true, true, DEFAULT_TARGET_CHARSET, false); 
		
		$title = $html->find('title',0);	
		$metaTitle = str_replace("'","'",$title->plaintext); // Чтобы не искать потом - заголовок страницы
		$imagealt = $metaTitle.'. Картинка '; //в конце подставляется порядковый номер начиная с 1
		$atitle = $metaTitle.' Ссылка '; //в конце подставляется порядковый номер начиная с 1
		
		//Проставляем тэг title для ссылок
		$links = $html->find("a"); 
		$ln = 1;	
		foreach ($links as $key => $link)
		{
			
			if (!$link->title)
			{
				if ($link->plaintext) 
				{
					
					$t = str_replace('"',"'",trim(mb_substr($link->plaintext,0,50)));
					if (strlen($t)<10)
					{
						$link->title = $atitle.' '.$ln;
						$ln++;
					}
					else $link->title = $t;
				}
				else 
				{
					$link->title = $atitle.' '.$ln;
					$ln++;
				}
			}
		}
		
		//Убираем циклические ссылки
		$url = substr($_SERVER['REQUEST_URI'], 1);
		
		if ($url)
		{
			$cu =  $html->find("a[href='".$url."']"); 
			foreach($cu as $u) $u->href = null;
		}
		
		//Закрываем внешние ссылки
		$outlink = $html->find("a[href^=http]");
		foreach($outlink as $ou) if (strpos($ou->href, MODX_SITE_URL)===false) $ou->href = MODX_SITE_URL.''.$error_page.'?url='.$ou->href;
		
		
		
		//Проставляем тэг alt для картинок	
		$imgs = $html->find("img"); 
		$ln = 1;	
		foreach ($imgs as $key => $img)
		{
			
			if (!$img->alt) 
			{
				$img->alt = $imagealt.' '.$ln;
				$ln++;
			}
			
		}
		
		//Генерим ключевики по тэгам
		
		$mk = $html->find('meta[name=keywords]',0);
		if (count($mk))		
		{
			$min = isset($min) ? $min : 4; //минимальное количество символов в слове при выборке
			$limit = isset($limit) ? $limit : 20; // сколько слов выводить

			$keywords = [];
			$keyws = $html->find("h1,h2,h3,h4,h5,h6,b,i,strong");
			foreach($keyws as $keyw) $keywords[]=$keyw->plaintext;

			$str = implode(',',$keywords);		
			$str = strip_tags($str);
			$str2 = preg_replace('/[^a-zA-Zа-яА-Я0-9\s]/ui', ' ',$str ); 
			$str2 = str_replace('  ',' ',$str2);
			$str2 = str_replace(PHP_EOL,' ',$str2);
			$arr = explode(' ',$str2);
			foreach($arr as $key=>$val) if ($val) $arr[$key] = mb_strtolower(trim($val),'utf-8');

			//Выбираем наиболее часто встречающиеся
			$array_words = array_count_values($arr);
			arsort($array_words);
			$out = array();
			foreach($array_words as $word => $val)
			{
				if (mb_strlen($word,'utf-8')>=$min) $out[]=$word;
				if (count($out)==$limit) break;
			}
			$mk->content = mb_substr($mk->content.','.implode(',',$out), 0, 200);
		}	
		
		// Хак для того, чтобы не сжимать тэг <pre>	
		$pre = $html->find("pre"); 
		if (count($pre)) 
		{
			$arr_pre = [];
			foreach ($pre as $p) $arr_pre[] = $p->outertext;
		}
		
		
		$content = $html->save();
		$html->clear();	
		
		$content = compress_html($content); //Вытягиваем в 1-у строку
		
		// Хак для того, чтобы не сжимать тэг <pre>
		if (count($pre)) 
		{	
			preg_match_all('~<pre(.*?)pre>~s', $content, $matches);
			foreach ($matches[0] as $key => $pre) $content = str_replace($pre,$arr_pre[$key],$content);
		}
		
		
		
		
		
		
		$modx->Event->output($content);
	}
	
	if ($e->name=='OnPageNotFound')
	{
		$q = $modx->db->escape($_REQUEST['q']);
		if (isset($_GET['url']) && (!empty($_GET['url'])) && ($q==$error_page))
		{
			$url = $_GET['url'];
			if (!preg_match('#(https?|ftp)://\S+[^\s.,>)\];\'\"!?]#i',$url)) 
			{
				exit ("<p>Неверный формат запроса! Проверьте URL!</p>");
			} 
			header("Location:$url");
			exit();
		}
	}	

Наслаждаемся результатом.

Да. Проверял уже на обновленных движках; на старых, у меня че-та не хотел срабатывать вывод на плагине OnWebPagePrerender, решил тогда echo $content; exit(); Ну это так, на всякий случай. Ну и если будут желающие задонатить, то как бы я не против)
Ну а так, предоставляю как есть. Будут всплывать баги — будем править. Если кому-то нужна какая-то тонка настройка — обращайтесь в личку.
Не реклама, но можете глянуть плагин в действии на моем сайте liber.pro

ВНИМАНИЕ! БЫЛ ОБНАРУЖЕН БАГ С 404 ошибкой! Кто ставил, обновите вызов функции в самом начале. Сорри, не заметил!

50 комментариев

avatar
приветствую как сие чудо работает с seo4evo?
avatar
четсно?) Хз. Как-то раз ставил я seo4evo, и че-то он у меня глючил напропалую. Я про него и забыл. Надо будет глянуть.
avatar
если вы думали о сео, то не рассматривали еще вариант от AMPx Accelerated Mobile Pages и SchemaWebPage Plugin for MODX Evolution от Nicola Lambathakis www.tattoocms.it/ если все это сдружить то будет именно лучшая CMF для сео )
avatar
seo4evo — подпиливал для себя, ну типа пангинации в титл + рандом дескрипшен для страниц пангинации в остальном просто гуд )
avatar
seo4evo это просто набор твшек с и правилами в mm_rules.

ничего полезного в нём нет, на 2017 год 90% из seo4evo уже не актуально. Разве что noindex.
avatar
не согласен что не актуально, есть возможность все это подпилить и работает на ура, тем более что все основные параметры для страницы это решает
Комментарий отредактирован 2017-03-18 08:05:47 пользователем DiTso
avatar
тв — не вариант, так как всё это хранить в базе, будет жесть. Если магазин допустим на 20 000 товаров. Вариант от Алексея намного лучше и генерит всё это дело исходя из стандартных полей + ничего не хранит в базе
avatar
пока не спорю, поставлю посмотрю, отпишу, просто интересовался как увидел решение
avatar
плюс ещё в том, что при смене допустим заголовка, или тегов в самом тексте — будет замена и в параметрах alt title…

что решает вопрос времени, так как 10 страничный сайт уйдет максимум час, то при 100 страницах уже напряги.
avatar
и исходя из этого prntscr.com/elemjw то можно сказать что это дополнение причем не плохое к seo4evo и зря вы так, что основные поля для страницы и генерация опенГраф не актуальна для страницы на 2017 год
avatar
не успел ответить)
avatar
seo4evo решало проблемы с head документа, shema и amp свои, решение Алексея допинывает то что было не гуд для страницы в body вот если все это объединить
avatar
эти несколько вариантов в одном — полное решение для автоматизации основных СЕО параметров
avatar
примерно так и будет, и без записи в базу. Не вижу смысла плодить данные в тв, которые могут быть обновлены, а это затраты на людские ресурсы. Такая себе автоматизация.

а основных полей для seo хватает с головой, pagetitle+longtitle+menutitle, при сложном макете можно добавить ещё 1 в твшку) не критично.
Комментарий отредактирован 2017-03-18 08:18:41 пользователем Fr3ddy
avatar
трабла в том что сео параметры должны быть регулируемые, h1, title, description да гуд на автомате, но не всегда, когда точится и пишется страница под СЕО жужно играть всеми параметрами, и если переспамишь или не допишешь то это отыграется на странице, хотя как Яша вон выкатил услугу — за ваши деньги поднимем страницу по позициям, то так гляди и все в коммерцию уйдет
avatar
Яша вон выкатил услугу — за ваши деньги поднимем страницу по позициям

Это где вы такое видели?
avatar
prnt.sc/ek8rxt вот но не для всех идет рассылка, хотя может и фейк просто.
Комментарий отредактирован 2017-03-19 04:55:22 пользователем DiTso
avatar
prnt.sc/ek8rxt вот но не для всех идет рассылка, хотя может и фейк просто.
Вы еще сомневаетесь? конечно фейк. Если бы это было правдой, то Яндекс перестал бы существовать как ПС наверное недели через две.
Комментарий отредактирован 2017-03-19 05:56:00 пользователем Harand
avatar
учитывая, коммерциализацию выдачи, введение дополнительных алгоритмов как многорукий бандит, персонализацию выдачи, запуск все новых и новых сервисов от Яндекса. Подсовывание ссылок как в советнике Яндекса. То от Яндекса можно ожидать всего ))
avatar
опять же сгенерировать все и записать или делать на лету, думаю перегенерация и запись в тв будет лучше как решение, но это «мои тараканы»
avatar
copyright
og:site_name (facebook)
og:type (facebook)
og:url (facebook)
og:image (facebook)
fb:app_id (facebook)
author (google+)
publisher (google+)

вот это интересно в сниппете на tattoocms.it, я думаю это можно сразу впилить в движок)
avatar
ну в движок не обязательно, а вот все объеденить это в пакет, было бы вообще гуд
avatar
я думаю при донате быстрее получиться)
буду первым!)
avatar
блин, сделаем-с… Но потом. Я проснулся спустя час чтобы допилить сие творение)
Пожелания услышал, на следующей недельке попилю.
avatar
маленький глюк с запятыми
prntscr.com/elqxsy

и думаю что вытягивание в строку нужно сделать переключаемым в настройках плагина, конечно можно удалить часть кода) но это варварство
avatar
Честно говоря, вообще не пойму, зачем нужно это самое вытягивание в строку.
avatar
Экономия на байтах… Скорее всего что так, насколько это актуально для сайтов визиток или лендингов понять не могу. Подозреваю что это может быть актуально для простыни в 4 27" монитора содержащих в основном только текст.
avatar
Гораздо больше экономии может дать сжатие. Так что не вижу смысла в этом.
avatar
уменьшение веса страниц, путём удаления пустых строк. Спорная штука ))
avatar
делал на одном сайте, который имел везде позиции 3-4-5. Никаких движения после перекеширования страниц как гуглом так и яндексом. Вернул обратно нормальный код, позиции выросли )
avatar
Во-во )
avatar
господа, так надо учитывать, что альты тоже считаются, например, это можно и добить в плане сео, использовать просто картинка и номер не совсем гуд, предлагаю, просто запилить общий пакет мое, предложение например включить это
if (isset($_GET['page'])) {
$dyndesc = preg_replace_callback('#\{(.*)\}#uU', function ($match){
$variants = explode('|', $match[1]);
return $variants[rand(0, count($variants) — 1)];
}, $dyndesc );
} else {
$dyndesc = preg_replace_callback('#\{(.*)\}#uU', function ($match){
$variants = explode('|', $match[1]);
return $variants[1];
}, $dyndesc );
}
Комментарий отредактирован 2017-03-20 15:59:59 пользователем DiTso
avatar
используем {мобила|сотовый телефон} это же будет гуд, делал на seo4evo
Комментарий отредактирован 2017-03-20 16:05:36 пользователем DiTso
avatar
получаем уникальное значение дескрипшен, добавляем к этому еще и в титл, номер страницы, и вот вам уникальное описание, а если еще и на выводе страницы запилить описание в боди то вообще в заходит в НЧ + к этому еще и по вешать на htracer то из 20-30 страниц 70-80 в индексе как полноценные, для нормального сайта думать надо, но для эдвордс, можно использовать
avatar
htracer — старенький валяется где то, если что в личку, но думаю кто работает, тот для себя запилит
avatar
Я про альты не говорил, только про вытягивание в строку. Альты и тайтлы и прочее вообще предпочитаю писать ручками, не так уж это и долго — если деньги за это платят, конечно ))
avatar
я говорил об автоматизации ))а так то да, когда платят то и писать можно, даже через биржи ))
avatar
Вот дополненный код:
cloud.mail.ru/public/D9cK/bDG7APpgz

На вкладку свойства добавить вот это:
{
  "compressed": [
    {
      "label": "Сжимать HTML?",
      "type": "list",
      "value": "true",
      "options": "true,false",
      "default": "true",
      "desc": ""
    }
  ]
}


И во вкладке конфигурация появится возможность отключать сжатие
avatar
вот за что я люблю .im, так это за то, что люди помогают развивать идеи, а не гонятся за рейтингом, обсирая всех вокруг) Спасибо!
avatar
что-то поломалось всё, не работает плагин адекватно )
avatar
С этого кода:
cloud.mail.ru/public/D9cK/bDG7APpgz — заметил что файл должен находжится в папке /assets/libs/, а не /assets/lib/ — так что либо подправить код либо перенести файл нужно!
avatar
phpQuery по шустрее чем simple_html_dom ИМХО
Встречался с задачей когда simple_html_dom на моём сервере попросту не мог распарсить таблицу в 2100 записей, кружилось это все конечно на виртуальном хостинге, но все же phpQuery справился на ура )
avatar
Если уж взялись за скорость бороться, то почему бы и не DOMDocument.
avatar
как вариант, но работать с ним крайне не удобно.
avatar
Наткнулся на данное решение! Очень заинтересовало, как раз нужно «Убирать внешние ссылки»
Создал плагин согласно инструкции, но почему-то не работает.
Возникло несколько вопросов, в первой строчке есть сразу же: файлик exit.php — нужно создавать? и что в нем писать или нужно создать новую страницу с редиректом, не совсем понятно.
нужно где-то плагин включить или запустить — возможно у меня из-за этого не работает!
avatar
$compress = str_replace("\n", '', $compress);
$compress = str_replace("\s", '', $compress);
$compress = str_replace("\r", '', $compress);
$compress = str_replace("\t", '', $compress);

а почему не через $compress = str_replace(array("\n","\s","\r","\t"), '', $compress);?
avatar
$str2 = str_replace(' ',' ',$str2);
$str2 = str_replace(PHP_EOL,' ',$str2);

не лучше ли их поменять местами? а то могут встречаться два пробела, если в конце строки слово было с пробелом.
и почему PHP_EOL?
Я бы переписал так:
$str2 = str_replace("\n",' ',$str2);
$str2 = str_replace("\r",' ',$str2);
$str2 = str_replace('  ',' ',$str2);
$str2 = str_replace('  ',' ',$str2);
Комментарий отредактирован 2017-04-07 17:31:54 пользователем zabudkin
avatar
PHP_EOL это переход каретки на другую строку.
На разных хостингах это осуществляется по разному
Windows — \r\n
Unix (Linux) — \n
Mac OS = \r
avatar
Плохо читаете документацию. PHP_EOL это для той платформы, где употребляется. НО! тажа винда 13+10, линух только 10 (причём иногда и только 13, например в бинде).
Так что мой код освобождает от гемороя!
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.