Victor Scrapper: Cómo hice un plugin WordPress para automatizar datos públicos

Introducción

En este artículo te cuento cómo construí Victor Scrapper, un plugin de WordPress que extrae datos de una fuente pública y los integra en la web de mi equipo de fútbol. El objetivo era claro: automatizar la actualización de información sin tareas manuales, mantener la web siempre al día y no sacrificar rendimiento ni SEO.

No revelo la fuente concreta (por acuerdos internos), pero el enfoque es genérico y puede adaptarse a otras webs o deportes cambiando el mapeo de campos. Aquí encontrarás el código completo, la arquitectura y recomendaciones de rendimiento y SEO.

Arquitectura del plugin

  • Admin UI: menú propio en el dashboard para lanzar y revisar el scraping.
  • Campos personalizados (ACF/CPT): el CPT equipo define las URLs (url_scrapper, url_todos_los_partidos).
  • Scraping: petición HTTP a la URL pública y parseo del HTML/JSON.
  • Persistencia: guardado en metadatos del CPT.
  • AJAX/seguridad: acciones protegidas con nonce y roles.
  • Rendimiento: scraping diferido (cron/manual) + uso de caché/transients.

Flujo de scraping y guardado

  1. Configurar en el CPT equipo las URLs (url_scrapper y/o url_todos_los_partidos).
  2. Lanzar scraping desde el admin o programar WP-Cron.
  3. El plugin solicita la fuente, parsea datos y los transforma al esquema interno.
  4. Los resultados se guardan como metadatos.
  5. El front lee esos metadatos y los muestra de forma rápida.

Tip: evita scraping en la carga pública. Hazlo en segundo plano y cachea resultados.

Rendimiento y SEO

Rendimiento

  • Evita llamadas externas en el front.
  • Usa WP-Cron para actualizar en horarios de baja carga.
  • Implementa transients para cachear datos.
  • Optimiza assets, fuentes y lazy loading.

Instalación y uso

  1. Copia la carpeta victorscrapper en wp-content/plugins/.
  2. Activa el plugin en Plugins > Victor Scrapper.
  3. En el CPT equipo, añade url_scrapper y url_todos_los_partidos.
  4. Lanza el scraping o programa un cron.
  5. Usa los metadatos en tus plantillas para mostrar los datos.

Código completo

El siguiente código pertenece a un plugin de WordPress llamado **Victor Scrapper**, diseñado para realizar tareas de **Web Scraping** (raspado web) en sitios con datos publicos. Su propósito es automatizar la extracción de datos de partidos, clasificaciones y calendarios para publicarlos en campos personalizados (ACF) asociados a un *Custom Post Type* (CPT) de ‘equipo’.

Funciones de Scraping (victorscrapper/src/scrapper.php)

El núcleo del scraping reside en este archivo, que utiliza la librería estándar de PHP **DOMDocument** y **DOMXPath** para analizar el código HTML.


1. `scrapper_victor_on_match($post_id, $url)`: Extraer el Próximo Partido

Esta función está diseñada para extraer la información clave de un solo partido o el resultado más reciente, utilizando la URL específica proporcionada para el post de equipo. Se centra en la tabla que contiene los resultados.

Código PHP de la función:

<?php
function scrapper_victor_on_match($post_id, $url)
{
    echo $url;
    $response = wp_remote_get($url, array(
        'timeout' => 20,
        'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
    ));

    if (is_wp_error($response)) {
        return ['error' => 'Error al obtener la URL'];
    }

    $html = wp_remote_retrieve_body($response);

    if (empty($html)) {
        return ['error' => 'No se pudo obtener contenido HTML'];
    }

    $html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');

    libxml_use_internal_errors(true);
    $dom = new DOMDocument();
    $dom->loadHTML($html);
    libxml_clear_errors();

    $xpath = new DOMXPath($dom);
    $tables = $xpath->query(
        "//table[contains(concat(' ', normalize-space(@class), ' '), ' table_resultats ')][1]"
    );

    $partidos = [];

    foreach ($tables as $table) {
        foreach ($table->getElementsByTagName('tr') as $tr) {
            $fila = [];
            $tds = $tr->getElementsByTagName('td');

            $i = 0;
            foreach ($tds as $td) {
                $class = $td->getAttribute('class');



                // Equipo
                if (strpos($class, 'resultats-w-equip') !== false) {
                    $a = $td->getElementsByTagName('a')->item(0);
                    $key = $i === 1 ? 'equip' : 'equip2';
                    $fila[$key] = $a ? trim($a->textContent) : trim($td->textContent);

                    update_field('equipo_local',     $fila['equip'],  $post_id);
                    update_field('equipo_visitante', $fila['equip2'], $post_id);
                }

                // Resultado / fecha
                if (strpos($class, 'resultats-w-resultat') !== false) {
                    $value = trim(preg_replace('/\s+/', ' ', $td->textContent));

                    // Obtener el href
                    $a = $td->getElementsByTagName('a')->item(0);
                    $href = $a ? $a->getAttribute('href') : '';

                    update_field('dia_proximo_partido',     $value  ?? '', $post_id);
                    update_field('url_federacion',  $href ?? '', $post_id);
                }


                $i++;
            }
        }
    }
}

2. `scrapper_victor_classificacion($post_id, $url)`: Extraer Tabla de Clasificación

Se encarga de raspar todos los datos de la tabla de clasificación de la liga, incluyendo estadísticas detalladas (puntos, jugados, ganados, etc.), y los guarda en un campo repetidor de ACF.

Código PHP de la función:

<?php
function scrapper_victor_classificacion($post_id, $url)
{
$response = wp_remote_get($url, [
'timeout' => 20,
'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
]);

if (is_wp_error($response)) {
return ['error' => 'Error al obtener la URL'];
}

$html = wp_remote_retrieve_body($response);
if (empty($html)) {
return ['error' => 'No se pudo obtener contenido HTML'];
}

$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');

libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom->loadHTML($html);
libxml_clear_errors();

$xpath = new DOMXPath($dom);
$tables = $xpath->query("//table[contains(concat(' ', normalize-space(@class), ' '), ' fcftable-e ')]");

$rows = [];

foreach ($tables as $table) {
foreach ($xpath->query('.//tbody/tr', $table) as $tr) {
$tds = $tr->getElementsByTagName('td');
if ($tds->length < 10) {
continue; // No parece una fila válida
}

$len = $tds->length;

$pos_cell = $tds->item(0);
$shield_cell = $tds->item(1);
$name_cell = $tds->item(2); // versión resumida
$points_cell = $tds->item(3);
$coef_cell = $tds->item(4); // coeficiente o segundo valor

$j_cell = $tds->item(6);
$g_cell = $tds->item(7);
$e_cell = $tds->item(8);
$p_cell = $tds->item(9);

$goles_favor_cell = $tds->item($len - 4);
$goles_contra_cell = $tds->item($len - 3);
$racha_cell = $tds->item($len - 2);
$sancion_cell = $tds->item($len - 1);

$pos_text = trim($pos_cell->textContent);
if (preg_match('/(\d+)/', $pos_text, $m)) {
$pos = (int)$m[1];
} else {
$pos = null;
}

$name_a = $name_cell->getElementsByTagName('a')->item(0);
$equipo = $name_a ? trim($name_a->textContent) : trim($name_cell->textContent);
$equipo_url = $name_a ? $name_a->getAttribute('href') : '';

$points_raw = trim($points_cell->textContent);
$puntos = null;
$puntos_extra = '';
if (preg_match('/^(\d+)(?:\s*\(([^)]+)\))?/', $points_raw, $m)) {
$puntos = (int)$m[1];
if (isset($m[2])) {
$puntos_extra = trim($m[2]);
}
} else {
$puntos = is_numeric($points_raw) ? (int)$points_raw : null;
}

$coeficiente = trim($coef_cell->textContent);

$jugados = (int)trim($j_cell->textContent);
$ganados = (int)trim($g_cell->textContent);
$empatados = (int)trim($e_cell->textContent);
$perdidos = (int)trim($p_cell->textContent);

$goles_favor = (int)trim($goles_favor_cell->textContent);
$goles_contra = (int)trim($goles_contra_cell->textContent);

$racha_span = null;
$racha = '';
$acta_url = '';
$anchor_racha = $racha_cell->getElementsByTagName('a')->item(0);
if ($anchor_racha) {
$acta_url = $anchor_racha->getAttribute('href');
foreach ($racha_cell->getElementsByTagName('span') as $sp) {
if (strpos($sp->getAttribute('class'), 'racha') !== false) {
$racha_span = $sp;
break;
}
}
}
if ($racha_span) {
$texto_racha = trim($racha_span->firstChild ? $racha_span->firstChild->textContent : $racha_span->textContent);
$racha = substr($texto_racha, 0, 1);
}

$sancion = trim($sancion_cell->textContent);

$rows[] = [
'pos' => $pos,
'equip' => $equipo,
'equip_url' => $equipo_url,
'punts' => $coef_cell->textContent,
'pj' => $jugados,
'ganados' => $ganados,
'empatados' => $empatados,
'perdidos' => $perdidos,
];
}
}

if (!empty($rows) && function_exists('update_field')) {
update_field('clasificacion', $rows, $post_id);
// Intento adicional por si el campo se creó con tilde (menos recomendable)
update_field('clasificacion', $rows, $post_id);
}

return $rows;
}

