Tout le monde sait ce que sont les spam ou pourriels qui à l’instar des prospectus inondent les boîtes aux lettres électroniques. Cette nuisance qui parasite le trafic internet pousse à la consommation dans les cas les plus inoffensifs ou bien va jusqu’à la tentative d’arnaque.
Il existe un autre type de nuisance sur le web. Il s’agit de l’occupation même du web à travers ses pages et ses réseaux sociaux. Cette présence à sans doute plus d’impact que le spam classique. En effet occuper l’espace numérique crée un buzz qui touche de nombreuses personnes… nous oseront même dire touche celles qui comptent car celle-ci peuvent changer le cours de l’histoire. Un président américain l’a d’ailleurs bien compris tandis que d’autres ne l’ont pas compris et ne sont plus au pouvoir.
Internet est aujourd’hui le média qu’il faut investir comme outils de propagande. A cette fin des sociétés se proposent, monnayant finance, de créer du buzz ou de lifter une e-réputation. Nous noterons toutefois que si l’objectif de manipuler l’opinion est le même que celui d’une publicité sur papier ou à la télévision, la forme diffère grandement. Les meilleurs buzz sont ceux qui ne ressemblent pas à de la publicité. En effet l’opinion du web est plus sensible à lui même qu’à des messages extérieurs: une forme de bouche à oreille numérique.
Comme rien n’est plus ressemblant à l’original que l’original lui-même, créer un buzz nécessite beaucoup de moyens. Il faut investir les forum, créer des sites web de propagande, simuler de nombreux fans. Pourtant nous sommes bien dans un univers créé par les ordinateurs, il doit donc être possible d’automatiser le buzz. L’idée est d’automatiser la création de contenu sur de multiple site. Chaque instance devra autant faire se peut être différente (pas de copier/coller) de l’autre afin de simuler des rédacteurs humains différents. L’automatisation doit permettre non seulement de simuler le nombre mais aussi de créer l’historique car de nombreuses références qui n’ont pas d’historique sont moins crédibles qu’une référence ayant pignon sur le web depuis des lustres. Cela est réalisable assez facilement puisque les hébergeurs de blog tels que blogger.com ou wordpress.com exposent une API pour publier des billet de manière programmatique. La modifications des billets sans en altérer le sens pour qu’ils ressemblent à ceux qu’aurait pu faire un humain, peut être obtenue en remplaçant les mots par des synonymes piochés par exemples sur le site de l’université de Caen. Le plus difficile est la créations des comptes initiaux (compte email google@par exemple) qui signent l’acte de naissance de la vraie fausse identité. En effet les fournisseurs utilisent des filtres pour repousser la création de compte par des robots.
Le code suivant montre un POC. Quand à savoir si la méthode est efficace pour manipuler le référencement des moteurs de recherche, le lecteur ce lancera lui-même dans l’expérience.
#!/usr/bin/python
# -*- encoding: UTF-8 -*-
'''
Created on 24 avr. 2011
@author: thierry
'''
import os.path
import random
from optparse import OptionParser
from gdata import service
import gdata
import atom
import urllib
from HTMLParser import HTMLParser
import random
import logging
LEVELS = {'debug': logging.DEBUG,
         'info': logging.INFO,
         'warning': logging.WARNING,
         'error': logging.ERROR,
         'critical': logging.CRITICAL}
class TextCloner():
   '''
   This class is responsible for cloning a text e.g. rewrite the text modifying
   some words to simulate human copying.  Â
   '''
   def clone(self, original):
       pass
class SynonymeCloner(TextCloner):
  Â
  Â
   def insert_synonyme(self, before, synonyme, after):
       url = 'http://dictionnaire.tv5.org/dictionnaires.asp?%s'
       params = urllib.urlencode({'Action':'1', 'mot': synonyme.split(' ')[-1:][0].encode("utf-8")})
       f = urllib.urlopen(url % params)
       response = f.read().decode('iso-8859-1')
       f.close()
       voyelle = ['a','e','i','o','u','y','é']
       if synonyme[0] in voyelle
           or ( synonyme[0] == 'h' and synonyme[1] in voyelle) :
           art = "l'"
       elif response.find("masculin") > -1 :
           art = 'le '
       else:
           art = 'la '
       if before.endswith(". ") or len(before.strip()) == 0:
           art = art.capitalize()
       clone = before + art + synonyme + after          Â
       return clone
      Â
   def clone(self, original):
       clone = original
       start = 0;
       while start > -1:
           START = "<%"
           start = clone.find(START)
           if start > -1:
               before, start_tag, after = clone.partition(START)
               END = "%>"
               word, end_tag, after = after.partition(END)
               synonyme = self.get_synonyme(word)
               logging.info("choose synonyme %s" % synonyme)
               clone = self.insert_synonyme(before, synonyme, after)                                              Â
              Â
       return clone
  Â
  Â
  Â
   def get_synonyme(self, word):          Â
       logging.info("looking for synonyme for %s" % word)      Â
       f = urllib.urlopen('http://www.crisco.unicaen.fr/des/synonymes/%s' % urllib.quote(word.encode("utf-8")))
       reponse = f.read().decode('utf-8')
       f.close()
      Â
       class MyParser (HTMLParser):
           result_found = False
           tag = None
           cdep = None
           cp_found = []
           grab_result = False
           def __init__(self):
               HTMLParser.__init__(self)
               self.tag = None
               self.result_found = False
               self.cp_found = []
           def handle_starttag(self, tag, attrs):
               if tag == "div":
                   for a, v in attrs:
                       if a == 'id' and v == 'synonymes':
                           self.result_found = True
                           self.tag = tag
                           self.tag_count = 1
                           return
          Â
               if self.result_found and tag == "div":
                   self.tag_count += 1
          Â
               if self.result_found and tag == "a":
                   self.grab_result = True
Â
           def handle_endtag(self, tag):
               if self.result_found:
                   if tag == self.tag:
                       self.tag_count -=1
                       if self.tag_count == 0:
                           self.result_found = False
                   if tag == "a":
                       self.grab_result = False
Â
           def handle_data(self, data):
               if self.result_found and self.grab_result:
                   self.cp_found.append(data)
      Â
       p = MyParser()
       p.feed(reponse)
       p.close()
       return random.choice(p.cp_found + [word])
  Â
class Blog():
   '''
   This class can post an entry to a blog
   '''
   def post(self, title, content):
       pass
  Â
class Blogger(Blog):
   '''
   This blog poster implement blogger API.
   '''
   def __init__(self, login, password, blog_url):
       self.blogger_service = service.GDataService(login, password)
       self.blogger_service.source = 'publisher'
       self.blogger_service.service = 'blogger'
       self.blogger_service.account_type = 'GOOGLE'
       self.blogger_service.server = 'www.blogger.com'
       self.blogger_service.ProgrammaticLogin()
      Â
       query = service.Query()
       query.feed = '/feeds/default/blogs'
       feed = self.blogger_service.Get(query.ToUri())
       print feed.title.text
       for entry in feed.entry:
           logging.info("t" + entry.title.text)
           for link in entry.link:
               if link.href.startswith(blog_url):
                   self.blog_id = entry.GetSelfLink().href.split("/")[-1]
                  Â
  Â
   def post(self, title, content):
      Â
       entry = gdata.GDataEntry()
       entry.title = atom.Title('xhtml', title)
       entry.content = atom.Content(content_type='html', text=content)
       return self.blogger_service.Post(entry, '/feeds/%s/posts/default' % self.blog_id)
class AccountParser():
   '''
   This class can read the account file
   '''
   def __init__(self, filename):
       self.filename = filename
  Â
   def account_iter(self):
       logging.info("parsing %s account file" % self.filename)
       with open(self.filename) as f:
           for line in f:
               splitted = line.split(",")
               url = splitted[0]
               login = splitted[1]
               password = splitted[2]
               if url.find("blogspot.com") > -1: Â
                   logging.info("found blospot account %s" % url)                      Â
                   yield Blogger(login, password, url)
               else:
                   logging.warn("skip unsupported blog %s" % url)
  Â
def run():
   usage = "%prog -a <account> -p <post> [-l <log level>]"
   str_version = "%prog 0.1"
   parser = OptionParser(usage=usage, version=str_version)
   parser.add_option("-a", "--account", action="store", type="string", dest="account", help="account file")
   parser.add_option("-p", "--post", action="store", type="string", dest="post", help="post file")
   parser.add_option("-l", "--log", action="store", type="string", dest="level_name", help="log level")
   parser.add_option("-t", "--title", action="store", type="string", dest="title", help="post title")  Â
   options, args = parser.parse_args()
   level = LEVELS.get(options.level_name, logging.NOTSET)
   logging.basicConfig(level=level)
  Â
   with open(options.post) as post_file:
       post = post_file.read()     Â
       for blog in AccountParser(options.account).account_iter():
           if options.title:
               title= options.title
           else:
               title = ""     Â
           blog.post(title, SynonymeCloner().clone(post))
if __name__ == '__main__':Â Â Â
   run()
Tags: Python Weblogs
