Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 | Suche | FAQ

Hauptmenü
myCSharp.de
» Startseite
» Forum
» Suche
» Regeln
» Wie poste ich richtig?

Mitglieder
» Liste / Suche
» Wer ist online?

Ressourcen
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Microsoft Docs

Team
» Kontakt
» Cookies
» Spenden
» Datenschutz
» Impressum

  • »
  • Community
  • |
  • Diskussionsforum
Screenshot einer Webseite per WebBrowser-Control
oliverp19
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

Screenshot einer Webseite per WebBrowser-Control

beantworten | zitieren | melden

Moin,

ich spiele gerade ein wenig mit C# rum und möchte von einer Webseite einen Screenshot erstellen. Das klappt prinzipiell auch. Bei einigen Seiten (nicht allen) öffnen sich aber unendlich viele IE-Fenster und ich habe keine Ahnung wieso.

Ich bin noch rechter Anfänger, was C# betrifft...das sollte noch erwähnt werden :)

Hier mal das Code-Snippet (im Netz gefunden):


WebBrowser wb = new WebBrowser();
wb.ScrollBarsEnabled = false;
wb.ScriptErrorsSuppressed = true;

wb.Navigate(url);

while (wb.ReadyState != WebBrowserReadyState.Complete) { 
  Application.DoEvents();
}

wb.Width = 1024;
wb.Height = 768;

Bitmap bitmap = new Bitmap(wb.Width, wb.Height);
wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
wb.Dispose();


Das Ganze funktioniert, wie schon gesagt, bei vielen URLs ohne Probleme. Eine URL, bei welcher der Fehler auftritt ist beispielsweise "http://www.mal-was-liebes.de". Wenn ich Application.DoEvents(); auskommentiere, öffnen sich zwar keine 1000 IE-Fenster, aber die Prozessorlast geht hoch und das Programm stürzt ab.

Wie könnte ich sonst noch vernünftig abfragen, ob der Browser fertig ist mit dem Laden des Dokumentes.

Vielen Dank & Grüße
Oli
Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von oliverp19 am .
private Nachricht | Beiträge des Benutzers
Adi
myCSharp.de - Member



Dabei seit:
Beiträge: 10
Herkunft: Innerschweiz

beantworten | zitieren | melden

Der WebBrowser hat ein event namens DocumentCompleted. Dieses kannst du abonnieren und im Eventhandler die Verarbeitung ausführen.
private Nachricht | Beiträge des Benutzers
oliverp19
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

beantworten | zitieren | melden

Vielen Dank! Das hat tatsächlich geholfen...so konnte ich diese komische while-Schleife eliminieren. Hier der Code für die Nachwelt:


            WebBrowser wb = new WebBrowser();
            wb.ScrollBarsEnabled = false;
            wb.ScriptErrorsSuppressed = true;
 
            wb.Navigate(url);

            wb.DocumentCompleted += delegate(object o, WebBrowserDocumentCompletedEventArgs e)
            {
                if (e.Url == wb.Url)
                {
                    wb.Width = 1024;
                    wb.Height = 768;

                    Bitmap bitmap = new Bitmap(wb.Width, wb.Height);
                    wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
                    wb.Dispose();
                }

            };

Grüße
Oli
private Nachricht | Beiträge des Benutzers
oliverp19
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

beantworten | zitieren | melden

moin,

ich muss mich doch nochmal melden :)

Die Anwendung läuft zwar nun prinzipiell so, wie sie soll...nur ist sie sehr instabil. heißt: sie stürzt regelmäßig ab. nun wüsste ich gern, wie ich das ganze etwas stabiler hinbekommen könnte...ich hatte folgende ansätze:

- auslagern der screenshot-methode in threads (threadpool)
- festlegen eines timeouts für die screenshot-methode

letzteres erscheint mir zunächst einfacher zu implementieren, allerdings konnte ich da im netz nicht wirklich was sinnvolles finden. hat da evtl. jemand nen tipp für mich, wie ich einen timout in meine methode (siehe oben) integrieren könnte?

wäre die thread-methode sinnvoll? also mehrere threads parallel die screenshots machen zu lassen? ich habe gelesen das webbrowser-objekt ist relativ resourcenlastig.

Dank & Grüße
Oli
private Nachricht | Beiträge des Benutzers
pdelvo
myCSharp.de - Member

Avatar #avatar-3354.png


Dabei seit:
Beiträge: 1407

beantworten | zitieren | melden

Das wird nicht gehen. Das Webbrowser-Steuerelement kann nur im Form-Thread ausgeführt werden.(Hat mich selber geärgert)

Gruß pdelvo
private Nachricht | Beiträge des Benutzers
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2885
Herkunft: München

beantworten | zitieren | melden

