Cтранного хочу: CSS-like селекторы на PHP

voituk

прозревший
Cтранного хочу: CSS-like селекторы на PHP

Хочется реализовать, или (что ещё лучше) найти готовое решения для такого:

Есть html-файл, который является почти варидным XML (некоторые параметры тэгов не в кавычках, некоторые XML-сущности не закодированы в &...;, часто стоит <br> вместо <br/>, и тд)
Далее используя синтаксис а-ля CSS хочу выполнять поиск блоков текста внутри этого XML.
Например:
select("span.hello) найти все элементы SPAN с аттрибутом class соредращим значение "hello"
или
select('div#body') найти все элементы div где id="body"
Про вложенные селекторы а-ля "div#body a.link" пока не думаю.

Первое что прийшло в голову - прогнать html через tidy, получить xml, и получать нужные данные XPath-запросами.
Но такое решение мне кажется слишком тяжеловесным.

Есть ещё мысль преобразовать селекторы в набор регулярных выражений, но тут надо придумать как учитывать вложенность тегов.

Может я не вижу простого решения? или есть уже что-то готовое?
Буду рад любым мыслям и идеям по этому поводу.
 

atv

Новичок
В той же tidy ты можеш пробежаться по дереву узлов. Вот и напиши пару методов, которые будут проверять необходимые значения узлов. Хотя XPath мне нравится больше.

Есть ещё мысль преобразовать селекторы в набор регулярных выражений
Да там и самому легко их распарсить (селекторы). По примеру этого
 

voituk

прозревший
Не очень то хочется завязываться на Tidy (у конечного пользователя его может не быть) - но идея здравая. Спасибо!
 

MbIX_22

Новичок
На мой скромный взгляд, тут решений куча, причем самое быстрое и оригинальное на регулярных выражениях. Никаких дополнительных утилит не требуется. Руки, и полтора часа труда.

Парсил я когда-то хмтл-о-иксмл ручками, щас поищу..

Ну вот, нашел. Только я данные сосал из сокета, так что тут реализовано склеивание фрагментов "незаконченных пакетов":
PHP:
<?php
class main_parser
{
var $fragments;

function main_parser()
         {
          $this->fragments = '';
         }
function extractParams($str)
                       {
                        $out = array();
                        if(preg_match_all("/(\S+)=\"(.+?)\"(\s|$)/sm", $str, $ok))
                                //print_r($ok);
                        for($i=0; $i<count($ok[0]); $i++)
                           {
                             $out[$ok[1][$i]] = $ok[2][$i];
                           }
                                return $out;
                       }

function handle($buffer)
              {
                 if($this->fragments)
                     {
                       $buffer = $this->fragments.$buffer;
                       $this->fragments = '';
                     }
                if(preg_match("/(<[^>]+)$/sm", $buffer, $ok)) $this->fragments = $ok[1];
                if(!preg_match_all("/<([^\/][^<]+)(\/)?>/Usm", $buffer, $matches))
                  {
                    return false;
                  }
          foreach($matches[1] as $buff)
                 {
                   $buff = trim($buff);
                   if(@!$buff) continue;
                   if(preg_match("/^KEY\s(.+?)?$/", $buff, $ok))//KEY - тип тега, div, span, etc.
                     {
                       $params = $this->extractParams($ok[1]);//многомерный массив параметров, реализован с учетом параметров в кавычках, при необходимости доработайте
                       //тут делаем что-то полезное

                     }//else if(preg_match("/^KEY\s(.+?)?$/", $buff, $ok)){ //другой тег или сделать общее: "/^(\S+)\s(.+?)?$/"
                 }
          }
}
?>
Код выдран из рабочей програмы, так что целостность не гарантирую, но суть думаю ясна и так :)
 

voituk

прозревший
Flanker
DOM умеет парсить невалидный xml?

MbIX_22
Я тоже когда-то велосипеды любил изобретать :)
Поверь моему опыту - парсить XML регулярками - зло!
То, что ты демонстрируешь своим примером называется SAX-парсер :)

Речь идет о высокоровневом механизме описания правил поиска по HTML.
А вот как оно уже там устроено внутри (tidy, регулярки и тд) - это уже дело третье.
В таком случае CSS-селекторы мне показались самым что ни есть правильным вариантом потому как семантика данных в исходной html-странице как раз и описывается аттрибутами class и id.
 

MbIX_22

Новичок
voituk
PHP:
//в класс заводим ещё одну переменную $this->tags;
//Регулярка отсекает закрывающие теги, которые не содержат параметров и нам не интересны.
//участок кода
if(preg_match("/^(\S+?)\s(.+?)?$/", $buff, $ok))
 {
     $params = $this->extractParams($ok[2]);
     //тут делаем что-нибудь полезное в зависимости от заданных параметров
     if($params['class'])//$params['id']
       {
           $this->tags[]=array(
                                               "tag"=>$ok[1],
                                               "params"=>$params
                                           );
       } 
  }
На выходе скрипт нам позволяет получить структурированный хтмл, разве что не отслеживающий вложенность элементов, которое можно до-реализовать. И никакого зла с XML и регулярками нет, если всё грамотно сделать, описав исключенгия. :)
 

voituk

прозревший
MbIX_22
Ты видать недопонял про велосипеды, повторяю ещё раз http://ru.wikipedia.org/wiki/SAX

demon_goo
Спасибо за совет.
Мой невалидный html DOM сьедает за милую душу, а методы getElementById и getElementsByTagName отлично решают мою задачу.
 
Сверху