• Publicidad

Extracción de datos de una página web

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Extracción de datos de una página web

Notapor agaton » 2007-12-12 19:13 @842

Hola.

Necesito hacer un programa que se conecte a esta página:

http://onlae.terra.es/loteria/navidad07/PrincipalD.htm

y después haga consultas de números de forma automática (por ejemplo del 10000 al 10999), y almacene los datos que devuelven las consultas en un archivo de texto.

¿Es esto posible con Perl? Si es así, ¿algún experto puede orientarme de cómo hacerse?

¡Gracias!
Última edición por agaton el 2008-01-17 18:21 @806, editado 2 veces en total
agaton
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2007-12-12 19:07 @838

Publicidad

Notapor explorer » 2007-12-12 21:37 @943

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

Tu problema es muy sencillo. Solo hay que combinar un par de módulos externos.

Esta es una de las posibles soluciones:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
        1 #!/usr/bin/perl
        2 #
        3 # Extractor de administraciones de la Lotería Nacional,
        4 # a partir de una serie de números, desde la página oficial.
        5 #
        6 # Joaquín Ferrero. Diciembre 2007.
        7
        8 use WWW::Mechanize;
        9 use HTML::TableExtract;
       10
       11 use strict;
       12 use warnings;
       13
       14 $|++; # no caché
       15
       16 # Constantes ################################################################
       17 my $URL = 'http://onlae.terra.es/loteria/navidad07/PrincipalD.htm';
       18 my $fichero_de_salida = 'administraciones.txt';
       19
       20 #############################################################################
       21
       22 # Vemos si el usuario nos manda un argumento, o le informamos
       23 # de cómo funciona este programa
       24 # Hay dos formas de indicarlo: o números sueltos, separados por comas,
       25 # y/o un rango, separados por '..'.
       26 @ARGV or die "Uso: $0 [<número[,número,...]|<número>..<número>]\n";
       27
       28 # Leemos los números indicados por el usuario
       29 my $numeros_a_buscar = shift @ARGV;
       30
       31 # Lo evaluamos como una posible lista de números
       32 my @numeros_a_buscar = eval ($numeros_a_buscar);
       33
       34 # Hacemos un bucle por todos ellos
       35 foreach my $numero ( @numeros_a_buscar ) {
       36     next if $numero > 84999;                    # Límites de números permitidos
       37     next if $numero < 0;
       38
       39     obtener_datos_del($numero);
       40
       41     sleep 3 + rand 3
       42         if $numero != $numeros_a_buscar[-1];    # Somos amables... esperamos
       43 }
       44
       45 sub obtener_datos_del {
       46     my $numero = shift;
       47
       48     print "Obteniendo información del número $numero...";
       49
       50     my $mech = WWW::Mechanize->new();           # Nuestro robot
       51     $mech->agent_alias('Linux Mozilla');        # Vamos a despistar un poco
       52
       53     $mech->get($URL)
       54         or die "ERROR: No me pude conectar al $URL\n";
       55
       56     $mech->submit_form(                         # Solicitamos información
       57         with_fields => {'numero' => $numero},
       58     );
       59
       60     $mech->success()                            # ¿Todo va bien?
       61         or die "ERROR: No pude hacer la petición para el número $numero\n";
       62
       63     my $pagina = $mech->content();              # A ver qué tenemos...
       64
       65     # Nos construímos el extractor de tablas y buscamos la que nos interesa
       66     my $tabla_extractor
       67         = HTML::TableExtract->new(
       68             headers => [qw(Admon. Domicilio Localidad Provincia Teléfono)],
       69         );
       70     $tabla_extractor->parse($pagina);           # Adelante... parsea...
       71
       72     my ($tabla) = $tabla_extractor->tables()    # Tabla obtenida
       73         or die "ERROR: No encontré la tabla de Administraciones\n";
       74
       75     # Extraemos información de la tabla
       76     # Y la guardamos en el fichero de salida
       77     open ADMON,">>$fichero_de_salida"
       78         or die "ERROR: No puedo añadir a $fichero_de_salida: $!\n";
       79
       80     # Primero, una cabecera
       81     print ADMON "Administraciones con el número $numero:\n";
       82
       83     # Luego, la información de la tabla
       84     foreach my $row ($tabla->rows) {
       85         print ADMON join(',', @$row), "\n";
       86     }
       87
       88     close ADMON;
       89
       90     print " Ok\n";
       91 }
       92
       93 __END__
Coloreado en 0.005 segundos, usando GeSHi 1.0.8.4
Haciendo una prueba
Código: Seleccionar todo
administraciones.pl 1000,1001..1002

Consideraciones:
  • El fichero se ha de grabar con codificación utf-8, porque las páginas de la Lotería vienen en esa codificación. Dentro del programa hacemos referencia a una de las cabeceras de la tabla resultado, llamada 'Teléfono', y, al tener un carácter acentuado, ha de corresponder su codificación a la de la página. Una forma de solventar este problema -en caso de no poder grabar el programa en esa codificación- es la de modificar la línea 68 del programa por esta otra:
    Sintáxis: [ Descargar ] [ Ocultar ]
    Using perl Syntax Highlighting
    attribs => { id => 'administraciones'};
    Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
  • Naturalmente, deberás quitar los números de línea, que los he puesto solo como referencia
  • La especificación de los números a extraer se puede hacer de muchas formas. En el ejemplo se ve que está mezclado las comas con la de rango de números ('..').
Última edición por explorer el 2007-12-13 20:03 @877, editado 1 vez en total
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor agaton » 2007-12-13 19:24 @850

Muchas gracias explorer por tu rápida y completa respuesta :D
agaton
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2007-12-12 19:07 @838

Notapor agaton » 2007-12-22 19:03 @835

