Перейти до вмісту
Пошук в
  • Детальніше...
Шукати результати, які ...
Шукати результати в ...

Дубли страниц категорий. Модуль 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, но каких-либо решений или подсказок  в сети не нашёл

Змінено користувачем andrus
Надіслати
Поділитися на інших сайтах


 

подсказка:

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_]);

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

_________________________________________

Змінено користувачем andrus
Надіслати
Поділитися на інших сайтах


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

 

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

Надіслати
Поділитися на інших сайтах

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

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

Змінено користувачем andrus
Надіслати
Поділитися на інших сайтах


Не смог разобраться со своею проблемой. Сам код этого файла "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))));
		}
	}
?>

Змінено користувачем andrus
Надіслати
Поділитися на інших сайтах


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

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

Надіслати
Поділитися на інших сайтах

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

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

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

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

Змінено користувачем andrus
Надіслати
Поділитися на інших сайтах


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

Печально.

Надіслати
Поділитися на інших сайтах


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

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

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

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

 

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

Надіслати
Поділитися на інших сайтах

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

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

 

 

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

 

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

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

Надіслати
Поділитися на інших сайтах

 

.....

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

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

  

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

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

 

Змінено користувачем andrus
Надіслати
Поділитися на інших сайтах


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

Надіслати
Поділитися на інших сайтах

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

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

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

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

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

Надіслати
Поділитися на інших сайтах

  • 5 months later...

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

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

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

 

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

Надіслати
Поділитися на інших сайтах


  • 2 months later...

Створіть аккаунт або увійдіть для коментування

Ви повинні бути користувачем, щоб залишити коментар

Створити обліковий запис

Зареєструйтеся для отримання облікового запису. Це просто!

Зареєструвати аккаунт

Вхід

Уже зареєстровані? Увійдіть тут.

Вхід зараз
  • Зараз на сторінці   0 користувачів

    • Ні користувачів, які переглядиють цю сторінку
×
×
  • Створити...

Important Information

На нашому сайті використовуються файли cookie і відбувається обробка деяких персональних даних користувачів, щоб поліпшити користувальницький інтерфейс. Щоб дізнатися для чого і які персональні дані ми обробляємо перейдіть за посиланням . Якщо Ви натиснете «Я даю згоду», це означає, що Ви розумієте і приймаєте всі умови, зазначені в цьому Повідомленні про конфіденційність.