Benutzer mit den meisten Antworten
RSS-Feed in MSSQL-Datenbank fortlaufend speichern

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 DatacenterIch 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
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 LibraryGruß
Marcel- Als Antwort markiert Ciprian Bogdan Montag, 31. März 2014 14:30
-
Problem löst sich mit:
DBCC CHECKIDENT ("Tabellen-Name", RESEED, 0)
Ohne Anführungszeichen, versteht sich :)- Als Antwort markiert Ciprian Bogdan Montag, 31. März 2014 14:30
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 -
Hallo Deniz,
Hat Dir die Antwort von Marcel geholfen? Wenn Ja bitte markiere diese als Antwort.
Danke und Gruss,
Ionut
Ionut Duma, MICROSOFT
Bitte haben Sie Verständnis dafür, dass im Rahmen dieses Forums, welches auf dem Community-Prinzip„Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.
-
**************************************************************************************************
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-Prinzip„Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.
-
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 -
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 -
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
-
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 LibraryGruß
Marcel- Als Antwort markiert Ciprian Bogdan Montag, 31. März 2014 14:30
-
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
-
Problem löst sich mit:
DBCC CHECKIDENT ("Tabellen-Name", RESEED, 0)
Ohne Anführungszeichen, versteht sich :)- Als Antwort markiert Ciprian Bogdan Montag, 31. März 2014 14:30
-
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-Prinzip„Entwickler helfen Entwickler“ beruht, kein technischer Support geleistet werden kann oder sonst welche garantierten Maßnahmen seitens Microsoft zugesichert werden können.