Données de Facebook: deux poids deux mesures

Voici le texte complet d’une opinion publiée aujourd’hui dans La Presse+, ainsi que quatre documents qui viennent appuyer ce que j’y raconte:

  1. La proposition que j’ai transmise à Facebook Research en décembre 2014.
  2. La réponse de Facebook Research, reçue en janvier 2015.
  3. Un exemple des données que mon application recueillait, telle qu’elle a été testée par Matthieu Dugal en juin 2015.
  4. Quelques précisions techniques qui permettent de voir que Facebook a, depuis, fermé la porte à un application comme la mienne et rendu encore plus difficile de comprendre le fonctionnement de son algorithme
  5. Un extrait du code que j’avais commencé à développer à l’époque, avec quelques précisions techniques

Facebook protège ses données. Mais attention. Ici, je parle bien de SES données et non de VOS données.

Pour comprendre la différence, laissez-moi vous raconter une histoire que j’ai vécue avec Facebook en 2014-2015.

Comme professeur de journalisme, je m’intéresse depuis longtemps aux effets de la technologie sur l’information. Ces dernières années, je me suis particulièrement penché sur les bouleversements engendrés par les médias socionumériques. Déjà, en 2014, on savait que les Québécois s’informaient majoritairement au moyen de Facebook. Mais quelle information, au juste, leur parvient lorsqu’ils consultent leur fil d’actualité?

Pour le savoir, j’ai élaboré un projet de recherche. Il s’agissait d’une application qui aurait demandé à un échantillon de participants de me laisser voir les 25 premiers items apparaissant dans leur fil d’actualité Facebook à raison de trois ou quatre fois par jour pendant six semaines.

Bien entendu, le projet allait devoir être approuvé par le comité d’éthique de la recherche de mon université. Pour faire partie de mon échantillon, les participants allaient devoir me donner un consentement libre et éclairé.

Mais pour que mon application puisse être déployée dans Facebook, j’avais également besoin de l’autorisation de Facebook.

J’ai donc contacté Kent Foster, un des responsables des relations avec le milieu académique de Facebook Research, la branche du réseau social qui supervise les projets de recherche internes ou externes, comme le mien. Je lui ai expliqué mon projet. Il m’a demandé de lui faire parvenir une proposition écrite, ce que j’ai fait le 16 décembre 2014.

Quelques semaines après les Fêtes, le 26 janvier 2015, leur réponse m’est parvenue. C’était un refus. On disait surtout qu’« aucun groupe à l’interne n’est intéressé à appuyer une collaboration » avec mon projet.

On pourrait penser, ici, que Facebook a agi en bon citoyen corporatif et qu’il a protégé les données de ses utilisateurs. Je ne suis pas du tout de cet avis.

Il y a un monde de différence entre le projet que j’ai présenté en 2014 et celui que le chercheur de l’Université de Cambridge, Aleksandr Kogan, a mis au point l’année suivante, projet qui est au cœur d’une fuite de données personnelles qui a fait les manchettes ces derniers jours et forcé le président de Facebook, Mark Zuckerberg, à s’excuser.

L’application de Kogan s’appellait This Is Your Digital Life. Il s’agissait d’un quiz qui réalisait votre portrait psychologique. Environ 270 000 personnes l’ont passé. Mais l’application demandait aussi que les utilisateurs lui permettent d’accéder aux informations de leurs amis. Ce faisant, Kogan a obtenu les données personnelles d’environ 50 millions d’utilisateurs de Facebook et ce, même si ceux-ci n’ont jamais utilisé l’application en question, encore moins consenti à ce qu’elle aspire leurs données personnelles. Aleksandr Kogan a permis à Cambridge Analytica d’accéder à ces données et la suite, maintenant connue, fait scandale.

L’application que j’avais commencé à programmer était d’une tout autre nature. Si vous aviez consenti à participer à mon projet de recherche, l’application vous aurait demandé votre âge, votre genre et votre région de résidence afin de constituer un échantillon représentatif de la population québécoise. Elle vous l’aurait demandé directement, sans puiser dans votre profil Facebook. Elle n’aurait pas été chercher votre liste d’amis ni aucune autre donnée personnelle possédée par Facebook.

