Instagram: Download der Follower- und Abodatensätze (JSON)

Durch die API-Änderungen von Instagram wurde – wie bekannt – das Herunterladen dieser Datensätze etwas komplizierter. Die folgende Prozedure kann hier Abhilfe schaffen.

procedure TForm1.get_accounts(userid: string; uname: string; abfragetyp: string; maxerg: string; ziel: tmemo);
var
lHTTP: TIdHTTP;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
Params, login : TStrings;
Reply, _Token, X, lf: string;
Cookie: TIdCookie;
begin
// login - anfang
try
Params := TStringList.Create;
Params.Add('username='+igaccounts.Text);
Params.Add('password='+token_w.text);
lHTTP := TIdHTTP.Create(nil);
try
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP);
IdSSL.SSLOptions.Method := sslvTLSv1;
IdSSL.SSLOptions.Mode := sslmClient;
lHTTP.IOHandler := IdSSL;
lHTTP.ReadTimeout := 30000;
lHTTP.HandleRedirects := True;
lHTTP.Request.UserAgent := 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36';
lHTTP.Get('https://www.instagram.com', TStream(nil));
Cookie := lHTTP.CookieManager.CookieCollection.Cookie['csrftoken', 'www.instagram.com'];
if Cookie <> nil then
_Token := Cookie.Value;
try
lHTTP.Request.CustomHeaders.Values['X-CSRFToken'] := _Token;
lHTTP.Request.CustomHeaders.Values['X-Instagram-AJAX'] := '1';
lHTTP.Request.CustomHeaders.Values['X-Requested-With'] := 'XMLHttpRequest';
lHTTP.Request.Referer := 'https://www.instagram.com/';
lHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
lHTTP.Request.UserAgent := 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36';
Reply := lHTTP.Post('https://www.instagram.com/accounts/login/ajax/', Params);
finally
end;
finally
end;
Finally
// login => ende
// fans usw. erfassen
lHTTP.Request.UserAgent := 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36';
lHTTP.Request.Connection := 'keep-alive';
cookie := lHTTP.CookieManager.CookieCollection.Cookie['csrftoken', 'www.instagram.com'];
if cookie <> nil then
_token := cookie.Value
else
_token := '';
Params.Clear;
Params.Add('q=ig_user(' + userid + ') {'+LF+
' '+abfragetyp+'('+maxerg+') {'+LF+
' count,'+LF+
' page_info {'+LF+
' end_cursor,'+LF+
' has_next_page'+LF+
' },'+LF+
' nodes {'+LF+
' id,'+LF+
' is_verified,'+LF+
' followed_by_viewer,'+LF+
' requested_by_viewer,'+LF+
' full_name,'+LF+
' profile_pic_url,'+LF+
' username'+LF+
' }'+LF+
' }'+LF+
'}'+LF);
Params.Add('ref=relationships::follow_list');
lHTTP.Request.CustomHeaders.Values['X-CSRFToken'] := _token;
lHTTP.Request.CustomHeaders.Values['X-Instagram-AJAX'] := '1';
lHTTP.Request.CustomHeaders.Values['X-Requested-With'] := 'XMLHttpRequest';
lHTTP.Request.Referer := 'https://www.instagram.com/'+uname+'/';
lHTTP.Request.ContentType := 'application/x-www-form-urlencoded';
lHTTP.Request.UserAgent := 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.76 Mobile Safari/537.36';
Reply := lHTTP.Post('https://www.instagram.com/query/', Params);
ziel.Text:=reply;
end;
end;

Verwenden der Prozedur
Die Prozedur verlangt die Übergabe der Account-ID, des Account-Names, der maximalen Datensätze und die „Memo“, welche das Ergebnis der Abfrage für die weitere Verarbeitung erfassen soll.

Sie wird wiefolgt aufgerufen:
1) Fans holen
get_accounts(account_id,account_name, ‚followed_by.first‘,’9999′, ziel_memo);

2) Abos holen

get_accounts(account_id,account_name, ‚follows.first‘,’9999′, ziel_memo);

Die Zahl „9999“ lässt sich beliebig austauschen und sie beschreibt hier den Maximalwert, unter dem die Prozedur fehlerfrei funktioniert. Bei höheren Zahlen muss der Automatismus um eine geeignete Schleife erweitert werden.

