Text von Website auslesen

04/05/2014 23:21 TommyGremy#1
Hey Coder/in,

ich bin ein sehr unerfahrender Programmierer und bringe mir vieles durch einfache Tutorials bei. Ich habe versucht über ein Tutorial ein HTML Code auszulesen und somit bei einem E*PVP Profil die Zeit auszulesen wann der User zuletzt Online war.

Das Programm was ich programmieren will soll jede 30min einmal dass letzte mal Online abfragen und anzeigen, falls die Onlinezeit sich ändern sollte es noch ein Geräusch abspielen.


Eigentlich relativ simpel aber bin beim dem auslesen schon gescheitert. Ich benutzte das "HtmlAgilityPack" zum herausfinden der Daten und die Daten sollten in einer DataGridView angezeigt werden.


Vielleicht kann mir jemand helfen, wäre nett :)
MfG!
04/05/2014 23:32 qkuh#2
HttpWebRequest/HttpWebResponse ansehen und verstehen, Seite aufrufen, Zeit rausfiltern, etc. pp.
04/05/2014 23:58 Jay Niize#3
Wie mein Vorposter sagt hilft HttpWebRequest/HttpWebResponse. Rausfiltern würde es relativ gut mit RegEx gehen. Da solltest du dich auch erst einmal einlesen und etwas aus einem Übungstext rausfiltern. Das mit dem Sound würde leicht gehen. Erstellst eine Variable für den alten Status und eine für den Neuen. Wenn sie gleich sind kein Sound, wenn sie ungleich sind Sound.
04/06/2014 21:01 TommyGremy#4
Danke für die Antworten. Habe bisher nicht so viel geschafft und komme nach einen halben Tag coden nicht weiter.

Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;

namespace chashrefausleser
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            dgDaten1.Columns.Add("cl1", "");
            dgDaten1.Columns[0].Width = 98;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var request = WebRequest.Create("http://www.elitepvpers.com/forum/members/3931150-tommygremy.html");
            request.Proxy = null;
            var response = request.GetResponse();
            var responseStream = response.GetResponseStream();

            var nodes = doc.DocumentNode.SelectNodes("//div[@id='last_online']");

            var col1 = doc.DocumentNode.InnerText;
            dgDaten1.Rows.Add(col1);
        }
    }
}
Kann mir jemand weiterhelfen bis es klappt eine Textzeile auszulesen? :(
04/06/2014 21:16 'Heaven.#5
Code:
 var wc = new WebClient();

var str = wc.DownloadString("http://www.elitepvpers.com/forum/members/5863087-xmaskulln.html");

//<div id="last_online">
//<span class="shade">Letzte Aktivität:</span> Heute <span class="time">21:07</span>
//</div>

var int1 = str.IndexOf("last_online", System.StringComparison.Ordinal);
var int2 = str.IndexOf("div",int1,System.StringComparison.Ordinal);
var str2 = str.Substring(int1, int2 - int1);

var int3 = str2.IndexOf("</span>", System.StringComparison.Ordinal);
var int4 = str2.IndexOf("</span>\r", int3, System.StringComparison.Ordinal);

var str3 = str2.Substring(int3, int4 - int3);

var strfinal = str3.Replace("<span class=\"time\">", "").Replace("</span> ","");

Kann man sicherlich auch eleganter lösen
04/06/2014 21:32 PC Jones#6
Code:
using System.Text.RegularExpressions;

            var wc = new WebClient();

            var str = wc.DownloadString("http://www.elitepvpers.com/forum/members/5863087-xmaskulln.html");

            Regex r = new Regex("<div id=\"last_online\">.*?<span class=\"shade\">.*?<span class=\"time\">(.*?)</span>", RegexOptions.Singleline);
            string lastOnline = r.Match(str).Groups[1].Value;
            MessageBox.Show(lastOnline);
lg :)
04/06/2014 23:17 TommyGremy#7
Hey, danke für die extrem hilfreichen Antworten =)

Habe den Code jetzt umgebaut damit er jede 40min nach dem Check einen Sound abspielt und von vorne den Download anfängt und somit aktualisiert.

Nun hab ich zwei Probleme.
1. Der Download von der Datei dauert immer ca.5sec und das Programm ist solang gefreezt, kann man dass umgehen?
2. und 2 durch meinen neuen Code funktioniert das Programm nicht mehr es hängt sich auf und macht nichts mehr, weiß jemand weiter? Liegt entweder an den Sleeps oder an dem goto wenn ich es ausklammer klappt alles wunderbar.

Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Threading;
using System.Media;

namespace Zeit
{
    public partial class Form1 : Form
    {
        private SoundPlayer _soundplayer;

        public Form1()
        {
            InitializeComponent();
            _soundplayer = new SoundPlayer("k.wav");
        }

        private void dataGridView1_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
        }

