CMS y plataformas 11 min de lectura

Scraper para WordPress

Cómo hacer scraping para sitios WordPress y llenarlos con el contenido recopilado: REST API, XML-RPC, plugins de importación y soluciones a medida.

EW
Equipo Web-Scraping.es
Recopilación de datos para las necesidades del negocio
Publicado: 18 febrero 2025

El web scraping es una de las herramientas más demandadas en los sitios hechos con WordPress. Con él puede llenar su sitio de forma automática con contenido siempre actualizado: tipos de cambio, noticias, pronósticos del tiempo, productos de proveedores y mucho más. En este artículo veremos qué plugins listos existen, en qué casos conviene escribir un scraper propio en PHP y analizaremos cuatro escenarios prácticos para integrar el scraping en WordPress.

Si desea profundizar en la propia tecnología de extracción y análisis de datos, le recomendamos el material aparte «Web scraping en PHP», donde se describen en detalle los principios de funcionamiento, las bibliotecas (cURL, Guzzle, Simple HTML DOM, Symfony DomCrawler) y los escollos habituales. Aquí ponemos el acento precisamente en la integración con WordPress.


Qué es el scraping en el contexto de WordPress

El scraping (web scraping) es la recopilación automática de datos de fuentes externas: sitios web, APIs, feeds RSS, listas de precios. En WordPress, esos datos normalmente no basta con obtenerlos; también hay que:

  • guardarlos en la base de datos (como entradas, productos, metacampos u opciones);
  • cachearlos, para no sobrecargar ni la fuente externa ni el propio servidor;
  • actualizarlos de forma programada (mediante wp_cron o el cron del sistema);
  • mostrarlos en el frontend (mediante shortcodes, widgets o bloques de Gutenberg).

Precisamente por eso, un «scraper para WordPress» no es un simple script que descarga algo, sino un módulo completo, integrado en el ecosistema del CMS.


Panorama de los plugins existentes

Antes de escribir una solución propia, tiene sentido evaluar los plugins disponibles. Cubren muchas tareas típicas sin una sola línea de código.

WP All Import

Una de las herramientas de importación más potentes. Acepta XML, CSV y formatos de listas de precios, permite mapear visualmente los campos de la fuente con los campos de entradas, productos de WooCommerce o usuarios. Admite la importación programada y la actualización de los registros ya importados. Encaja bien en el escenario de los productos de un proveedor, pero exige que la fuente entregue un feed estructurado.

Ventajas: mapeo flexible, interfaz amigable, actualización fiable. Inconvenientes: las funciones avanzadas están en la versión de pago; no «scrapea» páginas HTML arbitrarias sin un feed preparado.

Content Egg

Plugin agregador para marketing de afiliación y de contenidos: sabe traer productos, precios, reseñas y otros contenidos desde multitud de fuentes y APIs. Se usa a menudo para comparar precios y completar fichas de producto.

Ventajas: muchos módulos de origen listos, plantillas de salida. Inconvenientes: está orientado a escenarios concretos (afiliación, marketplaces) y tiene su propia lógica de presentación.

WP RSS Aggregator / Feedzy

Plugins especializados en la importación de feeds RSS y Atom. Permiten combinar varios feeds, filtrar por palabras clave y convertir los elementos del feed en entradas de WordPress.

Ventajas: configuración sencilla para noticias y RSS, filtrado y deduplicación. Inconvenientes: están limitados al formato de feed; no sirven para HTML arbitrario.

Scrapers universales (WP Content Pilot, plugins de scraping)

Existen plugins capaces de extraer datos de páginas arbitrarias mediante selectores CSS. Son lo más parecido a un scraper «de verdad», pero cualquier cambio en la maquetación de la fuente rompe la configuración, y resulta difícil implementar en ellos una lógica de posprocesamiento compleja.

Cuándo los plugins no bastan