Welche konkreten Funktionen lassen sich nun aus den Ergebnissen dieser Prozedur ableiten?
– Datenabgleich der Account-IDs mit den Likenden
– Datenabgleich der Account-IDs mit den Kommentierenden
– Erfassen und Auswerten des Fanengagements
– Erfassen von Likenden / Kommentierenden, welche weder Fans noch Abos sind
– Aktivitätsanalysen konkreter Zielgruppen
– Aufdecken und Analysen von Fakes
– Aktivitätsanalysen der Accounts aus den IDs via Postanalysen

Inspirationsquelle für den Code
How to get Instagram following list using http component in Delphi 10
Find my Instagram-ID

Instagramrecherchearbeiten: ohne API und Token

Folgende Varianten wurden von mir getestet und als „produktiv“ eingestuft:

1) Grobüberlick über einen Account
https://www.instagram.com/[Accountname]/?__a=1
Diese Abfragemöglichkeit zeigt – im Wesentlichen – genau dieselben Inhalte (Accountgrunddaten, Medien plus Likes // Comments, usw) an, als wenn man eine beliebige Account-URL mit Hilfe des Browsers aufruft. Das Ergebnis wird hier im JSON-Format präsentiert und lässt sich über die (hoffentlich) bekannten Scrape- und Auswertungsprozeduren weiter verarbeiten.

2) Suche nach Medien, Accounts und Locations
https://www.instagram.com/web/search/topsearch/?query=test
Diese Abfragemöglichkeit fasst die Tagsuche, Accountsuche und Locationsuche zusammen und präsentiert die Ergebnisse in JSON-Format. Inhalte sind die Mediadaten der Tags, die Grunddaten der Accounts (Name, Nickname, Fans und Abos, Posts usw.) und die Grunddaten der Locations

3) Grunddaten eines Accounts
https://www.instagram.com/query/?q=ig_user(ID){id,username,external_url,full_name,profile_pic_url,biography,followed_by{count},follows{count},media{count},is_private,is_verified}
Diese Abfragemöglichkeit zeigt die wesentlichen Grunddaten eines Accounts an. Diese sind hier: ID, Username, externe URL, Name, Profil-Bild-URL, Abos und Fans, Posts und Bio. Das Ausgabeformat ist JSON.

4) Accountmedien
https://www.instagram.com/[accountname]/media/?max_id=20
Diese Abfragemöglichkeit zeigt die letzten 20 Posts eines beliebigen Accounts inklusive der Rohdaten (Likes, Comments, Zeitstempel usw.) der jeweiligen Medien. Ein komplettes Scrapen großer Feeds erscheint hier als unmöglich.

Instagram: Bilder in der großen Auflösung finden und zum Download aufbereiten

Unser Instagram-„Bot“ verfügt nun in der Versionsnummer „0.7b“ über eine Backupfunktion. Diese umfasst im Wesentlichen das Sichern der Bilder aus den IG-Beiträgen des eigenen Accounts.
Diese Prozedure wurde wiefolgt realisiert:

1. Scrapen, Abholen des eigenen Streams
2. Speichern der Posturl-Liste in eine „Memo“
3. Schleife: (for-to-do) Ansurfen der Post-Urls
3.1. Quellcodeanalyse und suchen nach:
og:image“ content=“bildurl?ig_cache_key=key“
3.2. Entferne cache_key
3.3. Download der Bilder

Weiter lesen, Quellcodebeispiele
Datei aus dem WWW downloaden (Delphi)
Quelltext aus TWebbrowser auslesen
Rohdatenanalyse, IG-Account

Instagram, Tagreichweiten und Tags via Json holen und interpretieren.

Möchte man sich die aufwändige Entwicklung eines eigenen Parsers sparen, lässt sich der Export der Daten via API problemlos via JSON realisieren. Hier geht man wiefolgt vor:

[Einbindung der Unit]
uses json;

[Prozedur]
procedure tagdaten_holen;
var lauf: integer;
JSONArray: tJSONArray;
JSONValue: tJSONValue;
JSONPair: TJSONPair;
JSON: TJSONObject;
i: integer;
begin
try
debug.Text:=idhttp1.Get(‚https://api.instagram.com/v1/tags/search?q=’+tagrecherche.text+’&access_token=’+token.text);
JSONValue := TJSONObject.ParseJSONValue(debug.text);
JSON := TJSONObject.ParseJSONValue(debug.Lines.Text) as TJSONObject;
JSONArray := TJSONArray(JSON.Get(‚data‘).JsonValue);
with tagr_roh do
begin
cells[0,0]:=’Nr.‘;
cells[1,0]:=’Tag‘;
cells[2,0]:=’Reichweite‘;
colcount:=3;
rowcount:=1;
end;
for i := 0 to JSONArray.Size – 1 do
begin
with tagr_roh do
begin
cells[0,rowcount]:=inttostr(rowcount);
cells[1,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get(’name‘)).JsonValue.Value);
cells[2,rowcount]:=(TJSONPair(TJSONObject(JSONArray.Get(i)).Get(‚media_count‘)).JsonValue.Value);
rowcount:=rowcount+1;
end;
end;
except
showmessage(‚Fehler mit der Internetverbindung. Prüfe Diese und den Token!‘);
end;
end;

