• Publicidad

Script para scrapear datos

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

Script para scrapear datos

Notapor agaton » 2024-12-01 05:49 @284

Buenos días,

Estoy intentando extraer en csv, txt o un formato legible, los datos de las tablas en la web https://desmace.com/provincia/asturias/

Lo que quiero es introducir unas fechas inicial y final de trámite, unas fechas inicial y final de matricula fijas (01/01/1900 a 31/12/2000) y una determinada provincia de matriculación.

Los datos que quiero almacenar están dentro del código, en # Escribir encabezados en el archivo CSV.

De momento tengo este código:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. use strict;
  2. use warnings;
  3. use LWP::Simple;
  4. use HTML::TreeBuilder;
  5. use Text::CSV;
  6.  
  7. # URL del sitio web
  8. my $url = 'https://desmace.com/provincia/asturias/';
  9.  
  10. # Criterios de filtro
  11. my $fecha_tramite_min = '24/11/2024';
  12. my $fecha_tramite_max = '27/11/2024';
  13. my $fecha_matricula_min = '01/01/1900';
  14. my $fecha_matricula_max = '31/12/2000';
  15. my $prov_matriculacion_filtro = 'ASTURIAS';
  16.  
  17. # Contenido HTML de la página
  18. my $html = get($url) or die "No se pudo acceder a la URL: $!";
  19.  
  20. # Parsear el HTML
  21. my $tree = HTML::TreeBuilder->new;
  22. $tree->parse($html);
  23.  
  24. # Abrir archivo CSV paraescribir
  25. my $csv = Text::CSV->new({ binary => 1, eol => "\n" });
  26. open my $fh, ">", "resultados.csv" or die "No se pudo crear el archivo CSV: $!";
  27.  
  28. # Encabezados en el archivo CSV
  29. $csv->print($fh, ['FECHA DEL TRÁMITE', 'TRÁMITE', 'FECHA MATRÍCULA', 'MARCA', 'MODELO', 'BASTIDOR (VIN)', 'PROV. MATRICULACIÓN']);
  30.  
  31. # Esta es la tabla que contiene los datos
  32. my @rows = $tree->look_down(_tag => 'tr');
  33.  
  34. foreach my $row (@rows) {
  35.     my @columns = $row->look_down(_tag => 'td');
  36.     my @data;
  37.    
  38.     # Extraer valores de las columnas
  39.     foreach my $col (@columns) {
  40.         push @data, $col->as_text;
  41.     }
  42.    
  43.     # Filtrar filas según los criterios
  44.     if (@data >= 9) {
  45.         my ($fecha_tramite, $fecha_matricula, $prov_matriculacion) = @data[0, 2, 7];
  46.        
  47.         if ($fecha_tramite ge $fecha_tramite_min && $fecha_tramite le $fecha_tramite_max &&
  48.             $fecha_matricula ge $fecha_matricula_min && $fecha_matricula le $fecha_matricula_max &&
  49.             $prov_matriculacion eq $prov_matriculacion_filtro) {
  50.             $csv->print($fh, \@data);
  51.         }
  52.     }
  53. }
  54.  
  55. # Cerrar el archivo y liberar recursos
  56. close $fh;
  57. $tree->delete;
  58.  
  59. print "Datos filtrados guardados en 'resultados.csv'.\n";
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4


Sin embargo, aunque se ejecuta "bien", no se almacenan los datos que necesito. ¿Alguna orientación de lo que puedo estar haciendo mal?

Muchas gracias de antemano.
agaton
Perlero nuevo
Perlero nuevo
 
Mensajes: 9
Registrado: 2007-12-12 19:07 @838

Publicidad

Re: Script para scrapear datos

Notapor MaterazziSan » 2024-12-02 13:58 @624

Buenas,

¿La lista "@data" en algún momento tiene valores?

Un saludo
Avatar de Usuario
MaterazziSan
Perlero nuevo
Perlero nuevo
 
Mensajes: 34
Registrado: 2020-08-05 12:17 @553
Ubicación: España

Re: Script para scrapear datos

Notapor explorer » 2024-12-02 16:13 @718

Hola.

He estudiado el problema, y el asunto no es tan sencillo.

Lo primero y más fácil del resolver es la extracción de datos. El problema está en la línea 45, en los índices de los datos a extraer.

Resulta que cada fila de la tabla contiene muchas más columnas que las que por defecto vemos al entrar en la página. He contado 54. Se pueden seleccionar pulsando en el botón "Columnas" arriba a la derecha. Pero siempre hay 54 columnas en las filas.

Entonces lo primero es saber los índices de esas columnas. Haciendo un dump de \@data es "fácil" saber cuáles son.

Pero hay otro problema más complicado: la página sólo muestra 50 registros cada vez. Las siguientes páginas salen con peticiones POST a la dirección /admin-ajax.php?action=get_wdtable&table_id=414 indicando en start el número del primer registro, y en length la longitud de los registros. Ahora muestra 1 208 614 registros.