Las soluciones listas funcionan de maravilla mientras la tarea es estándar. Pero en cuanto aparece una fuente atípica, una lógica de transformación compleja, autenticación en el lado del proveedor o requisitos específicos de caché, la flexibilidad del plugin deja de ser suficiente. En ese caso se escribe un scraper propio.


Un scraper propio en PHP dentro de WordPress

La gran ventaja de una solución propia es el control total. Usted decide cómo obtener los datos, cómo limpiarlos, dónde almacenarlos y cuándo actualizarlos. WordPress ofrece un buen conjunto de herramientas para ello, y no hace ninguna falta recurrir al «crudo» file_get_contents.

Los «ladrillos» básicos sobre los que se construye cualquier scraper para WordPress:

php
// 1. Obtención de datos: mediante la HTTP API integrada de WordPress
$response = wp_remote_get( 'https://example.com/data', array(
    'timeout' => 15,
    'headers' => array( 'User-Agent' => 'MySiteBot/1.0' ),
) );

if ( is_wp_error( $response ) ) {
    error_log( 'Error en la petición: ' . $response->get_error_message() );
    return;
}

$body = wp_remote_retrieve_body( $response );
php
// 2. Caché mediante la Transients API, para no golpear la fuente en cada visualización
function my_get_cached_data() {
    $cache_key = 'my_parser_data';
    $data = get_transient( $cache_key );

    if ( false === $data ) {
        $data = my_fetch_and_parse(); // su función de scraping
        set_transient( $cache_key, $data, HOUR_IN_SECONDS );
    }

    return $data;
}
php
// 3. Actualización programada mediante WP-Cron
add_action( 'init', function () {
    if ( ! wp_next_scheduled( 'my_parser_update' ) ) {
        wp_schedule_event( time(), 'hourly', 'my_parser_update' );
    }
} );

add_action( 'my_parser_update', 'my_fetch_and_parse' );

El análisis detallado de las bibliotecas de parseo de HTML, el manejo de codificaciones, la evasión de protecciones y la construcción de un scraper robusto está en el material aparte «Web scraping en PHP». A continuación nos apoyamos en esos principios y mostramos justamente lo específico de WordPress.

Pasemos ahora a los escenarios concretos.


Escenario 1. Scraping de tipos de cambio

Una tarea típica de una tienda online o de un sitio de contenidos: mostrar los precios en varias divisas a la vez y convertirlos automáticamente. Como fuente sirven los datos oficiales de un banco central — por ejemplo, el feed XML del Banco Central Europeo con los tipos de referencia del euro.

La lógica es sencilla: una vez al día descargamos el XML con los tipos de cambio, lo parseamos, guardamos el resultado en las opciones de WordPress y lo usamos al mostrar los precios.

php
function parse_currency_rates() {
    $url = 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml'; // feed de tipos de referencia del BCE
    $response = wp_remote_get( $url, array( 'timeout' => 15 ) );

    if ( is_wp_error( $response ) ) {
        return;
    }

    $xml = simplexml_load_string( wp_remote_retrieve_body( $response ) );
    if ( false === $xml ) {
        return;
    }

    $xml->registerXPathNamespace( 'e', 'http://www.ecb.int/vocabulary/2002-08-01/eurofxref' );

    $rates = array();
    foreach ( $xml->xpath( '//e:Cube[@currency]' ) as $cube ) {
        $code  = (string) $cube['currency'];          // USD, GBP ...
        $value = (float) $cube['rate'];
        $rates[ $code ] = $value;                     // unidades de divisa por 1 EUR
    }

    update_option( 'my_currency_rates', $rates );
    update_option( 'my_currency_rates_updated', current_time( 'mysql' ) );
}
add_action( 'my_currency_update', 'parse_currency_rates' );

Conversión de un precio a la divisa deseada:

php
function convert_price( $eur_amount, $currency = 'USD' ) {
    $rates = get_option( 'my_currency_rates', array() );
    if ( empty( $rates[ $currency ] ) ) {
        return $eur_amount;
    }
    return round( $eur_amount * $rates[ $currency ], 2 );
}

