В предыдущей статье был рассмотрен простейший вариант парсинга xml на php, теперь практика. Перенос xml в базу данных. Итак, первый рабочий вариант: Сначала стандартные включения шаблона PHP-Fusion 7-й версии. Если у вас другой движок, строки надо будет заменить на свои:
<?php require_once "maincore.php"; require_once THEMES."templates/header.php";
Создаём две таблицы в базе данных: для категорий и для самих товаров: $result = dbquery("CREATE TABLE IF NOT EXISTS ".$db_prefix."ozon_cats ( cat_id MEDIUMINT(8) UNSIGNED NOT NULL, cat_parentId MEDIUMINT(8) UNSIGNED NOT NULL, cat_name VARCHAR(100) NOT NULL DEFAULT '', PRIMARY KEY (cat_id), KEY cat_parentId (cat_parentId), KEY cat_name (cat_name) ) TYPE=MyISAM;");
$result = dbquery("CREATE TABLE IF NOT EXISTS ".$db_prefix."ozon_offers ( offer_id MEDIUMINT(8) UNSIGNED NOT NULL, offer_type VARCHAR(20) NOT NULL DEFAULT '', offer_available TINYINT(1) UNSIGNED NOT NULL DEFAULT '1', offer_url VARCHAR(100) NOT NULL DEFAULT '', offer_price MEDIUMINT(8) UNSIGNED NOT NULL, offer_currencyId VARCHAR(8) NOT NULL DEFAULT 'RUR', offer_categoryId MEDIUMINT(8) UNSIGNED NOT NULL, offer_picture VARCHAR(100) NOT NULL DEFAULT '', offer_delivery TINYINT(1) UNSIGNED NOT NULL DEFAULT '1', offer_author VARCHAR(100) NOT NULL DEFAULT '', offer_name VARCHAR(100) NOT NULL DEFAULT '', offer_publisher VARCHAR(100) NOT NULL DEFAULT '', offer_series VARCHAR(100) NOT NULL DEFAULT '', offer_year INT(4) UNSIGNED NOT NULL DEFAULT '2020', offer_page_extent INT(4) UNSIGNED NOT NULL DEFAULT '0', offer_table_of_contents VARCHAR(100) NOT NULL DEFAULT '', offer_description VARCHAR(1000) NOT NULL DEFAULT '', PRIMARY KEY (offer_id), KEY offer_categoryId (offer_categoryId), KEY offer_name (offer_name) ) TYPE=MyISAM;");
Объявляем массивы, в которые будем считывать данные из xml-каталога и из них же будем вставлять данные в базу:
$cats = array(); $offer = array();
Следующая функция нужна для очистки и инициализации массива, содержащего запись о товаре. Это нужно в связи с тем, что в описании возможны пропуски тегов, тогда как для записи в базу данных должны существовать все элементы массива:
function offercorrect(){ global $offers; if (!isset($offers['id']))$offers['id'] = ''; if (!isset($offers['type']))$offers['type'] = ''; if (!isset($offers['url']))$offers['url'] = ''; if (!isset($offers['price']))$offers['price'] = ''; if (!isset($offers['currencyId']))$offers['currencyId'] = ''; if (!isset($offers['categoryId']))$offers['categoryId'] = ''; if (!isset($offers['picture']))$offers['picture'] = ''; if (!isset($offers['delivery']))$offers['delivery'] = ''; if (!isset($offers['author']))$offers['author'] = ''; if (!isset($offers['name']))$offers['name'] = ''; if (!isset($offers['publisher']))$offers['publisher'] = ''; if (!isset($offers['series']))$offers['series'] = ''; if (!isset($offers['year']))$offers['year'] = ''; if (!isset($offers['page_extent']))$offers['page_extent'] = ''; if (!isset($offers['table_of_contents']))$offers['table_of_contents'] = ''; if (!isset($offers['description']))$offers['description'] = ''; }
Эта функция - обработчик открывающего тега. Для наглядности процесса оставлена строка вывода названия тега echo "<b>Element: $name</b><br />";. Нас интересуют только два тега: category и offer.
function startElement($parser, $name, $attrs) { echo "<b>Element: $name</b><br />"; global $table, $cats, $offers, $offer; if($table == 'offer')$offer = $name;
switch($name) { case 'category': $cats = $attrs; $table = $name; break; case 'offer': $offers = $attrs; $table = $name; break; default: echo "<br />"; } }
Следующая функция - обработчик закрывающего тега.
function endElement($parser, $name) { global $table, $cats, $offers, $db_prefix; switch($name) { case 'category': if (!isset($cats['parentId']))$cats['parentId'] = '0'; if (!isset($cats['name']))$cats['name'] = ''; $result = dbquery("INSERT IGNORE INTO ".$db_prefix."ozon_cats (cat_id, cat_parentId, cat_name) VALUES (".$cats['id'].", ".$cats['parentId'].", '".$cats['name']."')"); break; case 'offer': offercorrect(); $result = dbquery("INSERT IGNORE INTO ".$db_prefix."ozon_offers (offer_id, offer_type, offer_url, offer_price, offer_currencyId, offer_categoryId, offer_picture, offer_delivery, offer_author, offer_name, offer_publisher, offer_series, offer_year, offer_page_extent, offer_table_of_contents, offer_description) VALUES (".$offers['id'].", '".$offers['type']."', '".$offers['url']."', ".$offers['price'].", '".$offers['currencyId']."', ".$offers['categoryId'].", '".$offers['picture']."', ".$offers['delivery'].", '".$offers['author']."', '".$offers['name']."', '".$offers['publisher']."', '".$offers['series']."', ".$offers['year'].", ".$offers['page_extent'].", '".$offers['table_of_contents']."', '".$offers['description']."')"); break; default: echo "<br />"; }
Следующая функция обрабатывает то, что находится между открывающим и закрывающим тегами. То есть сохраняет эти значения в массиве. Те значения, которые находятся в атрибутах, к этому моменту уже сохранены обработчиком открывающего тэга:
function stringElement($parser, $str) { global $table, $cats, $offers, $offer; if (strlen(trim($str)) > 0) { $str = iconv('utf-8','windows-1251',$str); $str = addslash($str); if($table == 'offer'){ if(isset($offers[$offer])){ $offers[$offer] = $offers[$offer].$str; }else{ $offers[$offer] = $str; } } if ($table == 'category'){ $cats['name'] = $str;
} echo $offers[$offer]; } }
$xml_parser = xml_parser_create(); xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 0); xml_set_element_handler($xml_parser, "startElement", "endElement"); xml_set_character_data_handler($xml_parser, "stringElement");
Назначаем входной файл, открываем его и парсим:
$file = "data/b12257.xml";
if (!($fp = fopen($file, "r"))) { die("could not open XML input"); }
while ($data = fgets($fp)) { if (!xml_parse($xml_parser, $data, feof($fp))) { echo "<br>XML Error: "; echo xml_error_string(xml_get_error_code($xml_parser)); echo " at line ".xml_get_current_line_number($xml_parser); break; } }
xml_parser_free($xml_parser);
Подключение файла шаблона (только php-fusion 7). Если у вас другой движок, строки надо будет заменить на свои:
require_once THEMES."templates/footer.php"; ?>
Этот скрипт реально работает, только почему-то на "денвере" довольно медленно. Пять тысяч записей он обрабатывал около двух часов. На хостинге, далеко не самом быстром, обработка прошла за несколько минут. Можно вводить записи частями. Можно добавлять записи. Будут добавляться записи только с новыми идентификаторами. Если запись с данным идентификатором уже существует, она добавляться не будет, старая запись остаётся, её содержимое новыми значениями не заменяется. То есть, если вы отредактируете запись в базе, она сохранится. Следующая статья - как выводить товары на страницах своего сайта. Дополнение: В PHP-5 есть более удобные способы извлечения данных из xml-файлов. Приведу пример.
Рассмотрите следующий XML-документ:
<clients> <client> <name>John Doe</name> <account_number>87234838</account_number> </client> <client> <name>Janet Smith</name> <account_number>72384329</account_number> </client> </clients>
Следующий фрагмент кода печатает имя каждого клиента и номер его аккаунта:
<?php $clients = simplexml_load_file('clients.xml'); foreach ($clients->client as $client) { print "$client->name has account number $client->account_number "; } ?>
|