(Web)browserautomatisierungen im B2B-Social

Ein aktuelles Experiment mit einem bekannten B2B-lastigen Socialmedium dreht sich um die Frage: „Wie-kann-ich-effektiv-fremde-Menschen-auf-mein-Geschäft-aufmerksam-machen?“ und der Ansatz lässt sich via Delphi über die folgenden Codezeilen realisieren:

WebBrowser:= TWebBrowser.Create(Form1);
TWinControl(WebBrowser).Name:= 'WebBrowser';
TWinControl(WebBrowser).Parent:= Form1;
WebBrowser.Silent := true;
WebBrowser.Visible:= true;
webbrowser.Navigate('blabla');
webbrowser.Destroy;

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

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.

Delphi: TWebbrowser – Formulare ausfüllen und „abschicken“

Für die Automatisierung mit Hilfe des TWebbrowser benötigt man den folgenden Code.
uses mshtml;

function FillForm(WebBrowser: TWebBrowser; FieldName: string; Value: string): Boolean;
var
i, j: Integer;
FormItem: Variant;
begin
Result := False;
//no form on document
if WebBrowser.OleObject.Document.all.tags('FORM').Length = 0 then
begin
Exit;
end;
//count forms on document
for I := 0 to WebBrowser.OleObject.Document.forms.Length - 1 do
begin
FormItem := WebBrowser.OleObject.Document.forms.Item(I);
for j := 0 to FormItem.Length - 1 do
begin
try
//when the fieldname is found, try to fill out
if FormItem.Item(j).Name = FieldName then
begin
FormItem.Item(j).Value := Value;
Result := True;
end;
except
Exit;
end;
end;
end;
end;


if FillForm(WebBrowser1, 'username', 'mein_username') = False then
Form1.caption:=version;

=> Ausfüllen des Formularfeldes „username“ mit dem Inhalt „Username“.
=> Ausweitbar auf weitere Formularfelder wie „passwort“, „webseite“ usw., sofern Objektnamen bekannt.
Webbrowser1.OleObject.document.forms.item(0).submit();
=> Ausgefüllte Inhalte werden abgeschickt

Delphi: Twebbrowser – Elemente (ohne ID) automatisch anklicken lassen

Bei einem unserer Projekte musste ich eine Lösung suchen, welche das Anklicken von Webseitenelementen erlaubt.
Vorausgesetzt, es handelt sich hierbei um einen „span“-tag, welcher einen eindeutigen (!) Namen trägt, gehe ich so vor:

var
ovElements: OleVariant;
i: Integer;


try
ovElements := WebBrowser1.OleObject.Document.all;
for i := 0 to (ovElements.Length - 1) do
if (ovElements.item(i).className = 'name') then
begin
try
ovElements.item(i).Click;
except
//fehlerbehandlung I
end;
end;
except
//fehlerbehandlung II => TEST
end;

Delphi: TWebbrowser – Version wechseln // festlegen

Im Normalfall muss man sich (leider) bei Delphi mit einer veralteten IExplorer-Version herum ärgern, wenn man Seiten wie bspw. Instagram aufrufen und „bearbeiten“ möchte.
Dieses Problem kann man nun so lösen:

uses registry

type
TIEMode = (iemIE7, iemIE8, iemIE10);

=> Vardeklaration
procedure SetWebbrowserMode(Mode: TIEMode; AppName: string = '');
const
REG_KEY = 'Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION';
var
Reg: TRegistry;
Value: Integer;
begin
if AppName = '' then
AppName := ExtractFileName(Application.ExeName);
Case Mode of
iemIE7 : Value := $1B58;
iemIE8 : Value := $1F40;
iemIE10 : value := $2710;

end;
Reg := TRegistry.Create();
try
Reg.RootKey := HKEY_CURRENT_USER;
if Reg.OpenKey(REG_KEY, True) then
begin
Reg.WriteInteger(AppName, Value);
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;

=> Funktion für Browserversionswechsel
SetWebbrowserMode(iemIE10);
=> Browserversion auf IE10 setzen (Funktionsaufruf)