k47.cz    — každý den dokud se vám to nezačne líbit
foto Praha výběr povídky kultura | twitter FB


Atrox\Matcher

23. 3. 2012 — k47 (CC by-sa)

Tak dlouho jsem se hrabal v cizím HTML kódu, scrapoval, crawloval, parsoval, až jsem si napsal Atrox\Matcher, který tuhle práci velice usnadňuje.


Matcher provádí stromové xpath dotazy, které transformují DOM dokument na PHP pole.

Například Matcher může vypadat následovně:

$matcher = Matcher::mutli('//div[@class="article"]', array(
  'title' => './h2',        // plný xpath výraz: //div[@class="article"]/h2
  'text'  => './text()',
  './div[@class="info"]' => array(
    'date'   => './div[@class="date"]', // plný xpath výraz: //div[@class="article"]/div[@class="info"]/div[@class="date"]
    'author' => './div[@class="author"]',
  ),
  'tags' => Matcher::multi('.//a[@class="tag"]'),
  'comments' => Matcher::multi('./div[@class="comment"]', array(
    'author' => './div[@class="author"]',
    'text'   => './div[@class="comment-text"]',
  )),
));

Pracuje se s ním jako s obyčejnou funkcí, která jako argument čeká objekt DomDocument (takže se hodí nejen pro scrapování HTML, ale i pro práci s různými API, které vracejí XML).

$matcher($dom);

// nebo

$matcher->processHtml($htmlData);

Výsledná data budou v tomto formátu:

array(
  array(
    'title' => 'xxx',
    'text' => 'txt',
    'date' => '2011-01-01',
    'author' => 'anon',
    'tags' => array('tag1', 'tag2'), // Matcher::multi s jedním argumentem
    'comments' => array( // Matcher::multi se dvěma argumenty
      array(
        'author' => 'anon',
        'text' => 'txt',
      ), // + další komentáře
    )
  ), // + další články
);

Pěkné, co?


Aktualizace:

Matcher má dvě možnosti jak z DOMNode získat nějaké smysluplné hodnoty.

Globálně se zadává jako třetí parametr metody multi:

Matcher::multi('//article', array(
  'title' => 'div[@class="title"]',
  'text'  => 'div[@class="text"]',
), function ($n) { return trim($n->nodeValue); }); // když nic nezadáte, zavolá se `$node->nodeValue`.

Nebo lokálně pro každou položku zvlášť:

Matcher::multi('//article', array(
  'title' => 'div[@class="title"]',
  'date' => Matcher::single('div[@class="date"]', function ($n) { return strtotime($n->nodeValue); }),
));
píše k47 & hosté, ascii@k47.cz