Я иногда ссылаюсь на данную утилиту в примерах. Выкладываю её код и примеры использования.
Примеры использования.
Я привожу пример экспорта статей с одного из своих рабочих проектов — delovoymir2003.ru. Я выполнял программирование и переносом публикаций со старого сайта на сайт под drupal. Пример максимально конкретный.
1 2 3 |
$source = file_get_contents($_POST['url']); //создаём экземпляр класса parser $p = new parser($source); |
Далее мы производим «первичный парсинг», т.е. извлекаем нужные куски данных из HTML документа. Они могут потребовать дополнительных проверок и преобразований позднее.
Заголовок статьи в данном примере находится в обрамлении — <h2 class=»itemTitle»>…</h2>. Было бы верно его разместить внутри H1, я сделаю на новом сайте после экспорта.
1 |
$h1 = $p->getInsideScope('<h2 class="itemTitle">', '</h2>'); |
Курсор внутри объекта parse перемещается за найденный заголовок. Далее идет дата, обрамленная <h4 class=»itemTitle»>…</h4>. Пока просто извлечем её, чтобы на 2м этапе парсинга преобразовать её в UNIX_TIMESTAMP.
1 |
$date = $p->getInsideScope('<h4 class="itemTitle">', '</h4>'); |
Картинка может быть в шаблоне, а может не быть, об этом нам сигнализирует наличие обрамления <div class=»itemImageBlock»>…</div>.
1 2 3 4 |
if ($p->setNext('<div class="itemImageBlock">')) $img = $p->getFromScope('<img', '>'); else $img = false; |
Вводный текст или тизер (краткое изложение) — тоже не обязательный атрибут статьи в примере.
1 2 3 4 5 |
if ($p->setNext('<div class="itemIntroText">')) { $text_intro = $p->getInsideScope('<div class="itemIntroText">', '</div>'); } else { $text_intro = ''; } |
Полный текст может подкинуть разные сюрпризы. Я, например, придерживаюсь правила : не использовать при создании статей тэг-а <div>. Но другие веб-мастера, верстальщики и программисты, могут придерживаться своих правил. :) В том числе, могут не соблюдать рекомендации W3C. Т.е. документ по разным причинам может содержать ошибки. Потому в поисках окончания текста я опираюсь на следующий после статьи блок <div class=»itemBody childs»>…</div>.
1 2 3 4 5 6 7 8 |
if ($p->setNext('<div class="itemFullText">')) { $text_full = $p->getInsideScope('<div class="itemFullText">', '<div class="itemBody childs">'); //надо отрезать с конца один закрывающий </div> $text_full = substr($text_full, 0, strrpos($text_full, '</div>')); } else { $text_full = ''; } |
Текст утилиты. Класс parser.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
// Автор - SHRA, 2014. // описание : // http://shra.ru/2016/02/utilita-dlya-izvlecheniya-dannykh-i-parsinga-html-koda // утилитарный парсер, для разбора HTML данных из шаблонов class parser { //позиция курсора в тексте var $c = 0; //рабочий текст var $s = ''; //конструктор, на вход передаем HTML текст function parser(&$s) { $this->s = $s; } //далее идут методы класса //переместить указатель на ближайшую позицию указанной подстроки function setNext($ss) { $new = stripos($this->s, $ss, $this->c); if ($new === false) return false; $this->c = $new; return true; } //переместить указатель на ближайшую позицию указанной подстроки, но двигаясь назад function setPrev($ss) { $part = substr($this->s, 0, $this->c); $new = strripos($part, $ss); if ($new === false) return false; $this->c = $new; return true; } //ищем ближайшую позицию строки из набора $right //курсор передвигается в начало ближайшего из найденных маркеров function setClosestPoint($right) { $posResult = -1; foreach($right as $str) { $posRight = stripos($this->s, $str, $this->c); if ($posRight !== false && ($posResult == -1 || $posRight < $posResult)) $posResult = $posRight; } if ($posResult == -1) return false; $found = substr($this->s, $this->c, $posResult - $this->c); $this->c = $posResult; return $found; } //выбрать элемент ограниченный строками //курсор передвигается за пределы найденного паттерна function getFromScope($left, $right) { $posLeft = stripos($this->s, $left, $this->c); if ($posLeft === false) return false; $posRight = stripos($this->s, $right, $posLeft + strlen($left)); if ($posRight === false) return false; $this->c = $posRight + strlen($right); return substr($this->s, $posLeft, $this->c - $posLeft); } //выбрать элемент ограниченный строками (возвращаем то, что внутри маркеров) //курсор передвигается за пределы найденного паттерна function getInsideScope($left, $right) { $posLeft = stripos($this->s, $left, $this->c); if ($posLeft === false) return false; $posLeft += strlen($left); $posRight = stripos($this->s, $right, $posLeft); if ($posRight === false) return false; $this->c = $posRight + strlen($right); return substr($this->s, $posLeft, $posRight - $posLeft); } /* str - кусочек html откуда извлекаем dictionary - словарь меток и имен полей delm - разделитель между меткой и значением */ static function extractValueFromStr($str, &$dictionary, $delm = ':') { $str = strip_tags($str); $a = explode($delm, $str); $label = trim($a[0]); $value = trim($a[1]); if (isset($dictionary[$label])) { $n = &$dictionary[$label]; switch($n[1]) { case 'num': $value = preg_replace("#[\s]+#sm", '', $value) + 0; break; case 'email': if (!valid_email_address($value)) return false; } return array($n[0], $value, $label); } return false; } /* варинат извлечения числового значения из строки */ static function extractNum($str) { $value = (float) str_replace(array(' ', ' ', ','), array('', '', '.'), strip_tags($str)); return $value; } } |