Freitag, 11. März 2016

Zufällig durch das Internet browsen mit C#

In diesem Post möchte ich zeigen, wie man mit C# durch das Internet surft indem man zufälligen Links folgt. Dabei ist nicht nur einfach unheimlich interessant und aufschlussreich wo man nach ein paar Klicks landet, sondern dies hat auch eine praktische Anwendung: Zum Beispiel Googles Pagerank Algorithmus zur Bewertung der Popularität von Webseiten nutzt ein ähnliches Modell.

Das folgende C# Programm enthält ein Webbrowser Steuerelement und einen Button. Klickt der Benutzer auf den Button, durchsucht das Programm die aktuelle Webseite, sucht einen zufälligen Link aus und zeigt die Zielseite im Webbrowser an.
Der Code sollte relativ selbst erklärend sein: Die Klasse Browser behandelt das Suchen von neuen Links. Sie speichert die aktuelle Seite sowie den aktuellen Seitenquelltext. Wird die Methode GoNext() aufgerufen, ruft diese FindLink() auf, um einen zufälligen Link zu finden. Dafür wird eine zufällige Startposition im Quelltext der aktuellen Seite bestimmt (der Quelltext wird mit einem Webclient heruntergeladen) und ab da nach dem ersten Vorkommen eines HTML Links gesucht. Ich halte dieses Vorgehen für effizienter anstatt zuerst den kompletten Quellcode zu durchsuchen und danach einen zufälligen Link auszuwählen. Aber Achtung: Auf diese Weise werden bestimmte Links bevor- oder benachteiligt, da wir nicht mehr die tatsächliche Wahrscheinlichkeit der Links benutzen! Wir arbeiten nun mit einer Wahrscheinlichkeit über Zeichenketten, und da die Links höchstwahrscheinlich nicht gleichverteilt im Text sind (am Anfang gibt es zum Beispiel einen großen Header usw.), hat unsere Ziehung ein gewisses Bias.
Haben wir einen Link gefunden benutzen wir die Methode aus dem vorigen Post um ggf. relative Links in absolute umzuwandeln, und folgen diesem Link.
Das Programm funktioniert ist aber noch relativ rudimentär, es kann zum Beispiel in Sackgassen laufen etc., auch wie vorhin angemerkt ist die Linkauswahl nicht völlig zufällig. Ich poste es hier in der Form da ich denke dass es für eine Anwendung vom Benutzer eh noch stark personalisiert werden muss und die Anforderungen dafür stark schwanken.
Viel Spaß beim Ausprobieren und schreibt mir interessante Linkketten in die Kommentare!

Hier der Code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net;

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

        Browser B;

        private void Form1_Load(object sender, EventArgs e)
        {
            // start with some arbitrary page, here a random article on Wikipedia
            string StartPage = "https://de.wikipedia.org/wiki/Spezial:Zuf%C3%A4llige_Seite";
            B = new Browser(StartPage);
            webBrowser1.Navigate(StartPage);
        }

        private void button1_Click(object sender, EventArgs e)
        {
            webBrowser1.Navigate(B.GoNext());
        }
    }

    public class Browser
    {
        string CurrentPage; // stores url current website
        string CurrentContent; // stores content of current website

        public Browser(string Start)
        {
            CurrentPage = Start;
            CurrentContent = GetText(CurrentPage);
        }

        private string GetText(string url)
        {
            // use a webclient to download the source code of a website
            WebClient Webclient1 = new WebClient();
         
            Webclient1.Encoding = System.Text.Encoding.UTF8;
            string Content = ((Webclient1.DownloadString(url)));
            return (Content);
        }

        public string GoNext()
        {
            // randomly go to a new website
            CurrentPage = FindLink(); // for this find a random link
            CurrentContent = GetText(CurrentPage); // and for the next seach store the source code of the current webpage
            return CurrentPage;
        }

        private string FindLink()
        {
            // find a random link
            int Length = CurrentContent.Length;
            Random Rnd = new Random();
            int RndStart;
            int LinkStart = -1;
            int LinkEnd;
            string Link = "";

            // select a random starting point in the source code and find the first link after that
            // repeat if none find
            while (LinkStart == -1)
            {
                RndStart = Rnd.Next(Length);
                LinkStart = CurrentContent.IndexOf("a href=\"", RndStart);
            }

            // extract the link
            LinkEnd = CurrentContent.IndexOf("\"", LinkStart + 8);
            Link = CurrentContent.Substring(LinkStart + 8, LinkEnd - LinkStart - 8);

            // resolve its global url
            System.Uri Base = new System.Uri(CurrentPage);
            System.Uri ResolvedAbsoluteURL = new System.Uri(Base, Link);
            return ResolvedAbsoluteURL.ToString();
        }
    }
}

Montag, 7. März 2016

Relative URL mit C# auflösen

Kürzlich wollte ich eine Webseite nach Links durchsuchen und diesen folgen. Dabei wurden natürlich auch relative Links gefunden, und ich stellte fest, dass es im Internet gar nicht so leicht ist, einen Weg, diese mit C# aufzulösen, zu finden. Zuerst dachte ich darüber nach, relative Links selber zu verarbeiten, wurde dann aber doch mit einer sehr einfachen Methode fündig, die ich hier vorstellen möchte.
Zuerst kurz etwas zu Links im Internet: In HTML kann wie folgt auf eine andere Seite verwiesen, also ein Link erstellt werden:

<a href="http://www.sudokudestages.blogspot.de/">Aussagekräftiger Linktext</a>

In obigem Link habe ich eine absolute URL als Ziel angegeben, zu erkennen an http://www.
Ich kann jedoch auch Seiten relativ zu meiner aktuellen Seite referenzieren, zum Beispiel:

<a href="../../p/youtube-konto.html">Aussagekräftiger Linktext</a>

Dieser Link ruft die Seite http://csharp-tricks.blogspot.de/p/youtube-konto.html auf. Da dieser Post in dem virtuellen Ordner "/2016/03/" liegt, gehen wir mittels "../../" zwei Verzeichnisse aufwärts zur Wurzel URL, und rufen von da die Seite /p/youtube-konto.html auf.
Wenn absolute Links angetroffen werden, stellt dies kein Problem dar. Befindet man sich jedoch auf einer Seite und möchte einem relativen Link folgen, ist dies etwas komplizierter, zumal auch alle anderen bekannten Pfadangaben wie oben gesehen "../../target" erlaubt sind.

Um diese Pfade nun nicht manuell zusammen bauen zu müssen kann man die Klasse System.Uri in der Form System.Uri RelativeURL = new System.Uri(BaseUri, "relative Path"); verwenden.
Als Beispiel nehmen wir den Wikipedia Artikel des "Gabelbocks". In diesem findet sich ein relativer Link zu dem Wikipedia Artikel über "Schneidezähne":

<a href="/wiki/Schneidezahn" title="Schneidezahn">Schneidezähne</a>

Um den gültigen absoluten Link zu erhalten führen wir den folgenden Code aus:
System.Uri Base = new System.Uri("https://de.wikipedia.org/wiki/Gabelbock");
System.Uri ResolvedAbsoluteURL = new System.Uri(Base, "/wiki/Schneidezahn");