Cайт без доступов
По долгу службы на мои плечи легла задача перенести пачку(>100) статей с одного сайта заказчика на другой. Одно из условий было — переносить не только название и текст статьи, но и метатеги keywords и description. В итоге статьи должны были перекочевать в инфоблок битрикса.
В силу реализации сайта-источника на неком конструкторе сайтов сделать sql dump никак не представлялось возможным, т.к. все, что позволяла админка сего конструктора — выбрать шаблон, привязать домен и, как бонус, добавлять/редактировать контент. Не было там ни phpMyAdmin, ни ftp, ни какого-либо инструмента, позволяющего легким движением руки осуществить задуманное.
Реализация
Единственным разумным вариантом, пришедшим в голову, стал парсер. Вроде бы ничего сложного — парсим со
страниц со статьями содержимое нужных тэгов и сохраняем их в csv файл для последующего скармливания битриксу.
Для начала надо было получить ссылки на все статьи. Путь к статье был вида:
http://sitename.com/articles/article_id
Причем article_id были совершенно хаотичны(по крайней мере мне так показалось), и простым циклом с инкремируемым номером статьи весь нужный контент было не заполучить. Пришлось сначала парсить ссылки на статьи на страницах со списками этих самых статей. Благо таких страничек было ограниченное количество, да и запрос был следующего вида:
http://sitename.com/atricles/page/page_number
Тут все просто — перебираем странички в цикле, на каждом шаге инкремируя page_number.
for($num_page = 1; $num_page < $num_page_max; $num_page++){
$html = file_get_contents('http://sitename.com/articles/page/'.$num_page);
}
И тут на тебе сюрприз — функция file_get_contents ругается, ссылаясь на то, что сервер, с которого она пытается get_contents говорит 403.
file_get_contents 403
Пошел в гугл, первая пачка ссылок вела на stackoverflow, где моим многим товарищам по несчастью другие товарищи по этому поводу яростно советовали забить на этот глупый file_get_contents и использовать curl, ничуть не проливая свет на причины внезапной 403. Curl это конечно круто, но для него нужен соответствующий модуль, подключать который для кратковременной задачи было лениво. И тут один из товарищей пояснил, что правильно настроенный сервер рубает запросы, посланные без заголовков, а file_get_contents
по умолчанию заголовки не отправляет, а только пытается выдернуть нужный документ. По умолчанию.
Смотрим документацию php — в file_get_contents третьим параметром можно передавать некий context, созданный с помощью stream_context_create(). Смотрим эту создавалку контекстов — и вот он примерчик, как присобачить заголовки. В итоге получаем странички без всяких курлов, притворившись браузером:
$context = stream_context_create(array(
'http'=>array(
'method'=>"GET",
'header'=>"Accept: text/html,application/xhtml+xml,application/xmlrn" .
"Accept-Charset: ISO-8859-1,utf-8rn" .
"Accept-Encoding: gzip,deflate,sdchrn" .
"Accept-Language: ru-RU,ru;q=0.8rn",
'user_agent'=>"User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/535.11 (KHTML, like Gecko)rn"
)
));
for($num_page = 1; $num_page < $num_page_max; $num_page++){
$html = file_get_contents('http://sitename.com/articles/page/'.$num_page, FALCE, $context);
}
Парсинг html, просто, как дважды два
Страницы получили, теперь надо решить, как эти страницы обрабатывать — выдергивать нужные ссылки. Тупо
парсить регулярками это конечно круто, но хардкора не хотелось, хотелось чего то простого и приятного. Товарищ гугл подсказал, что есть такой проект, как simplehtmldom, созданный специально для удобного выдергивания нужного контента из html кода страниц. Повтыкал в документацию, скачал, подключил, сделал как в quick start, но то ли в силу невнимательного чтения документации, то ли из-за недостаточной степени прямоты рук, завести это чудо я так и не смог, и решил попробовать найти что-нибудь другое. И нашел.
Второй, более податливый для меня экземпляр назывался phpQuery — название говорит само за себя, можно дергать на php информацию из html с помощью JQuery подобного синтаксиса. Он оказался на порядок жирнее simplehtmldom, но оно определенно того стоило. То, что могло бы глазодробяще выглядеть на регулярках, с помощью этого чуда выглядело так:
$link_arr = array(); // сюда мы складываем адреса ссылок на статьи
for($num_page = 1; $num_page < 15; $num_page++){
$html = file_get_contents('http://sitename.com/articles/page/'.$num_page, FALSE, $context);
$pq = phpQuery::newDocumentHTML($html); // запускаем волшебные процессы PHPQuery, отдавая ему притянутую страницу
foreach ($pq->find('a.article-title') as $link){ // вот так просто, как в jquery, парсим со страницы все ссылки с классом .article_link
array_push($link_arr, pq($link)->attr('href')); // и выдергиваем из каждой ссылки ее адрес, заталкивая его в массив
}
}
Хвала, почет и уважуха авторам этой библиотеки.
Парсинг html 2, типы текстовых данных битрикса
Адреса всех статей получили. Теперь тянем каждую статью и выдергиваем ее название, текст, meta keyword и meta description страницы со статьей, попутно заталкивая все это в массив, из которого будем формировать будущий csv:
$csv_array = array();
foreach($link_arr as $val){
$html = file_get_contents($val, FALSE, $context);
$pq = phpQuery::newDocumentHTML($html);
$page_title = pq("title")->text();
$page_keywords = pq("meta[name=keywords]")->attr('content');
$page_description = pq("meta[name=description]")->attr('content');
$article_title = pq("article header h1")->text();
$article_text = pq(".article-content")->html();
array_push($csv_array, array($page_title, $page_keywords, $page_description, $article_title, $article_text, 'html'));
}
Внимательный читатель мог заметить, что среди элементов массива, помимо нужной информации присутствует совершенно неадекватно выглядящий элемент, который у всех статей является строкой 'html'. Ибо битрикс. Дело в том, что в тексте статей присутствовала разметка(), которую так же надо было сохранить, т.к. пять абзацев сплошняком — некрасиво, а вставлять тэги в нужные места сотне статей никому бы не захотелось.А если при импорте в инфоблок у csv файла не будет отдельного поля, указывающего битриксу тип данных(text или html), то по умолчанию все теги эта cms считает текстом, и выводит их текст, даже если в настройках инфоблока указать тип по умолчанию html.
Создание csv для импорта в битрикс
Теперь вся информация собрана и структурирована, осталось сделать из нее вменяемый csv файл:
$file = fopen("articles.csv", 'w');
function createCSV(&$vals, $key, $file) {
fputcsv($file, $vals, ';', '"');
}
array_walk($csv_array, 'createCSV', $file);
fclose($file);
Вот и конец, битрикс успешно скушал csv со статьями, заказчик доволен.
Автор: moveax3