Y un shortcode para mostrar el tipo de cambio en una página:

php
add_shortcode( 'currency_rate', function ( $atts ) {
    $atts  = shortcode_atts( array( 'code' => 'USD' ), $atts );
    $rates = get_option( 'my_currency_rates', array() );
    $code  = strtoupper( $atts['code'] );

    return isset( $rates[ $code ] )
        ? esc_html( '1 € = ' . number_format( $rates[ $code ], 2 ) . ' ' . $code )
        : '—';
} );
// Uso: [currency_rate code="GBP"]

El análisis completo de los formatos XML/JSON de los bancos centrales, del trabajo con varias divisas y del cálculo de tipos cruzados está en el material específico «Scraping de tipos de cambio».


Escenario 2. Scraping de noticias y RSS

El segundo escenario popular es llenar el sitio de noticias automáticamente. Aquí WordPress trae un extra agradable: el núcleo ya incluye un parser de feeds integrado, fetch_feed(), basado en la biblioteca SimplePie. Es la forma más fiable de trabajar con RSS sin reinventar la rueda.

php
function import_news_from_rss( $feed_url ) {
    include_once ABSPATH . WPINC . '/feed.php';

    $feed = fetch_feed( $feed_url );
    if ( is_wp_error( $feed ) ) {
        return;
    }

    $max_items = $feed->get_item_quantity( 10 );
    $items     = $feed->get_items( 0, $max_items );

    foreach ( $items as $item ) {
        $guid = $item->get_id();

        // Protección contra duplicados: buscamos una entrada con el mismo GUID de origen
        $existing = get_posts( array(
            'meta_key'   => '_source_guid',
            'meta_value' => $guid,
            'post_type'  => 'post',
            'fields'     => 'ids',
        ) );
        if ( $existing ) {
            continue;
        }

        $post_id = wp_insert_post( array(
            'post_title'   => sanitize_text_field( $item->get_title() ),
            'post_content' => wp_kses_post( $item->get_content() ),
            'post_date'    => $item->get_date( 'Y-m-d H:i:s' ),
            'post_status'  => 'draft', // a moderación, para evitar contenido basura
            'post_type'    => 'post',
        ) );

        if ( $post_id && ! is_wp_error( $post_id ) ) {
            update_post_meta( $post_id, '_source_guid', $guid );
        }
    }
}

Lanzamos la importación de forma programada:

php
add_action( 'my_news_import', function () {
    import_news_from_rss( 'https://example.com/feed/' );
} );

Si la fuente no ofrece RSS y las noticias hay que sacarlas directamente de la página HTML mediante selectores, eso ya es scraping en toda regla, con análisis de la maquetación. Este tipo de proyectos los realizamos como extracción de noticias llave en mano: configuramos la extracción de titulares, fechas, imágenes y texto de sitios arbitrarios teniendo en cuenta su estructura.

Los matices del trabajo con feeds, los formatos RSS 2.0 y Atom, el filtrado y la combinación de varias fuentes los tratamos en el artículo aparte sobre RSS.


Escenario 3. Scraping del pronóstico del tiempo

Un widget del tiempo es una petición frecuente de portales regionales, sitios de hoteles y agencias de viajes. Los datos suelen tomarse de una API meteorológica que devuelve JSON. La tarea, en el contexto de WordPress, consiste en integrar ese widget con cuidado en el sitio: obtener el pronóstico, cachearlo durante un periodo corto y mostrarlo mediante un shortcode o un bloque.

