Jump to content
Sign in to follow this  
andrus

Дубли страниц категорий. Модуль SeoPro

Recommended Posts

Доброго времени всем!

При сканировании магазина прогой Xenu обнаружилось ряд страниц, многое дублирующее. Большинство проблем устранил через настройки в админке и в robots/.htaccess Но есть страницы, дублирующие категории. Причём самым наглым образом - в конец ссылки категории ставим alias бренда(производителя) и получаем дубль страницы категории любой. Как пример: 

вот тут структура ссылок, когда всё нормально - 

myshop.com/category -  основная категория

myshop.com/category/podcat/ - адрес подкатегории 

 

а вот дубли тех же самых страниц что выше, но с приставкой в конце урла алиаса на бренд(производитель) - 
 

myshop.com/category/brand -  основная категория дубль 

myshop.com/category/podcat/brand  - адрес подкатегории дубль

Т.е. не важно какая категория, не важно какой в конец урла ставить алиас имеющего на сайте бренда, но получается 100500 комбинаций, если подставлять ко всем категориям все бренды по очереди. Здесь однозначно что-то надо изменить в файле seo_pro.php, но каких-либо решений или подсказок  в сети не нашёл

Edited by andrus

Share this post


Link to post
Share on other sites

подсказка:

isset($this->request->get['manufacturer_id'])

Share this post


Link to post
Share on other sites

 

подсказка:

isset($this->request->get['manufacturer_id'])

Благодарю за наводку, но не без вопроса тут же: "Удалить это место из кода, или изменить код исполнения условия? Если да, то какое нужно прописать .." 

___________________________________

