Monadologiska undersökningar 3: Visualisera kommentarsfält på Facebook

faschmap{.alignnone .size-full .wp-image-3466 width="500" height="500"}

Jag blev lite knäpp av att bara göra något halvgjort i förra inlägget. Så här kommer någon form av fortsättning, dels ett lite mera aggressivt inhämtningsskript och dels ett sätt att skapa nätverksfiler av datan som man får ned från facebook.

Först testade jag att hämta ned så mycket jag kunde från ett gäng nyfascistiska Facebooksidor. Mest för att se om det fanns kopplingar på monad/divid-nivå. Jag uppgraderade det förra skriptet att iterera över en dictionary och spara i en ny katalog med passande filnamn:

``` {.EnlighterJSRAW data-enlighter-language="python"} from facepy import GraphAPI from django.core.serializers.json import DjangoJSONEncoder import json import time import os

fascistfeeds = {"Sverigedemokraterna": 676246449160270, "Avpixlat": 1456773041274824, "SvP": 277195872311475, "Sdkvinnor": 169003303168526, "Ungsvenskarna": 1491422081182615, "Sdkuriren": 665250476818577, "Samtiden": 256690774389618, "Folkomrostningen": 1710097892544837, "Giveemhell": 1075216222492967, "Kabbelsvetsarn": 742925315824067, "Armanrasist": 146597985381739 }

Create a directory with today's date.

print("Today's date is:" + time.strftime("%Y%m%d")) todaydate = time.strftime("%Y%m%d")

if not os.path.exists("scrape" + todaydate): dirdatename = "scrape" + todaydate os.makedirs(dirdatename) else: print("Directory already exists, exiting.") quit()

for key, value in fascistfeeds.items(): print(key, value)

group_id = "%s" % value
access_token = "Put here the token from https://developers.facebook.com/tools/explorer/"
graph = GraphAPI(access_token)
pages = graph.get(group_id + "/feed", page=True, retry=3, limit=1) #This is the number of posts per file!! original value 1
i = 0
for p in pages:
    print('Downloading page', key, i)
    with open('%s/%scontent%i.json' % (dirdatename, key, i), 'w') as outfile:
        json.dump(p, outfile, indent = 4, cls=DjangoJSONEncoder, ensure_ascii=False)
    i += 1
Urvalet är pseudo-slumpmässigt. Jag klickade bara runt lite i den bruna
sörjan för att få mera data. När jag körde skriptet ovan fick jag hem
7858 postningar som sparas i varsin fil, sammanlagt 168 Mb data.

Redan här har man en datamängd som man kan jobba rätt bra med. Man kan
söka efter ord och användare. Troligtvis finns det intressanta saker man
kan göra med alla kommentarers fulltext, deras språkbruk, vem som säger
vad etc. Men det är en annan form av analys.

Jag är intresserad av hur vanligt det är att en person kommenterar på en
post på Sverigedemokraternas sida och sedan går över till Avpixlat och
gör det samma, och så vidare längs hela datamängden. Då får vi fram en
relation mellan dessa två aktörer som är medierad av en divid.
Sverigedemokraterna och Avpixlat kan närma sig varandra till exempeln
när en en [Sd-politiker skriver på
nyhetssidan](http://www.expressen.se/nyheter/mattias-karlsson-skrev-at-avpixlat/).
Men ett närmande kan också ske "underifrån" i och med att en aktör-divid
kommenterar på båda sidorna, ett närmande som är minst lika viktigt för
att skapa associationer. Att den ena aktören är en politiker och den
andra en "helt vanlig människa" är inte nödvändigtvis en gradskillnad,
utan snarare en slags modulation av en liknande relation. Attraktion är
en kraft för mikrofascismen.

Nog med teori! För att vaska fram de aktörer som kommenterat på sidorna
som nedskrapats från Facebook, skrev jag det här krytpiska skriptet:

``` {.EnlighterJSRAW data-enlighter-language="python"}
from gexf import *
from json import load
from os import listdir
import hmac
import hashlib


gexf = Gexf("Facebook interactions", "Comments to Page")
graph = gexf.addGraph("directed", "static", "Facebook directed network")

edgelist = []

#### Multi file parser from directory
for filename in listdir("scrape20151128/"): #change path here (1/2)
    with open("scrape20151128/" + filename) as currentFile: #change path here (2/2)
        jsondata = load(currentFile) #parse json.

    ###The hash function. Note that the hashlib function needs to
    ###be inside the loop to preserve data integrity.
    for item in jsondata['data']:
        #print(item['from']['name'])
        targetgroup = item['from']['name']

        #This loop extracts comments. make sure to separate "i" and "item"
        #Change whatever you want to print.
        try:
            if item['comments']['data']:

                for i in item['comments']['data']:

                    digest_maker = hmac.new(b'rep7ac3withs0p3recretk3y', i['from']['id'].encode(), hashlib.sha256)
                    #print("Encrypted id: " + digest_maker.hexdigest()) #for debugging
                    hasheduser = digest_maker.hexdigest()
                    user = i['from']['id'] + i['from']['name'] #Plaintext user, for debugging only.
                    #print(hasheduser + targetgroup)
                    #strhasheduser = hasheduser.decode(encoding='UTF-8')
                    graph.addNode(hasheduser, hasheduser)
                    graph.addNode(targetgroup, targetgroup)
                    edgelist.append( (hasheduser, targetgroup) )

        except KeyError:
            break

#print(edgelist) #for debugging

for k, v in enumerate(edgelist):
    print(k, v[0], v[1])
    graph.addEdge(k, v[0], v[1])
    #print(k, v)

output_file=open("fascistfeeds.gexf","wb")
gexf.write(output_file)

Om allt går som det ska så spottar skriptet sedan ur sig:Facebook directed network directed static number of nodes : 13138 number of edges : 73721

Den .gexf-fil som sedan genereras kan öppnas med Gephi. Det blir lite kärvt med minne och processor vid så här stora datamängder, men det gick på min laptop.

På bilden ovan (ursäkta unicode-haveriet, någonstans i Gephi gick det fel) ser vi alltså facebook-sidorna som de stora noderna. Varje gång en divid kommenterar på en sida, skapas en relation, en så kallad "edge". Varje divid kan kommentera på flera olika sidor (de hashade facebook-idnumrena håller reda på dem utan att avslöja identiteten) vilket gör att sidorna hamnar lite närmare varandra.

Det här var återigen bara ett test. Inga slutsatser att går att dra av resultatet. Men däremot borde det gå att fundera på vad som kan göras rent metodologiskt.