Elle se serait ensuite contenté de recueillir uniquement les publications apparaissant dans votre fil d’actualité. Pour chacune de ces publications, elle aurait enregistré 14 variables relatives à ces publications (titre, date, contenu, provenance de la publication, nombre de j’aime, de commentaires et de partages, etc.). C’est tout.

En somme, je ne m’intéressais pas à vous. Je m’intéressais à Facebook. Je m’intéressais à l’information que Facebook trie et vous sert dans votre fil d’actualité.

L’application d’Aleksandr Kogan et de Cambridge Analytica s’intéressait à vous, à vos données personnelles.

Cette différence fondamentale explique, à mon avis, pourquoi Facebook a refusé une application comme la mienne et accepté une application comme celle qui a été financée par Cambridge Analytica. Je fais l’hypothèse qu’en voyant ce que Facebook présente à des milliers d’utilisateurs, j’aurais été en mesure de déduire en partie comment fonctionne l’algorithme de Facebook.

On peut comprendre l’entreprise de vouloir protéger ses données « industrielles ». C’est son pain et son beurre. Facebook connaît exactement la valeur que peuvent avoir des données. C’est précisément ce qui rend encore plus scandaleux le laxisme dont elle a fait preuve dans la gestion des données personnelles de ses utilisateurs dans toute l’affaire Cambridge Analytica.

Facebook savait depuis longtemps que Cambridge Analytica avait violé le contrat qui avait initialement permis la cueillette des données de ses utilisateurs. Elle savait que les données recueillies dans un dessein de recherche étaient détournées à des fins commerciales et politiques. Facebook n’a pas pris de moyens suffisants pour que cette pratique cesse. Facebook, surtout, n’a pas prévenu ses utilisateurs.

Il y a donc deux poids, deux mesures avec les données, chez Facebook. Quand il s’agit de ses données à elle, l’entreprise les protège avec zèle. Quand il s’agit des données de ses utilisateurs, par contre, les derniers jours ont démontré qu’elle pouvait en disposer non pas avec nonchalance (le mot est trop faible), mais avec négligence.


Précisions techniques

Vous avez peut-être remarqué que lorsque vous vous connectez à Facebook, l’adresse est simplement: https://www.facebook.com/home.php. En somme, vous lisez la page «home.php» du site Facebook.

Eh bien l’application que j’avais commencé à développer demandait aux utilisateurs de me permettre de voir une partie de cette page home. L’API Graph de Facebook contenait justement une section appelée /home qui permettait d’afficher programmatiquement le contenu de votre fil d’actualité, la colonne principale que vous voyez lorsque vous vous connectez à Facebook.

Pour y accéder, les utilisateurs devaient accorder à mon application une permission appelée read_stream. Mais voilà, dans la version 2.4 de son API, en vigueur à partir de d’octobre 2015, Facebook a annulé («deprecated») la permission read_stream et l’accès à la section /home, ce qui fait qu’un projet comme celui que j’avais en 2014-15 ne serait plus possible aujourd’hui.

À mon humble avis, c’est parce que cette permission et cette section ouvraient une fenêtre sur le fonctionnement de l’algorithme de sélection du contenu de Facebook que l’entreprise les a annulées.


Fichier fb.rb

Voici du code brut, dans lequel je n’ai rien changé, depuis 2015, sinon que j’ai retranché des clés ou des jetons d’utilisation (qui sont en quelque sorte des mots de passe). C’est un work-in-progress inachevé.

L’application demandait à un utilisateur d’aller dans l’explorateur de l’API Graph de Facebook et de copier son jeton d’accès (Access Token) dans un formulaire Google. L’application allait ensuite lire un tableur Google dans lequel le jeton d’accès des utilisateurs était enregistré pour s’en servir ensuite pour aller lire les dernières publications apparaissant dans son fil d’actualité (en mode test, j’allais lire les 100 plus récentes publications).

Comme je cherchais à voir quelle était la part de l’information dans le fil d’actualité des utilisateurs, je tentais de déterminer, en remontant le plus près possible de la source, si une publication donnée provenait d’un média ou non. Par exemple, si la publication contenait un lien ou une vidéo, j’allais voir s’il s’agissait du compte Youtube, Vimeo, Soundcloud ou Facebook d’un média.

#!/usr/bin/env ruby
# ©2015 Jean-Hugues Roy. GNU GPL v3.