3. `scrapper_victor_all_match($post_id, $url)`: Extraer Todos los Partidos del Calendario

Esta función analiza un calendario completo, pero aplica un filtro estricto: solo guarda los partidos donde el equipo ‘ATICO’ sea local o visitante. Esto permite mantener un historial o calendario del equipo relevante.

Código PHP de la función:

<?php
function scrapper_victor_all_match($post_id, $url)
{
$response = wp_remote_get($url, array(
'timeout' => 20,
'user-agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
));

if (is_wp_error($response)) {
return ['error' => 'Error al obtener la URL'];
}

$html = wp_remote_retrieve_body($response);

if (empty($html)) {
return ['error' => 'No se pudo obtener contenido HTML'];
}

$html = mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8');

libxml_use_internal_errors(true);
$dom = new DOMDocument();
$dom->loadHTML($html);
libxml_clear_errors();

$xpath = new DOMXPath($dom);
$tables = $xpath->query("//table[contains(concat(' ', normalize-space(@class), ' '), ' calendaritable ')]");

$rows = [];

foreach ($tables as $table) {

$th2 = $xpath->query(".//thead/tr/th[2]", $table)->item(0);
$setmana = $th2 ? trim($th2->textContent) : '';

foreach ($xpath->query(".//tbody/tr", $table) as $tr) {

$links = $xpath->query(".//td/a[normalize-space(text())]", $tr);
if ($links->length < 2) continue;

$aLocal = $links->item(0);
$aVisitante = $links->item($links->length - 1);

$nombreLocal = trim($aLocal->textContent);
$urlLocal = $aLocal->getAttribute('href');
$nombreVisitante = trim($aVisitante->textContent);
$urlVisitante = $aVisitante->getAttribute('href');

if (
stripos($nombreLocal, 'ATICO') === false &&
stripos($nombreVisitante, 'ATICO') === false
) {
continue;
}

$rows[] = [
'nombre_equipo_local' => $nombreLocal,
'url_equipo_local' => $urlLocal,
'equipo_visitante' => $nombreVisitante,
'url_equipo_visitante' => $urlVisitante,
'setmana_de_' => $setmana, // 👈 nuevo subcampo
];
}
}

if (!empty($rows)) {
update_field('todos_los_partidos', $rows, $post_id);
} else {
// si quieres vaciar el repeater cuando no hay partidos de ATICO:
update_field('todos_los_partidos', [], $post_id);
}
}

