Rohdatenerfassung zu den Locations via Instagram-API (finaler Ansatz)

Im Zuge des Roland-Berger-Projektes fand ich endlich Gelegenheit, den Locationrohdatenscraper zu optimieren. Hier nun der Quellcode:

(1) Scrapingprozedur
Erklärung:
get_loc_short(mytable: TStringGrid; locid: string; rounds: integer);
mytable -> Stringgridobjekt, verlangt die Übergabe des Objektnamens
locid -> LocationID aus Instagram (siehe: Locationparser)
rounds -> maximale Anzahl der Subprüfung je Durchlauf

procedure TForm1.get_loc_short(mytable: TStringGrid; locid: string; rounds: integer);
var
JSONArray: tJSONArray;
JSONValue,jvalue: tJSONValue;
JSONPair: TJSONPair;
JSON, json_sub: TJSONObject;
size: integer;
j_array: tJSONArray;
s: string;
i,j: integer;
next_id: string;
zaehl: integer;
begin
zaehl:=0;
try
debug.text:=idhttp1.Get('https://api.instagram.com/v1/locations/'+locid+'/media/recent?access_token='+token.text);
JSONValue := TJSONObject.ParseJSONValue(debug.text);
JSON := TJSONObject.ParseJSONValue(debug.Lines.Text) as TJSONObject;
JSONArray := TJSONArray(JSON.Get('data').JsonValue);
try next_id:= JSONValue.GetValue('pagination.next_url');
except
next_id:='N/A';
end;
for i := 0 to JSONArray.Size - 1 do
begin
with mytable do
begin
cells[0,rowcount]:=inttostr(rowcount);
cells[1,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('link')).JsonValue.Value);
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('tags')).ToString);
s:= StringReplace(s, '"tags":[', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, ']', '', [rfReplaceAll,rfIgnoreCase]);
cells[2,rowcount]:=s;
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('likes')).ToString);
s:= StringReplace(s, '"likes":{"count":', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, '}', '', [rfReplaceAll,rfIgnoreCase]);
cells[3,rowcount]:=s;
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('comments')).ToString);
s:= StringReplace(s, '"comments":{"count":', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, '}', '', [rfReplaceAll,rfIgnoreCase]);
cells[4,rowcount]:=s;
cells[5,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('created_time')).JsonValue.Value);
cells[5,rowcount]:=datetimetostr(UnixToDateTime(strtoint(cells[5,rowcount])));
cells[6,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('id')).JsonValue.Value);
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('user')).ToString);
s:= StringReplace(s, '"user":{"username":', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, '}', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, '"', '', [rfReplaceAll,rfIgnoreCase]);
cells[7,rowcount]:=s;
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('location')).ToString);
s:= StringReplace(s, '"location":', '', [rfReplaceAll,rfIgnoreCase]);
cells[8,rowcount]:=s;
cells[9,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('filter')).JsonValue.Value);
cells[10,rowcount]:=datetimetostr(now);
rowcount:=rowcount+1;
end;
grdColWidth(mytable, 40);
end;
except
end;
repeat
// -> tiefenpruefung
try
debug.text:=idhttp1.Get(next_id);
JSONValue := TJSONObject.ParseJSONValue(debug.text);
JSON := TJSONObject.ParseJSONValue(debug.Lines.Text) as TJSONObject;
JSONArray := TJSONArray(JSON.Get('data').JsonValue);
try next_id:= JSONValue.GetValue('pagination.next_url');
except
next_id:='N/A';
end;
for i := 0 to JSONArray.Size - 1 do
begin
with mytable do
begin
cells[0,rowcount]:=inttostr(rowcount);
cells[1,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('link')).JsonValue.Value);
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('tags')).ToString);
s:= StringReplace(s, '"tags":[', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, ']', '', [rfReplaceAll,rfIgnoreCase]);
cells[2,rowcount]:=escape(s);
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('likes')).ToString);
s:= StringReplace(s, '"likes":{"count":', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, '}', '', [rfReplaceAll,rfIgnoreCase]);
cells[3,rowcount]:=s;
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('comments')).ToString);
s:= StringReplace(s, '"comments":{"count":', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, '}', '', [rfReplaceAll,rfIgnoreCase]);
cells[4,rowcount]:=s;
cells[5,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('created_time')).JsonValue.Value);
cells[5,rowcount]:=datetimetostr(UnixToDateTime(strtoint(cells[5,rowcount])));
cells[6,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('id')).JsonValue.Value);
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('user')).ToString);
s:= StringReplace(s, '"user":{"username":', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, '}', '', [rfReplaceAll,rfIgnoreCase]);
s:= StringReplace(s, '"', '', [rfReplaceAll,rfIgnoreCase]);
cells[7,rowcount]:=s;
s:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('location')).ToString);
s:= StringReplace(s, '"location":', '', [rfReplaceAll,rfIgnoreCase]);
cells[8,rowcount]:=s;
cells[9,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get('filter')).JsonValue.Value);
cells[10,rowcount]:=datetimetostr(now);
rowcount:=rowcount+1;
end;
grdColWidth(mytable, 40);
end;
except
end;
// -> tiefenpruefung, ende
zaehl:=zaehl+1;
until zaehl=rounds;
//uebertrag auf tabelle
grdColWidth(mytable, 40);
form1.Caption:=version+' alle Posts herunter geladen';
end;

