[ Поиск ] - [ Пользователи ] - [ Календарь ]
Полная Версия: Парсинг XML для тех кто в танке + атрибуты
killich
Парсинг XML для тех кто в танке:
killich@yandex.ru

Добрый день!

Данную тему предлагаю тем, кто еще ни разу не встречался с XML парсингом. Сам только что с ним познакомился и хочу поделиться с тамими же как я чайниками тем, что у меня получается. Хочу надеятся, что им это поможет. Пусть профи не ругаются, если я чего не понимаю в XML и парсинге. К каждому утверждению, сделанному мною далее предлагаю делать добавку - мне так кажется. Ну, а если кажется, то перекрестимся и начнем... ;0)

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

XML это язык разметки, *цензура*ожий на HTML, но более обширный по своим пременениям. XML часто используют для передачи данных от одной системы к другой. Взаимодействие между разнородными системами, как правило, не определено, а потому потребовался обобщенный способ передачи данных. Под системами я, в основном, подразумеваю базы данных.

Для парсинга в PHP можно использовать встроенные функции:

$xml_parser = xml_parser_create();

xml_parser_set_option($xml_parser, НАЗВАНИЕ_ОПЦИИ, ЗНАЧЕНИЕ);
xml_set_element_handler($xml_parser, "FunctName1", "FunctName2");
xml_set_character_data_handler($xml_parser, "FunctName3");
xml_parse($xml_parser, $data, feof($fp))
xml_parser_free($xml_parser);

Если надо подробнее найдите эти ф-ии в доках.

Предположим, что некоторая система сгенерировала следующий файл с данными:

*** one.xml ***




Jhon Smith
Mega Book
123-456-87
fantastic


Will Duglas
Ha, Ha and Ha!
436-434-76
poems
13$


Klark Rodriges
it't simple book

tragedi
Good book


Иванов Аркадий
Книга
555-666-999
action
VERY Good book
15$



Вот это и есть XML файл. Даже не вооруженным глазом видно, как организованна информация.
Наша задача, разобрать этот файл, извлеч данные и поместить на web страницу.

Что бы на сайте что-то появилось нужен вот такой шаблон:

*** index.php ***




simple XML parser





// Имя файла, который будем читать
$file = "one.xml";

function startElement($parser, $name, $attrs)
{
// Эта ф-ия работает при встрече открывающего тега
}

function endElement($parser, $name)
{
// Эта ф-ия работает при встрече закрывающего тега
}

function characterData($parser, $data)
{
// Эта ф-ия работает с данными внутри тега
}

// Создаем парсер
$xml_parser = xml_parser_create();

// Настраиваем его (не чувствителен к регистру)
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);

// Указываем ф-ии обработки начального тега и конечного тега (см. выше)
xml_set_element_handler($xml_parser, "startElement", "endElement");

// Ф-ия обработки данных внутри тега (см. выше)
xml_set_character_data_handler($xml_parser, "characterData");

// Открываем файл
if ( !($fp = fopen($file, "r")) )
{ die("could not open XML input"); }

// Читаем файл и парсингуем
while ($data = fread($fp, 4096))
{
if ( !xml_parse($xml_parser, $data, feof($fp)) )
{
// Сообщение если возникла ошибка парсинга
die(
sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser))
);
}
}
// удаляем парсер, он сделал свое дело ;0)
xml_parser_free($xml_parser);
?>




Если запустите index.php, то ничего не увидете.
Пусть мы хотим увидеть названия тегов и данные в них. Измените функции след. образом.

function startElement($parser, $name, $attrs)
{
print "Открыт тег ". $name . "
";
}

function endElement($parser, $name)
{
print "Закрыт тег " . $name . "
";
}

function characterData($parser, $data)
{
print "".$data. "
";
}

Запускаем index.php. Видим на странице след. результат:

Открыт тег BOOKDB

Открыт тег BOOK

Открыт тег AUTHOR
Jhon Smith
Закрыт тег AUTHOR

Открыт тег BOOKNAME
Mega Book
Закрыт тег BOOKNAME

Открыт тег CODE
123-456-87
Закрыт тег CODE

Открыт тег GANRE
fantastic
Закрыт тег GANRE

Закрыт тег BOOK
.
// прочие данные из one.xml
.

Открыт тег PRICE
15$
Закрыт тег PRICE

Закрыт тег BOOK

Закрыт тег BOOKDB

Все остальное достаточно просто, вы можете определить, как поступать с любым из элементов: вывести на страницу, не вывести, выделить его жирным или сделать из него ссылку - все это ваша фантазия. Основа у вас есть, как это все работает можно понять и самому ( я же понял... надеюсь ;0) ). На этом практически все, но еще немного об атрибутах тегов.

