• Publicidad

Enlazar dos módulos entre sí y con el script final

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

Enlazar dos módulos entre sí y con el script final

Notapor Jestfer » 2017-07-02 15:15 @677

Muy buenas a todos, voy a intentar explicar el problema que tengo lo mejor posible, porque creo que ni yo termino de entenderlo.

Me han mandado un ejercicio en el que necesito, por un lado, crear dos módulos desde cero que hagan lo siguiente:
- Módulo 1 ("Contenido.pm"): pasar una URL en la "command line" y obtener el contenido de dicha URL.
- Módulo 2 ("Show.pm"): extraer solo las URL de la página pasada en la "command line" y hacer "print" de las mismas.

Entonces, tengo el script que funciona perfectamente que sería este:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4.  
  5. use LWP::UserAgent;
  6. use HTML::LinkExtor;                           
  7. ## parse content, extract links and make them absolute
  8. use Digest::MD5 qw(md5_hex);
  9.  
  10. my $url = $ARGV[0];                               ## Url passed in command
  11. if ($url !~ m{^https?://[\w]+-?[\w]+\.com/?}i) {
  12.     exit(0);                                      ## Program stops if not valid URL
  13. }  
  14.  
  15. my $ua = LWP::UserAgent->new;
  16. $ua->timeout( 10 );
  17.  
  18. my $response = $ua->get($url);
  19.  
  20. my $content = $response->decoded_content;
  21. my $base = $response->base;                                               ## Absolutifies relative links from response
  22.  
  23. my @links;
  24. my $p = HTML::LinkExtor->new(
  25.         sub {
  26.                 my ($tag, %attrs) = @_;
  27.                 if ($tag eq 'a' && $attrs{href}) {
  28.                         push @links, "$attrs{href}";          ## stringify
  29.                 }
  30.         },
  31.         $base,
  32. );
  33.  
  34. $p->parse($content);
  35. $p->eof;
  36.  
  37. for my $link (@links) {
  38.         print "$link\n";
  39.         print "Digest for the above URL is " . md5_hex($link) . "\n";
  40. }
Coloreado en 0.003 segundos, usando GeSHi 1.0.8.4

Entonces, estoy haciendo el Módulo 1 (pasar la URL y extraer el contenido), que está quedando así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. package Contenido;
  2. use strict;
  3. use warnings;
  4.  
  5. use LWP::Simple;
  6.  
  7. sub content {
  8.         my $url = $ARGV[0];
  9.                 if ($url !~ m{^https?://[\w]+-?[\w]+\.com/?}i) {
  10.                 exit(0);                                  
  11.                 }
  12.  
  13.         my $content = LWP::Simple::get($url) || exit(0);
  14. };
  15.  
  16. 1;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Luego, en el Módulo 2 (extraer las URL y hacer "print"), me he dado cuenta de que no puedo enlazar la variable $url del primero. Creo que estoy cometiendo un error muy básico y quizá esa parte deba ir en el script principal ejecutable... Igual es que no se puede hacer en 2 módulos por separado y debe ser uno, os muestro lo que llevo:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. package Show;
  2. use strict;
  3. use warnings;
  4.  
  5. use LWP::UserAgent;
  6. use HTML::LinkExtor;
  7. ## parse content, extract links and make them absolute
  8.  
  9. sub urls {
  10.  
  11. my $ua -> LWP::UserAgent->new;
  12. $ua->timeout( 10 );
  13.  
  14. my $response = $ua->get($url)
  15. ## ...
  16.  
  17.  
  18.  
  19. };
  20.  
  21. 1;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Entonces, de momento el script general lo he empezado pero al ver los problemas no puedo seguirlo como lo tenía pensado en mi cabeza:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4.  
  5. use lib '/Users/macbookair/training/lib';
  6. use Contenido;
  7.  
  8. Contenido::content();
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

¿Me podríais orientar un poco? La verdad es que me he atascado.

Muchísimas gracias.
Jestfer
Perlero nuevo
Perlero nuevo
 
Mensajes: 6
Registrado: 2017-06-20 19:48 @867

Publicidad

Re: Enlazar dos módulos entre sí y con el script final

Notapor explorer » 2017-07-02 19:47 @866

Te falta la "conexión" entre los módulos y el programa principal. Te falta el cómo enviar información de un sitio a otro.

Por ejemplo, en el módulo Contenido, has puesto una subrutina llamada content() que hace la labor que le pides, pero... ¿dónde queda el resultado?

Si declaras la variable $content con my(), la estás declarando como local a esa subrutina, por lo que dejará de existir en cuanto termine la subrutina. El contenido de la página, se pierde.

La solución es usar return, al final de la subrutina. Por que... eso es lo que son las subrutinas, también: funciones que devuelven un valor.

Y en la llamada que haces desde el programa principal, es donde recoges el resultado:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $contenido = Contenido::content();
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Luego, en el Show, tienes un error: no estás analizando el contenido para extraer los enlaces. Simplemente, los estás bajando de Internet, con el método get().

Debes analizar el $contenido obtenido antes, quizás con una expresión regular que lo recorra entero.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Enlazar dos módulos entre sí y con el script final

Notapor Jestfer » 2017-07-03 14:45 @656

En primer lugar, muchas gracias, explorer. Cada vez que leo una respuesta tuya se me abre la mente un poco más con esto de la programación :)

Ahora esto pinta un poco mejor, pero sigue habiendo un error en algún sitio que no logro entender... El programa principal ahora solo me devuelve una URL, y en bucle infinito, en lugar de todas las URL de una página y, claro está, siempre el mismo MD5, al no variar esta.

Módulo 1: Contenido (pasar una URL en la terminal y obtener el contenido del fuente):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. package Contenido;
  2. use strict;
  3. use warnings;
  4.  
  5. use LWP::Simple;
  6.  
  7. sub content {
  8.     my $url = $ARGV[0];
  9.     if ( $url !~ m{^https?://[\w]+-?[\w]+\.com/?}i ) {
  10.         exit(0);
  11.     }
  12.  
  13.     my $content = LWP::Simple::get($url) || exit(0);
  14.     return $content;
  15. }
  16.  
  17. 1;
  18.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Módulo 2: Show (Extraer las URL del contenido del módulo anterior y mostrarlas cada una con su MD5 correspondiente):
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. package Show;
  2. use strict;
  3. use warnings;
  4.  
  5. use Contenido;
  6. use URI::URL;
  7. use Digest::MD5 qw(md5_hex);
  8.  
  9. my $links;
  10.  
  11. sub urls {
  12.  
  13.     while ( Contenido::content() =~ m{<a[^>]\s*href\s*=\s*"?([^"\s>]+)}gis ) {
  14.         my $links   = $1;
  15.         my $abs     = new URI::URL "$links";
  16.         my $abs_url = $abs->abs( 'http:', $links );
  17.         print "$abs_url\n";
  18.         print "Digest for the above URLs is " . md5_hex($abs_url) . "\n";
  19.     }
  20.  
  21. }
  22.  
  23. 1;
  24.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Por último, el script principal, que debe enlazar con ambos módulos:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4.  
  5. use lib '/Users/macbookair/training/lib';
  6. use Contenido;
  7. use Show;
  8.  
  9. my $contenido = Contenido::content();
  10.  
  11. my $enlaces = Show::urls($contenido);
  12. print "$enlaces\n";
  13.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Estos pequeños avances son los que me siguen dando ganas de aprender Perl y programación en general :) Espero algún día poder contribuir de la misma forma en que los expertos lo hacéis.
