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

Atrox\Matcher

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

Tak dlouho jsem se hrabal v cizím HTML kódu, scra­po­val, crawlo­val, par­so­val, až jsem si napsal Atrox\Matcher, který tuhle práci velice usnad­ňuje.


Matcher pro­vádí stro­mové xpath dotazy, které trans­for­mují DOM do­ku­ment na PHP pole.

Na­pří­klad Matcher může vy­pa­dat ná­sle­dovně:

$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"]',
  )),
));

Pra­cuje se s ním jako s oby­čej­nou funkcí, která jako ar­gu­ment čeká objekt Dom­Do­cu­ment (takže se hodí nejen pro scra­po­vání HTML, ale i pro práci s růz­nými API, které vra­cejí XML).

$matcher($dom);

// nebo

$matcher->processHtml($htmlData);

Vý­sledná data budou v tomto for­má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?


Ak­tu­a­li­zace:

Matcher má dvě mož­nosti jak z DO­MNode získat nějaké smys­lu­plné hod­noty.

Glo­bálně se zadává jako třetí pa­ra­metr 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 lo­kálně pro každou po­lož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