Kontynuujemy poznawanie OOP w PHP. Tym razem klasa Parser, która otwiera, pobiera i parsuje zawartość strony internetowej. Poznajemy pola prywatne oraz kompozycję – czyli instancję innej klasy jako pole klasy. Do dzieła.

Rzecz pierwsza, czyli nasza klasa i konstruktor:

<?php
class DomDocumentParser {

	private $doc;

	public function __construct($url) {

		$options = array(
			'http'=>array('method'=>"GET", 'header'=>"User-Agent: scraperBot/0.1\n")
			);
		$context = stream_context_create($options);

		$this->doc = new DomDocument();
		@$this->doc->loadHTML(file_get_contents($url, false, $context));
	}

}

Doc jest prywatny, czyli tylko wewnątrz klasy możemy się nim bawić metodami. Samego doc „po strzałce” na obiekcie wykonać nie możemy i się do niego dostać.

Kompozycja to przedostatnia linijka konstruktora, gdzie jako pole klasy przypisujemy obiekt innej klasy, tutaj wbudowanej w PHP klasy DomDocument.

Ładujemy HTML z urla (zakładam, że mamy allow_url_fopen w php.ini) i robimy to pod error supressor operator, czyli „@”, zatem błędy nas nie interesują.

Teraz metoda getTags i getById:

<?php
class DomDocumentParser {

	private $doc;

	/(...)
	
	public function getTags($tag) {
		return $this->doc->getElementsByTagName($tag);
	}

    public function getById($elem_id){
        return $this->doc->getElementById($elem_id);
    }
}

Doc jest prywatny, więc metody wewnątrz klasy mogą się nim bawić, metody publiczne, a zatem w ten sposób tworzymy interfejs dla użytkownika końcowego, który będzie obsługiwać obiekt naszej klasy.

Doc jest sam w sobie obiektem innej klasy (kompozycja) i posiada własne metody (getElementsByTagName, getElementById).

Robimy z tych dwóch rzeczy użytek. Teraz funkcja showTags:

<?php
class DomDocumentParser {

	private $doc;

	//(...)
	
	public function getTags($tag) {
		return $this->doc->getElementsByTagName($tag);
	}

    public function showTags($tag){
        $iter = $this->getTags($tag)->getIterator();
        foreach($iter as $element){
            echo $element->textContent;
            echo "</br>";
        }
    }

    
    public function getById($elem_id){
        return $this->doc->getElementById($elem_id);
    }
}

$parser = new DomDocumentParser("https://www.scrapethissite.com/pages/simple/");
$parser->showTags("h3");

Od razu z przykładem do użycia. getTags zwraca klasę, która ma funkcję getIterator. Po tym iteratorze możemy przejść wyświetlając każdy element, a w zasadzie jego textContent.

Dokładnie to robimy. Łączymy się ze stroną do scrapowania, pobieramy jej HTML, łapiemy tagi h3 i wyświetlamy.

Teraz kolejna metoda:

class DomDocumentParser {

	private $doc;

	//(...)

    public function getTagsWithClass($tag, $class){
        $tags = $this->doc->getElementsByTagName($tag)->getIterator();
        $tagsWithClass = array();
        foreach($tags as $tag){
            if(stripos($tag->getAttribute('class'), $class) !== false){
                $tagsWithClass[] = $tag;
            }
        }
        return $tagsWithClass;
    }
   //(...)
}

$parser = new DomDocumentParser("https://www.scrapethissite.com/pages/simple/");
$iter = $parser->getTagsWithClass("div", "container");
print_r($iter[0]->textContent);

Pobieramy elementy z doc po tagu i łapiemy iterator. Iterujemy dodając do output array tylko te, które zawierają szukaną przez nas klasę. Zwracamy tablicę.

Teraz możemy tam sobie wejść i złapać element div o klasie container i wyświetlić jego textContent.

Dodam, że tutaj lepiej by się sprawdził XPath, ale nie wszystko od razu. Temat będziemy kontynuować.