Атрибуты открывающего тега:

Иногда в первом теге определяют атрибуты, ну, например, в HTML так размечена таблица:

<table width="400" border="1" cellspacing="0" cellpadding="0" >
<tr >
<td >1111</td >
<td >2222</td >
<td >3333</td >
</tr >
</table >

Здесь width="400" border="1" cellspacing="0" cellpadding="0" - это атрибуты тега и их значения. Эти данные так же можно извлеч при парсинге, т.к. ф-ия отвечающая за открывающий тег обозначена след. образом:

function startElement($parser, $name, $attrs)

Здесь $name - имя самого открывающего тега, а $attrs - это массив атрибутов. О работе с массивами писать не буду ;0) . Если в теге определен хотя бы один атрибут, то этот массив будет не пустой.

******************************

Кстати, не обязвтельно ф-ия startElement() должна иметь такое имя, можно назвать ее и по другому, только зарегистрируйте ее в ф-ии :

// Указываем ф-ии обработки начального тега и конечного тега (см. выше)
xml_set_element_handler($xml_parser, "ИМЯ_ФУНКЦИИ1", "ИМЯ_ФУНКЦИИ2");

******************************

Определите ф-ии обработки след. образом:

function startElement($parser, $name, $attrs)
{
print "
TEG is ";
print "".$name . "";

// Каждый атрибут представим в виде пары
// имя атрибута $n и его значение $v и печатаем эти данные
foreach ( $attrs as $n => $v)
{
print " {". $n ."} = " . $v;
}
print "
";
}

function endElement($parser, $name)
{
print " TEG " . $name ." is closed " ;
print "
";
}

function characterData($parser, $data)
{
print "".$data."
";
}

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

TEG is A {HREF} = index.html
Front page
TEG A is closed

При парсинге можно находит на указанной странице все ссылки и сохранять их адреса. Зачем это может пригодится не знаю, но возможность такая есть ;0)

Главное при парсинге HTML, да и любой другой разметки, что бы она соответствовала стандарту. Т.е. теги не тр*цензура*ющие в HTML закрытия:
, ,
и и некоторые другие, были записаны по след. стандарту:
, ,
и .
А атрибуты тегов обязательно оформлялись в кавычки, вот так:

<table width="400" border="1" cellspacing="0" cellpadding="0" >

а не так:

<table width=400 border=1 cellspacing=0 cellpadding=0 >

Иначе парсер выдаст ошибку и заглохнет.

Ну, вроде, все... строго не судите... удачи!

_____________
Аксиома Дучарма:
Если рассмотреть проблему достаточно внимательно, то Вы увидите себя, как часть этой проблемы.
Timok
2killich спасибо за статью :)
2Admin было бы неплохо скопировать ее сюды http://www.phpforum.ru/printpages.php
artoodetoo
Понравилось. Просто и поучительно.

_____________
evg
Пробовал использовать скрипт на подобном xml с Colibri.ru.
Там в теге встречаются символы - <BR>
В массив записывается только текст находящийся после последнего вхождения в тег символов - <BR>
Хочется всю инфу из тега. Как это сделать.
AlexBB
Коллеги, с появлением PHP5 я бы рекомендовал все-таки обратить взгляд на SimpleXML.
Код, который делает все тоже самое будет раза в три короче и читабельнее. Не говоря уж о поддержке XPath.
evg
А если можно подскажите как будет выглядеть данный код на XPath.
Вопрос: необходимо из Xml-файла размером 33 мега, вытащить информацию по 1 книге с определенным значением в теге id. Как это можно сделать быстрее по времени выполнения скрипта.
evg
Изменил вышеуказанный скрипт с целью получения инфы по книге с атрибутом id=111406 в теге <offer>.<br>Ничего не выводит, даже почему-то значение переменной $kod не выводит, если ставлю print ($kod) в функциях.<br>Помогите, плиз.<br><br>
function startElement($parser, $name, $attrs)
{
 
 
   
    foreach ( $attrs as $n => $v)
    {
        if (($name=='offer') && (($n=='id') && ($v==111406)))
$kod=1; 
    }
}
 
function endElement($parser, $name)
{
    // Эта ф-ия работает при встрече закрывающего тега
if (($name=='offer') && ($kod==1)) $kod=0;
 
}
 
function characterData($parser, $data)
{
    // Эта ф-ия работает с данными внутри тега
if ($kod==1) print "<b>".$data. "</b>
";
 
}
 
mazahaka
нужна помощ

допустим у меня есть xml :