php
function get_weather( $city = 'Madrid' ) {
    $cache_key = 'weather_' . sanitize_key( $city );
    $weather   = get_transient( $cache_key );

    if ( false !== $weather ) {
        return $weather; // lo servimos desde la caché
    }

    $url = add_query_arg( array(
        'q'     => $city,
        'units' => 'metric',
        'lang'  => 'es',
        'appid' => 'YOUR_API_KEY',
    ), 'https://api.example-weather.com/data/forecast' );

    $response = wp_remote_get( $url, array( 'timeout' => 10 ) );
    if ( is_wp_error( $response ) ) {
        return null;
    }

    $data = json_decode( wp_remote_retrieve_body( $response ), true );

    $weather = array(
        'temp'        => $data['main']['temp'] ?? null,
        'description' => $data['weather'][0]['description'] ?? '',
        'icon'        => $data['weather'][0]['icon'] ?? '',
    );

    // El tiempo cambia a menudo: cacheamos solo 30 minutos
    set_transient( $cache_key, $weather, 30 * MINUTE_IN_SECONDS );

    return $weather;
}

Shortcode para insertar el widget en cualquier entrada o página:

php
add_shortcode( 'weather', function ( $atts ) {
    $atts    = shortcode_atts( array( 'city' => 'Madrid' ), $atts );
    $weather = get_weather( $atts['city'] );

    if ( empty( $weather ) ) {
        return '<span class="weather-error">Pronóstico no disponible</span>';
    }

    return sprintf(
        '<div class="weather-widget">
            <span class="weather-city">%s</span>
            <span class="weather-temp">%s°C</span>
            <span class="weather-desc">%s</span>
        </div>',
        esc_html( $atts['city'] ),
        esc_html( round( $weather['temp'] ) ),
        esc_html( $weather['description'] )
    );
} );
// Uso: [weather city="Barcelona"]

Los puntos clave específicos de WordPress: un TTL de caché corto (el pronóstico caduca rápido), la salida mediante shortcode/widget y el escapado obligatorio de los datos antes de mostrarlos.

La guía completa sobre las fuentes de datos meteorológicos, el parseo de pronósticos de varios días y el manejo de la geolocalización está en el material sobre scraping de datos meteorológicos; aquí lo hemos visto justamente desde la óptica de su integración en un sitio WordPress.


Escenario 4. Scraping del sitio de un proveedor e importación de productos a WooCommerce

El escenario más completo: llenar automáticamente una tienda online con los productos de un proveedor. La fuente puede ser una lista de precios (CSV/XML) o el propio sitio del proveedor, que hay que analizar a partir del HTML. El resultado se importa a WooCommerce en forma de productos con precios, existencias, imágenes y categorías.

Veamos la importación desde un feed estructurado (la variante más estable):

php
function import_supplier_products() {
    // Protección contra ejecuciones en paralelo
    if ( get_transient( 'import_lock' ) ) {
        return;
    }
    set_transient( 'import_lock', 1, 10 * MINUTE_IN_SECONDS );

    $response = wp_remote_get( 'https://supplier.example.com/price.xml', array(
        'timeout' => 60,
    ) );

    if ( is_wp_error( $response ) ) {
        delete_transient( 'import_lock' );
        return;
    }

    $xml = simplexml_load_string( wp_remote_retrieve_body( $response ) );

    foreach ( $xml->offer as $offer ) {
        $sku   = (string) $offer->sku;
        $name  = (string) $offer->name;
        $price = (float) $offer->price;
        $stock = (int) $offer->stock;

        upsert_product( $sku, $name, $price, $stock, (string) $offer->picture );
    }

    delete_transient( 'import_lock' );
}

La función «crear o actualizar» producto (upsert): busca el producto por su referencia (SKU) y, o bien actualiza el existente, o bien crea uno nuevo:

php
function upsert_product( $sku, $name, $price, $stock, $image_url = '' ) {
    $product_id = wc_get_product_id_by_sku( $sku );

    if ( $product_id ) {
        // El producto ya existe: actualizamos solo el precio y el stock
        $product = wc_get_product( $product_id );
    } else {
        // Producto nuevo
        $product = new WC_Product_Simple();
        $product->set_sku( $sku );
        $product->set_name( $name );

        // La imagen se descarga solo al crear el producto
        if ( $image_url ) {
            $attach_id = media_sideload_image( $image_url, 0, $name, 'id' );
            if ( ! is_wp_error( $attach_id ) ) {
                $product->set_image_id( $attach_id );
            }
        }
    }

    $product->set_regular_price( (string) $price );
    $product->set_stock_quantity( $stock );
    $product->set_manage_stock( true );
    $product->set_stock_status( $stock > 0 ? 'instock' : 'outofstock' );

    $product->save();
}

Lanzamiento de la importación de forma programada (por ejemplo, dos veces al día):

php
add_action( 'init', function () {
    if ( ! wp_next_scheduled( 'supplier_import_event' ) ) {
        wp_schedule_event( time(), 'twicedaily', 'supplier_import_event' );
    }
} );
add_action( 'supplier_import_event', 'import_supplier_products' );

A qué prestar atención en este escenario:

  • Identificación por SKU. La referencia es la clave más fiable para el emparejamiento de los productos entre el proveedor y la tienda.
  • Las imágenes se descargan una sola vez. media_sideload_image() es una operación costosa; no hay que repetirla en cada actualización.
  • Procesamiento por lotes. Las listas de precios grandes (decenas de miles de referencias) conviene procesarlas por partes (batch), para no chocar con los límites de tiempo y de memoria de PHP.
  • Retirada de la venta. Los productos que desaparecen de la lista del proveedor conviene pasarlos al estado «sin stock» en lugar de borrarlos.

Si el proveedor no entrega un feed y hay que extraer las fichas de producto directamente de las páginas HTML de su sitio, la tarea se vuelve más laboriosa y tiene sus propios matices (paginación, carga dinámica, protección anti-bots). Los principios de ese tipo de análisis se describen en detalle en el material «Web scraping en PHP».


Recomendaciones técnicas

Algunas reglas universales que harán más fiable cualquier scraper para WordPress:

  1. Use la HTTP API de WordPress (wp_remote_get/wp_remote_post) en lugar de file_get_contents o de cURL «en crudo»: tiene en cuenta la configuración de proxy, los timeouts y los filtros del CMS.
  2. Cachee mediante la Transients API. Reduce la carga tanto de su servidor como de la fuente.
  3. No scrapee al vuelo durante la carga de la página. El scraping pesado debe ejecutarse en segundo plano vía cron; el frontend lee datos ya preparados.
  4. Sustituya WP-Cron por el cron del sistema para las tareas pesadas: WP-Cron solo se dispara cuando alguien visita el sitio y no sirve para importaciones largas.
  5. Escape siempre los datos antes de mostrarlos (esc_html, esc_url, wp_kses_post): una fuente externa nunca puede considerarse de confianza.
  6. Registre los errores y compruebe is_wp_error() en cada paso: la fuente externa puede no estar disponible, cambiar de formato o devolver basura.
  7. Cumpla las normas legales y éticas: respete robots.txt, los límites de peticiones y las condiciones de uso de la fuente.

Conclusión

El scraping es una forma potente de automatizar el llenado de un sitio WordPress. Para las tareas típicas suelen bastar plugins listos como WP All Import o Feedzy. Pero en cuanto la fuente o la lógica de procesamiento se salen del estándar, un scraper propio en PHP ofrece un control y una flexibilidad incomparablemente mayores.

Hemos repasado cuatro escenarios prácticos — tipos de cambio, noticias y RSS, pronóstico del tiempo e importación de los productos de un proveedor a WooCommerce — y hemos mostrado cómo integrar cada uno de ellos en el ecosistema de WordPress mediante la HTTP API, los transients y el cron.

¿Quiere profundizar en la propia tecnología de análisis de datos? Empiece por el material básico «Web scraping en PHP». Y si necesita un scraper listo para su tarea, nos encargamos de todo: desde la configuración de la extracción de noticias hasta la importación completa del catálogo de un proveedor.