........................

} elseif (isset($this->request->get['path'])) {
$this->request->get['route'] = 'product/category';
} elseif (isset($this->request->get['manufacturer_id'])) {
$this->request->get['route'] = 'product/manufacturer/info';
} elseif (isset($this->request->get['information_id'])) {
$this->request->get['route'] = 'information/information';
} elseif(isset($this->cache_data['queries'][$route_])) {
header($this->request->server['SERVER_PROTOCOL'] . ' 301 Moved Permanently');
$this->response->redirect($this->cache_data['queries'][$route_]);

..........................

_________________________________________

Edited by andrus

Share this post


Link to post
Share on other sites

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

 

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

Share this post


Link to post
Share on other sites

Otvet, совершенно верно ,что сложнее будет сделать. Покопался уже полдня, чего только не придумывал.. в итоге не достиг желаемого.
Пока пауза, так как времени нет вообще, во многом не успеваю. А когда тебе особо не понять что там и куда в этом файле "seo_pro.php" заворачивает, генерирует.. то тем более.

Просто собственно вопрос к заглянувшим сюда в тему -  "У вас также как описано выше? Что если приставить алиас бренда к любой категории или подкатегории, то получишь ссылку на эту же категорию, но с новым "урл" ?" Магазину уже как год, а я вот только на днях заметил подобный дуль, родом вообще из ниоткуда. :/ 

Edited by andrus

Share this post


Link to post
Share on other sites

Не смог разобраться со своею проблемой. Сам код этого файла "seo_pro.php" прикрепляю ниже. Что там и как редиректит с помощью 301, так и осталось неизвестным... :/ 

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

<?php
class ControllerCommonSeoPro extends Controller {
	private $cache_data = null;

	public function __construct($registry) {
		parent::__construct($registry);
		$this->cache_data = $this->cache->get('seo_pro');
		if (!$this->cache_data) {
			$query = $this->db->query("SELECT LOWER(`keyword`) as 'keyword', `query` FROM " . DB_PREFIX . "url_alias ORDER BY url_alias_id");
			$this->cache_data = array();
			foreach ($query->rows as $row) {
				if (isset($this->cache_data['keywords'][$row['keyword']])){
					$this->cache_data['keywords'][$row['query']] = $this->cache_data['keywords'][$row['keyword']];
					continue;
				}
				$this->cache_data['keywords'][$row['keyword']] = $row['query'];
				$this->cache_data['queries'][$row['query']] = $row['keyword'];
			}
			$this->cache->set('seo_pro', $this->cache_data);
		}
	}

	public function index() {

		// Add rewrite to url class
		if ($this->config->get('config_seo_url')) {
			$this->url->addRewrite($this);
		} else {
			return;
		}

		// Decode URL
		if (!isset($this->request->get['_route_'])) {
			$this->validate();
		} else {
			$route_ = $route = $this->request->get['_route_'];
			unset($this->request->get['_route_']);
			$parts = explode('/', trim(utf8_strtolower($route), '/'));
			list($last_part) = explode('.', array_pop($parts));
			array_push($parts, $last_part);

			$rows = array();
			foreach ($parts as $keyword) {
				if (isset($this->cache_data['keywords'][$keyword])) {
					$rows[] = array('keyword' => $keyword, 'query' => $this->cache_data['keywords'][$keyword]);
				}
			}

			if (isset($this->cache_data['keywords'][$route])){
				$keyword = $route;
				$parts = array($keyword);
				$rows = array(array('keyword' => $keyword, 'query' => $this->cache_data['keywords'][$keyword]));
			}

			if (count($rows) == sizeof($parts)) {
				$queries = array();
				foreach ($rows as $row) {
					$queries[utf8_strtolower($row['keyword'])] = $row['query'];
				}

				reset($parts);
				foreach ($parts as $part) {
					if(!isset($queries[$part])) return false;
					$url = explode('=', $queries[$part], 2);

					if ($url[0] == 'category_id') {
						if (!isset($this->request->get['path'])) {
							$this->request->get['path'] = $url[1];
						} else {
							$this->request->get['path'] .= '_' . $url[1];
						}
					} elseif (count($url) > 1) {
						$this->request->get[$url[0]] = $url[1];
					}
				}
			} else {
				$this->request->get['route'] = 'error/not_found';
			}

			if (isset($this->request->get['product_id'])) {
				$this->request->get['route'] = 'product/product';
				if (!isset($this->request->get['path'])) {
					$path = $this->getPathByProduct($this->request->get['product_id']);
					if ($path) $this->request->get['path'] = $path;
				}
			} elseif (isset($this->request->get['path'])) {
				$this->request->get['route'] = 'product/category';
			} elseif (isset($this->request->get['manufacturer_id'])) {
				$this->request->get['route'] = 'product/manufacturer/info';
			} elseif (isset($this->request->get['information_id'])) {
				$this->request->get['route'] = 'information/information';
			} elseif(isset($this->cache_data['queries'][$route_])) {
					header($this->request->server['SERVER_PROTOCOL'] . ' 301 Moved Permanently');
					$this->response->redirect($this->cache_data['queries'][$route_]);
			} else {
				if (isset($queries[$parts[0]])) {
					$this->request->get['route'] = $queries[$parts[0]];
				}
			}

			$this->validate();

			if (isset($this->request->get['route'])) {
				return new Action($this->request->get['route']);
			}
		}
	}

	public function rewrite($link) {
		if (!$this->config->get('config_seo_url')) return $link;

		$seo_url = '';

		$component = parse_url(str_replace('&', '&', $link));

		$data = array();
		parse_str($component['query'], $data);

		$route = $data['route'];
		unset($data['route']);

		switch ($route) {
			case 'product/product':
				if (isset($data['product_id'])) {
					$tmp = $data;
					$data = array();
					if ($this->config->get('config_seo_url_include_path')) {
						$data['path'] = $this->getPathByProduct($tmp['product_id']);
						if (!$data['path']) return $link;
					}
					$data['product_id'] = $tmp['product_id'];
					if (isset($tmp['tracking'])) {
						$data['tracking'] = $tmp['tracking'];
					}
				}
				break;

			case 'product/category':
				if (isset($data['path'])) {
					$category = explode('_', $data['path']);
					$category = end($category);
					$data['path'] = $this->getPathByCategory($category);
					if (!$data['path']) return $link;
				}
				break;

			case 'product/product/review':
			case 'information/information/agree':
				return $link;
				break;

			default:
				break;
		}

		if ($component['scheme'] == 'https') {
			$link = $this->config->get('config_ssl');
		} else {
			$link = $this->config->get('config_url');
		}

		$link .= 'index.php?route=' . $route;

		if (count($data)) {
			$link .= '&' . urldecode(http_build_query($data, '', '&'));
		}

		$queries = array();
		if(!in_array($route, array('product/search'))) {
			foreach($data as $key => $value) {
				switch($key) {
					case 'product_id':
					case 'manufacturer_id':
					case 'category_id':
					case 'information_id':
					case 'order_id':
						$queries[] = $key . '=' . $value;
						unset($data[$key]);
						$postfix = 1;
						break;

					case 'path':
						$categories = explode('_', $value);
						foreach($categories as $category) {
							$queries[] = 'category_id=' . $category;
						}
						unset($data[$key]);
						break;

					default:
						break;
				}
			}
		}

		if(empty($queries)) {
			$queries[] = $route;
		}

		$rows = array();
		foreach($queries as $query) {
			if(isset($this->cache_data['queries'][$query])) {
				$rows[] = array('query' => $query, 'keyword' => $this->cache_data['queries'][$query]);
			}
		}

		if(count($rows) == count($queries)) {
			$aliases = array();
			foreach($rows as $row) {
				$aliases[$row['query']] = $row['keyword'];
			}
			foreach($queries as $query) {
				$seo_url .= '/' . rawurlencode($aliases[$query]);
			}
		}

		if ($seo_url == '') return $link;

		$seo_url = trim($seo_url, '/');

		if ($component['scheme'] == 'https') {
			$seo_url = $this->config->get('config_ssl') . $seo_url;
		} else {
			$seo_url = $this->config->get('config_url') . $seo_url;
		}

		if (isset($postfix)) {
			$seo_url .= trim($this->config->get('config_seo_url_postfix'));
		} else {
			$seo_url .= '/';
		}

		if(substr($seo_url, -2) == '//') {
			$seo_url = substr($seo_url, 0, -1);
		}

		if (count($data)) {
			$seo_url .= '?' . urldecode(http_build_query($data, '', '&'));
		}

		return $seo_url;
	}

	private function getPathByProduct($product_id) {
		$product_id = (int)$product_id;
		if ($product_id < 1) return false;

		static $path = null;
		if (!isset($path)) {
			$path = $this->cache->get('product.seopath');
			if (!isset($path)) $path = array();
		}

		if (!isset($path[$product_id])) {
			$query = $this->db->query("SELECT category_id FROM " . DB_PREFIX . "product_to_category WHERE product_id = '" . $product_id . "' ORDER BY main_category DESC LIMIT 1");

			$path[$product_id] = $this->getPathByCategory($query->num_rows ? (int)$query->row['category_id'] : 0);

			$this->cache->set('product.seopath', $path);
		}

		return $path[$product_id];
	}

	private function getPathByCategory($category_id) {
		$category_id = (int)$category_id;
		if ($category_id < 1) return false;

		static $path = null;
		if (!isset($path)) {
			$path = $this->cache->get('category.seopath');
			if (!isset($path)) $path = array();
		}

		if (!isset($path[$category_id])) {
			$max_level = 10;

			$sql = "SELECT CONCAT_WS('_'";
			for ($i = $max_level-1; $i >= 0; --$i) {
				$sql .= ",t$i.category_id";
			}
			$sql .= ") AS path FROM " . DB_PREFIX . "category t0";
			for ($i = 1; $i < $max_level; ++$i) {
				$sql .= " LEFT JOIN " . DB_PREFIX . "category t$i ON (t$i.category_id = t" . ($i-1) . ".parent_id)";
			}
			$sql .= " WHERE t0.category_id = '" . $category_id . "'";

			$query = $this->db->query($sql);

			$path[$category_id] = $query->num_rows ? $query->row['path'] : false;

			$this->cache->set('category.seopath', $path);
		}

		return $path[$category_id];
	}

	private function validate() {
		if (isset($this->request->get['route']) && $this->request->get['route'] == 'error/not_found') {
			return;
		}
		if (ltrim($this->request->server['REQUEST_URI'], '/') =='sitemap.xml') {
			$this->request->get['route'] = 'feed/google_sitemap';
			return;
		}

		if(empty($this->request->get['route'])) {
			$this->request->get['route'] = 'common/home';
		}

		if (isset($this->request->server['HTTP_X_REQUESTED_WITH']) && strtolower($this->request->server['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
			return;
		}

		if (isset($this->request->server['HTTPS']) && (($this->request->server['HTTPS'] == 'on') || ($this->request->server['HTTPS'] == '1'))) {
			$config_ssl = substr($this->config->get('config_ssl'), 0, $this->strpos_offset('/', $this->config->get('config_ssl'), 3) + 1);
			$url = str_replace('&', '&', $config_ssl . ltrim($this->request->server['REQUEST_URI'], '/'));
			$seo = str_replace('&', '&', $this->url->link($this->request->get['route'], $this->getQueryString(array('route')), true));
		} else {
			$config_url = substr($this->config->get('config_url'), 0, $this->strpos_offset('/', $this->config->get('config_url'), 3) + 1);
			$url = str_replace('&', '&', $config_url . ltrim($this->request->server['REQUEST_URI'], '/'));
			$seo = str_replace('&', '&', $this->url->link($this->request->get['route'], $this->getQueryString(array('route')), false));
		}

		if (rawurldecode($url) != rawurldecode($seo)) {
			header($this->request->server['SERVER_PROTOCOL'] . ' 301 Moved Permanently');

			$this->response->redirect($seo);
		}
	}

	private function strpos_offset($needle, $haystack, $occurrence) {
		// explode the haystack
		$arr = explode($needle, $haystack);
		// check the needle is not out of bounds
		switch($occurrence) {
			case $occurrence == 0:
				return false;
			case $occurrence > max(array_keys($arr)):
				return false;
			default:
				return strlen(implode($needle, array_slice($arr, 0, $occurrence)));
		}
	}

	private function getQueryString($exclude = array()) {
		if (!is_array($exclude)) {
			$exclude = array();
			}

		return urldecode(http_build_query(array_diff_key($this->request->get, array_flip($exclude))));
		}
	}
?>

Edited by andrus

Share this post


Link to post
Share on other sites

В раздел платных услуг, как минимум час надо покопаться/поэкспериментировать

Я вот представляю куда примерно копать но тратить  кучу времени ни кто не станет просто так

Share this post


Link to post
Share on other sites

Otvet, Благодарю за участие в данной дискуссии.

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

Только вот следующее теперь постоянно будет интересовать: это именно в версии сборки ocStore 1.5.5.1.2 такой баг и недуг с этим SeoPRO, или такое на всех магазинах наблюдается. Но в тоже время никто на форуме данную проблему не поднял, возможно и всё нормально у других - не дублирует "алиас" производителя содержимое страницы категории.  Может нам переустановить движок OC на другую версию? Загадка, откуда такая ошибка появилась и была ли она изначально на сайте. 

п.с. Отпишитесь те, по желанию, кто если и заглянули в эту тему случайно.. как у вас на сайте с такими ссылками?))
 

Edited by andrus

Share this post


Link to post
Share on other sites

Так и есть, посмотрел еще у многих магазинах, которые тут представлены в разделе форума "магазины на опенкарте" - везде такая вот проблема. Не только у Вас.

Печально.

Share this post


Link to post
Share on other sites

Otvet, Благодарю за участие в данной дискуссии.

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

Только вот следующее теперь постоянно будет интересовать: это именно в версии сборки ocStore 1.5.5.1.2 такой баг и недуг с этим SeoPRO, или такое на всех магазинах наблюдается. Но в тоже время никто на форуме данную проблему не поднял, возможно и всё нормально у других - не дублирует "алиас" производителя содержимое страницы категории.  Может нам переустановить движок OC на другую версию? Загадка, откуда такая ошибка появилась и была ли она изначально на сайте. 

п.с. Отпишитесь те, по желанию, кто если и заглянули в эту тему случайно.. как у вас на сайте с такими ссылками?))

 

