• Publicidad

Conexiones simultaneas múltiples con módulo LWP

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

Conexiones simultaneas múltiples con módulo LWP

Notapor Zeokat » 2007-07-29 18:57 @831

Estoy haciendo un programita y lo que hace es (usando el modulo LWP) descargar el código fuente de una web y ese código fuente lo analiza para separar y capturar las urls, las cuales almacena en un txt.

Bien, ahora tengo que ir bajando el código fuente de cada una de esas urls almacenadas en el txt, el problema es que son muchas y el módulo LWP descarga una url de cada vez, lo que pretendo es que se descargue por lo menos 10 de cada vez, sino el proceso es eterno.


Este es el código que tengo escrito y funciona pero va descargando url a url, a ver si es posible arreglar eso. Estuve buscando en CPAN y en Google, pero no encontré nada al respecto, a ver si tenéis alguna idea o quizás no se pueda, hacer no se...

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
open (URL_REVIEWS,"url_reviews.txt");
my @reviews_all = <URL_REVIEWS>;

print "Extraidas ", $#reviews_all +1 , " urls de reviews para lechear\n";

foreach my $review_en_cuestion (@reviews_all)  {
   
    my $req4 = HTTP::Request->new(GET => $review_en_cuestion);
    my $response4 = $ua->request($req4);
    my $content4 = $response4->content();
    open (TODO_CODIGO_REVIEWS,">>todo_codigo_reviews.txt");
    print TODO_CODIGO_REVIEWS $content4 , "\n";
    close(TODO_CODIGO_REVIEWS);
    print "Descargando  " , $review_en_cuestion , "\n";
}
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4
Zeokat
Perlero nuevo
Perlero nuevo
 
Mensajes: 125
Registrado: 2006-08-22 08:08 @380

Publicidad

Notapor explorer » 2007-07-29 19:41 @862

He encontrado un ejemplo, hecho con threads:
http://kodegeek.com/2005/11/echando-cdi ... ueva.shtml
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

Notapor Zeokat » 2007-07-29 20:21 @889

Bueno, vamos aya... con lo novatillo que soy me queda grande todo ese código todavía. Pero vamos a ver si lo analizamos pasito a pasito y van saliendo las cosas. Analizo la parte del código que en principio interesa que es la de los threads.

Veo que crea un bucle y en la línea 32 pone: "\&getURL" , eso es nuevo para mi... mmm.... no consigo averiguar que es... por su nombre puedo deducir que es lo que ordena que se descarge (el archivo o lo que sea).

Otra duda que me surge, ¿es cuantos threads crea?... yo diría que tantos como elemento hay en @url, pero no estoy muy seguro...

Voy a hacer algunas pruebas que es lo mejor en estos casos. :)

Gracias de nuevo explorer. :wink:
Zeokat
Perlero nuevo
Perlero nuevo
 
Mensajes: 125
Registrado: 2006-08-22 08:08 @380

Notapor Zeokat » 2007-07-30 05:10 @257

Vale ya me respondo yo, es una subrutina que está definida un poco más abajo, en la que se crea un $ua para cada thread.

No doy con el camino para implementar esto en mi código... sigo dándole vueltas a ver si sale.
Zeokat
Perlero nuevo
Perlero nuevo
 
Mensajes: 125
Registrado: 2006-08-22 08:08 @380

Notapor explorer » 2007-07-30 05:33 @273

El resumen del fucionamiento es: cada thread es un hilo de ejecución del programa, que, para simplificar, es como si el programa se dividiera a sí mismo en otro programa idéntico.

En el programa indicado, sólo hay dos threads, porque sólo hay dos elementos en @url, pero podrían haber sido más.

Cada thread ejecuta la función getURL, que es la que se encarga de bajar la página y almacenarla en ficheros externos (con la ayuda del mirror de LWP).

En la línea 37 el autor hace otro bucle por cada thread uniéndose a él con el uso de join() y mira el resultado de la operación. Si hubo error, informa y termina ese thread.

Si no hubo error y, según sea el número de thread en donde estemos, creará otro thread para su parseo (línea 44) con la función de parseo correspondiente, que esperará su ejecución en la línea 47.

Creo que se podría haber simplificado un poco porque hay varios bucles y se podría dejar en uno solo... pero bueno, el autor dice que lo hizo de forma rápida.

En tu caso, como son muchos enlaces, habrá quizás que hacer un bucle para trabajar de 10 en 10. Lanzar esos 10 threads, esperar la terminación de ellos y continuar. O mejor aún (no probado), lanzar 10 threads que vayan cogiendo enlaces desde el array, mientras le queden enlaces por procesar.

Puede ser muy interesante esta última solución. Y muy instructiva, para aplicarla a otros casos.
Última edición por explorer el 2007-07-30 07:30 @354, 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: 14476
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Notapor Zeokat » 2007-07-30 06:39 @318

Pues estas aclaraciones me viene de lujo, intentaré hacer algo parecido lo que dices: "lanzar 10 threads que vayan cogiendo enlaces desde el array, mientras le queden enlaces por procesar."

Saludos.
Zeokat
Perlero nuevo
Perlero nuevo
 
Mensajes: 125
Registrado: 2006-08-22 08:08 @380

Notapor explorer » 2007-07-30 07:24 @350

Como vas a usar variables comunes entre los hilos (al menos una, la lista de enlaces), te es imprescindible leerte el módulo threads::shared.
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

Notapor Zeokat » 2007-07-30 10:56 @497

Bueno, como siempre voy a tratar de plantear el ejercicio en su forma más básica.

Tenemos en nuestro disco duro un archivo con 10 urls , que se llama urls.txt y es de la siguiente forma:

Código: Seleccionar todo
http://web1.com/index.html
http://web2.com/index.html
http://web3.com/index.html
http://web4.com/index.html
http://web5.com/index.html
http://web6.com/index.html
http://web7.com/index.html
http://web8.com/index.html
http://web9.com/index.html
http://web10.com/index.html


El objetivo es lanzar 2 threads y que vayan cogiendo los enlaces del archivo de texto y almacenar los datos que se baje cada thread en un archivo txt común a ambos threads y que se llame codigo.txt

Empezamos:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl -w
use strict;
use LWP::UserAgent;
use HTTP::Request;
use threads;
use threads::shared;

open (URLS,"urls.txt");
my @urls = <URLS>;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Me pierdo cuando trato de entender (más que perderme, más bien no entiendo nada :oops: )
http://search.cpan.org/~jdhedden/thread ... threads.pm
http://search.cpan.org/~jdhedden/thread ... /shared.pm

No sé ni cómo hacer threads, ni cómo definir el número de threads, ni cómo decir que cada thread se baje una url... Me parece que esto todavía es demasiado para mi... buff no sé... le dí varias vueltas pero no consigo nada.

:cry:
Zeokat
Perlero nuevo
Perlero nuevo
 
Mensajes: 125
Registrado: 2006-08-22 08:08 @380

Notapor Zeokat » 2007-07-30 16:44 @739

Bueno, pues busque ayuda en perlmonks y bueno... parece que la solución mas asequible para mi nivel de conocimientos de perl (escasísimos) es usar el módulo LWP::Parallel::UserAgent, que permite hacer exactamente lo que yo busco.

Os dejo una solución con threads que me pusieron en perlmonks, he de decir que no la entiendo ... pero la revisaré en un futuro para tratar de entenderla.

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl -w
use strict;
use threads;
use threads::shared;

use LWP::UserAgent;
use HTTP::Request;

print "Working...\n";

my $ua = LWP::UserAgent->new;
$ua->agent("Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)");
$ua->timeout(15);

open URL_PLANETS, '<', "url_planets.txt"  or die $!;
my @urls = <URL_PLANETS>;
close(URL_PLANETS);
chomp @urls;

open NAMES, '>>', 'planet_names.txt' or die $!;

my $mutexStdout :shared;
my $mutexFile   :shared;
my $running     :shared = 0;

foreach my $planet (@urls)  {
    async {
        { lock $running; ++$running; }
        { lock $mutexStdout; print "Downloading: " , $planet , "\n" };
        my $req = HTTP::Request->new(GET => $planet);
        my $response = $ua->request($req);
        my $content = $response->content();
       
        lock $mutexFile;
        print NAMES $content =~ m[Rotations<i>(.*)</i>]m,"\n";
        { lock $running; --$running; }
    }->detach;
    sleep 1 while $running > 10;
}

close(NAMES);
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


También os dejo el link donde podéis encontrar más información a cerca del uso de los threads.
http://perlmonks.org/index.pl?node_id=629647

Saludos.
Zeokat
Perlero nuevo
Perlero nuevo
 
Mensajes: 125
Registrado: 2006-08-22 08:08 @380

Notapor creating021 » 2007-07-30 18:55 @829

:oops: Yo creo que podrías mejorar ese código, pero no estoy seguro si es la mejor forma:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
#!/usr/bin/perl -w
use strict;
use threads;
use threads::shared;

use LWP::UserAgent;
use HTTP::Request;

print "Working...\n";

my $ua = LWP::UserAgent->new;
$ua->agent("Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt)");
$ua->timeout(15);

open URL_PLANETS, '<', "url_planets.txt"  or die $!;
my @urls = <URL_PLANETS>;
close(URL_PLANETS);
chomp @urls;

open NAMES, '>>', 'planet_names.txt' or die $!;

my $mutexStdout :shared;
my $mutexFile   :shared;
my $running     :shared = 0;

INICIO: foreach my $planet (@urls)  {
    sleep 1 and redo INICIO if $running < 10; # <-- Aqui
    async {
        { lock $running; ++$running; }
        { lock $mutexStdout; print "Downloading: " , $planet , "\n" };
        my $req = HTTP::Request->new(GET => $planet);
        my $response = $ua->request($req);
        my $content = $response->content();
       
        lock $mutexFile;
        print NAMES $content =~ m[Rotations<i>(.*)</i>]m,"\n";
        { lock $running; --$running; }
    }->detach;
}

close(NAMES);
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

¿Por qué?
Bueno, realmente no sabemos si el proceso sigue activo o no al pasar un 1 seg. (de sleep) por lo tanto debemos estar seguros.

Entonces lo que hacemos es dormir el proceso y "damos un paso atrás", continuando el foreach con el argumento que teníamos (al momento de que $running era 10).
Expect the worst, is it the least you can do?
Avatar de Usuario
creating021
Perlero frecuente
Perlero frecuente
 
Mensajes: 595
Registrado: 2006-02-23 16:17 @720
Ubicación: Frente al monitor

Siguiente

Volver a Básico

¿Quién está conectado?

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