none
RSS-Feed in MSSQL-Datenbank fortlaufend speichern RRS feed

  • Frage

  • Hallo liebe Community,

    ich stehe vor dem Problem, einen RSS-Feed (bekannterweise ja nichts anderes als XML) in eine MSSQL-Datenbank einzufügen. Hintergrund ist der, dass ich die im RSS-Feed vorhandenen Informationen selektiv säubern und für meine Zwecke dann in einer Anwendung verwenden möchte.

    Im Detail:

    Beispielhaft sei hier der RSS-Feed von finanznachrichten.de: http://www.finanznachrichten.de/rss-nachrichten-meistgelesen
    Dieser soll nun nach dem Schema des XML-Formats in Spalten einer MSSQL-Datenbank eingefügt werden.

    Ich habe hier einen Code-Schnippsel in C gefunden, der so etwas für eine SQL-Datenbank bewerkstelligen soll...

    {syntaxhighlighter class="brush: bash"}
    #!/bin/sh
    
    RSS=http://alex.nofftz.name
    USER=datenbank_benutzername
    PASS=datenbank_passwort
    DB=datenbank_name
    
    wget -q -O - $RSS | xsltproc rss.xslt - | mysql --user=$USER --password=$PASS $DB
    {/syntaxhighlighter}

    Leider ist dies mein erstes C#-Projekt und irgendwo muss man ja mal anfangen :o) Meine bisherige Programmiererfahrung beschränkt sich leider nahezu auf Java.

    Ich erwarte jetzt keine Rundum-Lösung, aber vielleicht die ein oder andere Denkhilfe bzgl. der Technologien und Vorgehensweise. So ist es für mich grundsätzlich schonmal gut zu wissen, ob die MSSQL-Tabelle selbst in XML sein sollte, wenn nicht, ob sie bereits die Spalten aufweisen sollte, falls man die XML direkt einlesen und importieren kann oder ob es eine Standard-Klasse gibt, die selbsständig die Tabelle aufbauen kann?

    Ich wäre um jeden hilfreichen Beitrag dankbar! :)

    In Verwendung ist
    - MSSQL Server 2014
    - MSSQL Data Tools für Server 2014
    - VisualStudio Ultimate
    - auf Windows Server 2008 R2 Datacenter

    Ich hoffe mein Anliegen ist nicht zu zerstreut dargeboten und ihr könnt mir helfen :)

    Danke!

    Viele Grüße,
    Deniz

    • Typ geändert Ionut DumaModerator Dienstag, 14. Januar 2014 11:08 Keine Rueckmeldung des Fragenstellender
    • Typ geändert Marcel RomaModerator Sonntag, 23. März 2014 17:08 Rückmeldung des Fragestellenden
    Freitag, 3. Januar 2014 14:19

Antworten

  • Hi Deniz,

    Gerne.

    Soweit ich verstehe, liest dein Code die Feeds ein und speichert dann deren Elemente in einer Datenbank (entsprechende Parameter werden an eine gespeicherte Prozedur geschickt).

    Die Console-Befehle sind nicht wirklich hilfreich, wenn es darum geht, die Methode wiederholt aufzurufen. Aber vielleicht hast Du die Zeilen auch nur fürs Testen unkommentiert gelassen.

    Im Grunde genommen benötigst Du einen einfachen Windows-Dienst mit einem Timer (System.Timers.Timer oder System.Threading.Timer). Wenn Du nach diesen Begriffen im Netz suchst, wirst Du schnell fündig.

    Im entsprechenden Ablauf-Ereignishandler des Timers kannst Du dann deine Methode aufrufen. Es macht Sinn die Dauer der Verarbeitung in Betracht zu ziehen und die Intervale der Timer entsprechend zu setzen.

    Wenn Du Multithreading einsetzen willst, lies bitte auch dies:
    Alex Calvo - Comparing the Timer Classes in the .NET Framework Class Library

    Gruß
    Marcel

    Sonntag, 23. März 2014 17:06
    Moderator
  • Problem löst sich mit:

    DBCC CHECKIDENT ("Tabellen-Name", RESEED, 0)
    Ohne Anführungszeichen, versteht sich :)
    Samstag, 29. März 2014 11:29