Таки да, и в 1.5.5.1.2 и в 2.1.0.1 присутствует. Проблема ли это? Скорее нет, ссылки такого вида вы создали сами. Сам себе их робот генерировать и индексировать не будет. Если это так беспокоит, а решения нет, выгрузите список категорий и брендов, и сцепите в экселе. Это займет 20 минут и вы получите длиннючий список ссылок, который можно всунуть в роботс.тхт или сделать редирект.

Share this post


Link to post
Share on other sites

ссылки такого вида вы создали сами. Сам себе их робот генерировать и индексировать не будет

именно так, уж не раз писалось тут

 

 

выгрузите список категорий и брендов ... сделать редирект.

 

только не редирект, проблема выливается в большую - тормоза

просто нужно резать то что генерирует

Share this post


Link to post
Share on other sites

 

.....

 ссылки такого вида вы создали сами. Сам себе их робот генерировать и индексировать не будет....

А вот совсем и не так. Я случайно заметил на 200 стр. в кабинете ЯндексВебмастера 8 ссылок вида myshop.com/category/podcat/brand Потом начал вбивать вариации разные в конец урл категорий, и получил доступные новые страницы с дублирующим содержимым.

  

Есть такая программа как "SiteMap Generator".. ну и "Xenu" тоже сюда же, которые можно сказать имитируют тот самый бот от Google и Яндекс. Если бы не эти инструменты, я бы никогда и не заметил такой баг в ocStore 1.5.5.1.2 с дублями категорий. "SiteMap Generator" такое сразу нагенерировал.  Возможно так и перекалашмативают спустя время, роботы ваши магазины. Находят те самые дубли и зашвыривают их однажды в индекс. А потом сидишь неделями и  листаешь то что есть в индексе дублированного из 1000 или 5000 товаров, категорий... в кабинете вебмастера. Рубишь всё это тегом "ноуиндекс" и "в роботс/хтацесс" 

