---
title: "Scraping web con Perl: guía paso a paso"
slug: web-scraping-with-perl
date: 2026-02-22T07:20:36+00:00
modified: 2026-02-22T07:20:38+00:00
permalink: https://brightdata.es/blog/datos-web/web-scraping-with-perl
type: blog
---

[ Blog ](https://brightdata.es/blog "Blog") / [Datos web](https://brightdata.es/blog/datos-web)







 [Datos web](https://brightdata.es/blog/datos-web)

# Scraping web con Perl: guía paso a paso

Aprenda a realizar scraping web con Perl utilizando varios métodos y comprenda los retos que ello conlleva.

 20 min de lectura





 [ ![](https://media.brightdata.es/2023/07/Aniket-Bhattacharyea-50x50.png) ](https://brightdata.com/blog/authors/aniket-bhattacharyea)

 [Aniket Bhattacharyea

 ](https://brightdata.com/blog/authors/aniket-bhattacharyea)





 ![Web Scraping with Perl blog image](https://media.brightdata.es/2024/06/Web-Scraping-with-Perl.svg)





[Perl](https://www.perl.org/)es uno de los lenguajes más populares y, gracias a su amplia colección de módulos, es una excelente opción para escribir Scrapers web.

En este artículo, trataremos lo siguiente:

- Cómo realizar Scraping web con Perl utilizando los siguientes métodos:
    - `LWP::UserAgent`y`HTML::TreeBuilder`
    - `Web::Scraper`
    - `Mojo::UserAgent`y`Mojo::DOM`
    - `XML::LibXML`
- Retos del scraping web con Perl
- Conclusión

## <a></a>Scraping web con Perl

Para seguir el artículo, asegúrate de tener instalada la última versión de Perl. El código de este artículo se ha probado con Perl 5.38.2. Este artículo también da por supuesto que sabes cómo[instalar módulos de Perl utilizando`cpanm`](https://www.cpan.org/modules/INSTALL.html).

En este artículo, extraerá las citas del[sitio web Quotes to Scrape](https://quotes.toscrape.com/). Antes de poder extraer datos del sitio web, debe comprender cómo está estructurado el HTML. Abra el sitio web en el navegador y presione**CTRL + Mayús + I**(Windows) o**Comando + Mayús + C**(Mac) para abrir el cuadro de diálogo**Inspeccionar elemento**.

Al inspeccionar los elementos, verás que cada cita se almacena en un`div`con la clase`quote`. Cada cita contiene un`span`con la clase`text`y un`pequeño`elemento para almacenar el texto y el nombre del autor, respectivamente:

![Inspecting an Albert Einstein quote](https://media.brightdata.es/2024/06/Inspecting-the-elements.png)### <a></a>Uso de LWP::UserAgent y HTML::TreeBuilder

[`LWP::UserAgent`](https://metacpan.org/pod/LWP::UserAgent)forma parte de`LWP`, un grupo de módulos que interactúan con la web. El módulo`LWP::UserAgent`se puede utilizar para realizar una solicitud HTTP a una página web y devolver el contenido HTML. A continuación, puede utilizar el módulo`HTML::TreeBuilder`de[`HTML::Tree`](https://metacpan.org/dist/HTML-Tree)para realizar el parseo del HTML y extraer información.

Para utilizar `LWP::UserAgent` y `HTML::TreeBuilder`, instale los módulos con los siguientes comandos:

```none
cpanm Bundle::LWP
cpanm HTML::Tree

```

Cree un archivo llamado `lwp-and-tree-builder.pl`. Aquí es donde escribirá el código. A continuación, pegue las dos líneas siguientes en ese archivo:

```none
use LWP::UserAgent;
use HTML::TreeBuilder;

```

Este código indica al intérprete de Perl que incluya los módulos `LWP::UserAgent` y `HTML::TreeBuilder`.

Defina una instancia de `LWP::UserAgent` y establezca el encabezado `User-Agent` en `Quotes Scraper`:

```none
my $ua = LWP::UserAgent->new;
$ua->agent("Quotes Scraper");

```

Defina la URL del sitio web de destino y cree una instancia de `HTML::TreeBuilder`:

```none
my $url = "https://quotes.toscrape.com/";
my $root = HTML::TreeBuilder->new();

```

Ahora puede realizar la solicitud HTTP:

```none
my $request = $ua->get($url) or die "Se ha producido un error $!n";

```

Pega la siguiente instrucción if-else que comprueba si la solicitud se ha realizado correctamente o no:

```none
if ($request->is_success) {

} else {
  print "No se puede realizar el parseo del resultado. " . $request->status_line . "n";
}

```

Si la solicitud se ha realizado correctamente, puede comenzar a extraer datos.

Utilice el método `parse` de `HTML::TreeBuilder` para realizar el parseo de la respuesta HTML. Pegue el siguiente código dentro del bloque `if`:

```none
$root->parse($request->content);

```

Ahora, utilice el método `look_down` para buscar los elementos `div` con la clase `quote`:

```none
my @quotes = $root->look_down(
    _tag => 'div',
    class => 'quote'
);

```

Itere sobre la matriz de citas, utilice `look_down` para encontrar el texto y el autor, y imprímalos:

```none
foreach my $quote (@quotes) {
    my $text = $quote->look_down(
        _tag => 'span',
        class => 'text'
    )->as_text;

    my $author = $quote->look_down(
        _tag => 'small',
        class => 'author'
    )->as_text;

    print "$text: $authorn";
}

```

El código completo tiene este aspecto:

```none
use LWP::UserAgent;
use HTML::TreeBuilder;

my $ua = LWP::UserAgent->new;
$ua->agent("Quotes Scraper");

my $url = "https://quotes.toscrape.com/";
my $root = HTML::TreeBuilder->new();

my $request = $ua->get($url) or die "Se ha producido un error $!n";

if ($request->is_success) {
    $root->parse($request->content);
    my @quotes = $root->look_down(
        _tag => 'div',
        class => 'quote'
    );

    foreach my $quote (@quotes) {
        my $text = $quote->look_down(
            _tag => 'span',
            class => 'text'
        )->as_text;

        my $author = $quote->look_down(
            _tag => 'small',
            class => 'author'
        )->as_text;

        print "$text: $authorn";
    }

} else {
  print "No se puede realizar el parseo del resultado. " . $request->status_line . "n";
}

```

Ejecute este código con `perl lwp-and-tree-builder.pl` y debería ver el siguiente resultado:

```none
«El mundo tal y como lo hemos creado es un proceso de nuestro pensamiento. No se puede cambiar sin cambiar nuestro pensamiento».: Albert Einstein
«Son nuestras elecciones, Harry, las que muestran lo que realmente somos, mucho más que nuestras habilidades».: J.K. Rowling
«Solo hay dos formas de vivir la vida. Una es como si nada fuera un milagro. La otra es como si todo fuera un milagro».: Albert Einstein
«La persona, ya sea caballero o dama, que no disfruta de una buena novela, debe ser intolerablemente estúpida»: Jane Austen.
«La imperfección es belleza, la locura es genio y es mejor ser absolutamente ridículo que absolutamente aburrido»: Marilyn Monroe.
«Intenta no convertirte en un hombre de éxito. Más bien, conviértete en un hombre de valor»: Albert Einstein.
«Es mejor ser odiado por lo que eres que ser amado por lo que no eres»: André Gide
«No he fracasado. Solo he encontrado 10 000 formas que no funcionan»: Thomas A. Edison
«Una mujer es como una bolsita de té: nunca sabes lo fuerte que es hasta que la sumerges en agua caliente»: Eleanor Roosevelt.
«Un día sin sol es como, ya sabes, la noche»: Steve Martin.

```

### <a></a>Uso de Web::Scraper

[`Web::Scraper`](https://metacpan.org/pod/Web::Scraper)es una biblioteca de Scraping web inspirada en[ScrAPI de Ruby](https://github.com/assaf/scrapi). Proporciona un lenguaje específico de dominio (DSL) para extraer documentos HTML y XML. Consulte este artículo para [obtener más información sobre el Scraping web con Ruby](/blog/how-tos/web-scraping-with-ruby).

Para utilizar `Web::Scraper`, instala el módulo con `cpanm Web::Scraper`.

Crea un nuevo archivo llamado `web-scraper.pl` e incluye los siguientes módulos necesarios:

```none
use URI;
use Web::Scraper;
use Encode;

```

A continuación, debe definir un bloque `de Scraper` utilizando el DSL del módulo. El DSL facilita la definición de un Scraper en solo unas pocas líneas. Comience definiendo un bloque `de Scraper` llamado `$quotes`:

```none
my $quotes = Scraper {

};

```

El método `Scraper` define la lógica del Scraper, que se ejecuta cuando se llama al método `scrape` más adelante. Dentro del bloque `Scraper`, utiliza el método `process` para buscar elementos utilizando selectores CSS y ejecutar una función.

Comience por buscar todos los elementos `div` con la clase `quote`:

```none
# Analizar todos los `div` con la clase `quote`
process 'div.quote', "quotes[]" => Scraper {

};

```

Este código busca todos los elementos `div` con la clase `quote` y los almacena en la matriz `quotes`. Para cada elemento, ejecuta el método `Scraper`, que se define de la siguiente manera:

```none
# Y, en cada div, busca `span` con la clase `text`
process_first "span.text", text => 'TEXT';
# obtén `small` con la clase `author`
process_first "small", author => 'TEXT';

```

El método `process_first` encuentra el primer elemento que coincide con el selector CSS. Aquí, se busca el primer elemento `span` con la clase `text` y, a continuación, se extrae su texto y se almacena en la clave `text`. Para el nombre del autor, se busca el primer elemento `small` y se extrae el texto para almacenarlo en la clave `author`.

El bloque completo `del Scraper` tiene este aspecto:

```none
my $quotes = Scraper {
    # Analizar todos los `div` con clase `quote`
    process 'div.quote', "quotes[]" => Scraper {
    # Y, en cada div, buscar `span` con clase `text`
    process_first "span.text", text => 'TEXT';
    # obtener `small` con la clase `author`
    process_first "small", author => 'TEXT';
    };
};

```

Ahora, llama al método `scrape` y pasa la URL para iniciar el rastreo:

```none
my $res = $quotes->scrape( URI->new("https://quotes.toscrape.com/") );

```

Por último, itera sobre la matriz `de citas` e imprime el resultado:

```none
# iterar sobre la matriz
for my $quote (@{$res->{quotes}}) {
    print Encode::encode("utf8", "$quote->{text}: $quote->{author}n");
}

```

El código completo tiene este aspecto:

```none
use URI;
use Web::Scraper;
use Encode;

my $quotes = scraper {
    # Analizar todos los `div` con la clase `quote`
    process 'div.quote', "quotes[]" => scraper {
    # Y, en cada div, buscar `span` con la clase `text`
    process_first "span.text", text => 'TEXT';
    # obtener `small` con clase `author`
    process_first "small", author => 'TEXT';
    };
};

my $res = $quotes->scrape( URI->new("https://quotes.toscrape.com/") );

# iterar sobre la matriz
for my $quote (@{$res->{quotes}}) {
    print Encode::encode("utf8", "$quote->{text}: $quote->{author}n");
}

```

Ejecute el código anterior con `perl web-scraper.pl` y debería obtener el siguiente resultado:

```none
«El mundo tal y como lo hemos creado es un proceso de nuestro pensamiento. No se puede cambiar sin cambiar nuestro pensamiento».: Albert Einstein
«Son nuestras elecciones, Harry, las que muestran lo que realmente somos, mucho más que nuestras habilidades».: J.K. Rowling
«Solo hay dos formas de vivir la vida. Una es como si nada fuera un milagro. La otra es como si todo fuera un milagro».: Albert Einstein
«La persona, ya sea caballero o dama, que no disfruta de una buena novela, debe ser intolerablemente estúpida»: Jane Austen.
«La imperfección es belleza, la locura es genio y es mejor ser absolutamente ridículo que absolutamente aburrido»: Marilyn Monroe.
«Intenta no convertirte en un hombre de éxito. Más bien, conviértete en un hombre de valor»: Albert Einstein.
«Es mejor ser odiado por lo que eres que ser amado por lo que no eres»: André Gide
«No he fracasado. Solo he encontrado 10 000 formas que no funcionan»: Thomas A. Edison
«Una mujer es como una bolsita de té: nunca sabes lo fuerte que es hasta que la sumerges en agua caliente»: Eleanor Roosevelt
«Un día sin sol es como, ya sabes, la noche»: Steve Martin

```

### <a></a>Uso de Mojo::UserAgent y Mojo::DOM

[`Mojo::UserAgent`](https://metacpan.org/pod/Mojo::UserAgent)y[`Mojo::DOM`](https://metacpan.org/pod/Mojo::DOM)forman parte del marco[Mojolicious](https://mojolicious.org/), un marco web en tiempo real para Perl. En términos de funcionalidad, son similares a`LWP::UserAgent`y`HTML::TreeBuilder`.

Para utilizar `Mojo::UserAgent` y `Mojo::DOM`, instala los módulos con el siguiente comando:

```none
cpanm Mojo::UserAgent
cpanm Mojo::DOM

```

Cree un nuevo archivo llamado `mojo.pl` e incluya los módulos `Mojo::USeragent` y `Mojo::DOM`:

```none
use Mojo::UserAgent;
use Mojo::DOM;

```

Defina una instancia de `Mojo::UserAgent` y realice la solicitud HTTP:

```none
my $ua  = Mojo::UserAgent->new;
my $res = $ua->get('https://quotes.toscrape.com/')->result;

```

De forma similar a `LWP::UserAgent`, utilice el siguiente bloque if-else para comprobar si la solicitud se ha realizado correctamente:

```none
if ($res->is_success) {

} else {
    print "No se puede realizar el parseo del resultado. " . $res->message . "n";
}

```

En el bloque `if`, inicializa una instancia de `Mojo::DOM`:

```none
my $dom = Mojo::DOM->new($res->body);

```

Utilice el método `find` para buscar todos los elementos `div` con la clase `quote`:

```none
my @quotes = $dom->find('div.quote')->each;

```

Iterar sobre la matriz `de citas` y extraer el texto y los nombres de los autores:

```none
foreach my $quote (@quotes) {
    my $text = $quote->find('span.text')->map('text')->join;
    my $author = $quote->find('small.author')->map('text')->join;

    print "$text: $authorn";
}

```

El siguiente es el código completo:

```none
use Mojo::UserAgent;
use Mojo::DOM;

my $ua  = Mojo::UserAgent->new;
my $res = $ua->get('https://quotes.toscrape.com/')->result;

if ($res->is_success) {
    my $dom = Mojo::DOM->new($res->body);
    my @quotes = $dom->find('div.quote')->each;

    foreach my $quote (@quotes) {
        my $text = $quote->find('span.text')->map('text')->join;
        my $author = $quote->find('small.author')->map('text')->join;

        print "$text: $authorn";
    }
} else {
    print "No se puede realizar el parseo del resultado. " . $res->message . "n";
}

```

Ejecute este código con `perl mojo.pl` y debería obtener el siguiente resultado:

```none
«El mundo tal y como lo hemos creado es un proceso de nuestro pensamiento. No se puede cambiar sin cambiar nuestra forma de pensar»: Albert Einstein.
«Son nuestras elecciones, Harry, las que muestran lo que realmente somos, mucho más que nuestras habilidades»: J. K. Rowling.
«Solo hay dos formas de vivir la vida. Una es como si nada fuera un milagro. La otra es como si todo fuera un milagro»: Albert Einstein.
«La persona, ya sea caballero o dama, que no disfruta de una buena novela, debe ser intolerablemente estúpida»: Jane Austen.
«La imperfección es belleza, la locura es genio y es mejor ser absolutamente ridículo que absolutamente aburrido»: Marilyn Monroe.
«Intenta no convertirte en un hombre de éxito. Más bien, conviértete en un hombre de valor»: Albert Einstein.
«Es mejor ser odiado por lo que eres que ser amado por lo que no eres»: André Gide
«No he fracasado. Solo he encontrado 10 000 formas que no funcionan»: Thomas A. Edison
«Una mujer es como una bolsita de té: nunca sabes lo fuerte que es hasta que la sumerges en agua caliente»: Eleanor Roosevelt.
«Un día sin sol es como, ya sabes, la noche»: Steve Martin.

```

### <a></a>Uso de XML::LibXML

El módulo Perl[`XML::LibXML`](https://metacpan.org/dist/XML-LibXML)es un envoltorio de la biblioteca[`libxml2`](http://www.xmlsoft.org/). El módulo`XML::LibXML`proporciona un potente analizador XHTML con capacidades[XPath](/faqs/xpath).

Utiliza `cpanm` para instalar el módulo:

```none
cpanm XML::LibXML

```

A continuación, crea un nuevo archivo llamado `xml-libxml.pl`. Al igual que con `HTML::TreeBuilder`, necesitas utilizar una biblioteca como `LWP::UserAgent` para realizar la solicitud HTTP al sitio web y obtener el contenido HTML, que pasas a `XML::LibXML`.

Pegue el siguiente código, que configura el módulo `LWP:UserAgent` y obtiene el contenido HTML de la página web:

```none
use LWP::UserAgent;
use XML::LibXML;
use open qw( :std :encoding(UTF-8) );

my $ua = LWP::UserAgent->new;
$ua->agent("Quotes Scraper");

my $url = "https://quotes.toscrape.com/";

my $request = $ua->get($url) or die "Se ha producido un error $!n";

if ($request->is_success) {

} else {
  print "No se puede realizar el Parseo del resultado. " . $request->status_line . "n";
}

```

Dentro del bloque `if`, comience realizando el parseo del documento HTML utilizando el método `load_html`:

```none
$dom = XML::LibXML->load_html(string => $request->content, recover => 1, suppress_errors => 1);

```

La opción `recover` le indica al analizador que continúe analizando el HTML en caso de error, y la opción `suppress_errors` hace que el analizador no imprima los errores de parseo HTML en la consola. Dado que los documentos HTML no se validan tan estrictamente como los documentos XHTML, es probable que encuentres errores de parseo no fatales. Estas opciones mantienen el código en funcionamiento en caso de que se produzcan esos errores.

Una vez realizado el parseo del HTML, puede utilizar el método `indnodes` para buscar los elementos basándose en su expresión XPath:

```none
my $xpath = '//div[@class="quote"]';

foreach my $quote ($dom->findnodes($xpath)) {
        my ($text) = $quote->findnodes('.//span[@class="text"]')->to_literal_list;

        my ($author) = $quote->findnodes('.//small[@class="author"]')->to_literal_list;

        print "$text: $authorn";
}

```

El código completo tiene este aspecto:

```none
use LWP::UserAgent;
use XML::LibXML;
use open qw( :std :encoding(UTF-8) );

my $ua = LWP::UserAgent->new;
$ua->agent("Quotes Scraper");

my $url = "https://quotes.toscrape.com/";

my $request = $ua->get($url) or die "Se ha producido un error $!n";

if ($request->is_success) {
    $dom = XML::LibXML->load_html(string => $request->content, recover => 1, suppress_errors => 1);

    my $xpath = '//div[@class="quote"]';

    foreach my $quote ($dom->findnodes($xpath)) {
        my ($text) = $quote->findnodes('.//span[@class="text"]')->to_literal_list;

        my ($author) = $quote->findnodes('.//small[@class="author"]')->to_literal_list;

        print "$text: $authorn";
    }

} else {
  print "No se puede realizar el parseo del resultado. " . $request->status_line . "n";
}

```

Ejecute el código con `perl xml-libxml.pl` y debería ver el siguiente resultado:

```none
«El mundo tal y como lo hemos creado es un proceso de nuestro pensamiento. No se puede cambiar sin cambiar nuestra forma de pensar».: Albert Einstein
«Son nuestras elecciones, Harry, las que muestran lo que realmente somos, mucho más que nuestras habilidades».: J.K. Rowling
«Solo hay dos formas de vivir la vida. Una es como si nada fuera un milagro. La otra es como si todo fuera un milagro».: Albert Einstein
«La persona, ya sea caballero o dama, que no disfruta de una buena novela, debe ser intolerablemente estúpida»: Jane Austen.
«La imperfección es belleza, la locura es genio y es mejor ser absolutamente ridículo que absolutamente aburrido»: Marilyn Monroe.
«Intenta no convertirte en un hombre de éxito. Más bien conviértete en un hombre de valor»: Albert Einstein.
«Es mejor ser odiado por lo que eres que ser amado por lo que no eres»: André Gide
«No he fracasado. Solo he encontrado 10 000 formas que no funcionan»: Thomas A. Edison
«Una mujer es como una bolsita de té: nunca sabes lo fuerte que es hasta que la sumerges en agua caliente»: Eleanor Roosevelt.
«Un día sin sol es como, ya sabes, la noche»: Steve Martin.

```

Puedes encontrar todo el código de este tutorial en este[repositorio de GitHub](https://github.com/heraldofsolace/perl-scraping-demo).

## <a></a>Retos del Scraping web en Perl

Aunque Perl facilita el scraping web gracias a sus potentes módulos, los desarrolladores suelen encontrarse con algunos problemas comunes que pueden ralentizar o impedir por completo el Scraping web. A continuación se enumeran algunos de los retos a los que es probable que te enfrentes.

### <a></a>Manejo de la paginación

Los sitios web que manejan un gran volumen de datos no suelen enviar todos los datos de una sola vez. Por lo general, los datos se envían en varias páginas, y es necesario manejar la paginación para asegurarse de extraer todos los datos. Hay dos pasos para manejar la paginación:

1. Compruebe si existen otras páginas. Por lo general, puede buscar un botón**«Página siguiente»**en la página, o puede intentar cargar la página siguiente y buscar un error.
2. Si existen otras páginas, cargue la página siguiente y extraiga los datos.

En el caso de los sitios web estáticos, en los que cada página tiene su propia URL, puede ejecutar un bucle y cargar nuevas páginas incrementando el parámetro de número de página en la URL. O si utiliza un módulo como[`WWW::Mechanize`](https://metacpan.org/pod/WWW::Mechanize), simplemente puede seguir la URL**de la página siguiente**.

Aquí tienes el Scraper de citas modificado para gestionar la paginación utilizando `WWW::Mechanize`. Ten en cuenta el uso de `follow_link`:

```none
use WWW::Mechanize ();
use HTML::TreeBuilder;
use open qw( :std :encoding(UTF-8) );

my $mech = WWW::Mechanize->new();

my $url = "https://quotes.toscrape.com/";
my $root = HTML::TreeBuilder->new();

my $request = $mech->get($url);
my $next_page = $mech->find_link(text_regex => qr/Next/);

while ($next_page) {
    $root->parse($mech->content);
    my @quotes = $root->look_down(
        _tag => 'div',
        class => 'quote'
    );

    foreach my $quote (@quotes) {
        my $text = $quote->look_down(
            _tag => 'span',
            class => 'text'
        )->as_text;

        my $author = $quote->look_down(
            _tag => 'small',
            class => 'author'
        )->as_text;

        print "$text: $authorn";
}

$mech->follow_link(url => $next_page->url);
$next_page = $mech->find_link(text_regex => qr/Next/);
}

```

Para gestionar sitios web dinámicos que cargan la página siguiente utilizando JavaScript, consulte nuestra guía sobre[cómo extraer datos de sitios web dinámicos con Python,](/blog/how-tos/scrape-dynamic-websites-python) o continúe leyendo.

### <a></a>Proxy rotativo

Los proxies son utilizados habitualmente por los scrapers web para proteger su privacidad y anonimato y para evadir las prohibiciones de direcciones IP. Módulos como`LWP::UserAgent`tienen la opción de configurar proxies para el scraping. Sin embargo, el uso de un único servidor proxy sigue entrañando el riesgo de que la IP sea prohibida. Por eso se recomienda utilizar varios servidores proxy y rotarlos. A continuación se muestra un ejemplo muy sencillo de cómo hacerlo utilizando`LWP::UserAgent`.

Comience por definir una matriz de Proxies. A continuación, elija uno al azar y configure el Proxy utilizando el método `Proxy`:

```none
my @proxies = ( 'https://proxy1.com', 'https://proxy2.com', 'http://proxy3.com' );

my $index = rand @proxies;
my $proxy = $proxies[$index];
$ua->proxy(['http', 'https'], $proxy);

```

Ahora puede enviar una solicitud como de costumbre. Si la solicitud falla, es probable que el Proxy haya sido bloqueado, por lo que puede eliminar ese Proxy de la lista, elegir otro diferente y volver a intentarlo:

```none
if(request->is_success) {
    # Continúa con el rastreo
} else {
    # Elimina el Proxy de la lista
    splice(@proxies, $index, 1);

    # Vuelve a intentarlo
}

```

### <a></a>Manejo de trampas honeypot

[Las trampas honeypot](https://www.imperva.com/learn/application-security/honeypot-honeynet/)son una técnica común empleada por los administradores web para atrapar bots y Scrapers. Por lo general, utilizan enlaces con la propiedad`de visualización`establecida en`none`, lo que los hace invisibles para los usuarios humanos. Sin embargo, un bot puede detectarlos y seguir el enlace, lo que le lleva a una página web señuelo y le aleja del producto principal.

Para solucionar este problema, comprueba la propiedad `de visualización` de los enlaces antes de seguirlos. A continuación se muestra una forma de hacerlo utilizando `HTML::TreeBuilder`:

```none
my @links = $root->look_down(
    _tag => 'a',
);

foreach my $link (@qlinks) {
    my $style = $link->attr('style');
    if(defined $style && $style =~ /dislay: none/) {
        # ¡Se ha detectado un honeypot!
    } else {
        # Se puede continuar con seguridad
    }
}

```

### <a></a>Resolución de CAPTCHA

Los CAPTCHA ayudan a evitar el acceso no autorizado a un sitio web. Sin embargo, también pueden impedir que los Scrapers recopilen información de las páginas web.

Para combatir los CAPTCHAs, puede utilizar un servicio como Bright Data Web Unlocker, que [realiza la resolución de CAPTCHA](/products/web-unlocker/captcha-solver) por usted.

A continuación se muestra un ejemplo del uso de Bright Data Web Unlocker para realizar una solicitud HTTP:

```none
use LWP::UserAgent;
my $agent = LWP::UserAgent->new();
$agent->Proxy(['http', 'https'], "http://brd-customer-hl_6d74fc42-zona-residential_proxy4:<a class="__cf_email__" data-cfemail="1c242d2e6d7364732a6c7328285c7e6e78326f696c796e6c6e736465327573" href="/cdn-cgi/l/email-protection">[email protected]</a>:22225");
print $agent->get('http://lumtest.com/myip.json')->content();

```

Cuando realizas una solicitud HTTP con Web Unlocker, este realiza la resolución de CAPTCHA, evita las medidas antibots y se encarga de la gestión del Proxy por ti.

### <a></a>Rastreo de sitios web dinámicos

Hasta ahora, todos los ejemplos que has aprendido aquí extraen datos de sitios web estáticos. Sin embargo, las aplicaciones de página única (SPA) y otros sitios web dinámicos necesitan técnicas más avanzadas.

Los sitios web dinámicos utilizan JavaScript para cargar el contenido de la página, lo que significa que necesitas herramientas de scraping que sean capaces de ejecutar JavaScript.[Selenium](/blog/how-tos/using-selenium-for-web-scraping#:~:text=heard%20of%C2%A0Selenium.-,It%E2%80%99s,-an%20open%20source)es una de esas herramientas que puede emular un navegador para ejecutar sitios web dinámicos. A continuación se muestra un pequeño fragmento de código de este módulo en acción:

```none
use Selenium::Remote::Driver;

my $driver = Selenium::Remote::Driver->new;
$driver->get('http://example.com');
my $elem = $driver->find_element_by_id('foo');
print $elem->get_text();
$driver->quit();

```

## <a></a>Conclusión

Perl, gracias a su robusta colección de módulos, es un lenguaje excelente para el Scraping web. En este artículo, has aprendido a extraer datos de páginas web en Perl utilizando lo siguiente:

- `LWP::UserAgent`y`HTML::TreeBuilder`
- `Web::Scraper`
- `Mojo::UserAgent`y`Mojo::DOM`
- `XML::LibXML`

Sin embargo, como has visto, el scraping web se enfrenta a muchos retos en situaciones reales cuando los propietarios de sitios web están decididos a impedir que los Scrapers realicen scraping. Este artículo ha arrojado algo de luz sobre algunas situaciones comunes y cómo combatirlas. Sin embargo, intentar resolver esos retos por tu cuenta puede resultar tedioso y propenso a errores. Ahí es donde[Bright Data](/)puede ayudar. Con los [mejores servicios de Proxy](/proxy-types), un[Navegador de scraping](/products/scraping-browser),[un Web Unlocker](/products/web-unlocker) y [la API de scraping web definitiva](/products/web-scraper), Bright Data es una solución integral para realizar scraping web con facilidad. ¡Comience hoy mismo una prueba gratuita!



Contactar ventasPrueba gratuita![google social icon](/wp-content/themes/brightdata/assets/images/ic_google.svg)









 Tabla de Contenidos













 [ ](https://news.ycombinator.com/submitlink?t=Scraping+web+con+Perl%3A+gu%C3%ADa+paso+a+paso&u=https://brightdata.es/blog/datos-web/web-scraping-with-perl) [ ](https://www.linkedin.com/shareArticle?mini=true&title=Scraping+web+con+Perl%3A+gu%C3%ADa+paso+a+paso&url=https://brightdata.es/blog/datos-web/web-scraping-with-perl) [ ](http://www.reddit.com/submit?title=Scraping+web+con+Perl%3A+gu%C3%ADa+paso+a+paso&url=https://brightdata.es/blog/datos-web/web-scraping-with-perl)







##  Usted también puede estar interesado en

 [ ![Best HTML Parsing Libraries main blog image](https://media.brightdata.es/2024/03/Best-HTML-Parsing-Libraries.svg) ](https://brightdata.es/blog/datos-web/best-html-parsers "Las mejores bibliotecas de parseo HTML para el Scraping web")

 [Datos web



 ![Antonello Zanini](https://media.brightdata.es/2022/12/Antonello-Zanini-2-50x50.jpg)

Antonello Zanini

Technical Writer





### Las mejores bibliotecas de parseo HTML para el Scraping web

Descubra los mejores analizadores HTML para el Scraping web y la extracción de datos, incluidos httpx, AIOHTTP y urllib.



 24-Mar-2024

 8 min de lectura

 ](https://brightdata.es/blog/datos-web/best-html-parsers)

 [ ![Best headless browsers main blog image](https://media.brightdata.es/2024/03/Best-Headless-Browsers.svg) ](https://brightdata.es/blog/datos-web/best-headless-browsers "Los mejores navegadores sin interfaz gráfica para el raspado web y realizar pruebas")

 [Datos web



 ![Antonello Zanini](https://media.brightdata.es/2022/12/Antonello-Zanini-2-50x50.jpg)

Antonello Zanini

Technical Writer





### Los mejores navegadores sin interfaz gráfica para el raspado web y realizar pruebas

Explora el mundo de los navegadores sin interfaz gráfica: aprende qué son, cómo controlarlos mediante programación y descubre las mejores bibliotecas para realizar pruebas web y automatizarlas sin problemas.



 20-Mar-2024

 12 min de lectura

 ](https://brightdata.es/blog/datos-web/best-headless-browsers)

 [ ![People coding at computers with data and media icons.](https://media.brightdata.es/2023/01/Web-scraping-with-R.png) ](https://brightdata.es/blog/procedimientos/web-scraping-with-r "Guía práctica para raspado web en R")

 [Procedimientos



 ![Man with beard smiling, wearing black shirt.](https://media.brightdata.es/2021/07/avivb-50x50.png)

Aviv Besinsky





### Guía práctica para raspado web en R

En este tutorial, recorreremos todos los pasos necesarios para hacer raspado web en R con rvest, con el objetivo de extraer reseñas de productos de una URL de acceso público del sitio web de Amazon.



 09-Jan-2023

 9 min de lectura

 ](https://brightdata.es/blog/procedimientos/web-scraping-with-r)