Zitat
heißt: sie stürzt regelmäßig ab.
Hmm.. und mit was für einer Exception?
Zitat
- festlegen eines timeouts für die screenshot-methode
Dafür bräuchtest du ja trotzdem Threads da wenn ein Thread hängt es keine Möglichkeit mehr gibt in selbigem einen Timeout festzustellen um ihn dann zu beenden.
private Nachricht | Beiträge des Benutzers
oliverp19
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

beantworten | zitieren | melden

Hallo,
Zitat
Hmm.. und mit was für einer Exception?

keine Exceptions...das Programm reagiert einfach nicht mehr (keine Rückmeldung).
Zitat
Dafür bräuchtest du ja trotzdem Threads da wenn ein Thread hängt es keine Möglichkeit mehr gibt in selbigem einen Timeout festzustellen um ihn dann zu beenden.

das scheint mir irgendwie das dilemma zu sein...

Ich möchte einfach Screenshots von einer größeren Anzahl Webseiten machen. Der Input kommt aus einer XML-Datei. Prinzipiell funktioniert eigentlich alles...bis auf 2 Hauptprobleme:

- wenn eine Webseite nicht geöffnet werden kann, gibt es kein Timout...die Anwendung friert ein

- bei einer großen Anzahl von Webseiten wird die oben gezeigte Methode direkt nacheinander für alle Webseiten aufgerufen und nicht sequentiell abgearbeitet, wie es eigentlich gedacht war...dann wird für 100 Seiten auf DocumentLoaded-Event gewartet, was leicht dazu führt, dass die Applikation einfriert.

An dieser Stelle komme ich einfach nicht weiter...hat da jemand Hinweise, wie ich sowas angehen könnte?

Grüße
Oli
private Nachricht | Beiträge des Benutzers
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2885
Herkunft: München

beantworten | zitieren | melden

Sicher nicht unbedingt ideal aber wahrscheinlich eine (auch nicht gerade Resourcensparende) Abhilfe wäre einfach mehrere Prozesse deiner Anwendung zu starten und einen Management-Prozess darüber zu legen.

Der Managementprozess bekommt das Xml und startet darauf hin das screenshot-Programm n-mal. Das Programm das die Screenshots macht beinhaltet einen Watchdog-Mechanismus der sich regelmäßig beim Manager melden muss um nicht abgeschossen zu werden (Das kann man z.B. über Remoting mit Named-Pipes oder TCP machen).
Wenn du das ganze mit TCP implementierst dann hast du zusätzlich die Möglichkeit die Arbeit auf mehrere Rechner zu verteilen.
private Nachricht | Beiträge des Benutzers
Lion1984
myCSharp.de - Member



Dabei seit:
Beiträge: 782
Herkunft: Österreich

beantworten | zitieren | melden

Hallo,

du sagst, du hast den Input aus einer XML Datei. Speichere den Input doch in einer Liste ab, z.B.: List<string> linkToDo. Dann öffnest du den string an der ersten Position, machst über DocumentCompleted den ScreenShot und anschließend, wenn er damit fertig ist, löscht du den string an der ersten Postion.
Somit rutschen alle Links eins nach, du rufst wieder die erste Postion ab (wäre ja der nächste Link, da du den ersten gelöscht hast) und machst erneut deinen Screenshot und das ganze beginnt von vorn.

Und so würde er nacheinander die Links abklappern bis die Liste keine Links mehr beinhaltet.

Zum anderen mach dir einen Timer für ein Timeout, denn du ebenfalls bei jedem DocumentCompleted neustartest.

Hoffe du verstehst was ich meine.
Lion
lg Lion
private Nachricht | Beiträge des Benutzers
jaensen
myCSharp.de - Experte

Avatar #avatar-2657.png


Dabei seit:
Beiträge: 2885
Herkunft: München

beantworten | zitieren | melden

Zitat
anschließend, wenn er damit fertig ist, löscht du den string an der ersten Postion.
Dem wäre wohl eine Queue vorzuziehen.
private Nachricht | Beiträge des Benutzers
Lion1984
myCSharp.de - Member



Dabei seit:
Beiträge: 782
Herkunft: Österreich

beantworten | zitieren | melden

Danke, hatte selbst mal mit Queue gearbeitet aber vergessen, dass es sie gibt *schäm*. Aber das Prinzip stimmt wohl :)
lg Lion
private Nachricht | Beiträge des Benutzers
oliverp19
myCSharp.de - Member



Dabei seit:
Beiträge: 5

Themenstarter:

beantworten | zitieren | melden

...danke Leute für eure Idee. Die Lösung mit der Queue klingt vernünftig. Sobald ich Zeit habe, werde ich mich mal daran versuchen und mich ggf. hier melden.

Schönes Wochenende :)

Grüße
Oli
private Nachricht | Beiträge des Benutzers
snoopy90
myCSharp.de - Member

Avatar #avatar-2063.gif


Dabei seit:
Beiträge: 95
Herkunft: Schweiz

beantworten | zitieren | melden

Ich habe den oben beschriebenen Code versucht zu verwenden. Ich hab mir dazu eine eigene Funktion programmiert, welche mir ein Bitmap zurückliefert. Bei mir funktioniert es mit folgendem Code jedoch nicht. Es wird immer null als Wert für das Bitmap zurückgeliefert.


public static Bitmap getScreenshotFromUrl(string url)
        {
            WebBrowser wb = new WebBrowser();
            wb.ScrollBarsEnabled = false;
            wb.ScriptErrorsSuppressed = true;
            wb.Navigate(url);

            Bitmap bitmap = null;
            wb.DocumentCompleted += delegate(object o, WebBrowserDocumentCompletedEventArgs e)
            {
                if (e.Url == wb.Url)
                {
                    wb.Width = 1280;
                    wb.Height = 1024;

                    bitmap = new Bitmap(wb.Width, wb.Height);
                    //Bildschirmauflösung für Screenshot
                    wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
                    wb.Dispose();
                }
            };
            return bitmap;
        }

Ich hoffe ihr könnt mir helfen. Irgenwie scheint er gar nie in den Event wb.DocumentCompleted += delegate(object o, WebBrowserDocumentCompletedEventArgs e) reinzukommen...
www.mkellenberger.ch Was ich täglich (neu) entdecke...
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4183

beantworten | zitieren | melden

Du solltest besser wb.DocumentCompleted VOR dem Aufruf wb.Navigate setzen...
private Nachricht | Beiträge des Benutzers
snoopy90
myCSharp.de - Member

Avatar #avatar-2063.gif


Dabei seit:
Beiträge: 95
Herkunft: Schweiz

beantworten | zitieren | melden

Hmm... aber das geht doch nicht. Ich muss doch die Website überhaupt mal aufrufen bevor ich prüfen kann, ob sie geladen wurde.

Also so funktionierts genau so wenig. Oder hab ich da was falsch verstanden?


wb.DocumentCompleted += delegate(object o, WebBrowserDocumentCompletedEventArgs e)
            {
                wb.Navigate(url);
                if (e.Url == wb.Url)
                {
                    wb.Width = 1280;
                    wb.Height = 1024;

                    bitmap = new Bitmap(wb.Width, wb.Height);
                    //Bildschirmauflösung für Screenshot
                    wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
                    wb.Dispose();
                }
            };
www.mkellenberger.ch Was ich täglich (neu) entdecke...
private Nachricht | Beiträge des Benutzers
pdelvo
myCSharp.de - Member

Avatar #avatar-3354.png


Dabei seit:
Beiträge: 1407

beantworten | zitieren | melden

Bei deinem Code navigiert er immer wieder zu der Seite, wqenn sie fertig geladen wurde.

Gruß pdelvo
private Nachricht | Beiträge des Benutzers
snoopy90
myCSharp.de - Member

Avatar #avatar-2063.gif


Dabei seit:
Beiträge: 95
Herkunft: Schweiz

beantworten | zitieren | melden

Ja. Dachte ich mir. Jedoch geht mein 1. oben geposteter Code auch nicht. Fehlt da noch was?
www.mkellenberger.ch Was ich täglich (neu) entdecke...
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4183

beantworten | zitieren | melden

Sorry, aber ich glaube, du hast die Events noch nicht verstanden (und das obwohl du anonyme Methoden benutzt).
Das Abonnieren eines Events mußt du vor dem Aufruf "Navigate()" durchführen, denn sonst kann die Methode ja nicht wissen, was sie machen muß, wenn das Dokument fertig geladen ist.
Und wo die Event-Methode im Code definiert ist (ob als anonyme Methode oder als eigenständige Methode) ist völlig egal.

Also richtig ist:


wb.DocumentCompleted += delegate(object o, WebBrowserDocumentCompletedEventArgs e)
            {
                if (e.Url == wb.Url)
                {
                    wb.Width = 1280;
                    wb.Height = 1024;

                    bitmap = new Bitmap(wb.Width, wb.Height);
                    //Bildschirmauflösung für Screenshot
                    wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
                    wb.Dispose();
                }
            }

wb.Navigate(url);
Wenn du den Debugger benutzt, dann wirst du sehen, in welcher Reihenfolge er die Methoden durchläuft...

Edit: immer diese Fehlerteufelchen
Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von Th69 am .
private Nachricht | Beiträge des Benutzers
snoopy90
myCSharp.de - Member

Avatar #avatar-2063.gif


Dabei seit:
Beiträge: 95
Herkunft: Schweiz

beantworten | zitieren | melden

Das geht irgendwie auch nicht. Man der kommt nach dem Navigate nicht in den Event rein obwohl er das meiner Ansicht nach müsste.

Ich häng hier gleich mal die ganze Klasse an, dann kann man damit nach belieben rumexperimentieren. Ich stoss da leider mit meinen Programmierkenntnissen an unüberwindbare Hürden...


using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace WebScreenGrabber
{
    class Screenshot
    {
        /// <summary>
        /// Macht automatisch einen Screenshot einer Website und gibt diesen als Bitmap zurück.
        /// </summary>
        /// <param name="url">URL der Website</param>
        /// <returns>Screenshot</returns>
        public static Bitmap getScreenshotFromUrl(string url)
        {
            WebBrowser wb = new WebBrowser();
            wb.ScrollBarsEnabled = false;
            wb.ScriptErrorsSuppressed = true;

            Bitmap bitmap = null;
            wb.DocumentCompleted += delegate(object o, WebBrowserDocumentCompletedEventArgs e)
            {
                if (e.Url == wb.Url)
                {
                    wb.Width = 1280;
                    wb.Height = 1024;

                    bitmap = new Bitmap(wb.Width, wb.Height);
                    //Bildschirmauflösung für Screenshot
                    wb.DrawToBitmap(bitmap, new Rectangle(0, 0, wb.Width, wb.Height));
                    wb.Dispose();
                }
            };

            wb.Navigate(url);

            return bitmap;
        }
    }
}

www.mkellenberger.ch Was ich täglich (neu) entdecke...
private Nachricht | Beiträge des Benutzers
Th69
myCSharp.de - Experte

Avatar #avatar-2578.jpg


Dabei seit:
Beiträge: 4183

beantworten | zitieren | melden

Ich habe deine Methode mal mit


getScreenshotFromUrl("http://www.google.de");
aufgerufen und komme dann in das DocumentCompleted-Event.

Dein Hauptproblem ist aber, daß du einfach nach "Navigate" sofort das (noch leere) Bitmap-Objekt zurückgibst...

Die Methode Navigate arbeitet aber asynchron, d.h. sie kehrt sofort wieder zurück.
Der Event wird aber erst nach einiger Zeit ausgelöst, so daß das Bitmap-Objekt erst dann gesetzt wird.

Du mußt also noch auf die Abarbeitung warten (am besten in einem eigenen Thread).
Zum Test kannst du auch ersteinmal die Schleife (aus em Anfangspost) verwenden:


wb.Navigate(url);

while (wb.ReadyState != WebBrowserReadyState.Complete) {
  Application.DoEvents();
}

return bitmap;
s. a. unbedingt den FAQ-Beitrag
[FAQ] Warum blockiert mein GUI?
private Nachricht | Beiträge des Benutzers
snoopy90
myCSharp.de - Member

Avatar #avatar-2063.gif


Dabei seit:
Beiträge: 95
Herkunft: Schweiz

beantworten | zitieren | melden

Vielen Dank für deine Antwort.
Ich werds mal wieder mit der Schleife machen.
Aber bringt die was? Ich meine, anstelle dieser sollte ich ja den DocumentCompleted-Event verwenden. nicht?

Ich werds aber auf alle Fälle nochmal so probieren und Rückmeldung geben ob nun alles klappt.

Achja, das WebBrowser-Control lässt sich leider soviel ich weiss nicht in einem eigenen Thread starten... :-(
www.mkellenberger.ch Was ich täglich (neu) entdecke...
private Nachricht | Beiträge des Benutzers
Lion1984
myCSharp.de - Member



Dabei seit:
Beiträge: 782
Herkunft: Österreich

beantworten | zitieren | melden

waum machst du es nicht auf die klassische art und hängt eine Methode dran?
Wenn du paarmal Tab nachdem += drückst, füllt er eh alles automatisch aus bzw. kannst du auch selbst codieren.


Lion
lg Lion
private Nachricht | Beiträge des Benutzers
snoopy90
myCSharp.de - Member

Avatar #avatar-2063.gif


Dabei seit:
Beiträge: 95
Herkunft: Schweiz

beantworten | zitieren | melden

Sry...ich kann mir nicht gerade vorstellen wie das funktionieren soll..

Kannst du mir nicht evtl gleich den entsprechenden Code hier posten?

Vielen Dank schon im voraus und ihr seid echt eine Hilfe.

Da gibt es ganz andere Foren im Netz...
www.mkellenberger.ch Was ich täglich (neu) entdecke...
private Nachricht | Beiträge des Benutzers
Lion1984
myCSharp.de - Member



Dabei seit:
Beiträge: 782
Herkunft: Österreich

beantworten | zitieren | melden

WebBrowser wb = new WebBrowser();
wb.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler(wb_DocumentCompleted);

void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
        throw new NotImplementedException();
}
lg Lion
private Nachricht | Beiträge des Benutzers