​Но это же не выход,  надо убрать генерацию подобного... 

 

Edited by andrus

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

А вот совсем и не так. Я случайно заметил на 200 стр. в кабинете ЯндексВебмастера 8 ссылок вида myshop.com/category/podcat/brand Потом начал вбивать вариации разные в конец урл категорий, и получил доступные новые страницы с дублирующим содержимым.

Есть такая программа как "SiteMap Generator".. ну и "Xenu" тоже сюда же, которые можно сказать имитируют тот самый бот от Google и Яндекс. Если бы не эти инструменты, я бы никогда и не заметил такой баг в ocStore 1.5.5.1.2 с дублями категорий. "SiteMap Generator" такое сразу нагенерировал. Возможно так и перекалашмативают спустя время, роботы ваши магазины. Находят те самые дубли и зашвыривают их однажды в индекс. А потом сидишь неделями и листаешь то что есть в индексе дублированного из 1000 или 5000 товаров, категорий... в кабинете вебмастера. Рубишь всё это тегом "ноуиндекс" и "в роботс/хтацесс"

​Но это же не выход, надо убрать генерацию подобного...

Знаете сколько сайтов я сканировал? Никаких проблем нет, физически этих ссылок не существует в чистом движке. Ищите причину

Есть такая замечательная функция как показать ссылки на страницу

Share this post


Link to post
Share on other sites

Т.е. не важно какая категория, не важно какой в конец урла ставить алиас имеющего на сайте бренда, но получается 100500 комбинаций, если подставлять ко всем категориям все бренды по очереди. Здесь однозначно что-то надо изменить в файле seo_pro.php, но каких-либо решений или подсказок  в сети не нашёл.

Кроме того, дубли возможны в таких вариантах:

mysite.com/статья1/категория1

 

Не найдено решение?

Share this post


Link to post
Share on other sites

найдено!

заплатить за реализацию

 

категория/бренд я сделал в модуле

а статья/категория и статья/бренд не вижу смысла

Share this post


Link to post
Share on other sites
категория/бренд я сделал в модуле

что за модуль?

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
You are posting as a guest. If you have an account, please sign in.
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×

Important Information

On our site, cookies are used and personal data is processed to improve the user interface. To find out what and what personal data we are processing, please go to the link. If you click "I agree," it means that you understand and accept all the conditions specified in this Privacy Notice.