Ver índice de contenidos del libro

10.5. El Crawler

Cada vez que haces una petición, el cliente devuelve una instancia del Crawler. Este objeto permite recorrer documentos HTML, seleccionar nodos, encontrar enlaces y formularios.

10.5.1. Recorriendo documentos HTML y XML

Al igual que jQuery, el Crawler tiene métodos para recorrer el DOM de un documento HTML/XML. El siguiente código por ejemplo encuentra todos los elementos input[type=submit], selecciona el último en la página, y luego selecciona su elemento padre :

$newCrawler = $crawler->filter('input[type=submit]')
    ->last()
    ->parents()
    ->first()
;

Este objeto dispone de muchos otros métodos:

Método Descripción
filter('h1.title') Nodos que coinciden con el selector CSS indicado
filterXpath('h1') Nodos que coinciden con la expresión XPath indicada
eq(1) Nodo correspondiente al índice especificado
first() El primer nodo
last() El último nodo
siblings() Los nodos hermanos del nodo seleccionado
nextAll() Los nodos hermanos siguientes
previousAll() Los nodos hermanos anteriores
parents() Los nodos padre del nodo seleccionado
children() Los nodos hijo del nodo seleccionado
reduce($lambda) Devuelve los nodos para los que la función anónima $lambda no devuelve false

Como cada uno de estos métodos devuelve una nueva instancia del Crawler, puedes seleccionar nodos encadenando varios métodos:

$crawler
    ->filter('h1')
    ->reduce(function ($node, $i)
    {
        if (!$node->getAttribute('class')) {
                return false;
        }
    })
    ->first();

Truco Puedes utilizar la función count() para obtener el número de nodos almacenados en un Crawler: count($crawler)

10.5.2. Extrayendo información

El Crawler también puede extraer información de los nodos:

// Devuelve el valor del atributo del primer nodo
$crawler->attr('class');
 
// Devuelve el valor del nodo para el primer nodo
$crawler->text();
 
// Extrae un array de atributos de todos los nodos
// (_text devuelve el valor del nodo)
// devuelve un array de cada elemento en 'crawler',
// cada uno con su valor y href
$info = $crawler->extract(array('_text', 'href'));
 
// Ejecuta una función anónima por cada nodo y
// devuelve un array de resultados
$data = $crawler->each(function ($node, $i)
{
    return $node->attr('href');
});

10.5.3. Enlaces

Para seleccionar enlaces, puedes usar los métodos anteriores para recorrer el código HTML de la página o mejor utilizar el atajo selectLink():

$crawler->selectLink('Click here');

Este método selecciona todos los enlaces que contienen el texto indicado o todas las imágenes pinchables cuyo atributo alt contiene el texto indicado. Al igual que los otros métodos de filtrado, también devuelve un objeto Crawler.

Una vez seleccionado un enlace, puedes acceder a un objeto especial de tipo Link que contiene métodos útiles y específicos para enlaces (tales como getMethod() y getUri()). Para pinchar el enlace, usa el método click() del cliente y pásale un objeto Link:

$link = $crawler->selectLink('Click here')->link();
 
$client->click($link);

10.5.4. Formularios

Al igual que sucede con los enlaces, existe un método para seleccionar directamente los formularios:

$buttonCrawlerNode = $crawler->selectButton('submit');

Nota Observa que el código anterior selecciona un botón de formulario y no el propio formulario. Esto es especialmente importante porque los formularios pueden contener varios botones y debes tenerlo en cuenta al recorrer el contenido de la página.

El método selectButton() puede seleccionar etiquetas <button> y etiquetas <input> de envío de formularios. Para encontrar estos últimos botones, utiliza diferentes estrategias:

  • El valor del atributo value del botón.
  • El valor del atributo id o alt de botones creados con imágenes.
  • El valor del atributo id o name de las etiquetas <button>.

Una vez que tienes un Crawler que representa un botón, utiliza el método form() para obtener el formulario que contiene a ese botón. El resultado que obtienes es una instancia de la clase Form:

$form = $buttonCrawlerNode->form();

Cuando utilizas el método form(), también puedes pasar un array de valores para reemplazar a los valores originales del formulario:

$form = $buttonCrawlerNode->form(array(
    'name'              => 'Fabien',
    'my_form[subject]'  => 'Symfony rocks!',
));

Y si quieres simular un método HTTP específico para el formulario, pásalo como segundo argumento:

$form = $buttonCrawlerNode->form(array(), 'DELETE');

Por último, envía el formulario pasando el objeto Form al cliente con el que realizas las peticiones:

$client->submit($form);

El formulario también se puede rellenar pasando un array de valores como segundo parámetro del método submit():

$client->submit($form, array(
    'name'              => 'Fabien',
    'my_form[subject]'  => 'Symfony rocks!',
));

Para tests más complejos, utiliza la instancia de Form como un array para establecer el valor de cada campo individualmente:

// Cambia el valor de un campo
$form['name'] = 'Fabien';
$form['my_form[subject]'] = 'Symfony rocks!';

También existe una API bastante completa para manipular los valores de los campos de acuerdo a su tipo:

// selecciona una opción o radiobutton
$form['country']->select('France');
 
// marca una casilla de verificación (checkbox)
$form['like_symfony']->tick();
 
// carga un archivo
$form['photo']->upload('/ruta/a/lucas.jpg');

Truco Puedes utilizar el método getValues() del objeto Form para acceder a los valores que contiene el formulario que se va a enviar. Los archivos subidos están disponibles en un array separado devuelto por getFiles(). Los métodos getPhpValues() y getPhpFiles() también devuelven los valores enviados, pero en "formato PHP" (transforma las claves con corchetes como my_form[subject] a valores normales de un array PHP).