Estructura del Plugin y Ejecución (victorscrapper/victorscrapper.php)

Este es el archivo principal que registra el plugin, crea las páginas de administración y, lo más importante, contiene la lógica para **ejecutar las funciones de scraping**.

Clase `Victor_Scrapper_Plugin`

El método `render_main_page()` es el punto donde se itera sobre los posts de tipo ‘equipo’ y se llama a cada una de las funciones de scraping, pasándoles las URLs configuradas mediante ACF.

Código PHP de la clase y ejecución:

<?php

/**
 * Plugin Name: Victor Scrapper
 * Description: Añade un menú principal y un submenú que muestran "hola".
 * Version: 1.0.0
 * Author: Víctor Gómez Luque
 * Text Domain: victor-scrapper
 */

if (! defined('ABSPATH')) exit; // Seguridad

class Victor_Scrapper_Plugin
{

    const MENU_SLUG  = 'victor-scrapper';
    const SUB_SLUG   = 'victor-scrapper-sub';

    public function __construct()
    {
        add_action('admin_menu', [$this, 'register_admin_menus']);

        // Endpoint AJAX para lanzar el scrapper por URL
        add_action('wp_ajax_victor_scrapper_run', [$this, 'ajax_run']);
        add_action('wp_ajax_nopriv_victor_scrapper_run', [$this, 'ajax_run']);
    }

    public function register_admin_menus()
    {
        // Menú principal
        add_menu_page(
            __('Victor Scrapper', 'victor-scrapper'),
            __('Victor Scrapper', 'victor-scrapper'),
            'manage_options',
            self::MENU_SLUG,
            [$this, 'render_main_page'],
            'dashicons-admin-tools',
            59
        );

        // Submenú
        add_submenu_page(
            self::MENU_SLUG,
            __('Victor Scrapper - Submenú', 'victor-scrapper'),
            __('Submenú', 'victor-scrapper'),
            'manage_options',
            self::SUB_SLUG,
            [$this, 'render_sub_page']
        );
    }

    public function render_main_page()
    {
        require_once 'src/scrapper.php';

        $query = new WP_Query([
            'post_type'      => 'equipo',
            'posts_per_page' => -1,
            'post_status'    => 'publish',
            'orderby'        => 'date',
            'order'          => 'DESC',
        ]);

        if ($query->have_posts()) :
            while ($query->have_posts()) : $query->the_post();

                $url_scrapper_on_match = get_field('url_scrapper', get_the_ID());
                $url_scrapper_all_match = get_field('url_todos_los_partidos', get_the_ID());
                $url_calendario = get_field('url_calendario', get_the_ID());
?>
                <div class="scrapper-victor">
                    <p><?php the_title(); ?></p>
                    <p><?php echo esc_html($url_scrapper_on_match); ?></p>
                    <p><?php echo esc_html($url_scrapper_all_match); ?></p>
                    <p><?php echo esc_html($url_calendario); ?></p>
                </div>
<?php
                scrapper_victor_on_match(get_the_ID(), $url_scrapper_on_match);
                scrapper_victor_all_match(get_the_ID(), $url_scrapper_all_match);
                scrapper_victor_classificacion(get_the_ID(), $url_calendario);

            endwhile;
        endif;
    }

    public function render_sub_page()
    {
        echo '<div class="wrap"><h1>Victor Scrapper — Submenú</h1><p>hola</p></div>';
    }

    /**
     * Lanza la misma lógica que el menú principal, pero accesible por AJAX
     */
    public function ajax_run()
    {

        // --- seguridad básica con token ---
        $token = isset($_GET['token']) ? sanitize_text_field($_GET['token']) : '';
        if ($token !== '1234') {
            wp_die('Acceso no autorizado', 403);
        }

        // Ejecutamos la misma función
        ob_start();
        $this->render_main_page();
        $output = ob_get_clean();

        // Devuelve el HTML generado
        wp_send_json_success($output);
    }
}

