{"id":4,"date":"2015-05-17T01:55:51","date_gmt":"2015-05-16T23:55:51","guid":{"rendered":"http:\/\/lab.fawno.com\/?page_id=4"},"modified":"2022-03-07T00:54:52","modified_gmt":"2022-03-06T23:54:52","slug":"","status":"publish","type":"post","link":"https:\/\/lab.fawno.com\/en\/2015\/05\/17\/clase-wget\/","title":{"rendered":"","raw":""},"content":{"rendered":"","protected":false,"raw":""},"excerpt":{"rendered":"","protected":false,"raw":""},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_typography_data":[],"_editorskit_blocks_typography":"","_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","_es_post_content":"\n

Esta es una clase que escrib\u00ed hace ya unos meses para parsear p\u00e1ginas web, bajar archivos de sitios web y otras muchas cosas para las que utilizaba Wget y scripts cmd.<\/p>\n\n\n\n

Cuando la cosa se complica, empieza a resultar muy dif\u00edcil hacer las cosas mediante wget, greps y truquitos... es entonces cuando conviene cambiar de lenguaje.<\/p>\n\n\n\n

En PHP utilizaba file_get_contents para los html normales, y abr\u00eda los ficheros en lectura en un bucle de lectura fopen-fread-fclose para los binarios. Este m\u00e9todo, si bien me ha funcionado muy bien durante a\u00f1os, implica que para gestionar las cookies tienes que abrir sockets y es un l\u00edo, aunque es posible y as\u00ed lo hicimos durante algunos a\u00f1os.<\/p>\n\n\n\n

M\u00e1s tarde descubr\u00ed la extensi\u00f3n pecl_http<\/a>, con esta extensi\u00f3n pod\u00eda gestionar de maravilla las cookies y otras muchas cosas... pero bajar binarios de m\u00e1s de 128Mb era un reto. El problema es obvio: pecl_http realiza todo el trabajo en memoria, lo que significa que la p\u00e1gina\/binario m\u00e1s grande que puede manejar viene determinado por la memoria asignada al PHP (memory_limit<\/a>). As\u00ed que otra vez ten\u00eda que recurrir a un bucle de lectura fopen-fread-fclose.<\/p>\n\n\n\n

Buscando otra manera de hacer las cosas me intereso por cURL<\/a>. Acostumbrado a pecl_http hab\u00eda algunas cosas que me resultaban extra\u00f1as, como que cURL por defecto env\u00eda el resultado de la petici\u00f3n a la salida est\u00e1ndar, por lo que para obtener el resultado en una variable se suele recurrir al truco de utilizar las funciones del control de buffer de salida<\/a> mediante ob_start-ob_get_contents-ob_end_clean. Esto me parec\u00eda burdo, demasiado burdo.<\/p>\n\n\n\n

Evidentemente sab\u00eda que cURL, como otras tantas cosas buenas que tiene el PHP, ven\u00eda de librer\u00edas con sobrada solvencia. Lo que significa que ten\u00eda que haber una manera mejor de hacer las cosas... y la hay.<\/p>\n\n\n\n

cURL es una librer\u00eda maravillosa, y toda su potencia est\u00e1 disponible en PHP. Sin embargo, como suele ocurrir con las herramientas potentes, manejar un monstruo no es tarea de un d\u00eda. As\u00ed que cree una clase para facilitar el trabajo con cURL al estilo de GNU Wget<\/a>, basado, que no igual.<\/p>\n\n\n\n

Wget ha sido mi inspiraci\u00f3n, tambi\u00e9n mi anterior experiencia con pecl_http, he creado una clase para encapsular todo de manera que sea m\u00e1s sencillo utilizar todas las posibilidades, adem\u00e1s eso previene la colisi\u00f3n de nombres de funciones y variables (es una manera muy eficaz de evitar el uso de variables globales, por ejemplo). Adem\u00e1s si trabajas con un framework es m\u00e1s f\u00e1cil de utilizar una clase externa que un conjunto de funciones sueltos por ah\u00ed, por ejemplo yo la he utilizado con \u00e9xito junto con CakePHP<\/a> con simplemente declararla:<\/p>\n\n\n\n

App::uses('wget', 'Vendor');<\/pre>\n\n\n\n

La clase no ha sido un trabajo de un s\u00f3lo d\u00eda... llevo meses utiliz\u00e1ndola para diversas tonter\u00edas sin importancia pero con necesidades distintas que me han hecho ir modificando y a\u00f1adiendo m\u00e1s funciones.<\/p>\n\n\n\n

Las \u00faltimas modificaciones tiene que ver con la versi\u00f3n de PHP y el entorno productivo... yo la he estado utilizando con PHP-CLI<\/a> y PHP-5.5\/5.6, sin embargo con versiones inferiores hay una funci\u00f3n de cURL que no existe. Adem\u00e1s en seg\u00fan que entorno con la configuraci\u00f3n del m\u00f3dulo de Apache<\/a> la constante STDOUT <\/a>no est\u00e1 definida.<\/p>\n\n\n\n

La soluci\u00f3n pr\u00e1ctica a dichas incompatibilidades es comprobar si la funci\u00f3n\/constante existe y de no existir se crea\/define. En el caso de la funci\u00f3n lo que he hecho es crear una funci\u00f3n \"dummy\", es decir, no hace nada, pero no importa porque es una funci\u00f3n de informaci\u00f3n. Para la constante STDOUT lo que hago es declararla como un handle a php:\/\/stdout<\/a>, y as\u00ed todo funciona bien en los tres entornos distintos en los que he probado la clase.<\/p>\n\n\n\n

if (!defined('STDOUT')) {\n  define('STDOUT', fopen('php:\/\/stdout', 'w'));\n}\n\nif (!function_exists('curl_strerror')) {\n  function curl_strerror ($curl_error) {\n    return $curl_error;\n  }\n}\n\n\/\/ Based on mipa code:\n\/\/ http:\/\/php.net\/manual\/es\/curlfile.construct.php#114539\nif (!function_exists('curl_file_create')) {\n  function curl_file_create ($filename, $mimetype = null, $postname = null) {\n    $file = null;\n    if (is_file($filename)) {\n      if (empty($mimetype) and function_exists('mime_content_type')) $mimetype = mime_content_type($filename);\n      if (empty($postname)) $postname = basename($filename);\n      $file = '@';\n      $file .= realpath($filename);\n      $file .= ';filename=' . $postname;\n      if (isset($mimetype)) $file .= ';type=' . $mimetype;\n    }\n    return $file;\n  }\n}<\/pre>\n\n\n\n

Como he dicho, una de las motivaciones de crear esta clase era interpretar y extraer informaci\u00f3n de p\u00e1ginas web. Hasta ahora utilizaba expresiones regulares, lo que era una lata porque llevaba mucho tiempo deducir las expresiones de b\u00fasqueda y cualquier peque\u00f1a variaci\u00f3n en los datos pod\u00eda dar al traste con el invento... y hablo de a\u00f1os de experiencia en estos menesteres.<\/p>\n\n\n\n

Recientemente, junto con el descubrimiento y aprendizaje de cURL, he cambiado de t\u00e9cnica de an\u00e1lisis de p\u00e1ginas web: XML. Efectivamente, se me ocurri\u00f3 cargar las p\u00e1ginas html dentro de un objeto xml y procesarlas mediante las funciones de PHP para XML<\/a>.<\/p>\n\n\n\n

Para la extracci\u00f3n de informaci\u00f3n mediante XML he decidido utilizar SimpleXML<\/a> porque, como su propio nombre indica, es una clase muy simple y r\u00e1pida de aprender. Sin embargo carece de la funci\u00f3n de importaci\u00f3n de un HTML como XML. Dicha funci\u00f3n si est\u00e1 disponible en el DOM<\/a>, pero no en SimpleXML, as\u00ed que he a\u00f1adido una funci\u00f3n suelta a la clase: simplexml_import_html. Esta funci\u00f3n se crea si no existe (en previsi\u00f3n de un futuro en el que la incluyan en el SimpleXML) y trabaja de la misma manera que simplexml_load_string<\/a> pero con HTML en lugar de XML.<\/p>\n\n\n\n

if (!function_exists('simplexml_import_html')) {\n  function simplexml_import_html ($html) {\n    $doc = new DOMDocument();\n    @$doc-&gt;loadHTML($html);\n    $xml = simplexml_import_dom($doc);\n    return $xml;\n  }\n}<\/pre>\n\n\n\n

La clase wget completa en GitHub<\/a><\/p>\n","_es_post_name":"clase-wget","_es_post_excerpt":"","_es_post_title":"Clase wget","_en_post_content":"","_en_post_name":"","_en_post_excerpt":"","_en_post_title":"","edit_language":"en","footnotes":""},"categories":[4,5],"tags":[6,12,13],"ninja_gutenberg_blocks_featured_media_urls":{"thumbnail":"","ninja_gutenberg_blocks_landscape_large":"","ninja_gutenberg_blocks_portrait_large":"","ninja_gutenberg_blocks_square_large":"","ninja_gutenberg_blocks_landscape":"","ninja_gutenberg_blocks_portrait":"","ninja_gutenberg_blocks_square":"","full":""},"_links":{"self":[{"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/posts\/4"}],"collection":[{"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/comments?post=4"}],"version-history":[{"count":16,"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/posts\/4\/revisions"}],"predecessor-version":[{"id":1845,"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/posts\/4\/revisions\/1845"}],"wp:attachment":[{"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/media?parent=4"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/categories?post=4"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/lab.fawno.com\/en\/wp-json\/wp\/v2\/tags?post=4"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}