Otra pregunta relacionada:
¿Se podría hacer lo mismo en una página escrita en jsp y que devuelve los datos de la consulta en textboxes?

Saludos
agaton
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2007-12-12 19:07 @838

Notapor explorer » 2007-12-22 20:34 @898

No importa que la página esté escrita en jsp siempre y cuando el resultado sea HTML.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor agaton » 2007-12-22 21:01 @917

Sí, pero pongamos por ejemplo esta página escrita en jsp:



En el apartado D, al introducir una matrícula, se completan los datos en los textboxes correspondientes. Eso es lo que se devuelve tras la consulta; es parecido a lo de las loterías solo que el resultado va en los textboxes en lugar de una tabla aparte.
Última edición por agaton el 2007-12-27 09:59 @457, editado 1 vez en total
agaton
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2007-12-12 19:07 @838

Notapor explorer » 2007-12-23 00:22 @057

Eso ya es algo más complicado.

La petición que indicas es una llamada Ajax con la librería DWR en una petición HTTP.

La librería DWR es conocida y podrías saber su funcionamiento, pero puede ser muy tedioso averiguar cómo intercambia los datos.

Una solución para ver su funcionamiento es hacer un volcado de los paquetes que intercambian el cliente y el servidor.
De resultas de esto, se ve que es una petición POST normal.

Bueno, no tan normal porque hay que hacer unos pequeños cambios en la cabecera, para acomodarla a lo que espera el servidor.

Ejemplo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl

use HTTP::Request::Common qw(POST);
use LWP::UserAgent;

my $ua = LWP::UserAgent->new(
            agent => 'Mozilla/5.0 (X11; U; Linux i686; es-ES; rv:1.8.1.8) Gecko/20071015 SUSE/2.0.0.8-1.1 Firefox/2.0.0.8',
);

my $matricula = '1253-BCN';

my $req = POST
            'http://www.juntadeandalucia.es/economiayhacienda/apl/surweb/dwr/call/plaincall/ProxyDWR.invocarPorReflexion.dwr',
            Content_Type => 'text/plain',
            Content      => <<"EOF",
callCount=1
page=/economiayhacienda/apl/surweb/modelos/modelo621/621.jsp
httpSessionId=0ae2803530d90efa31d72dc24a68aff2f815336f489f.e38Ob38NahaLbi0LbhqSb3iRcheRe0
scriptSessionId=96DA3B6AB5E867109CF9AF7A7D6DBD93892
c0-scriptName=ProxyDWR
c0-methodName=invocarPorReflexion
c0-id=0
c0-param0=string:dgt.AjaxDGT.devolverDatosMatriculacion
c0-e1=string:$matricula
c0-param1=Array:[reference:c0-e1]
batchId=14
EOF
            ;

$req->header(
    'Accept'            => 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5',
    'Accept-Language'   => 'es,es-es;q=0.8,en;q=0.5,en-us;q=0.3',
    'Accept-Charset'    => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
    'Keep-Alive'        => 300,
    'Connection'        => 'keep-alive',
    'Content-Type'      => 'text/plain',
    'Referer'           => 'http://www.juntadeandalucia.es/economiayhacienda/apl/surweb/modelos/modelo621/621.jsp',
    'Cookie'            => 'JSESSIONID=0ae2803530d90efa31d72dc24a68aff2f815336f489f.e38Ob38NahaLbi0LbhqSb3iRcheRe0',
    'Pragma'            => 'no-cache',
    'Cache-Control'     => 'no-cache',
);

my $respuesta = $ua->request($req)->as_string;

#print $respuesta,"\n";

if ( (my $datos) = $respuesta =~ m/\{(.*)\}/ ) {

    foreach ( split q{,}, $datos ) {
        my ($tipo, $valor) = split q{:};
        printf "%-22s %s\n", $tipo, $valor;
    }

}
else {
    print "No hubo respuesta\n";
}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Obtenemos como salida:
Código: Seleccionar todo
bastidor               "VF1DA0W0523282733"
cilindrada             "1390"
errorConexion          false
fechaMatriculacion     "16/11/2000"
marca                  "RENAULT"
matriculaIncorrecta    false
matriculaInexistente   false
modelo                 "MEGANE COUPE"
nifTitular             "15769621Q"
potencia               "10.7"
tipoMotor              "GASOLINA"
tipoVehiculo           "TURISMO"


A destacar que, en lugar de un POST normal, hay que indicar que los parámetros que estamos pasando lo hacen en formato text/plain.

Es probable que las variables de sesión no sea necesario ponerlas, pero eso ya lo investigarás tu.

Casi todos estos ejemplos se pueden conseguir en lwpcook, WWW::Mechanize::Cookbook y HTTP::Request::Common.

Actualización: el formulario exige ahora la introducción adicional del NIF del propietario.
Última edición por explorer el 2008-05-04 04:52 @244, editado 1 vez en total
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor agaton » 2007-12-25 19:46 @865

Uff, sí que tiene enjundia por lo que veo. He intentado ejecutarlo (en Windows) pero me sale un mensaje de error:

Código: Seleccionar todo
C:\Perl>perl prueba1.pl
 name                         'org.directwebremoting.extend.ServerException'
 message                      'The specified call count is not a number


¿Hace falta algo más que el intérprete Perl y los módulos para que funcione?
agaton
Perlero nuevo
Perlero nuevo
 
Mensajes: 7
Registrado: 2007-12-12 19:07 @838

Notapor explorer » 2007-12-26 16:43 @738

Lo he probado en otra máquina, con un copia y pega, en otro ordenador distinto de donde lo hice, y me da el mismo resultado que me daba antes.

No sé dónde puede estar el fallo, en tu caso. En caso de que faltase un módulo, perl lo diría.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España


Volver a Básico

¿Quién está conectado?

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