• Publicidad

Extracción datos de web

¿Ya sabes lo que es una referencia? Has progresado, el nível básico es cosa del pasado y ahora estás listo para el siguiente nivel.

Extracción datos de web

Notapor LuisFra » 2008-07-17 16:50 @743

Hola, soy nuevo en esto pero necesito sacar de un sitio web público ciertos datos de las páginas y ordenarlos en una hoja Excel, por ejemplo. El sitio es: http://www.protecnet.go.cr/InsumoSys/Principal.htm

En la consulta por plaguicidas y coadyuvantes obtengo toda la lista de unos 2876 registros de los cuales me interesa sacar los datos (por ejemplo éstos que corresponden al primer registro) y así que el programa me extraiga esos datos de todos los registros y los pueda exportar en una hoja Excel, p.ej.

Código: Seleccionar todo
Código:      3110
Nombre comercial:      2,4-D 40 SL
Fecha de registro:      1/Oct/1991
Tomo Folio Asiento:      VII  404  1977
Clases:     Herbicida
Ingredientes :
concentración mínima - máxima.     2,4-D 0 - 40
Cultivos:      Arroz. Caña de azúcar. Maiz. Pastos. Sorgo.
Toxicidad:      Moderadamente peligroso
DL50 Técnico:      0
DL50 Formulado:      0
Tipo:      Formulado
Registro cancelado:      NO
Observaciones:     
   
Fabricantes aprobados:
Formulaciones Quimicas S.A      Costa Rica
   
Datos de la Compañía Registrante:
Código:       6



De antemano, gracias por la ayuda.
LuisFra
Perlero nuevo
Perlero nuevo
 
Mensajes: 1
Registrado: 2008-07-17 16:25 @726

Publicidad

Notapor explorer » 2008-07-17 17:17 @762

Bienvenido a los foros de Perl en Español, LuisFra.

Muy interesante lo que propones... pero... ¿dónde está el código que estás probando? ¿En dónde te has atascado?
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14477
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor OzX » 2008-07-17 19:21 @848

Hi brota, podrías obtener el código fuente y empezar a limpiar con regex.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$browser = LWP::UserAgent->new;                                                        
$browser->timeout(10);
$browser->agent("Mozilla/5.0 (Windows; U; Windows NT 5.1; es-ES; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14");
$browser->default_header("Referer" => $url);
@code = split("\n",($source=($browser->get($url))->content));
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Luego podrías hacer un foreach y con reg, empezar a sacar los valores que desees luego (y no sé, la verdad, si Excel soporta xml), podrías exportarlo a un xml, para luego importarlo desde Excel.


¡Saludos!
OzX
Perlero nuevo
Perlero nuevo
 
Mensajes: 8
Registrado: 2008-07-14 18:15 @802

Notapor explorer » 2008-07-29 21:55 @955

Este es un proyecto de web scrapping.

Requiere hacer algo de investigación para saber dónde y cómo debemos extraer la información.

Partimos de un enlace http://www.protecnet.go.cr/InsumoSys/Principal.htm, del cual vemos que consiste en un conjunto de marcos. El marco que nos interesa está en el enlace http://www.protecnet.go.cr/InsumoSys/Menu.asp, pues contiene el formulario que hace la petición de información.

Necesitamos averiguar todos los detalles del formulario y del resultado.

Instalamos el módulo WWW::Mechanize. Además del propio módulo, se instalará el programa mech_dump, que nos dirá el formato del formulario.