Y esto es lo más complicado: se trata de un proceso javascript que rellena la tabla. No encuentro la manera de cómo pedir los datos de forma directa.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Script para scrapear datos

Notapor explorer » 2024-12-02 16:52 @744

Se me ocurre una idea.

En la petición POST a admin-ajax.php hay que hacer una petición POST con las condiciones de la tabla a representar.

  • El parámetro 'draw' debe ser correlativo en cada petición. Aparecerá en la respuesta con el mismo número.
  • Hay que pasar un array llamado 'columns'. En este enorme array se pueden indicar los filtros de la respuesta que queremos obtener. Por ejemplo, tienes puesto fechas límite del trámite y de la matriculación, además del nombre de la provincia. Esto se pone en los elementos 1, 4 y 23. O sea, confeccionar una petición así:
    Sintáxis: [ Descargar ] [ Ocultar ]
    1. draw=29&columns%5B0%5D%5Bdata%5D=0&columns%5B0%5D%5Bname%5D=ID&columns%5B0%5D%5Bsearchable%5D=false&columns%5B0%5D%5Borderable%5D=false... 

  • La respuesta es un json así:
    Sintáxis: [ Descargar ] [ Ocultar ]
    1. {"draw":29,"recordsTotal":"1208614","recordsFiltered":"119","data":[["73754047","26\/11\/2024","TRANSFERENCIA","ORDINARIA","12\/01\/1994","","26\/11\/2024","IMPORTACI\u00d3N U.E","NUEVO","PARTICULAR","2","PARTICULAR PART - SIN ESPECIFICAR","NO DESTINADO A RENTING","--","OPEL             ","VECTRA        ","W0L000087P1202992  ","1.998","13","0","0","EURO 1 ","             ","ASTURIAS","ASTURIAS","OVIEDO            ",...<\/form>"]]} 
  • Y en la primera petición también devuelve un valor llamado "recordsFiltered" que indica el número de registros (ver en el punto anterior el 119), así que en la siguiente petición, se puede modificar los argumentos 'start' y 'length' para hacer otra petición para obtener toda la respuesta.
O sea, hacer que sea la página web la que haga el filtrado, ya que no tiene sentido bajarse más de un millón de registros haciendo miles de peticiones de 50 registros cada vez. Los 119 registros obtenidos ahora son justamente la aplicación de los filtros de tu script.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Script para scrapear datos

Notapor agaton » 2024-12-03 10:25 @475

¡Muchas gracias por las respuestas!

Sí, se supone que debería almacenar los valores de las columnas que interesa filtrar.

Explorer, gracias por los detallados comentarios, el problema es que hay cosas que no entiendo bien a la hora de insertar tus ayudas en mi código (por eso estoy en Perl Básico :lol: ).

Por un lado, la dirección /admin-ajax.php?action=get_wdtable&table_id=414 por sí sola me devuelve error.

Luego, si no he entendido mal el parámetro draw es para ir haciendo las peticiones POST (empezando con un valor de 1 y aumentando), pero no entiendo bien la sintaxis que va a continuación. ¿Puedes decirme cómo defines ese parámetro en el código, y el array colummns? Aparte, comentas que se puede modificar start y length en la petición, pero en esa sintaxis no veo cómo.

¡Muchas gracias!
agaton
Perlero nuevo
Perlero nuevo
 
Mensajes: 9
Registrado: 2007-12-12 19:07 @838

Re: Script para scrapear datos

Notapor explorer » 2024-12-03 17:03 @752

Efectivamente, es un asunto complicado.

Debes realizar las primeras pruebas con alguna herramienta que analice el tráfico que está ocurriendo entre el navegador web y el servidor.

Por ejemplo, si abres la página con un Firefox o un Chrome, pulsa F12 para sacar la herramienta para el desarrollador. Vas luego a la pestaña Red y así podrás analizar el diálogo que se produce entre los dos.

Selección_003.png
Selección_003.png (162.71 KiB) Visto 29 veces


Después de pulsar F12, selecciona Red (flecha 2 en la imagen), y verás en 3 el tráfico. Entra un filtro, por ejemplo en 1, y pulsa sobre el último admin-ajax.php que veas.

A la derecha aparecerá la petición y su respuesta (4 y 5).

Esto requiere entender lo que es un json para hacer la petición, y esperar un json con la respuesta.

Para llamar a admin-ajax.php debes imitar a la perfección una petición POST. Eso implica mirar todos los argumentos en Solicitud (al final de la ventana está puesto en una sola cadena de caracteres), replicarla tal cual en el programa, y hacer la petición. La respuesta será el json que vemos en Respuesta. Luego con el módulo JSON es fácil acceder a cada elemento.

Pero con esto tenemos hecho una llamada a la base de datos. Queda por hacer la lectura de la tabla. Lo estabas haciendo bien, así que hay que hacer lo mismo pero después de conseguir que la base de datos nos devuelva todos los registros, no de 50 cada vez. Y buscar los índices correctos de las columnas que nos interesan de las 54 que hay.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14486
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: Bing [Bot] y 0 invitados