Цитата
<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE rss PUBLIC "-//Netscape Communications//DTD RSS 0.91//EN" "http://my.netscape.com/publish/formats/rss-0.91.dtd">
<rss version="2.0">
<channel>
<title>Lenta.ru: НОВОСТИ, 09.11.2007</title>
<link>http://lenta.ru/</link>
<description>Ежедневная интернет-газета. Новости со всего мира на русском языке</description>

<item>
<title>Польским премьером стал лидер оппозиции</title>
<link>http://lenta.ru/news/2007/11/09/tusk/</link>
<description>Президент Польши Лех Качиньский назначил главой правительства Дональда Туска и поручил ему сформировать новый кабинет. По закону у лидера оппозиционной партии &quot;Гражданская платформа&quot; есть 14 дней на решение кадровых вопросов, но Туск пообещал назвать новых министров уже на следующей неделе.</description>
<pubDate>Fri, 09 Nov 2007 17:43:26 +0300</pubDate>
<category>В мире</category>
</item>

<item>
<title>В поезде &quot;Юность&quot; бомба не обнаружена</title>
<link>http://lenta.ru/news/2007/11/09/evac1/</link>
<description>Оперативники, обследовавшие все 20 вагонов фирменного поезда &quot;Юность&quot; сообщением Петербург-Москва, не обнаружили взрывного устройства. Неизвестный мужчина, сообщивший о бомбе дежурному Московского вокзала, объявлен в розыск. Состав был остановлен на станции Тосно, а около тысячи пассажиров эвакуированы.</description>
<pubDate>Fri, 09 Nov 2007 17:07:52 +0300</pubDate>
<category>В России</category>
</item>

<item>
<title>Лондонский террорист признал себя виновным в попытке взрыва в метро</title>
<link>http://lenta.ru/news/2007/11/09/plead/</link>
<description>Манфо Кваку Азиеду, один из шести обвиняемых по делу о подготовке серии взрывов на лондонском транспорте 21 июля 2005 года, признан виновным в заговоре с целью осуществления теракта. На процессе он признал выдвинутые против него обвинения. Четверо сообщников Азиду в июле 2007 года уже были осуждены на 40 лет.</description>
<pubDate>Fri, 09 Nov 2007 19:20:20 +0300</pubDate>
<category>В мире</category>
</item>

</channel>
</rss>



мне надо пропарсить и записать все это в массивы и при этом я не знаю какая глубина и какие теги в xml
всю инфу надо выдерать только между тегами "item" но это для примеря я поги их и не знать.


_____________
Как я завидую безмозглым!
vasa_c
DOM - http://pyha.ru/forum/topic/32.0
так же есть специальные библиотеки именно для RSS.

_____________
Блог ГО | Таблица символов Юникода | Графомания
mazahaka
все навоял разобрался немного


Код
class Acid {
    var $name;   // имя aa
    var $symbol; // трёхбуквенный символ
    var $code;  // однобуквенный код
    var $type;  // hydrophobic, charged или neutral
    
    function Acid ($aa) {
        foreach ($aa as $k=>$v)
            $this->$k = $aa[$k];
    }
}

function readDatabase($filename) {
global $m;
    // читать xml БД
    $data = implode("",file($filename));
    $parser = xml_parser_create();
    xml_parser_set_option($parser,XML_OPTION_CASE_FOLDING,0);
    xml_parser_set_option($parser,XML_OPTION_SKIP_WHITE,1);
    xml_parse_into_struct($parser,$data,$values,$tags);
    xml_parser_free($parser);
    foreach ($tags as $key0=>$val0) {
        if ($m<count($val0)){
            $m=count($val0);
            $tg = $key0;
        }        
    }
    // цикл по этим структурам
    foreach ($tags as $key=>$val) {
        if ($key == "$tg") {
            $molranges = $val;
            // каждая пара вхождений массива это нижняя и верхняя
            // границы диапазона для определения
            for ($i=0; $i < count($molranges); $i+=2) {
                    $offset = $molranges[$i] + 1;
                $len = $molranges[$i + 1] - $offset;
                $tdb[] = parse(array_slice($values, $offset, $len));
            }
        } else {
            continue;
        }
    }
    return $tdb;
}

function parse($mvalues) {
    for ($i=0; $i < count($mvalues); $i++)
        $mol[$mvalues[$i]["tag"]] = $mvalues[$i]["value"];
    return new Acid($mol);
}

$db = readDatabase("lenta.xml");
for ($i=0;$i<count($db);$i++){
    foreach ($db[$i] as $key=>$val){
      if ($val){
      echo "$key -> $val <br>";
      }
    }
echo "<br>";
}


_____________
Как я завидую безмозглым!
Yurien
Подскажите пожалуйста. Имеется следующий скрипт:


$parser = xml_parser_create("");
xml_set_element_handler( $parser, "start_handler", "end_handler" );
xml_set_character_data_handler( $parser, "character_handler" );
xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, 0 );
$file = file_get_contents($document);
xml_parse( $parser, $file) or die ( format_error( $parser ) );
xml_parser_free( $parser );

function start_handler( $p, $name, $atts ) {
global $open_stack;
$open_stack[] = array($name, "");

}
function character_handler( $p, $txt ) {
global $open_stack;
$cur_index = count($open_stack)-1;
$open_stack[$cur_index][1] = $open_stack[$cur_index][1] . $txt;
}

function end_handler( $p, $name) {

global $open_stack;
$el = array_pop( $open_stack );

// ОБЩИЕ ЭЛЕМЕНТЫ
// простой текст
if ( $name == "p") {
echo "<p/>$el[1]</p/>";
}
// простые ссылки
// ссылка-текст с указанием пути к ресурсу внутри сайта
if ( $name == "ahref") {
echo "<a href=rus.php?link=$el[1]>";
}
// ссылка-текст с названием страницы
if ( $name == "atext") {
echo "$el[1]</a/>";
}
// глобальная ссылка-текст к ресурсу
if ( $name == "webref") {
echo "<a href=\"$el[1]\">";
}
// текст глобальной ссылки
if ( $name == "webtext") {
echo "$el[1]</a/>";
}
// текст-постскриптум
if ( $name == "ps") {
echo "<p class=\"ps\"/>$el[1]</p/>";
}

// НОВОСТИ

// дата
if ( $name == "datenews") {
echo "<br/>";
echo "<div align=\"left\"/>";
echo "<i class=\"news\"/>$el[1]</i/>";
echo "</div/>";
}
// заголовок
if ( $name == "hnews") {
echo "<br/>";
echo "<h2 class=\"news\"/>$el[1]</h2/>";
}
// текст
if ( $name == "pnews") {
echo "<p class=\"news\"/>$el[1]</p/>";
echo "</div/>";
}
// изображения
if ( $name == "imagenews") {
echo "<img class=\"news\">$el[1]</img>";
}
if ( $name == "news") {
echo "<div class=\"news\"/>";
echo "<hr/ class=\"news\">";
echo "</div/>";
}

// КОНЦЕРТЫ

// дата
if ( $name == "dateconzert") {
echo "<br/>";
echo "<div align=\"left\">";
echo "<i class=\"conzert\"/>$el[1]</i>";
//echo "</div/>";
}
// заголовок
if ( $name == "hconzert") {
echo "<h2 class=\"conzert\"/>$el[1]</h2/>";
}
// текст
if ( $name == "pconzert") {
echo "<p class=\"conzert\"/>$el[1]</p/>";
echo "</div/>";
}
// изображения
if ( $name == "imgconzert") {
echo "<img class=\"conzerts\">$el[1]</img>";
}
if ( $name == "conzert") {
echo "<div/>";
echo "<hr/ class=\"conzert\">";
echo "</div/>";
}

// ГОСТЕВАЯ

// дата
if ( $name == "dateguest") {
echo "<br/>";
echo "<div align=\"left\"/>";
echo "<p class=\"guestdate\"/>$el[1]</p/>";
}
// имя
if ( $name == "nameguest") {
echo "<h2 class=\"guest\"/>Гость: $el[1]</h2/>";
}
// город
if ( $name == "cityguest") {
echo "<h2 class=\"guest2\"/>Страна/город: $el[1]</h2/>";
}
// текст
if ( $name == "pguest") {
echo "<p class=\"guest\"/>$el[1]</p/>";
}
// ответ администратора
if ( $name == "padmin") {
echo "<p class=\"admin\"/>$el[1]</p/>";
echo "</div/>";
}
if ( $name == "comment") {
echo "<div/>";
echo "<hr/ class=\"guest\" align=\"center\">";
echo "</div/>";
}
///////////////////////////////////////////////////////////////////////////////////
}

function format_error ( $p ) {
$code = xml_get_error_code( $p );
$str = xml_error_string( $code );
$line = xml_get_current_line_number ( $p );
return "Ошибка XML ($code): $str в строке $line";
}

Как его изменить, чтобы он выводил документ наоборот, т.е. первым выводилась информация в последнем <news>, <comment>, затем в предпоследнем, пред-предпоследнем и так до первого?

p.s.: пытался применить "array_reverse", никак не помогло - или переворачивает не те теги, или php выдает ошибки.
Быстрый ответ:

 Графические смайлики |  Показывать подпись
Здесь расположена полная версия этой страницы.
Invision Power Board © 2001-2024 Invision Power Services, Inc.