(2) Aufruf der Scrapingprozedur
procedure TForm1.Button45Click(Sender: TObject);
var lauf: integer;
begin
with locmedia do
begin
cells[0,0]:='Nr.';
cells[1,0]:='URL';
cells[2,0]:='Tag';
cells[3,0]:='Likes';
cells[4,0]:='Comments';
cells[5,0]:='Erstellzeit';
cells[6,0]:='ID';
cells[7,0]:='User';
cells[8,0]:='Location';
cells[9,0]:='Filter';
colcount:=10;
rowcount:=1;
end;
for lauf := 1 to locroh.RowCount do
begin
randomize;
token.Text:=token.Items[random(token.Items.Count-1)];
get_loc_short(locmedia,locroh.Cells[1,lauf],20);
savetocsv(locmedia,verz+'\support\dummydata.csv');
form1.Caption:=version+' Locationscraper: '+inttostr(lauf)+' / '+inttostr(locroh.RowCount);
delay(1000);
end;
end;

Socialmedia-Profil-Analyse (Rohdaten), v.0.1B [Instagram]

Die angekündigte Software ist nun in der ersten Versionsnummer fertig gestellt und sie produziert die folgenden Daten:

[Accountüberblick]
Komplettdatensatz aus dem Account „hanneswobushanneswobus_25022015 (Excel, xlsx)

Die Exceltabelle zeigt: (a) URL der Posts, (b) Erstelldatum, (c) Comments, (d) Likes, (e) Tags, (f) Beschreibung – leer, (g) Post-ID

[Todo]

  1. Auswertung von Comments je individuellen Tag
  2. Präsentation: Top10 der wirkungsvollsten Tags (gem. an Likes und Comments)
  3. Präsentation: Top10 der „besten“ Posts (gem. an Likes und Comments)
  4. Präsentation: schlechteste Posts und Tags (ggf. auch Taggruppen)
  5. Auswertung von Likes je individuellen Tag
  6. Diagram: zeitliche Entwicklung f. Posts, Likes und Comments (siehe: „Socialtracker“)

[Tortendiagram]
hanneswobus__diag

Die Beobachtung der Abonnenten und abonnierten Accounts wird im aktuellen Konzeptstadium gesondert betrachtet. Nach einigen internen Diskussionen und Gesprächen mit Externen / Kund_innen wird die nächste Versionsnummer um einen entsprechenden Komplex erweitert.

Die Todo hierfür ist:

  1. Erfassen der Abonnenten und Erfassen der abonnierten Accounts als „Liste“.
  2. Ausgabe: „Wer folgt dem beobachteten Account und welchen Accounts folgt der beobachtete Account?“
  3. Aktivitätsbeobachtung: „Wer ist in welcher Form auf den beobachteten Accounts aktiv (gez. durch Likes, Comments)?“

Die aktuellen Engagements und Projekte werden selbstverständlich um die Dienstleistung „Instagram-Account-Reports“ und die entsprechende Beratung erweitert.