TWebbrowser: ID-lose Elemente anklicken

Bei einem aktuellen Projekt aus dem Bereich „Webautomatisierung“ musste das folgende Problem gelöst werden:

[Ausgangslage]
– Klicken von Tags
– anzuklickende Tags besitzen keine individuelle ID
– es existiert eine Tagliste, aus der der anzuklickende Tag ausgewählt werden muss
– anzuklickende Tags besitzen individuelle „innertext

[Vorgehensweise]

(1) Variablendeklaration
uses mshtml;
var ovElements, ovelements_1: OleVariant;

(2) Suchen und Klicken
ovElements_1 := WebBrowser1.OleObject.Document.all;
for j := 0 to (ovElements.Length - 1) do
if (ovElements_1.item(j).className = 'name') then
begin
if ovelements_1.item(j).innertext="wort" then
begin
ovElements_1.item(j).Click;
end;
end;

Weitere Infos:
TWebbrowser – Oledata

TWebbrowser: Einbindung und Arbeitsspeicherprobleme

Bei der Arbeit an den Webautomatismen entdeckte ich, dass der TWebbrowser enorm viel Arbeitsspeicher verbraucht. Das Problem lässt sich wie folgt lösen:

(1) Webbrowser – Prozess durchführen und auf Beendigung warten
(2) SetProcessWorkingSetSize(GetCurrentProcess, $FFFFFFFF, $FFFFFFFF); in den Code einfügen

Bei einem konkreten Projekt wurde hierüber der durchschnittliche Verbrauch von ca. 650MB auf 60-80MB (schwankend) gedrückt.

[Updatenotiz] Marktrecherche 0.8b

Die neue Version der Software wurde soeben fertig gestellt und verteilt.
Die folgende Galerie zeigt einige Ausschnitte aus der Pinterestrecherchefunktion. Hierbei habe ich das System der „Interessensmatrizen“ wieder aufgenommen.
Das Programm „verlangt“ die Eingabe eines Projektes inklusive der zu analysierenden Themenwelten. Hier wurden die Daten aus dem Projektbeispiel „Wohnen“ (=>wohnzimmer, schlafzimmer, kinderzimmer, badezimmer, bad, flur, haus, wohnung) analysiert.

(A) Screenshots

(B) Beispieldatensätze
Die nachfolgenden Excel-Tabellen sind Exports aus der Anwendung.
Sie zeigen:
(1) Rohdaten – Pinterest // Suchvorschläge
pint_suggest.xlsx
Die Datenerhebung erfolgt über die Funktionen, welche ich im Artikel „Pinterest – Suchvorschläge auslesen“ beschrieb.
(2) Rohdaten – Pinterest // Themenwelten
pint_rohdaten.xlsx
Die Datenerhebung erfolgt über die Funktionen, welche ich im Artikel „Pinterest – Themenwelten auslesen“ beschrieb.
(3) Pinterest // Themenwelten gewichten, Zusammenzählung
Um die erhobenen Daten in ein interpretationsfähiges Format zu bringen, werden in der Anwendung die gefundenen „Unterthemen“ oder „weiterführende Themen“ zusammen gezählt und nochmal in Zuordnung zu den „Themenwelten“ dargestellt. Die Datei pint_zfg.xlsx zeigt das entsprechende Beispiel.

[Updatenotiz] Schliessung vom Suchvorschlagstool und Übertrag in „Marktrecherche“

Nach einigen Zahlenstudien, Gesprächen und Experimenten kam ich zum Entschluss, das besprochene Projekt „globaler“ und weitestgehend unabhängig von den üblichen und fast schon klischeehaften Keywordrechercheprozeduren weiter zu entwickeln. Im nächsten Schritt werden hier alle Analysealgorithmen zusammen gefasst, welche in den letzten Jahren gestaltet worden sind.
Die Versionsnummer 0.5b wird folgendes umfassen:

(A) Suchmaschinen
[Datenquelle „Google“]
– Suchvorschläge
– generierte Fragen analog zum „W-Fragen-Tool“
– generierte Phrasen auf Basis von Städten für die Vermarktung von lokal abhängigen „Dingen“
– GoogleTrends