require "json"
require "koala"
require "nokogiri"
require "open-uri"
require "open_uri_redirections"
require "csv"
require "google/api_client"
require "google_drive"

id = "xxx.apps.googleusercontent.com"
codeSecret = "xxx-xxx"
refreshToken = "1/xxx-xxx"

client = Google::APIClient.new(
  :application_name => 'FB',
  :application_version => '1.0.0'
)

auth = client.authorization
auth.client_id = id
auth.client_secret = codeSecret
auth.scope =
    "https://docs.google.com/feeds/ " +
    "https://docs.googleusercontent.com/ " +
    "https://spreadsheets.google.com/feeds/"

auth.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
auth.refresh_token = refreshToken
auth.refresh!
access_token = auth.access_token
session = GoogleDrive.login_with_oauth(access_token)

xl = session.spreadsheet_by_key("xxx").worksheets[0]

# puts "Il y a #{xl.num_rows-1} noms."

utilisateur = xl[xl.num_rows,2]

puts "Le dernier utilisateur s'appelle #{utilisateur}"

cle = xl[xl.num_rows,3]

# for row in 2..xl.num_rows
#     listeEnvoi.push xl[row,3]
# end

# cles = [
# "xxx"
# ]

fb = Koala::Facebook::API.new(cle)

r = fb.get_connections("me","?fields=home.limit(100)&locale=fr_CA")
# puts fb.get_connection('search', type: :place)
# fb.put_wall_post("Test avec Koala")

r=r.to_json

puts r

File.open("#{utilisateur}.json","w") do |f|
  f.write(r)
end

# fichier1 = "#{utilisateur}.json"
# source1 = "#{utilisateur}.txt"

# n = 0
# ligne1 = 0
# ligne2 = 0

# File.readlines(source1).each do |ligne|
# 	n += 1
# 	if ligne[1] == "\n"
# 		puts n.to_s + " : " + ligne.size.to_s + " : " + ligne
# 		if ligne1 == 0
# 			ligne1 = n
# 		else
# 			ligne2 = n
# 		end 
# 	end
# end
# puts "On extrait de la ligne #{ligne1} à la ligne #{ligne2}"

# File.readlines(source)[ligne1-1..ligne2-1].each do |linea|
# 	# puts linea
# 	l = linea.to_s.gsub(/\u00A0\u00A0/, "\t")
# 	# puts l
# 	File.open(fichier,"a") do |f|
# 		f.write(l)
# 	end
# end

# puts "Nom de fichier?"

# source = gets.chomp

fichier = "#{utilisateur}.csv"
source = "#{utilisateur}.json"

f = File.read(source)

p = JSON.parse(f)

p = p["home"]
data = p["data"]
# u = p["paging"].to_h
# u = u["previous"]
# u = u[32..-1]
# numFB = u[0..u.index("/")-1]

# puts numFB

medias = {
  "radio-canada.ca" => "Radio-Canada",
  "icimusique.ca" => "Radio-Canada",
  "cbc.ca" => "CBC",
  "lapresse.ca" => "La Presse",
  "ledevoir.com" => "Le Devoir",
  "huffingtonpost.com" => "Huffington Post",
  "huffingtonpost.ca" => "Huffington Post",
  "journaldequebec.com" => "Journal de Québec",
  "journaldemontreal.com" => "Journal de Montréal",
  "tvanouvelles.com" => "TVA",
  "tvanouvelles.ca" => "TVA",
  "lemonde.fr" => "Le Monde",
  "lefigaro.fr" => "Le Figaro",
  "lepoint.fr" => "Le Point",
  "rds.ca" => "RDS",
  "nytimes.com" => "New York Times",
  "theglobeandmail.com" => "The Globe and mail",
  "thestar.com" => "Toronto Star",
  "urbania.ca" => "Urbania",
  "journalmetro.com" => "Métro",
  "courrierinternational.com" => "Courrier International",
  "rfi.my" => "Radio-France Internationale",
  "globalnews.ca" => "Global News",
  "voir.ca" => "Voir",
  "chatelaine.com" => "Châtelaine",
  "businessinsider.com" => "Business Insider",
  "rue89.nouvelobs.com" => "Nouvel Observateur",
  "independent.co.uk" => "The Independent",
  "bbc.co.uk" => "BBC",
  "nationalpost.com" => "National Post",
  "wired.com" => "Wired",
  "pagina12.com.ar" => "Pagina 12"
}