Jestfer
Perlero nuevo
Perlero nuevo
 
Mensajes: 6
Registrado: 2017-06-20 19:48 @867

Re: Enlazar dos módulos entre sí y con el script final

Notapor explorer » 2017-07-04 06:19 @304

El error está en la línea 13 de Show.

Cada vez que el bucle llega ahí, estás volviendo a ejecutar Contenido::content(), es decir, estás llamando a content(), recuperando la página con el get() y guardándola en $content, una y otra vez. Por eso se vuelve en un bucle infinito (y además, en infinitas peticiones para el sitio web. Ten cuidado con esto, pues podrían molestarse).

La solución es que Show procese $contenido, que es lo que nos ha devuelto Contenido::content(), como queda reflejado en la línea 9 del programa, y acertadamente se lo pasas a Show en la línea 11.

Entonces, en urls() debes leer lo que te ha pasado el usuario.

En la línea 12 de urls() pones
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.     my $contenido = shift;      # leemos el primer argumento pasado con el usuario: la página entera
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

y luego cambias la línea 13:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1.     while ( $contenido =~ m{<a.+?href\s*=\s*"(.+?)"}gis ) {
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Enlazar dos módulos entre sí y con el script final

Notapor Jestfer » 2017-07-04 14:50 @660

Muchas gracias de nuevo, explorer, otra muy buena explicación que me despeja más dudas, funciona correctamente.

Tenías razón con el tema de las peticiones al sitio web, me han denegado el acceso a este (solo quería practicar, no darle trabajo a nadie... cachis :oops: ) y de hecho pensaba que no funcionaba el programa, pero he probado con otro sitio y va todo perfecto.

He comprobado, por mera curiosidad, que la línea 12 también funcionaría así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $contenido = Contenido::content();
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Y luego en la 13, como bien dices:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. while ( $contenido =~ m{<a.+?href\s*=\s*"(.+?)"}gis ) {
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

pero supongo que es más aconsejable usar la función "shift" :)

Una última pregunta: he visto que has cambiado la "regex" para el "match" de las URL del contenido, y que has usado "?" delante de un "quantifier" (+). Imagino que es por el tema del "greedy", acabo de echar un vistazo porque no conocía esta opción hasta ahora.

Voy a seguir practicando, ahora me toca MySQL y JSon, después a seguir con Perl.

Un saludo y buen resto de la semana.
Jestfer
Perlero nuevo
Perlero nuevo
 
Mensajes: 6
Registrado: 2017-06-20 19:48 @867

Re: Enlazar dos módulos entre sí y con el script final

Notapor explorer » 2017-07-05 12:41 @570

Jestfer escribiste:He comprobado, por mera curiosidad, que la línea 12 también funcionaría así:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my $contenido = Contenido::content();
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Y luego en la 13, como bien dices:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. while ( $contenido =~ m{<a.+?href\s*=\s*"(.+?)"}gis ) {
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

pero supongo que es más aconsejable usar la función "shift" :)
Un momento... Funciona, sí, pero... NO hay que hacerlo... porque ya llamamos a content() en la línea 9 del programa principal... así que NO hay que volver a llamarlo dentro de Show. Estarías volviendo a llamar y bajar la página otra vez, algo innecesario.

Jestfer escribiste:Una última pregunta: he visto que has cambiado la "regex" para el "match" de las URL del contenido, y que has usado "?" delante de un "quantifier" (+). Imagino que es por el tema del "greedy", acabo de echar un vistazo porque no conocía esta opción hasta ahora.

Sí, así es.

Si solo pusiéramos "<a.+href", tendríamos un problema si la línea tuviera dos enlaces:

... <a href="...">...</a> ... <a href="..."> ... </a>

El operador ".+" se "comería todos los caracteres, desde la primera '<a' hasta el segundo 'href', así que perderíamos el procesamiento de un enlace.

En cambio, con "<a.+?href" le estamos diciendo que "pare de comer" justo antes del primer 'href' que se encuentre en la línea. El mismo efecto ocurre más tarde, cuando queremos capturar lo que hay entre las comillas dobles.

Más información en tu propio ordenador en perldoc perlre, y en la Web. Y traducido al español.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Enlazar dos módulos entre sí y con el script final

Notapor Jestfer » 2017-07-06 16:10 @715

Fantástico, explorer, he comprendido completamente cómo funcionaría el programa en su totalidad y cómo optimizar mi regex. Muchas gracias de nuevo y te deseo muy buen fin de semana, toca seguir practicando por aquí :)
Jestfer
Perlero nuevo
Perlero nuevo
 
Mensajes: 6
Registrado: 2017-06-20 19:48 @867


Volver a Básico

¿Quién está conectado?

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