[Datenquelle „Bing“]
– Suchvorschläge

(B) Socialmedia
[Datenquelle „Instagram“]
– Tagrecherche zzgl. Reichweitenauswertung(en)

[Datenquelle „Pinterest“]
– Suchvorschläge
– Themenweltenanalyse(n)

(C) Recherchefunktionen
– Abgleich: Suma <> Socialmedia
– Abgleich: G-Trends <> Socialmedia

Weitere potentielle Datenquellen, welche sich aktuell in einer Art „Werthaltigkeitsanalyse“ befinden, sind:
– GooglePlaces inkl. Öffnungszeiten und ggf. vorhandenen Bewertungen
– GoogleShopping inkl. Preisdifferenzen und ggf. vorhandenen Bewertungen
– Twitter: Hashtagsuche und Trends
– Gplus: Hashtagauswertung
– Facebookpages, sofern „offen“ und auslesbar
– Youtube: Suchvorschläge (?), Taganalysen (!)
– Keywordplaner, sofern kostenlos abrufbar (muss ich noch prüfen)

Interessante Blogcommentspambeispiele

Neulich entdeckte ich weitere „interessante“ Beispiele aus der Ecke „Blogcommentspam“ auf dem Fotoblog.

(a) Spammer für eine Berufsinfoseite zzgl. Verkauf von eBooks
„Ich war kürzlich auch noch im Johannapark. Sehr schön dort und ja – das Objekt soll einen Barockpark darstellen, habe ich auch gehört.“

Dieser Spam zielte auf einen Beitrag mit Thema „Johannapark“ ab und das Impressum der hinterlegten Url sagte mir, dass der Spammer wohl mit einer enorm geringen Wahrscheinlichkeit den Park besucht hatte. Das banale Zitieren von Hinweisen bzgl. des fotografierten Kunstwerks zauberter mir ein Lächeln auf die Lippen. Der Linktext war – selbstverständlich – ein Name, welcher nicht deckungsgleich mit dem Namen aus dem Impressum der hinterlegten Url ist.

(b) Amazonaffiliate-Spammer (Wohnen, Einrichten, Deko)
„Ich denke gerade auch auf Messen findet man tolle Ideen für Deko. Ich habe eine tolle Seite im Netz gefunden, welche von Weinkisten und deren Verwendung handelt. Tolle Dekoideen sind dort zu sehen. Ich finde es einfach schön, wenn sich leute die Zeit dafür nehmen.
So eine Messe möchte ich allerdings auch einmal besuchen! Wie gesagt auf [domainname] findet Ihr tolle Dekoideen“

Dieser Spam zielte auf eine Galerie ab, welche ich nach einem Messebesuch (Themen: Einrichten, Wohnen, Wohnwelten etc.) gestaltete. Interessant bei dieser „Werbung“ ist natürlich, dass der Linktext genau deckungsgleich zur hinterlegten Domain war. Amüsant fand ich hier auch die halbgar ausformulierte „Empfehlung“ und ich dachte schon darüber nach, den Spammer direkt anzurufen und ihn darüber zu informieren, dass diese Pseudoempfehlung sich im hinterlegten Spamlink und noch im Linktext // Name befindet.

Die ausgewählten Spambeispiele sind nur exemplarisch und ich beobachte derzeit zwischen 10-12 „frische“ Spams / Tag auf unseren Fotoblog. Neu scheint hier zu sein, dass nicht mehr ausschliesslich die klischeehaften Amazonaffiliatespammer auftauchen, sondern dass auch Shops, relativ bekannte Geschäfte und auch Vertreter_innen aus Coaching & Beratung sichtbar werden.

Pinterest – weiterführende Themen recherchieren // auslesen

Sucht man bei dem Medium „Pinterest“ bspw. nach „Kids“ mit Hilfe der Url https://de.pinterest.com/search/pins/?q=kids, entdeckt man interessante Vorschläge unterhalb des Suchformulars wie „Basteln“, „Hochbett“ oder „Room Deko“.
Diese Informationen lassen sich wiefolgt auslesen:

[Schritt 1]
=> Download: https://de.pinterest.com/search/pins/?q=[suchwort]
[Schritt 2]
=> Identifikation von [suchwort] im Quellcode
[Schritt 3]
=> Übergabe in die Rohdaten und spätere Interpretationen (Matrix, Zusammenzählung, Abgleich mit anderen Themenwelten usw.)