        private void button1_Click(object sender, EventArgs e)
        {
            var wc = new WebClient();

            newstart:
            var str = wc.DownloadString("http://www.elitepvpers.com/forum/members/3931150-tommygremy.html");

            //<div id="last_online">
            //<span class="shade">Letzte Aktivität:</span> Heute <span class="time">21:07</span>
            //</div>

            var int1 = str.IndexOf("last_online", System.StringComparison.Ordinal);
            var int2 = str.IndexOf("div", int1, System.StringComparison.Ordinal);
            var str2 = str.Substring(int1, int2 - int1);

            var int3 = str2.IndexOf("</span>", System.StringComparison.Ordinal);
            var int4 = str2.IndexOf("</span>\r", int3, System.StringComparison.Ordinal);

            var str3 = str2.Substring(int3, int4 - int3);

            var strfinal = str3.Replace("<span class=\"time\">", "").Replace("</span> ", "");

            var col1 = strfinal;
            label1.Text = col1;

            Thread.Sleep(2400000);
            _soundplayer.Play();
            Thread.Sleep(10000);
            _soundplayer.Stop();

            goto newstart;


        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void label1_Click(object sender, EventArgs e)
        {

        }
    }
}
04/06/2014 23:28 Shawak#8
Sleeps & goto weg und Timer benutzen

Wenn du nicht willst dass dein Programm während des Requests hängt tasks nutzen
04/06/2014 23:41 TommyGremy#9
Hey,
Wie kann ich das Programm als "task" umbauen und das Problem an dem Timer ist ich weiß nicht wie ich eine Abfrage aufstelle ob sich die Zeit/der Text verändert hat und wie ich dann wieder zurückspringen kann damit alles von vorne anfängt mit herunterladen,verarbeiten etc...
04/07/2014 01:36 Terreox#10
Ich hab dir im Anhang mal das Projekt angehangen.

Ich erkläre in diesem Beispiel nur, wie man die Zeit bekommt. Sounds etc. können später hinzugefügt werden.

Schritt 1:
Für dieses Projekt benutze ich das HtmlAgilityPack.
Dazu rechtsklick auf das Projekt -> NuGet Paket verwalten (oder so ähnlich) -> den Reiter "Online" auswählen -> oben in die Suchleiste "HtmlAgilityPack" eingeben -> suchen und installieren.

Schritt 2:
Code:
public partial class Form1 : Form
{
    private readonly Timer _refreshTimer;

    public Form1()
    {
        InitializeComponent();

        _refreshTimer = new Timer(TimeSpan.FromSeconds(5).TotalMilliseconds);
        _refreshTimer.Elapsed += UpdateLastSeen;
        _refreshTimer.Start();
    }
.
.
.
Hier hab ich erstmal den System.Timers.Timer deklariert und initialisiert.
Im Konstruktur kannst du bereits das Interval in Millisekunden festlegen.
Hier habe ich einmal als Beispiel ein Interval von 5 Sekunden gewählt.

Das Elapsed-Event wird ausgelöst, wenn das Interval abgelaufen ist.
Im Konstruktor habe ich eine Methode registriert, die beim Auslösen des Events ausgelöst wird. Dazu später mehr.

Am Ende noch den Timer starten und fertig.

Schritt 3:
Nun gehts zum Eingemachten.

Code:
private void UpdateLastSeen(object sender, EventArgs e)
{
    _refreshTimer.Stop();

    var doc = new HtmlDocument();
    var webclient = new WebClient();

    // Load html into document.
    doc.LoadHtml(webclient.DownloadString("http://www.elitepvpers.com/forum/members/3931150-tommygremy.html"));

    var lastSeenNode = doc.DocumentNode.SelectSingleNode("//*[@id=\"last_online\"]/span[2]");

    var lastSeen = lastSeenNode.InnerHtml;

    _refreshTimer.Start();
}
Zunächst sei gesagt, dass _refreshTimer.Start() und _refreshTimer.Stop() optional sind. Ich habe dies hier nur gemacht, damit der Timer bei der kurzen Intervaldauer nicht mehrfach auslöst, sollte der Code was länger brauchen.

Als erstes deklariere ich zwei Variablen:
Ein HtmlAgilityPack.HtmlDocument und ein WebClient.

Zunächst lade ich mit Hilfe des WebClient die Webseite als String in das HtmlDocument.
Danach brauch ich nur noch das Element, welches die Zeit enthält.
Dieses Element bekomme ich per XPath.

Wenn man das Element hat, kann man die Zeit einfach über InnerHtml auslesen.

Achtung: Wenn du in der UpdateLastSeen Methode irgendwelche Elemente auf deiner GUI änderst, musst du dies via Control.Invoke tun, da UpdateLastSeen in einem separaten Thread ausgeführt wird (falls die Methode von dem Timer aufgerufen wird).

Bitte beachten: Ich habe hier keinerlei Fehlerchecks drin. Du musst also noch prüfen, ob überhaupt die Daten richtig geladen wurden und das Element mit der Zeit gefunden wurde.

Hoffe das hat geholfen :)