new Victor_Scrapper_Plugin();

Optimización de WordPress: Mejora la Velocidad y el Rendimiento de tu Sitio Web

Introducción a la Optimización de WordPress

La optimización de WordPress es un aspecto crucial para cualquier propietario de un sitio web en la actualidad. A medida que la competencia online se intensifica, la velocidad de carga y el rendimiento web WordPress se han convertido en elementos determinantes para no solo atraer visitantes, sino también para proporcionarles una experiencia agradable. Un sitio web lento puede frustrar a los usuarios y, como resultado, aumentar las tasas de rebote, lo que impacta negativamente en el SEO y en la conversión de clientes potenciales.

Uno de los objetivos centrales de la optimización de WordPress es mejorar la velocidad de carga, un factor que Google considera esencial para el posicionamiento en los motores de búsqueda. Herramientas como PageSpeed Insights WordPress permiten a los administradores del sitio identificar áreas que requieren atención, incluyendo tiempos de carga, interactividad y visualización. Al abordar estos aspectos, se puede mejorar la experiencia del usuario y, simultáneamente, cumplir con los estándares de Core Web Vitals, que son criterios críticos para evaluar la calidad de la experiencia del usuario en la web.

Entre las estrategias de optimización, se destaca el uso de plugins de caché WordPress, como Litespeed Cache, que ayudan a almacenar contenido estático y a minimizartiempos de carga. Junto con técnicas para optimizar imágenes WordPress, estas herramientas son esenciales para conseguir un rendimiento web óptimo y eficiente. La optimización SEO técnico WordPress también juega un papel clave en la mejora del rendimiento general del sitio. Así, resulta evidente que invertir tiempo y esfuerzo en la optimización de WordPress no solo ofrece beneficios en términos de velocidad, sino que también contribuye al éxito a largo plazo del sitio web.

Consejos Técnicos para Mejorar la Velocidad de Carga

Mejorar la velocidad de carga de un sitio de WordPress es esencial no solo para la experiencia del usuario, sino también para el posicionamiento en buscadores. Existen diversas estrategias técnicas que pueden ser implementadas para lograr este objetivo. Una de las primeras recomendaciones es la optimización de imágenes WordPress. Las imágenes a menudo representan un gran porcentaje del tamaño total de la página; por lo tanto, es crucial asegurarse de que están en el formato correcto y comprimidas adecuadamente para reducir su peso sin comprometer la calidad. Utilizar herramientas de compresión como TinyPNG o plugins específicos de WordPress puede resultar de gran ayuda.

Además de la optimización de imágenes, minimizar el tiempo de respuesta del servidor es fundamental. Esto se puede lograr mediante la elección de un proveedor de alojamiento web que ofrezca un rendimiento confiable y escalable. Opciones como Litespeed Cache pueden mejorar significativamente el rendimiento web WordPress, al integrar funcionalidades que optimizan la carga de las páginas. También es aconsejable reducir el uso de plugins innecesarios que puedan ralentizar el sitio. Una revisión periódica de los plugins instalados puede ayudar a mantener un sitio ágil y optimizado.

El uso de herramientas como Pagespeed Insights WordPress puede proporcionar valiosos datos sobre cómo se está desempeñando el sitio y qué áreas necesitan atención. Los informes generados ofrecen sugerencias específicas para mejorar el core web vitals WordPress, lo cual es crucial para cumplir con las expectativas de Google en cuanto a calidad de página. Finalmente, la implementación del SEO técnico WordPress debe ir de la mano con las mejoras de velocidad, asegurando que la infraestructura del sitio esté diseñada para optimizar el wpo WordPress y, en última instancia, ofrecer una mejor experiencia a los visitantes.

Implementación de Caché y Mejora del Rendimiento

La optimización de WordPress para mejorar la velocidad y el rendimiento web es crucial para ofrecer una experiencia satisfactoria a los usuarios. Una de las estrategias más efectivas para lograrlo es la implementación de caché. El uso de plugins de caché WordPress permite almacenar versiones estáticas de las páginas de tu sitio, lo que reduce el tiempo de carga y mejora los Core Web Vitals, cruciales para un buen posicionamiento en buscadores. Uno de los plugins más recomendados es Litespeed Cache, que no solo proporciona una funcionalidad de caché robusta, sino que también permite una optimización avanzada de imágenes y archivos, lo que contribuye a mejorar la velocidad de WordPress.

Para configurar Litespeed Cache de manera efectiva, primero, es fundamental asegurarse de que tu servidor soporte este plugin. A continuación, se deberá instalar y activar el plugin desde el repositorio de WordPress. Una vez activado, los ajustes pueden ser personalizados según las necesidades del sitio. Es recomendable habilitar la opción de caché de página, así como la compresión de recursos, lo que permitirá una carga más rápida de elementos estáticos. También es útil implementar el modo de optimización automática de CSS y JavaScript, minimizando así la cantidad de archivos que el navegador necesita descargar.

Además del uso de plugins de caché, el servicio de alojamiento que elijas también tiene un impacto significativo en el rendimiento web de WordPress. Optar por un proveedor que ofrezca servidores optimizados para WordPress, como aquellos con integración de caché avanzada, puede resultar en un aumento notable en la velocidad de carga. Un entorno óptimo de alojamiento combina bien con las técnicas de optimización, asegurando que no solo se cumplan con los estándares de rendimiento, sino también con las recomendaciones de herramientas como PageSpeed Insights.

Por lo tanto, implementar caché y seleccionar un buen servicio de alojamiento son pasos críticos para mejorar la velocidad y el rendimiento de tu sitio web en WordPress, asegurando que tus esfuerzos en SEO técnico sean efectivos y que tu sitio esté preparado para ofrecer la mejor experiencia a los usuarios.

SEO Técnico y Herramientas para Monitorear el Rendimiento

La optimización de WordPress no solo se trata de velocidad, sino que también está intrínsecamente relacionada con el SEO técnico. Para garantizar que un sitio web tenga un buen rendimiento, es esencial supervisar y analizar diversas métricas que afectan tanto la velocidad como la visibilidad en los motores de búsqueda. A través de herramientas como PageSpeed Insights y GTmetrix, se pueden obtener informes detallados sobre el rendimiento de un sitio web, permitiendo identificar áreas que requieren mejoras.

PageSpeed Insights es una herramienta fundamental que evalúa el rendimiento del sitio en dispositivos móviles y de escritorio. Proporciona un puntaje basado en diversas métricas y sugiere recomendaciones que ayudan a mejorar la velocidad de carga. Estos consejos incluyen la optimización de imágenes, el uso de plugins de caché WordPress como LiteSpeed Cache, entre otros. Por su parte, GTmetrix ofrece un análisis más profundo, combinando métricas de PageSpeed y YSlow para ofrecer un desglose más completo del rendimiento web. La información presentada por estas herramientas puede ser crucial para mejorar Core Web Vitals, que son indicadores esenciales en la experiencia de usuario y en el SEO técnico WordPress.

Es importante implementar las recomendaciones que estas herramientas ofrecen. Por ejemplo, al optimizar imágenes WordPress, puedes reducir significativamente los tiempos de carga, lo que a su vez puede mejorar el rendimiento y la experiencia del usuario. Además, trabajar en el SEO técnico también implica asegurar que el código esté limpio y que las configuraciones de los plugins estén optimizadas para no afectar la velocidad. En este contexto, tanto PageSpeed Insights como GTmetrix son aliados valiosos en la estrategia de WPO (Web Performance Optimization) que busca no solo mejorar la velocidad sino también potenciar la visibilidad en los motores de búsqueda. Al seguir estas pautas, un sitio no solo será más rápido, sino también más competitivo en el ámbito digital.