Lo ejecutamos y vemos
Código: Seleccionar todo
explorer@casa:~/Documents/Desarrollo> mech-dump http://www.protecnet.go.cr/InsumoSys/Menu.asp
POST http://www.protecnet.go.cr/InsumoSys/ConsultarListaInsumos.asp [frmMenu]
  selConsulta=Plaguicidas        (option)   [*Plaguicidas/Plaguicidas y coadyuvantes|Fertilizantes|Eq
  txtCodigo=                     (text)
  selCultivos=0                  (option)   [*0/Cualquiera|264/(Reg.Crec.) Caña de azúcar|38/Aceituna
  selPlagComercial=Cualquiera    (option)   [*Cualquiera|2,4-D 40 SL|2,4-D 60 SL|2,4-D 72 SL|2,4-D 97
  selClasePlag=Cualquiera        (option)   [*Cualquiera|2/Acaricida|45/Acaricida Invertebrado|9/Acei
  selActivos=0                   (option)   [*0/Cualquiera|345/1-Naftil Acido Acetico|180/2,4-D|657/2
  selAgentes=0                   (option)   [*0/Cualquiera|123/Acacia cornigera|217/Acacia farneciana
  selToxicidad=0                 (option)   [*0/Cualquiera|1/Altamente peligroso|2/Extremadamente pel
  selFertComercial=Cualquiera    (option)   [*Cualquiera|FCC  Acido Borico 99.8%/FCC Acido Borico 99.
  selComponentes=0               (option)   [*0/Cualquiera|322/1 - Triacontanol|239/AATC|304/Aceite d
  selEqpMarcas=Cualquiera        (option)   [*Cualquiera|Aerospatiale|AGRO-LHAURA|Agro-top|Agroboss|A
  selTiposEqp=0                  (option)   [*0/Cualquiera|44/Abonadora|46/Abonadora de traccion hum|
  btnConsultar=Consultar         (submit)
(las líneas son mucho más largas, así que las he abreviado)

Ahora ya sabemos cómo hacer la consulta. Escribimos la siguiente prueba:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

use WWW::Mechanize;

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

# Página de dónde partimos
$mech->get( 'http://www.protecnet.go.cr/InsumoSys/Menu.asp' );

# Solicitamos lista de Plaguicidas
$mech->submit_form(
    form_name => 'frmMenu',
    fields    => {
        selConsulta      => 'Plaguicidas',
        txtCodigo        => '',
        selCultivos      =>  0,
        selPlagComercial => 'Cualquiera',
        selClasePlag     => 'Cualquiera',
        selActivos       =>  0,
        selAgentes       =>  0,
        selToxicidad     =>  0,
        selFertComercial => 'Cualquiera',
        selComponentes   =>  0,
        selEqpMarcas     => 'Cualquiera',
        selTiposEqp      =>  0,
    },
    button => 'btnConsultar',
);

# Recorremos todos los enlaces
foreach my $enlace ( $mech->links() ) {
    print $enlace->text(), "->", $enlace->url(), "\n";
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
y vemos que, efectivamente, aparecen todos los enlaces.

Bueno, ahora ya sabemos cómo hacer la petición al formulario para obtener el listado.

Como el resultado de cada plaguicida está en una página HTML en la que los datos están puestos en forma de tabla, voy a instalar también el módulo HTML::TableContentParser.

Vamos a ver el aspecto que va a tener la estructura devuelta por HTML::TableContentParser. Nos interesa, además, quedarnos solo con la tabla 'id' => 'AutoNumber1', así que filtraremos el array de las @$tablas por la primera ([0]) que corresponda a esa identificación.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

use WWW::Mechanize;
use HTML::TableContentParser;

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

$mech->get('http://www.protecnet.go.cr/insumosys/ConsultarInsumo.asp?cCodigo=4913&sTipoQry=Plaguicidas');
my $tablas = $parser->parse( $mech->content() );

my @tablas_principales = grep { exists $_->{id} and $_->{id} eq 'AutoNumber1' } @$tablas;
my $tabla_principal = $tablas_principales[0];

use Data::Dumper;
print Dumper($tabla_principal);
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
y ya tenemos la estructura de nuestra página:
Código: Seleccionar todo
$VAR1 = {
          'width' => '75%',
          'style' => 'border-collapse: collapse',
          'cellspacing' => '4',
          'id' => 'AutoNumber1',
          'cellpadding' => '0',
          'rows' => [
                      {
                        'cells' => [
                                     {
                                       'width' => '100%',
                                       'bgcolor' => '#008080',
                                       'colspan' => '2',
                                       'data' => ' <font color="#FFFFFF" face="Verdana, Arial, Helvetica, sans-serif" size="2">Datos del Plaguicida:</font>',
                                       'height' => '30'
                                     }
                                   ],
                        'data' => '
'
                      },
...

Es decir, es un hash cuyo clave 'rows' contiene un array cuyos elementos son hashes, cuya clave 'data' contiene el valor de las celdas.

Solo nos quedaría filtrar las marchas HTML dentro de las celdas, pero eso lo haremos con otro módulo.

Hay algunas excepciones que debemos tratar:
  • Algunas celdas contienen cabeceras que no nos interesan almacenar. Son las que tienen un color de fondo verde y letra blanca; y las que tienen fondo blanco en la primera celda (titular)
  • Hay celdas de contenidos que pueden corresponder a una sola de titular, como el caso de 'Clases', 'Ingredientes' y 'Agentes'. Se supone que queremos que salgan todos juntos en la misma celda, en Excel
  • En la sección de 'Fabricantes aprobados', siempre puede ser distinto el nombre del fabricante, luego quiere decir que esa sección se debe tratar como un conjunto y no como celdas individuales.

Vamos a hacer una prueba de sacar la información contenida en esa página:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

$|++;

use WWW::Mechanize;
use HTML::TableContentParser;
use HTML::Entities;
use HTML::Strip;

my $mech   = WWW::Mechanize->new();
my $parser = HTML::TableContentParser->new();
my $hs     = HTML::Strip->new();

$mech->get('http://www.protecnet.go.cr/insumosys/ConsultarInsumo.asp?cCodigo=4913&sTipoQry=Plaguicidas');
my $tablas = $parser->parse( $mech->content() );

my $tabla_principal = (grep { exists $_->{id} and $_->{id} eq 'AutoNumber1' } @$tablas)[0];

my $contenido;

foreach my $fila (@{$tabla_principal->{rows}}) {
    foreach my $celda (@{$fila->{cells}}) {
        if ($celda->{data}) {
            $contenido = $hs->parse( $celda->{data} );  # Quitamos las marcas HTML
            $contenido =~ s/\xa0/ /g;                   # Quitamos espacios superfluos
            $contenido =~ s/\s+/ /g;
            $contenido =~ s/^\s+//;
            $contenido =~ s/\s+$//;
       
            if ($celda->{bgcolor}) {
                if ($celda->{bgcolor} eq '#008080') {     # Titulares de sección
                    print "[$contenido=>]\n";
                }
                elsif ($celda->{bgcolor} eq '#C6E3E3') {  # Titulares normales
                    print "\t[$contenido]\n";
                }
                else {
                    print "\t\t!!!$contenido!!!\n";
                }
            }
            else {
                print "\t\t[$contenido]\n";
            }
        }
    }
}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

La salida es:
Código: Seleccionar todo
[Datos del Plaguicida:=>]
        [Código:]
                [4913]
        [Nombre comercial:]
                [Vydate Azul 24 SL]
        [Fecha de registro:]
                [12/Dic/2002]
        [Tomo Folio Asiento:]
                [XV 136 135]
        [Clases:]
                [Insecticida]
        [Ingredientes : concentración mínima - máxima.]
                [Oxamil 0 - 24]
        [Agentes:]
                [Nematodo (Rhadopholus similis)]
        []
                [Nemátodo (Helicotylenchus spp)]
        []
                [Picudo del banano (Cosmopolites sordidus)]
        [Cultivos:]
                [Banano. Plátano.]
        [Toxicidad:]
                [Altamente peligroso]
        [DL50 Técnico:]
                [6]
        [DL50 Formulado:]
                [25]
        [Tipo:]
                [Formulado]
        [Registro cancelado:]
                [NO]
        [Observaciones:]
                []
[Fabricantes aprobados:=>]
        [E.I. Dupont De Nemours]
                [Estados Unidos]
[Datos de la Compañía Registrante:=>]
        [Código:]
                [27]
        [Fecha de registro:]
                [14/Nov/2007]
        [Registrante:]
                [E.I. Dupont de Nemours and Co . Inc.]
        [Cédula:]
                [3-012-061907-36]
        [Teléfono:]
                [290-8822]
        [Nombre del Regente:]
                [José Luis Rojas Castro]
        [Otros datos del Regente:]
                []
        [Nombre del Representante:]
                [Milton Pineda Cheves.]
        [Otros datos del Representante:]
                []
        [Autorizados:]
                []
        [Email:]
                []
        [Observaciones:]
                []
                []
                []
                []


La estrategia que podemos seguir es la siguiente:
  • Hacemos un bucle por todas las celdas que queremos extraer
  • Buscamos dentro de la tabla y sacamos la información

Vamos a hacer una subrutina, a la que le pasamos el titular de la celda que queremos leer, y nos devuelve ese contenido.

Caso especial es el de 'Fabricantes aprobados', de las que nos interesa el valor de las dos columnas.
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

$|++;

use WWW::Mechanize;
use HTML::TableContentParser;
use HTML::Entities;
use HTML::Strip;

my $mech   = WWW::Mechanize->new();
my $parser = HTML::TableContentParser->new();
my $hs     = HTML::Strip->new();

$mech->get('http://www.protecnet.go.cr/insumosys/ConsultarInsumo.asp?cCodigo=3110&sTipoQry=Plaguicidas');
my $tablas = $parser->parse( $mech->content() );

my $tabla_principal = (grep { exists $_->{id} and $_->{id} eq 'AutoNumber1' } @$tablas)[0];

sub valor_de {
    my ($seccion,$titular) = @_;
    my $estamos_en_la_seccion;
    my $estamos_en_el_titular;
    my $lo_hemos_encontrado;
    my @valor_de_la_celda;

    foreach my $fila (@{$tabla_principal->{rows}}) {
        next if !$fila->{cells};

        my $primera_celda = $fila->{cells}->[0];

        if (!$primera_celda->{bgcolor} or !$primera_celda->{data}) {
            $estamos_en_la_seccion = 0;
        }

        if ($primera_celda->{data}) {
            my $contenido = celda_filtrada( $primera_celda->{data} );
            $contenido =~ s/[\s:]+$//;

            if ($primera_celda->{bgcolor} and $primera_celda->{bgcolor} eq '#008080') {
                $estamos_en_la_seccion = ( $contenido eq $seccion ) ? 1 : 0;
                next;
            }

            if ($primera_celda->{bgcolor} and $primera_celda->{bgcolor} eq '#C6E3E3') {
                $estamos_en_el_titular
                    = ( $contenido eq $titular or $seccion eq 'Fabricantes aprobados' ) ? 1 : 0;
            }
        }

        if ($estamos_en_la_seccion and $estamos_en_el_titular) {

            if (!$lo_hemos_encontrado) {
                $lo_hemos_encontrado = 1;
                $titular = '';
            }

            push @valor_de_la_celda, celda_filtrada( $fila->{cells}->[0]->{data} )
                if $seccion eq 'Fabricantes aprobados';

            push @valor_de_la_celda, celda_filtrada( $fila->{cells}->[1]->{data} );
        }
        elsif ($lo_hemos_encontrado) {
            return join '|', @valor_de_la_celda;
        }
    }

    return '';
}

sub celda_filtrada {
    my $celda = shift;

    $celda ||= '';
    $celda = $hs->parse( $celda );  # Quitamos las marcas HTML
    $celda =~ s/\xa0/ /g;           # y los espacios superfluos
    $celda =~ s/\s+/ /g;
    $celda =~ s/\s+$//;
    $celda =~ s/^\s+//;

    return $celda;
}

my @celdas_a_sacar = (
    [ 'Datos del Plaguicida', "C\xF3digo"                                               ],
    [ 'Datos del Plaguicida', 'Nombre comercial'                                        ],
    [ 'Datos del Plaguicida', 'Fecha de registro'                                       ],
    [ 'Datos del Plaguicida', 'Tomo Folio Asiento'                                      ],
    [ 'Datos del Plaguicida', 'Clases'                                                  ],
    [ 'Datos del Plaguicida', "Ingredientes : concentraci\xF3n m\xEDnima - m\xE1xima."  ],
    [ 'Datos del Plaguicida', 'Agentes'                                                 ],
    [ 'Datos del Plaguicida', 'Cultivos'                                                ],
    [ 'Datos del Plaguicida', 'Toxicidad'                                               ],
    [ 'Datos del Plaguicida', "DL50 T\xE9cnico"                                         ],
    [ 'Datos del Plaguicida', 'DL50 Formulado'                                          ],
    [ 'Datos del Plaguicida', 'Tipo'                                                    ],
    [ 'Datos del Plaguicida', 'Registro cancelado'                                      ],
    [ 'Datos del Plaguicida', 'Observaciones'                                           ],
    [ 'Fabricantes aprobados', ''                                                       ],
    [ "Datos de la Compa\xF1\xEDa Registrante", "C\xF3digo"                             ],
);

foreach my $celda (@celdas_a_sacar) {

    my ($seccion, $titular) =  @{$celda};

    my $valor_de_celda = valor_de($seccion, $titular);

    print "$seccion/$titular => $valor_de_celda\n";
}
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4

Sale
Código: Seleccionar todo
explorer@casa:~/Documents/Desarrollo> ./parsea_plaguicida.pl
Datos del Plaguicida/Código => 3110
Datos del Plaguicida/Nombre comercial => 2,4-D 40 SL
Datos del Plaguicida/Fecha de registro => 1/Oct/1991
Datos del Plaguicida/Tomo Folio Asiento => VII 404 1977
Datos del Plaguicida/Clases => Herbicida
Datos del Plaguicida/Ingredientes : concentración mínima - máxima. => 2,4-D 0 - 40
Datos del Plaguicida/Agentes =>
Datos del Plaguicida/Cultivos => Arroz. Caña de azúcar. Maiz. Pastos. Sorgo.
Datos del Plaguicida/Toxicidad => Moderadamente peligroso
Datos del Plaguicida/DL50 Técnico => 0
Datos del Plaguicida/DL50 Formulado => 0
Datos del Plaguicida/Tipo => Formulado
Datos del Plaguicida/Registro cancelado => NO
Datos del Plaguicida/Observaciones =>
Fabricantes aprobados/ => Formulaciones Quimicas S.A|Costa Rica
Datos de la Compañía Registrante/Código => 6


Naturalmente, no es eficiente, porque estamos haciendo una búsqueda por la tabla por cada valor que queremos buscar. Hubiera sido mejor leer toda la tabla, pasarla a una estructura interna y de allí, leer los valores que nos interesan.

Y además... estamos haciendo algunas cosas que se supone que no deberíamos hacer (somos muy vagos). Estamos escribiendo mucho código para algo que debería ser, en teoría, sencillo. Es el momento de cambiar de enfoque. En Perl, eso se consigue cambiando de módulos.

Cambiaremos el módulo HTML::TableContentParser por HTML::TableParser.

El código queda mucho más simple:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;

use WWW::Mechanize;
use HTML::TableParser;

## Titulares que realmente nos interesan extraer
# Se indican en el orden en que queremos que aparezcan
my @cabeceras_utiles = (
    "Datos del Plaguicida|C\xF3digo",
    'Datos del Plaguicida|Nombre comercial',
    'Datos del Plaguicida|Fecha de registro',
    'Datos del Plaguicida|Tomo Folio Asiento',
    'Datos del Plaguicida|Clases',
    "Datos del Plaguicida|Ingredientes :concentraci\xF3n m\xEDnima - m\xE1xima.",
    'Datos del Plaguicida|Agentes',
    'Datos del Plaguicida|Cultivos',
    'Datos del Plaguicida|Toxicidad',
    "Datos del Plaguicida|DL50 T\xE9cnico",
    'Datos del Plaguicida|DL50 Formulado',
    'Datos del Plaguicida|Tipo',
    'Datos del Plaguicida|Registro cancelado',
    'Datos del Plaguicida|Observaciones',
    'Fabricantes aprobados',
    "Datos de la Compa\xF1\xEDa Registrante|C\xF3digo",
);
my $i = 0;
my %cabeceras_utiles = map { $_ => $i++ } @cabeceras_utiles;

## Nuestro robot rascador
my $mechanize = WWW::Mechanize->new();

## Definición de extracción de los datos en tabla
my $htmltableparser
    = HTML::TableParser->new(
        [{ id  => '1.1', row => \&fila }],
        { Decode => 1, DecodeNBSP => 1, Trim => 1, Chomp => 1 }
);

my $seccion;
my $titulo_anterior;
my @fila;

sub fila {                                          # Por cada fila
    my ( $id, $linea, $columnas, $udata ) = @_;
    my @columnas = map { s/\s+/ /g; s/\s*:$//; $_ } @$columnas;

    if ($columnas[0] or $columnas[1]) {
        if ($columnas[0] eq $columnas[1]) {       # Nueva sección
            $seccion = $columnas[0];
        }
        else {
            if ($columnas[0]) {
                $titulo_anterior = $columnas[0];    # Nuevo titular
            }
            my $clave = $seccion;
            $clave .= "|$titulo_anterior" if $seccion ne 'Fabricantes aprobados';
            return if !exists $cabeceras_utiles{$clave};

            #print "$cabeceras_utiles{$clave}:$clave|$titulo_anterior => [$columnas[1]]\n";

            push @{ $fila[ $cabeceras_utiles{ $clave } ] }, $columnas[0] if $seccion eq 'Fabricantes aprobados';
            push @{ $fila[ $cabeceras_utiles{ $clave } ] }, $columnas[1];
        }
    }
}

## Por cada nueva fila
# Inicializamos
@fila = ();
$seccion = $titulo_anterior = '';

# Obtenemos
$mechanize->get('http://www.protecnet.go.cr/insumosys/ConsultarInsumo.asp?cCodigo=4913&sTipoQry=Plaguicidas');

# Filtramos
$htmltableparser->parse( $mechanize->content() );
@fila = map { join "\n", @$_ if $_ } @fila;

# Sacamos
use Data::Dumper;
print Dumper(\@fila);

__END__
 
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Sale
Código: Seleccionar todo
$VAR1 = [
          '4913',
          'Vydate Azul 24 SL',
          '12/Dic/2002',
          'XV 136 135',
          'Insecticida',
          'Oxamil 0 - 24',
          'Nematodo (Rhadopholus similis)
Nem�todo (Helicotylenchus spp)
Picudo del banano (Cosmopolites sordidus)',
          'Banano. Pl�tano.',
          'Altamente peligroso',
          '6',
          '25',
          'Formulado',
          'NO',
          '',
          'E.I. Dupont De Nemours
Estados Unidos',
          '27'
        ];

Cambiando de módulos, tenemos un sistema mucho más sencillo, rápido y directo.

Como detalle, decir que los datos de las páginas web están en codificación ISO-8859-1. Por eso, en el código (y en la salida), aparecen los caracteres acentuados con el código hexadecimal correspondiente a su codificación.

Tenemos ya las herramientas para sacar la información de las páginas web.

Ahora solo queda saber cómo escribir las hojas de cálculo en formato Excel.

Yo voy a usar el módulo Spreadsheet::SimpleExcel. Lo podía haber hecho con el módulo Spreadsheet::WriteExcel (que, además, es necesario para el primer módulo), pero hoy quería hacer las cosas sencillas :)

La forma de hacer la hoja será muy sencilla: introduciremos los datos que vamos leyendo como un array de array, para formar una estructura bidimensional. Y luego, en una sola línea, creamos la hoja Excel a partir de esa estructura.

El programa final queda así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl
#
# Rascando plaguicidas costaricenses.
#
# Joaquín Ferrero. Julio 2008.
#

use strict;
use warnings;
use diagnostics;

use WWW::Mechanize;
use HTML::TableParser;

$|++;

## Titulares que realmente nos interesan
# Se indican en el orden en que queremos que aparezcan
my @cabeceras_utiles = (
    "Datos del Plaguicida|C\xF3digo",
    'Datos del Plaguicida|Nombre comercial',
    'Datos del Plaguicida|Fecha de registro',
    'Datos del Plaguicida|Tomo Folio Asiento',
    'Datos del Plaguicida|Clases',
    "Datos del Plaguicida|Ingredientes :concentraci\xF3n m\xEDnima - m\xE1xima.",
    'Datos del Plaguicida|Agentes',
    'Datos del Plaguicida|Cultivos',
    'Datos del Plaguicida|Toxicidad',
    "Datos del Plaguicida|DL50 T\xE9cnico",
    'Datos del Plaguicida|DL50 Formulado',
    'Datos del Plaguicida|Tipo',
    'Datos del Plaguicida|Registro cancelado',
    'Datos del Plaguicida|Observaciones',
    'Fabricantes aprobados',
    'Fabricantes aprobados (pais)',
    "Datos de la Compa\xF1\xEDa Registrante|C\xF3digo",
);
my $i = 0;
my %cabeceras_utiles = map { $_ => $i++ } @cabeceras_utiles;

## Nuestro robot rascador
my $mechanize = WWW::Mechanize->new();

## Parseo de las páginas
my $seccion;
my $titulo_anterior;
my @fila;

sub fila {                                          # Por cada fila
    my ( $id, $linea, $columnas, $udata ) = @_;
    my @columnas = map { s/\s+/ /g; s/\s*:$//; $_ } @$columnas;

    if ($columnas[0] or $columnas[1]) {
        if ($columnas[0] eq $columnas[1]) {       # Nueva sección
            $seccion = $columnas[0];
        }
        else {
            if ($columnas[0]) {
                $titulo_anterior = $columnas[0];    # Nuevo titular
            }
            my $clave = $seccion;
            $clave .= "|$titulo_anterior" if $seccion ne 'Fabricantes aprobados';
            return if !exists $cabeceras_utiles{$clave};

            #print "$cabeceras_utiles{$clave}:$clave|$titulo_anterior => [$columnas[1]]\n";

            # Caso especial para una columna
            if ($seccion eq 'Fabricantes aprobados') {
                push @{ $fila[ $cabeceras_utiles{ $clave }    ] }, $columnas[0];
                push @{ $fila[ $cabeceras_utiles{ $clave } +1 ] }, $columnas[1];
            } else {
                push @{ $fila[ $cabeceras_utiles{ $clave }    ] }, $columnas[1];
            }
        }
    }
}

## Por cada nueva fila
sub traemos_una_fila {
    my $enlace = shift;
   
    # Inicializamos
    @fila = ();
    $seccion = $titulo_anterior = '';

    # Obtenemos
    $mechanize->get($enlace);

    # Filtramos
    my $htmltableparser = HTML::TableParser->new(
        [{ id  => '1.1', row => \&fila }],
        { MultiMatch => 1, Decode => 1, DecodeNBSP => 1, Trim => 1, Chomp => 1 }
    );
    #$HTML::TableParser::Verbose = 1;
    $htmltableparser->parse( $mechanize->content() );
    @fila = map { join "\n", @$_ if $_ } @fila;
}

### Programa principal

## Conectamos con la página web
print "Conectando a página principal... ";
$mechanize->get( 'http://www.protecnet.go.cr/InsumoSys/Menu.asp' );
print "Ok\n";

# Solicitamos lista de Plaguicidas
print "Solicitando lista... ";
$mechanize->submit_form(
    form_name => 'frmMenu',
    fields    => {
        selConsulta     =>  'Plaguicidas',
        txtCodigo       =>  '',
        selCultivos     =>  0,
        selPlagComercial=>  'Cualquiera',
        selClasePlag    =>  'Cualquiera',
        selActivos      =>  0,
        selAgentes      =>  0,
        selToxicidad    =>  0,
        selFertComercial=>  'Cualquiera',
        selComponentes  =>  0,
        selEqpMarcas    =>  'Cualquiera',
        selTiposEqp     =>  0,
    },
    button => 'btnConsultar',
);
print "Ok\n";
                                                                                                               
# Recorremos todos los enlaces de la página
my @filas;
foreach my $enlace ($mechanize->links()) {
    print $enlace->text(), "->", $enlace->url(), "\n";

    traemos_una_fila( $enlace->url() );

    # y la guardamos
    push @filas, [ @fila ];

    #last if !$i--;
}

# Sacamos a Excel
use Spreadsheet::SimpleExcel;
binmode(\*STDOUT);

# Cabeceras
my @header;
foreach (@cabeceras_utiles) {
    my ($s, $t) = split /\|/;
    push @header, ( $t ) ? $t : $s;
}

# Creamos un nuevo objeto Excel
my $excel = Spreadsheet::SimpleExcel->new();

# Añadimos una hoja
$excel->add_worksheet('Plaguicidas',{-headers => \@header, -data => \@filas});

# Salida
$excel->output_to_file("plaguicidas_cr.xls") or die $excel->errstr();

__END__
Coloreado en 0.004 segundos, usando GeSHi 1.0.8.4

Y ya está.

Quedan algunas cosas sin comentar, como por ejemplo, qué hacen los join(), map(), split() y alguna expresión regular, a lo largo del programa, pero... este post se estaba haciendo muy largo... Si hay dudas, pues a continuación de éste.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14477
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Intermedio

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 0 invitados