Продолжая тему парсеров. Одна из самых распространенных задач, которые я постоянно встречаю - заполнить Joomla сайт на основе другого сайта. Полный переезд, обычно с более старой версии Joomla, но быть может и с другой CMS, не важно.
Обычно, проще в ручную скопировать все тексты с сайта, чем писать отдельный граббер под это дело. Когда же дело касается нескольких сотен статей, мой внутренний прагматик запрещает мне работать руками и в дело вступает мозг.
В идеале парсер должен съедать лишь одну страницу сайта на входе и обходить все найденные страницы сам. В статье Как написать универсальный парсер сайтов за 1 час я уже рассказывал, как это сделать. Когда дело касается переезда на Joomla, то необходимо точно воссоздать структуру сайта донора, посему немного упростим задачу, и будем парсить лишь заданный список ссылок, который будет определенным образом привязан к конкретному разделу из сайта источника. А этот самый раздел на нашем сайте, создадим руками.
Т.е. в этой статье мы автоматизируем рутинные операции копипаста: копирование текста, создание статьи в материалах, создание ссылки на статью в меню.
К этим трем операциям можно еще добавить - автоматическое создание разделов сайта, на основе сайта донора. Однако, эту задачу я оставлю вам. Она не сложная, поверьте. Пишите в комментариях, как вы ее решаете.
Писать будем на php. Для начала нам потребуется функция парсер, которая будет загружать страницу сайта.
Тут ничего нового:
function get($url) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 0); curl_setopt($ch, CURLOPT_USERAGENT, 'User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30); curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1); //curl_setopt($ch, CURLOPT_PROXY, "111.111.111.111:8000"); //curl_setopt($ch, CURLOPT_PROXYUSERPWD, 'user:password'); //curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTPS); return iconv('windows-1251', 'UTF-8', curl_exec($ch)); }
В моей задаче, сайт источник был в кодировке windows-1251
, поэтому в последней строке я конвертирую кодировку. Вы можете возвращать разультат curl_exec($ch)
как есть, если у вас кодировка не меняется. Плюс, донор по какой-то причине заблокировал ip моего сервера, и пришлось использовать прокси сервер. Вы также можете раскомментировать 3 предпоследние строки и парсить сайт через прокси сервер.
Далее, нам нужно создать стандартный материал Joomla
function createArticle($title, $text, $category_id) { $jarticle = new stdClass(); $jarticle->title = $title; $jarticle->alias = JFilterOutput::stringURLSafe($jarticle->title); $jarticle->introtext = $text; $jarticle->state = 1; $jarticle->catid = $category_id; $jarticle->created = JFactory::getDate()->toSQL(); $jarticle->created_by = JFactory::getUser()->id; $jarticle->access = 1; $jarticle->metadata = '{"page_title":"","author":"Valera","robots":""}'; $jarticle->language = '*'; $table = JTable::getInstance('content', 'JTable'); $data = (array)$jarticle; // Bind data if (!$table->bind($data)) { //Handle the errors here however you like (log, display error message, etc.) var_dump($table->getError()); //return false; } // Check the data. if (!$table->check()) { //Handle the errors here however you like (log, display error message, etc.) var_dump($table->getError()); //return false; } // Store the data. if (!$table->store()) { //Handle the errors here however you like (log, display error message, etc.) var_dump($table->getError()); //return false; } return table->id; }
Это копипаст кода из интернета. Как видите если есть ошибка, она никак не обрабатывается, а просто выводится на экран. Если хотите, обработайте ошибки сами.
Функция возвращает идентификатор созданного материала. Он потребуется нам в следующей функции.
И последняя функция, которая нам потребуется - это функция создания элемента пункта меню:
function createItem($title, $article_id, $parent_id) { $menuItem = [ 'menutype' => 'mainmenu', 'title' => $title, 'alias' => JFilterOutput::stringURLSafe($title), 'type' => 'component', 'component_id' => 22, 'link' => 'index.php?option=com_content&view=article&id='.$id, 'language' => '*', 'published' => 1, 'parent_id' => $parent_id, 'params' => '{"show_title":"","link_titles":"","show_intro":"","info_block_position":"","info_block_show_title":"","show_category":"","link_category":"","show_parent_category":"","link_parent_category":"","show_author":"","link_author":"","show_create_date":"","show_modify_date":"","show_publish_date":"","show_item_navigation":"","show_vote":"","show_icons":"","show_print_icon":"","show_email_icon":"","show_hits":"","show_tags":"","show_noauth":"","urls_position":"","menu-anchor_title":"","menu-anchor_css":"","menu_image":"","menu_text":1,"menu_show":1,"page_title":"","show_page_heading":"","page_heading":"","pageclass_sfx":"","menu-meta_description":"","menu-meta_keywords":"","robots":"","secure":0}', 'level' => 1, ]; $menuTable = JTable::getInstance('Menu', 'JTable', array()); $menuTable->setLocation($parent_id, 'last-child'); // Bind data if (!$menuTable->bind($menuItem)) { var_dump($menuTable->getError()); //return false; } // Check the data. if (!$menuTable->check()) { var_dump($menuTable->getError()); // return false; } // Store the data. if (!$menuTable->store()) { var_dump($menuTable->getError()); //return false; } }
Это тоже, практически, копипаст из интернетов.
Для того, чтобы начать работу, нам потребуется создать категорию в материалах (параметр $category_id
для функции createArticle
.) И пункт в меню (параметр $parent_id
для функции createItem
.)
Имея все необходимые функции и идентификаторы раздела меню и категории, напишем наш парсер:
<?php define('_JEXEC', 1); define('JPATH_BASE', __DIR__); // replace to valid path require_once JPATH_BASE . '/includes/defines.php'; require_once JPATH_BASE . '/includes/framework.php'; $app = JFactory::getApplication('site'); $parent_id = 146; $category_id = 27; // полный код функций берите выше function get($url) {...} function createArticle($title, $text, $category_id) {...} function createItem($title, $article_id, $parent_id) {...} $links = [ 'http://sitename.ru/partnerstvo/obshchee-sobranie/', 'http://sitename.ru/partnerstvo/sovet-partnerstva/', 'http://sitename.ru/partnerstvo/ispolnitelnaya-direktsiya/', 'http://sitename.ru/partnerstvo/distsiplinarnyy-komitet/', ]; foreach ($links as $link) { $html = get($link); preg_match('#<title>(.*)</title>#uis', $html, $stitle); $title = $stitle[1]; preg_match('#<article>(.*)</article>#uis', $html, $sarticle); $article = str_replace(['"/upload'], ['"upload'], $sarticle[1]); $article_id = createArticle($title, $article, $category_id); createItem($title, $article_id, $parent_id); }
Разберем, что же тут происходит. Сам файл скрипта подключает стандартную точку входа Joomla, и даже инициализирует новое приложение. После этого вы можете использовать все методы и классы Joomla, также как бы вы это делали в компонентах или плагинах.
Далее по циклу, перебираем массив $links
и получаем содержимое заданной ссылки, при помощи функции get
. Далее идет непонятный кусок кода. Он специфичен для данного сайта, но будет иным для вас. Название статьи я беру просто из тега title
. В большинстве сайтов, в этом теге будет всякий мусор - название статьи - название сайта - ключевики
. Поэтому этот момент вам надо обработать самостоятельно.
Основной контент сайта, был заключен в тег article
и на 29-ой строке, я его оттуда изымаю.
Возможно Ваш сайт донор, окажется более сложным и я бы порекоменлдовал разбирать такие данные при помощи библиотеки SimpleHTMLDom
Вот собственно и все. Желаю вам успехов, пишите ваши вопросы в комментариях. С удовольствием отвечу на них.