Attention ! Etes vous sûrs d'avoir une bonne assurance emprunteur pour votre crédit immobilier ?


sept 14

Exemple d’utilisation de la classe SpiceCurl : le ScoopeoBot

Tag: Codingnoreply @ 16:17

Je vous ai expliqué il y a quelques jours comment utiliser les fonctions curl de php pour récupérer des pages web et vous ai proposé à titre d’exemple une petite classe php5 prête à l’emploi (SpiceCurl). C’est l’occasion aujourd’hui de la tester, et pour cela j’ai imaginé une petite application bien sympa : un assistant pour Scoopeo, le ScoopeoBot :D

En effet, il faut se rendre à l’évidence : passer son temps à promouvoir ses propres scoops, pourrir ceux des autresmodérer le spam, plusser les copains et moinser les commentaires des nombreux boulets de Scoopeo toujours à l’affût d’un troll ou d’une indignation ; que de tâches répétitives qui nuisent à notre productivité et font perdre quelques précieuses places à notre employeur dans une compétition internationale toujours plus impitoyable (vous pouvez respirer) !

Heureusement, avec un peu d’imagination et de technique, nous allons pouvoir déléguer toutes ces tâches ingrates à un robot et ainsi nous remettre au travail l’esprit serein !

J’ai donc imaginé le cahier des charges suivant pour notre robot :

  • Le robot doit pouvoir gérer un nombre illimité de comptes (bien sûr nous n’en avons qu’un chacun, mais peut être vos amis vous demanderont d’utiliser votre robot donc autant prévoir)
  • Le robot doit reconnaitre les sites ET les comptes de vos amis, ainsi que des spammeurs
  • Le robot évitera d’être trop bête et de systématiquement cliquer les amis et moinser les autres par exemple
  • Pour ne pas surcharger les serveurs de scoopeo, le robots devra observer des pauses entre chaque action
  • Le robot devra pouvoir : voter ou modérer un scoop, plusser ou moinser un commentaire

Bon, ça me semble déjà pas mal donc voici ce que vous attendez tous, le code ; qui se présente sous la forme de 4 petites classes :

  1. ScoopeoAnonymousAccount : une classe de base pour définir un utilisateur de scoopeo non identifié, avec des méthodes pour récupérer une liste de scoops, une liste de commentaires et cliquer.
  2. ScoopeoAccount : étend la classe précédente et ajoute la possibilité de s’identifier avec un compte scoopeo.
  3. ScoopeoSource : définit une source sur scoopeo, ce peut être un membre de scoopeo, ou un domaine d’où provient des scoops. Cette classe a deux méthodes pour savoir comment réagir face à un scoop ou face à un commentaire de cette source.
  4. ScoopeoBot: la classe principale qui sert d’interface pour ajouter des sources, des comptes utilisateurs, des pages à analyser et qui va dire au robot ce qu’il doit faire.

Mise à jour : j’ai posté un peu vite hier et il y avait des choses qui ne tournaient pas rond, c’est corrigé et je vous ai gratifié de plus de nouvelles fonctionnalités ! Notez que j’ai également apporter quelques modifications à la classe SpiceCurl et que vous devez avoir la dernière version pour que le ScoopeoBot fonctionne…

Mise à jour 2 : encore des petites corrections, mais cette fois c’est bon. Du coup j’ai codé une petite bidouille qui permet d’ajouter comme pages les pages associées à un membre (scoops proposés, cliqués, etc). On peut facilement imaginer des petites moulinettes sympa avec ça…

Class ScoopeoAnonymousAccount

Je suis désolé, je n’ai pas pris le temps de commenter le code. Ceci est valable pour les 4 classes d’ailleurs…

class ScoopeoAnonymousAccount
{
        public $proxy;
        public $login = ‘Anonymous’;
        protected $useragent;
        protected $browser;
        const DEBUG = false;
       
        function __construct($proxy=null,$useragent=null)
        {
                $this->proxy = $proxy;
                $this->useragent = $useragent ? $useragent : ‘Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1.14) Gecko/20080404 Firefox/2.0.0.14′;
               
                $this->browser = new SpiceCurl(null,‘UTF-8′);
                if ($this->proxy) $this->browser->use_proxy($this->proxy);
                $this->browser->set_useragent($this->useragent);
                $this->browser->use_referer(true);
        }

        function __destruct()
        {
                unset($this->browser);
        }
       
        function back($url)
        {
                $this->browser->set_referer($url);
        }
       
        function get_scoops($url)
        {
                $page = $this->browser->get($url);
                $this->browser->set_referer($url);
                if (self::DEBUG) echo "$page\n";
                if (!preg_match_all(‘#<div class="list_scoop [^"]*">(.*?)<div class="alt_url"#s’,$page,$matches)) return null;
               
                $scoops = array();
               
                foreach ($matches[1] as $i=>$scoop)
                {
                        $scoops[$i] = array(
                                ‘id’=>preg_match(‘#<div class="scoop_title"><a name="([^"]*)"></a>#s’,$scoop,$m) ? $m[1] :  »,
                                ‘author’=>preg_match(‘#<a href="/membre/([a-z0-9_]*)">#si’,$scoop,$m) ? strtolower($m[1]) :  »,
                                ‘domain’=>preg_match(‘#\(via <a .*?target="_blank" .*?href="[^"]*">(.*?)</a>#s’,$scoop,$m) ? strtolower($m[1]) :  »,
                                ’sec’=>preg_match(‘#\?sec=(.*?)\’#s’,$scoop,$m) ? $m[1] :  »,
                                ’scoopUrl’=>preg_match(‘#<div class="scoop_footer">[^<]*<a href="([^"]+)">#s’,$scoop,$m) ? (‘http://www.scoopeo.com’ . $m[1]) :  »,
                                ‘comments’=>preg_match(‘#[^0-9]([0-9]+) commentaires?</a>#s’,$scoop,$m) ? $m[1] : 0,
                                ‘canModerate’=>preg_match(‘#<div id="mod_[0-9]+#s’,$scoop) ? true : false,
                                ‘canClick’=>preg_match(‘#<div class="clic">clic</div>#s’,$scoop) ? true : false
                        );
                }
               
                return $scoops;
        }
       
        function get_comments($url)
        {
                $page = $this->browser->get($url);
                $this->browser->set_referer($url);
                if (self::DEBUG) echo "$page\n";
                if (!preg_match_all(‘#(<div id="comment_[0-9]+".*?)<div id="ct_[0-9]+"#s’,$page,$matches)) return null;
               
                $comments = array();
               
                foreach ($matches[1] as $i=>$comment)
                {
                        $comments[$i] = array(
                                ‘id’=>preg_match(‘#<div id="comment_([0-9]+)"#s’,$comment,$m) ? $m[1] :  »,
                                ‘author’=>preg_match(‘#<a href="/membre/([a-z0-9_]+)"#si’,$comment,$m) ? strtolower($m[1]) :  »,
                                ‘canUpDown’=>preg_match(‘#/images/cdown_grey\.gif#s’,$comment) ? false : true
                        );
                }
               
                return $comments;
        }
       
        function click($action)
        {
                if ($action)
                {
                        return $this->browser->get(sprintf(‘http://www.scoopeo.com%s’,$action),true,array(‘X-Requested-With: XMLHttpRequest’,‘X-Prototype-Version: 1.5.1.1′));
                }
               
                return null;
        }
}

Class ScoopeoAccount

Notez que la méthode signout() n’est pas utilisée mais que dans ma grande bonté je la laisse quand même !

class ScoopeoAccount extends ScoopeoAnonymousAccount
{
        private $password;
       
        function __construct($login,$password,$proxy=null,$useragent=null)
        {
                $this->login = $login;
                $this->password = $password;
                $this->proxy = $proxy;
                $this->useragent = $useragent ? $useragent : ‘Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)’;
               
                $this->browser = new SpiceCurl("scoopeo-$login",‘UTF-8′,‘.’);
                if ($this->proxy) $this->browser->use_proxy($this->proxy);
                $this->browser->set_useragent($this->useragent);
                $this->browser->use_referer(true);
        }
       
        function init()
        {
                $page = $this->browser->get(‘http://www.scoopeo.com/scoop/list’);
                if (self::DEBUG) echo "$page\n";
                sleep(mt_rand(3,9));
                if (!preg_match(‘#<a href="/membre/’ . $this->login . ‘/scoops">#si’,$page))
                {
                        $this->signin();
                        sleep(mt_rand(3,9));
                }
        }
               
        private function signin()
        {
                if (self::DEBUG) echo "signing in for $this->login\n";
                $page = $this->browser->get(‘http://www.scoopeo.com/user/login’,sprintf(‘user%%5Blogin%%5D=%s&user%%5Bpassword%%5D=%s’,$this->login,$this->password));
                if (self::DEBUG) echo "$page\n";
                return $page;          
        }
       
        private function signout()
        {
                return $this->browser->get(‘http://www.scoopeo.com/user/logout’);
        }
}

Class ScoopeoSource

Pour les modérations, si rien n’est passé, nous prendront au hasard spam (spam), dépassé (old), déplacé (lame), intox (intox) ou manip (nul). Ceci ne sera utilisé que si $isFriend vaut false bien évidemment. Le paramètre p est la probabilité d’agir (entre 0 et 1 donc). Par défaut on le voit à 0.5 ce qui signifie que lorsque le robot tombe sur un scoop ou un commentaire de cette source, il a 50% de chance de faire quelque chose (cliquer, modérer, plusser, moinser).

class ScoopeoSource
{
        private $id;
        private $p;
        private $isFriend;
        private $mods = ’spam,old,lame,intox,nul’;
        private $modIndex = array(‘dup’=>1,‘bad’=>2,’spam’=>3,‘old’=>4,‘lame’=>5,‘intox’=>6,‘nul’=>7,‘no’=>8);
       
        function __construct($id,$isFriend=true,$mods=null,$p=0.5)
        {
                $this->id = strtolower($id);
                $this->isFriend = ($isFriend === true);
                $this->mods = $this->isFriend ?  » : ($mods ? $mods : $this->mods);
                $this->p = $p;
        }
       
        function action_on_scoop($scoopid,$sec= »,$canModerate=false,$canClick=false,$ano=false)
        {
                if ((mt_rand(0,100)/100) > $this->p)
                {
                        return null;
                }
                if ($this->isFriend && $canClick)
                {
                        return $ano ? sprintf(‘/clic/anoclick/%s’,$scoopid) : sprintf(‘/clic/click/%s/?sec=%s’,$scoopid,$sec);
                }
                elseif (!$this->isFriend && $canModerate)
                {
                        $mods = preg_split(‘#,#s’,$this->mods,-1,PREG_SPLIT_NO_EMPTY);
                        $mod = @$this->modIndex[$mods[mt_rand(0,count($mods)-1)]];
                        if (!$mod) return null;
                        return sprintf(‘/clic/moderate/%s?sec=%s&mod=%s’,$scoopid,$sec,$mod);
                }
                return null;
        }
       
        function action_on_comment($commentid,$canUpDown=false)
        {
                if (!$canUpDown || ((mt_rand(0,100)/100) > $this->p))
                {
                        return null;
                }
                if ($this->isFriend)
                {
                        return sprintf(‘/scoop/cup/%s’,$commentid);
                }
                else
                {
                        return sprintf(‘/scoop/cdown/%s’,$commentid);
                }
        }
}

Class ScoopeoBot

Petite explication pour les paramètres de la classe : $checkComments est un nombre entre 0 et 1 qui indique la probabilité d’aller voir la page d’un scoop (afin d’agir sur les commentaires). $minTempo et $maxTempo permettent au robot de se reposer entre deux actions : une valeur en secondes sera tirée au sort entre $minTempo et $maxTempo pour déterminer le temps de la pause.

J’ai ajouté de plus un paramètre $randomClick qui est une probabilité de cliquer un scoop quelconque. Si $randomClick est supérieur à 0 alors une source ‘*’ (catchall) est créée (comme un ami). Ceci va permettre de cliquer d’autre scoops ou de plusser des inconnus de temps en temps.

La méthode run() prend un paramètre optionnel $ano qui indique si le robot doit d’abord faire une passe en anonyme sur la home page de scoopeo pour éventuellement cliquer les scoops amis. Par défaut oui donc.

class ScoopeoBot
{
        const DEBUG = true;
        private $accounts = array();
        private $sources = array();
        private $pages = array();
        private $checkComments;
        private $minTempo;
        private $maxTempo;
        private $randomClick;
               
        function __construct($checkComments=0.5,$minTempo=5,$maxTempo=90,$randomClick=0.1)
        {
                set_time_limit(0);
                $this->checkComments = $checkComments;
                $this->minTempo = $minTempo;
                $this->maxTempo = $maxTempo;
                $this->randomClick = $randomClick;
                if ($randomClick) $this->add_source(‘*’,true,null,1);
        }
       
        function __destruct()
        {
                foreach ($this->accounts as $i=>$account) unset($this->accounts[$i]);
        }
       
        function wait()
        {
                $tempo = mt_rand($this->minTempo,$this->maxTempo);
                if (self::DEBUG) echo "waiting $tempo secs\n";
                sleep($tempo);
        }
       
        function add_source($id,$isFriend=true,$mods=null,$p=null)
        {
                $id = strtolower($id);
                $this->sources[$id] = new ScoopeoSource($id,$isFriend,$mods,$p);
        }
       
        function add_account($login,$password,$proxy=null,$useragent=null)
        {
                $this->accounts[] = new ScoopeoAccount($login,$password,$proxy,$useragent);
        }
       
        function add_page($url,$isList=true)
        {
                $this->pages[$url] = $isList;
        }
       
        function run($ano=true)
        {
                if (!count($this->sources)) return 0;
               
                $proxies = array();
               
                shuffle($this->accounts);
                               
                foreach ($this->accounts as $account)
                {
                        $proxy = $account->proxy ? $account->proxy :  »;
                        if ($ano && !@$proxies[$proxy])
                        {
                                $this->run_anonymous($proxy);
                        }
                       
                        $proxies[$account->proxy] = true;
                       
                        $account->init();
                       
                        foreach ($this->pages as $url=>$isList)
                        {
                                if ($isList) $this->process_scoops($account,$url);
                                else $this->process_comments($account,$url);
                        }
                }
        }
       
        function run_anonymous($proxy)

        {
                $scoopNinja = new ScoopeoAnonymousAccount($proxy);
                $this->process_scoops($scoopNinja,‘http://www.scoopeo.com/’,false,true);
                unset($scoopNinja);     
        }
       
        function process_scoops(&$user,$url,$checkComments=true,$ano=false)
        {
                if (self::DEBUG) echo "processing scoops on $url for $user->login\n";
                $scoops = $user->get_scoops($url);
               
                if (preg_match(‘#^http://www\.scoopeo\.com/membre/[a-z0-9_]+/((scoops)|(hp)|(clicks)|(commentaires))$#si’,$url)) $url = ‘http://www.scoopeo.com/’;
               
                if (self::DEBUG) echo count($scoops) . " scoops loaded\n";
                $this->wait();
               
                if (!is_array($scoops)) return null;
               
                shuffle($scoops);
               
                foreach ($scoops as $scoop)
                {
                        $user->back($url);

                        if (is_object(@$this->sources[$scoop[‘domain’]]))
                        {
                                $source = $this->sources[$scoop[‘domain’]];
                        }
                        else if (is_object(@$this->sources[$scoop[‘author’]]))
                        {
                                $source = $user->login != $this->sources[$scoop[‘author’]] ? $this->sources[$scoop[‘author’]] : null;
                        }
                        elseif ((mt_rand(0,100)/100) < $this->randomClick)
                        {
                                $source = $this->sources[‘*’];
                        }
                        else
                        {
                                $source = null;
                        }
                       
                        if ($checkComments && $scoop[‘comments’] && ((mt_rand(0,100) / 100) < $this->checkComments))
                        {
                                $this->process_comments($user,$scoop[’scoopUrl’]);
                        }
                                               
                        if (is_object($source))
                        {
                                $action = $source->action_on_scoop($scoop[‘id’],$scoop[’sec’],$scoop[‘canModerate’],$scoop[‘canClick’],$ano);
                                if ($action)
                                {
                                        if (self::DEBUG) echo "$user->login for " . $scoop[‘author’] . " : $action\n";
                                        $r = $user->click($action);
                                        if (self::DEBUG) echo "$r\n";
                                        $this->wait();
                                }
                        }
                }
        }

        function process_comments(&$user,$url)
        {
                if (self::DEBUG) echo "processing comments on $url for $user->login\n";
                $comments = $user->get_comments($url);
                if (self::DEBUG) echo count($comments) . " comments loaded\n";
                $this->wait();
               
                if (!is_array($comments)) return null;
               
                shuffle($comments);
               
                foreach ($comments as $comment)
                {
                        if (is_object(@$this->sources[$comment[‘author’]]))
                        {
                                $source = $user->login != $this->sources[$comment[‘author’]] ? $this->sources[$comment[‘author’]] : null;
                        }
                        elseif ((mt_rand(0,100)/100) < $this->randomClick)
                        {
                                $source = $this->sources[‘*’];
                        }
                        else
                        {
                                $source = null;
                        }
                       
                        if (is_object($source))
                        {
                                $action = $source->action_on_comment($comment[‘id’],$comment[‘canUpDown’]);
                                if ($action)
                                {
                                        if (self::DEBUG) echo "$user->login for " . $comment[‘author’] . " : $action\n";
                                        $r = $user->click($action);
                                        if (self::DEBUG) echo "$r\n";
                                        $this->wait();
                                }
                        }
                }
        }
}

Et après ?

Avant déjà… N’oubliez pas bien sûr de faire un include de la classe SpiceCurl.

Ensuite, voici un exemple d’utilisation :

$s = new ScoopeoBot;
$s->add_account(‘login_scoopeo’,‘password_scoopeo’); // le compte à utiliser

$s->add_source(‘patooye’,false,’spam,intox’,0.9); // le membre patooye sera moinser et ses scoops modérés comme spam ou intox dans 90% des cas
$s->add_source(‘porn-sex-viagra-casino-spam.com’,true,null,1); // les scoops de ce superbe domaine seront toujours cliqués !

$s->add_page(‘http://www.scoopeo.com/limit’); // on va lancer le bot sur la page des scoops presque promus
$s->add_page(‘http://www.scoopeo.com/limit?page=2′); // puis sur la deuxième page tiens !
$s->add_page(‘http://www.scoopeo.com/fresh’); // et sur les scoops en attente aussi…

$s->run(); // c’est parti !

Ici le robot va d’abord regarder la home de scoopeo et voter en anonyme les scoops provenant de porn-sex-viagra-casino-spam.com si il y en a, puis il va regarder les deux premières pages des scoops presque promus et la page des scoops en attente pour cliquer tous ceux de porn-sex-viagra-casino-spam.com, et modérer ceux de Patooye (toute ressemblance avec un membre existant est purement fictive blabla) dans 90% des cas.

Il ira de plus voir la page des scoops une fois sur deux environ pour moinser les commentaires de Patouille avec une probabilité de 90%.

Petite précision enfin quand vous ajoutez une page : le deuxième paramètre doit être passé à false si il s’agit de a page d’un scoop, et à true (par défaut) si il s’agit d’une page de liste (home, limit, fresh). Il n’y a que ces deux genres de pages qui sont gérés, donc inutile de passer la page Live ou le profil d’un membre par exemple (NOUVEAU : vous ne pouvez pas ajouter directement le profil d’un membre mais les pages de listes qui lui sont associées oui : …/scoops …/hp …/clicks …/commentaires ; cependant c’est à éviter car un utilisateur normal ne peut pas modérer directement un scoop depuis ce genre de pages _mais le robot si bien sûr).

Voilà c’est tout, et en fait c’est déjà pas mal !

Dernière minute : comme la première version était un peu bugguée, j’ai ajouté quelques petits bonus : en plus des clics aléatoires, il y a aussi maintenant une sécurité pour éviter de voter un scoop déjà voté ou essayer d’agir sur un commentaire déjà plussé ou moinsé.

Pour tous ceux ont lu jusqu’au bout : Source du ScoopeoBot et de la classe SpiceCurl !

Le robot est fait pour se lancer en ligne de commande, donc après avoir renommé le fichier en scoopeobot.php et avoir correctement indiqué vos identifiants scoopeo ainsi que les sources amies ou pas (en fin de fichier), vous pourrez voir le bot en action en tapant dans votre console : php scoopeobot.php :)

10 Responses à “Exemple d’utilisation de la classe SpiceCurl : le ScoopeoBot”

  1. MagicYoyo, référenceur white spirit dit :

    FB, t’es un barbare ! :D

  2. billyboylindien dit :

    J’avais une version pour les pligg.
    Il faut mutualiser les Ip / comptes / user agent

    Quelles sont les protections mises en place sur scoopeo (ip, user agent …) ?
    J’ai quelques ip qui trainent au besoin :o .

  3. noreply dit :

    pour scoopeo, si on veut clicker plusieurs fois un scoop, il faut une IP par compte disons. Le useragent n’a pas d’influence, par contre il faut bien penser a séparer les fichiers de cookies (ce que fait la classe).
    Ensuite, l’idéal est de passer un proxy par compte, où si vous avez une ip dynamique chez vous et un routeur accessible via une console web par exemple, modifier un peu la methode run() pour forcer une reconenxion a chaque nouvel account utilisé :D
    On peut cliquer les scoops qui sont deja en home de façon anonyme, mais seulement une fois par IP.

  4. Pauline dit :

    Vraiment princier de partager ton savoir aux pauvres francophones qui n’ont pas souvent d’articles de qualité présentant des applications pratiques super pratiques :-)

    http://www.visitezmonsite.com/TECHNOLOGIE/Comment-Spammer-Scoopeo-Et-Les-Autres-Digg-like

    A quand un visitezmonsitebot? Quand il y aura du monde, je sais oui.. :-)

    Merci

  5. tango73 dit :

    pas eu le temps de bien regarder mais en tout cas bravo !

  6. michaelj dit :

    Merci c’est bien pratique!

  7. Yuston dit :

    L’est passée où ta politique du « Very very closed source » ?

  8. BlackMelvyn dit :

    Cool tes classes :)
    Je me demandais quand un mec serait assez fou pour rendre public ce genre de code –> pas longtemps :lol:

  9. SEO dit :

    Bonjour,

    merci pour ce partage qui semble très intéressant (pour mon apprentissage du blackhat) mais j’ai essayé de faire fonctionner le bot sous vista & tor avec easyphp en ligne de commande mais ca dis qu’il manque curl

    et quand je le lance avec firefox ca me dis :

    adding account for MONPSEUDO
    Notice: Use of undefined constant CURLOPT_MUTE – assumed ‘CURLOPT_MUTE’ in D:\EasyPHP5.3.0\www\robots\scoopeobot.php on line 36

    Warning: curl_setopt() expects parameter 2 to be long, string given in D:\EasyPHP5.3.0\www\robots\scoopeobot.php on line 36

    Notice: Use of undefined constant CURLOPT_MUTE – assumed ‘CURLOPT_MUTE’ in D:\EasyPHP5.3.0\www\robots\scoopeobot.php on line 36

    Warning: curl_setopt() expects parameter 2 to be long, string given in D:\EasyPHP5.3.0\www\robots\scoopeobot.php on line 36
    processing scoops on http://www.scoopeo.com/ for Anonymous 0 scoops loaded waiting 25 secs processing scoops on http://www.scoopeo.com/fresh for MONPSEUDO 0 scoops loaded waiting 17 secs

    Pouvez vous me dire la meilleur facon de tester cette source en local si c’est possible ?

    Je vous remercie d’avance
    Nicolas

  10. noreply dit :

    elle date un peu cette classe, avec les nouveaux proprios de scoopeo ya de forte chances que des choses aient changées et qu’elle ne fonctionne plus. mais je n’ai ni le temps ni l’envie de regarder, desolé :)

Poster un commentaire