Alle Antworten

  • Hallo Deniz,

    dein Vorhaben sieht nach was Größerem aus, aber vielleicht können wir dir hier punktuell helfen.
    Ich mach den Anfang, indem ich dir zeige, wie man das SyndicationFeed von der o.g. URL einlesen könnte (mithilfe von Klassen aus dem System.ServiceModel.Syndication-Namensraum):

    using System;
    using System.Globalization;
    using System.IO;
    using System.Xml;
    
    // Aus: %programfiles%\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\System.ServiceModel.dll
    using System.ServiceModel.Syndication;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            static void Main(string[] args)
            {
                // Feed lesen
                string url = "http://www.finanznachrichten.de/rss-nachrichten-meistgelesen";
                XmlReader xmlReader = new CustomDateXmlReader(url);
                SyndicationFeed feed = SyndicationFeed.Load(xmlReader);
                xmlReader.Close();
    
                // Feed-Elemente in Datenbank speichern
                foreach (SyndicationItem item in feed.Items)
                {
                    // Hier könnte man z.B. ein typisiertes DataSet verwenden
                    // um Daten in der Datenbank zu speichern. Oder man könnte
                    // EntityFramework für die Persistenz verwenden.
                    // Stellvertretend gebe ich hier die Daten einfach aus
                    Console.WriteLine("Publish Date: {0}", item.PublishDate);
                    Console.WriteLine("Title: {0}", item.Title.Text);
                    Console.WriteLine("Summary: {0}", item.Summary.Text);
                    Console.WriteLine("Item Links:");
    
                    foreach (SyndicationLink link in item.Links) {
                        Console.WriteLine(" " + link.Uri);
                    }
    
                    Console.WriteLine();
                }
    
                Console.Write("\nFeed wurde gelesen. Mit beliebiger Taste beenden.");
                Console.ReadKey(true);
            }
        }
    
        // SyndicationFeed.Load() erwartet Datumsangaben im Format: Mon, 05 Oct 2009 08:00:06 GMT.
        // Andernfalls wird eine XmlException geworfen. Folgende Klasse schafft Abhilfe.
        // Quelle: http://support.microsoft.com/kb/2020488/en-us (leicht abgeändert)
        class CustomDateXmlReader : XmlTextReader
        {
            private bool readingDate = false;
            private readonly string CustomUtcDateTimeFormat = "ddd MMM dd HH:mm:ss Z yyyy"; // Wed Oct 07 08:00:07 GMT 2009
            public CustomDateXmlReader(Stream s) : base(s) { }
    
            public CustomDateXmlReader(string inputUri) : base(inputUri) { }
    
            public CustomDateXmlReader(string inputUri, string customUtcDateTimeFormat)
                : this(inputUri)
            {
                CustomUtcDateTimeFormat = customUtcDateTimeFormat;
            }
    
            public CustomDateXmlReader(Stream s, string customUtcDateTimeFormat)
                : this(s)
            {
                CustomUtcDateTimeFormat = customUtcDateTimeFormat;
            }
    
    
            public override void ReadStartElement()
            {
                if (string.Equals(base.NamespaceURI, string.Empty, StringComparison.InvariantCultureIgnoreCase) &&
                    (string.Equals(base.LocalName, "lastBuildDate", StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(base.LocalName, "pubDate", StringComparison.InvariantCultureIgnoreCase)))
                {
                    readingDate = true;
                }
                base.ReadStartElement();
            }
    
            public override void ReadEndElement()
            {
                if (readingDate)
                {
                    readingDate = false;
                }
                base.ReadEndElement();
            }
    
            public override string ReadString()
            {
                if (readingDate)
                {
                    string dateString = base.ReadString();
                    DateTime dt;
                    if (!DateTime.TryParse(dateString, out dt))
                        dt = DateTime.ParseExact(dateString, CustomUtcDateTimeFormat, CultureInfo.InvariantCulture);
                    return dt.ToUniversalTime().ToString("R", CultureInfo.InvariantCulture);
                }
                else
                {
                    return base.ReadString();
                }
            }
        }
    }
    

    Siehe auch: Übersicht über WCF Syndication.

    Gruß
    Marcel

    Freitag, 3. Januar 2014 22:54
    Moderator

  • **************************************************************************************************
    Dieser Thread wurde mangels weiterer Beteiligung des Fragestellenden ohne bestätigte Lösung abgeschlossen.
    Neue Rückfragen oder Ergänzungen zu diesem Thread bleiben weiterhin möglich.
    **************************************************************************************************

    Ionut Duma, MICROSOFT   Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-PrinzipEntwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.

    Dienstag, 14. Januar 2014 11:08
    Moderator
  • Hi Marcel!

    Danke für deine so ausführliche Antwort für mein Problem!

    Ich dachte die ganze Zeit es hätte keiner geantwortet, weil keine Benachrichtigungsmail kam und ich Eumel nicht mehr hier reingeschaut habe.

    Ich werde das ausprobieren und berichten!

    Danke nochmal!

    LG
    Deniz

    Mittwoch, 19. März 2014 16:48
  • Hallo nochmal,

    ich habe den Code für meine Zwecke anpassen können, großen Dank dafür! Der Code sieht jetzt so aus:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.IO;
    using System.Xml;
    using System.ServiceModel.Syndication;
    using System.Data.SqlClient;
    using System.Globalization;
    using System.Data;
    
    namespace RecoApplication
    {
        class RSSImportClass
        {
            public static void RSSImport()
            {
                using (var conn = new SqlConnection(MainClass.ConnectionString))//Verbindung herstellen
                {
                    conn.Open();//Verbindung öffnen
    
                    int i = 1;
    
                    // Feed lesen
                    string url = "http://www.finanznachrichten.de/rss-aktien-analysen";
                    XmlReader xmlReader = new CustomDateXmlReader(url);
                    SyndicationFeed feed = SyndicationFeed.Load(xmlReader);
                    xmlReader.Close();
    
    
    
                    // Feed-Elemente in Datenbank speichern
                    foreach (SyndicationItem item in feed.Items)
                    {
    
                        using (var command = conn.CreateCommand())
                        {
                            command.CommandText = "InsertRow_RSS_Empfehlungen";
                            command.CommandType = CommandType.StoredProcedure;
    
                            command.Parameters.AddWithValue("@Id", i++);
                            command.Parameters.AddWithValue("@Zeit", item.PublishDate);
                            command.Parameters.AddWithValue("@Titel", item.Title.Text);
                            command.Parameters.AddWithValue("@Vorschau", item.Summary.Text);
                            
                            foreach (SyndicationLink link in item.Links)
                            {
                                command.Parameters.AddWithValue("@Link","" + link.Uri);
                            }
    
                            //Console.WriteLine();
                            command.ExecuteNonQuery();//Befehl ausführen
                        }
    
                        Console.Write("\nFeed wurde gelesen. Mit beliebiger Taste beenden.");
                        
                    }
                    Console.ReadKey(true);
                conn.Close();
            }}
        }
    
        // SyndicationFeed.Load() erwartet Datumsangaben im Format: Mon, 05 Oct 2009 08:00:06 GMT.
        // Andernfalls wird eine XmlException geworfen. Folgende Klasse schafft Abhilfe.
        // Quelle: http://support.microsoft.com/kb/2020488/en-us (leicht abgeändert)
        class CustomDateXmlReader : XmlTextReader
        {
            private bool readingDate = false;
            private readonly string CustomUtcDateTimeFormat = "ddd MMM dd HH:mm:ss Z yyyy"; // Wed Oct 07 08:00:07 GMT 2009
            public CustomDateXmlReader(Stream s) : base(s) { }
    
            public CustomDateXmlReader(string inputUri) : base(inputUri) { }
    
            public CustomDateXmlReader(string inputUri, string customUtcDateTimeFormat)
                : this(inputUri)
            {
                CustomUtcDateTimeFormat = customUtcDateTimeFormat;
            }
    
            public CustomDateXmlReader(Stream s, string customUtcDateTimeFormat)
                : this(s)
            {
                CustomUtcDateTimeFormat = customUtcDateTimeFormat;
            }
    
    
            public override void ReadStartElement()
            {
                if (string.Equals(base.NamespaceURI, string.Empty, StringComparison.InvariantCultureIgnoreCase) &&
                    (string.Equals(base.LocalName, "lastBuildDate", StringComparison.InvariantCultureIgnoreCase) ||
                    string.Equals(base.LocalName, "pubDate", StringComparison.InvariantCultureIgnoreCase)))
                {
                    readingDate = true;
                }
                base.ReadStartElement();
            }
    
            public override void ReadEndElement()
            {
                if (readingDate)
                {
                    readingDate = false;
                }
                base.ReadEndElement();
            }
    
            public override string ReadString()
            {
                if (readingDate)
                {
                    string dateString = base.ReadString();
                    DateTime dt;
                    if (!DateTime.TryParse(dateString, out dt))
                        dt = DateTime.ParseExact(dateString, CustomUtcDateTimeFormat, CultureInfo.InvariantCulture);
                    return dt.ToUniversalTime().ToString("R", CultureInfo.InvariantCulture);
                }
                else
                {
                    return base.ReadString();
                }
            }
        }
    }
    

    Der ConnectionString liegt in einer Main-Methode, wo auch die RSSImport-Methode aufgerufen wird.

    Ergänzend dazu habe ich die Frage, wie ich diese Methode fortlaufend speichern, d.h. neue Feeds entsprechend an die SQL-Tabelle alle paar Minuten ergänzen kann?

    Die erste Idee war als Windows-Dienst auf einem Server, nur habe ich wirklich gar keine Ahnung davon, wie man das bewerkstelligt. Auch problematisch für mich ist, wie ich das Code-technisch so löse, dass die neuen Einträge fortlaufend in der SQL-Tabelle erfolgen.

    Um jeden hilfreichen Beitrag bin ich dankbar! :)

    Danke und Gruß
    Deniz

    Sonntag, 23. März 2014 14:47
  • Hi Deniz,

    grundlegend ist es sinnvoll für jede Frage, einen eigenen Thread zu erstell, es hilf anderen Antworte auf ihre Frage zu finden und die Leute schauen eher mal rein und  müssen dann nicht x Beiträge durchzulesen, die nichts mit der Fragen zu tun haben.

    Einen Dienst zu erstellen ist nicht schwer schau hier

    Wenn du die ID Spalte in der Datenbank auf AutoIncrement stellst oder eine Guid Verwendest. Kannst du einfach neue Spalten anfügen.

    MFG

    Björn

    Sonntag, 23. März 2014 16:15
  • Hi Deniz,

    Gerne.

    Soweit ich verstehe, liest dein Code die Feeds ein und speichert dann deren Elemente in einer Datenbank (entsprechende Parameter werden an eine gespeicherte Prozedur geschickt).

    Die Console-Befehle sind nicht wirklich hilfreich, wenn es darum geht, die Methode wiederholt aufzurufen. Aber vielleicht hast Du die Zeilen auch nur fürs Testen unkommentiert gelassen.

    Im Grunde genommen benötigst Du einen einfachen Windows-Dienst mit einem Timer (System.Timers.Timer oder System.Threading.Timer). Wenn Du nach diesen Begriffen im Netz suchst, wirst Du schnell fündig.

    Im entsprechenden Ablauf-Ereignishandler des Timers kannst Du dann deine Methode aufrufen. Es macht Sinn die Dauer der Verarbeitung in Betracht zu ziehen und die Intervale der Timer entsprechend zu setzen.

    Wenn Du Multithreading einsetzen willst, lies bitte auch dies:
    Alex Calvo - Comparing the Timer Classes in the .NET Framework Class Library

    Gruß
    Marcel

    Sonntag, 23. März 2014 17:06
    Moderator
  • Hi zusammen,

    danke für eure Hinweise.

    Nach derzeitigem Stand speichert die Anwendung nun die IDs fortlaufend, zählt sie auch hoch, aber leider beginnt sie nicht bei 1 sondern bei dem Wert, der zuletzt gespeichert wurde. Bei 20 Zeilen in der Tabelle fährt sie nach Löschung der Tabelle mit der ID "21" fort...

    Habt ihr Ideen?

    Hier noch die Einstellungen der Datenbank.

    Dank' euch und viele Grüße

    Deniz

    Freitag, 28. März 2014 09:51
  • Problem löst sich mit:

    DBCC CHECKIDENT ("Tabellen-Name", RESEED, 0)
    Ohne Anführungszeichen, versteht sich :)
    Samstag, 29. März 2014 11:29
  • Es freut uns, dass Du Dein Problem lösen konntest und danke, dass Du Deine Lösung hier im Forum gepostet hast. Jetzt können auch andere die das gleiche Problem haben unter Umständen davon profitieren.


    Ciprian Bogdan, MICROSOFT   Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-PrinzipEntwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.





    Montag, 31. März 2014 14:30