Ich bekomme derzeit erstaunlich viele Rückmeldungen mit ein und denselben Inhalten: das Erfassen der Daten über Instagram, Pinterest und anderen Socialmedia ist (a) nicht möglich oder (b) extrem aufwändig / teuer. Nunja: hier möchte ich auf den Inhalt „Gesprächspartner_innen für das Internetmarketing“ hinweisen. Das Erfassen und Verstehen der extrem gut dokumentierten Socialmedia-API gehört zum Berufsbild „Internetmarketing“ oder „Socialmedia-Marketing“ dazu und das Entwickeln eigener Lösungsansätze für das Beobachten und die Datenerfassung ist gerade mit Hilfe der APIs mit einem enorm geringen Zeitaufwand durchaus möglich.

Informationen zu den APIs inklusive (!) Codebeispiele findet man hier:
[Instagram]
Instagram (Developer)
Endpoints der API
Infos zu der Limitierung
Tagsuche
Grobrecherchen, Rohdatenerfassung (hierauf basiert die aktuelle Version der Software!!!)
Zielgruppenanalysen auf Basis der Locations

[Pinterest]
Developer – Doku
User, Usermanagement, Follower & Co.
Dokus zu den Boards, Boardmanagement
Pins, Management und Recherche
Zielgruppenanalysen via Pinterest-API
Basisfunktionen

Rohdaten: Accountanalyse (Instagram)

Die Datenerhebung für die angekündigte Accountanalyse-Anwendung ist nun soweit fertig und in der aktuellen Version gehe ich wie folgt vor:

[Erfassen der Account-ID]
– via API
– via „lookup your instagram user id

[Erfassen der hinterlegten Daten]
(1) Ansprache der API
$api = „https://api.instagram.com/v1/users/[ID]/media/recent/?access_token=[TOKEN]“;
$response = get_curl($api);

(2) Auswertung und Auslesen der Datensätze
if($response){
foreach(json_decode($response)->data as $item){
$url = $item->link;
$zeitstempel = $item->created_time;
$comment = $item->comments->count;
$gefaellt = $item->likes->count;
$id_code = $item->id;
$schlagworte = $item->tags;
$comma_separated = implode(“ „, $schlagworte);
echo $url.“|“.gmdate(„Y-m-d“, $zeitstempel).“|“.$comment.“|“.$gefaellt.“|“.$id_code.“|“.$comma_separated;
}

Wir hatten im Zuge der Entwicklungsarbeiten auch intensiv über eine Backupfunktion oder eine Massdownloadfunktion bzgl. der geposteten Fotos diskutiert und letztendlich entsprechende Features (vorerst) verworfen, da die bekannte Instagram-App bearbeitete Fotos auf den Geräten speichert.
Die Angabe „zeitstempel“ bleibt vorerst auf die Ausgabe von „Datum“ beschränkt und ich behalte mir eine Erweiterung auf „Zeit“, „Uhrzeit“ zzgl. „Minuten“ vor.

Für die weitere Analyse verwende ich die Angabe „id_code“ und die folgenden Endpoints:
(a) Liste: Likes
https://api.instagram.com/v1/media/[ID]/likes?access_token=[TOKEN]
– gibt die likende Accounts zur hinterlegten Media-ID aus

(b) Liste: Kommentare
https://api.instagram.com/v1/media/[ID]/comments?access_token=[TOKEN]
– gibt die kommentierenden Accounts zzgl. der (!) Kommentare zur hinterlegten Media-ID aus

Die Erfassung der o.g. „Rohdaten“ funktioniert recht stabil und weitestgehend fehlerfrei. Ausgehend hiervon ergeben sich nun Antworten auf die folgenden Fragen:

  1. Welche Posts werden am häufigsten frequentiert (sichtbar durch Likes und Kommentare)?
  2. Welche Hashtags generieren das effektivste Like / Kommentar-Verhältnis?
  3. Welche Postingzeiten sind effektiv? (sichtbar durch die Ergebnisse von (1) und (2)
  4. Wer mag diese oder jene Fotos gepaart mit konkreten Botschaften (Tags) (bezogen auf den eigenen oder fremde Accounts)?
  5. Woher kommen die Fans (eigener Account, fremder Account)?
  6. Wie aktiv und treu sind die Fans?

Die Fertigstellung einer ersten Version ist für 21.02.2016 geplant.