# f = Nokogiri::HTML(open("http://www.facebook.com/" + numFB, :allow_redirections => :safe))

# utilisateur = f.css("title").text.strip

# puts utilisateur

tout = []

# fb["Utilisateur"] = utilisateur

data.each do |item|
  fb = {}
  # fb["Numero d'utilisateur"] = numFB
  item = item.to_h
  # puts item.size

  if item.has_key?("from")
    qui = item["from"].to_h
    fb["Contact"] = qui["name"]
    if qui.has_key?("category")
      fb["Categorie"] = qui["category"]
    else
      fb["Categorie"] = ""
    end
  else
    fb["Contact"] = ""
  end

  if item.has_key?("story")
    fb["Story"] = item["story"]
  else
    fb["Story"] = ""
  end

  if item.has_key?("message")
    fb["Message"] = item["message"]
  else
    fb["Message"] = ""
  end

  if item.has_key?("link")
    fb["Lien"] = item["link"]
    # puts item["link"][0..6]
    if item["link"][0..6] == "http://"
      site = item["link"][7..-1]
      site = site[0..site.index("/")-1]
      # puts site
      fb["Site"] = site
    elsif item["link"][0..7] == "https://"
      site = item["link"][8..-1]
      site = site[0..site.index("/")-1]
      # puts site
      fb["Site"] = site
    else
      fb["Site"] = ""
    end
  else
    fb["Lien"] = ""
    fb["Site"] = ""
  end

  if fb["Site"] != ""
    s = fb["Site"]

    if s[-4..-1] == "t.co" || s[-11..-1] == "youtube.com" || s[-12..-1] == "facebook.com" || s[-9..-1] == "vimeo.com" || s[-14..-1] == "soundcloud.com" || s[-6..-1] == "bit.ly"
      social = Nokogiri::HTML(open(item["link"], :allow_redirections => :safe))
      titre = social.css("title")
      t = titre.text
      if t.include?("adio-Canada") || t.include?("ICI ")
        fb["Media"] = "Radio-Canada"
      elsif t.include?("CBC")
        fb["Media"] = "CBC"
      elsif t.include?("Protégez")
        fb["Media"] = "Protégez-Vous"
      elsif t.include?("Huffington")
        fb["Media"] = "Huffington Post"
      else
        fb["Media"] = ""
      end
    else
      fb["Media"] = ""
    end
    medias.each do |urlMedia, nomMedia|
      if s[-(urlMedia.size)..-1] == urlMedia
        fb["Media"] = nomMedia
      end
    end
  else
    fb["Media"] = ""
  end

  if fb["Categorie"].include?("ews")
    fb["Media"] = fb["Contact"]
    if fb["Media"].include?("adio-Canada")
      fb["Media"] = "Radio-Canada"
    elsif fb["Media"].include?("Huffington")
      fb["Media"] = "Huffington Post"
    end
  end

  puts fb["Media"]

  if item.has_key?("name")
    fb["Titre"] = item["name"]
  else
    fb["Titre"] = ""
  end	

  if item.has_key?("caption")
    fb["Legende"] = item["caption"]
  else
    fb["Legende"] = ""
  end	

  if item.has_key?("description")
    fb["Description"] = item["description"]
  else
    fb["Description"] = ""
  end

  if item.has_key?("type")
    fb["Type"] = item["type"]
  else
    fb["type"] = ""
  end

  if item.has_key?("likes")
    jaime = item["likes"].to_h
    fb["Jaime"] = jaime["data"].size
  else
    fb["Jaime"] = 0
  end

  if item.has_key?("comments")
    commentaires = item["comments"].to_h
    fb["Commentaires"] = commentaires["data"].size
  else
    fb["Commentaires"] = 0
  end

  if item.has_key?("shares")
    partages = item["shares"].to_h
    fb["Partages"] = partages["count"]
  else
    fb["Partages"] = 0
  end

  tout.push fb
end

CSV.open(fichier, "wb") do |csv|
  csv << tout.first.keys
  tout.each do |hash|
    csv << hash.values
  end
end

 

Vous aimerez aussi...

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *