using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
namespace CraigslistCars
{
class Car
{
public string Subject { get; set; }
public string Description { get; set; }
public string Link { get; set; }
public string Price { get; set; } = "-1";
}
class Program
{
static void Main(string[] args)
{
string url = "https://portland.craigslist.org/search/ … format=rss";
XmlReader reader = XmlReader.Create(url);
FeedFormatter formatter = new FeedFormatter();
formatter.ReadFrom(reader);
reader.Close();
List<Car> cars = new List<Car>();
foreach (SyndicationItem item in formatter.Feed.Items)
{
string subject = System.Net.WebUtility.HtmlDecode(item.Title.Text);
string summary = item.Summary.Text;
Car car = new Car();
car.Subject = subject;
car.Description = summary;
car.Link = item.Links[0].Uri.ToString();
car.Price = (SearchForPrice(car.Subject) == "-1") ? SearchForPrice(car.Subject) : SearchForPrice(car.Description);
cars.Add(car);
}
foreach(var car in cars)
{
Console.WriteLine($"{car.Link}\n{car.Subject} | Price: {car.Price}\n {car.Description.Replace("\n", "").Truncate(5*15)}...\n");
}
Console.ReadLine();
}
static string SearchForPrice(string input)
{
foreach (var word in input.Split(' '))
{
var price = new Regex(@"\$\ ?[+-]?[0-9]{1,3}(?:,?[0-9])*(?:\.[0-9]{1,2})?").Matches(word);
if (price.Count >= 1)
return price[0].Value;
}
return "-1";
}
}
public static class StringExt
{
public static string Truncate(this string value, int maxLength)
{
if (string.IsNullOrEmpty(value)) return value;
return value.Length <= maxLength ? value : value.Substring(0, maxLength);
}
}
public class FeedFormatter : SyndicationFeedFormatter
{
#region Constructors
public FeedFormatter() { }
public FeedFormatter(SyndicationFeed feed) : base(feed) { }
#endregion
#region Properties
public override string Version
{
get { return "Rss10"; }
}
public virtual string RdfNamespaceUri
{
get { return "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; }
}
public virtual string NamespaceUri
{
get { return "http://purl.org/rss/1.0/"; }
}
#endregion
#region Methods
#region Read XML Methods
public override bool CanRead(System.Xml.XmlReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
return reader.IsStartElement("RDF", this.RdfNamespaceUri);
}
protected override SyndicationFeed CreateFeedInstance()
{
return new SyndicationFeed();
}
public override void ReadFrom(System.Xml.XmlReader reader)
{
if (!this.CanRead(reader))
throw new XmlException("Unknown RSS 1.0 feed format.");
this.ReadFeed(reader);
}
private void ReadFeed(XmlReader reader)
{
this.SetFeed(this.CreateFeedInstance());
this.ReadXml(reader, base.Feed);
}
protected virtual void ReadXml(XmlReader reader, SyndicationFeed result)
{
if (result == null)
throw new ArgumentNullException("result");
else if (reader == null)
throw new ArgumentNullException("reader");
reader.ReadStartElement(); // Read in <RDF>
reader.ReadStartElement("channel"); // Read in <channel>
while (reader.IsStartElement()) // Process <channel> children
{
if (reader.IsStartElement("title"))
result.Title = new TextSyndicationContent(reader.ReadElementString());
else if (reader.IsStartElement("link"))
result.Links.Add(new SyndicationLink(new Uri(reader.ReadElementString())));
else if (reader.IsStartElement("description"))
result.Description = new TextSyndicationContent(reader.ReadElementString());
else
reader.Skip();
}
reader.ReadEndElement(); // Read in </channel>
while (reader.IsStartElement())
{
if (reader.IsStartElement("item"))
{
result.Items = this.ReadItems(reader, result);
break;
}
else
reader.Skip();
}
}
protected virtual IEnumerable<SyndicationItem> ReadItems(XmlReader reader, SyndicationFeed feed)
{
if (feed == null)
throw new ArgumentNullException("feed");
else if (reader == null)
throw new ArgumentNullException("reader");
Collection<SyndicationItem> items = new Collection<SyndicationItem>();
while (reader.IsStartElement("item"))
{
items.Add(this.ReadItem(reader, feed));
}
return items;
}
protected virtual SyndicationItem ReadItem(XmlReader reader, SyndicationFeed feed)
{
if (feed == null)
throw new ArgumentNullException("feed");
else if (reader == null)
throw new ArgumentNullException("reader");
SyndicationItem result = new SyndicationItem();
this.ReadItemFrom(reader, result);
return result;
}
protected virtual void ReadItemFrom(XmlReader reader, SyndicationItem result)
{
if (result == null)
throw new ArgumentNullException("result");
else if (reader == null)
throw new ArgumentNullException("reader");
reader.ReadStartElement();
while (reader.IsStartElement())
{
if (reader.IsStartElement("title"))
result.Title = new TextSyndicationContent(reader.ReadElementString());
else if (reader.IsStartElement("link"))
result.Links.Add(new SyndicationLink(new Uri(reader.ReadElementString())));
else if (reader.IsStartElement("description"))
result.Summary = new TextSyndicationContent(reader.ReadElementString());
else
reader.Skip();
}
reader.ReadEndElement();
}
#endregion
#region Write XML Methods
public override void WriteTo(System.Xml.XmlWriter writer)
{
if (writer == null)
throw new ArgumentNullException("writer");
writer.WriteStartElement("rdf", "RDF", this.RdfNamespaceUri); // Write <RDF>
writer.WriteAttributeString("xmlns", this.NamespaceUri);
this.WriteFeed(writer);
writer.WriteEndElement();
}
protected virtual void WriteFeed(XmlWriter writer)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (base.Feed == null)
throw new InvalidOperationException("Feed formatter does not have a feed.");
string alternateLink = string.Empty, selfLink = string.Empty;
foreach (SyndicationLink lnk in base.Feed.Links)
{
if (alternateLink.Length == 0 && lnk.RelationshipType == "alternate")
alternateLink = lnk.Uri.IsAbsoluteUri ? lnk.Uri.AbsoluteUri : lnk.Uri.ToString();
else if (selfLink.Length == 0 && lnk.RelationshipType == "self")
selfLink = lnk.Uri.IsAbsoluteUri ? lnk.Uri.AbsoluteUri : lnk.Uri.ToString();
}
if (selfLink.Length == 0 && alternateLink.Length > 0)
selfLink = alternateLink;
else if (alternateLink.Length == 0 && selfLink.Length > 0)
alternateLink = selfLink;
writer.WriteStartElement("channel"); // Write <channel>
writer.WriteAttributeString("about", this.RdfNamespaceUri, selfLink);
if (base.Feed.Title == null || string.IsNullOrEmpty(base.Feed.Title.Text))
throw new ArgumentException("Feed title required for RSS 1.0 feeds.");
writer.WriteElementString("title", base.Feed.Title.Text); // Write <title>
if (alternateLink.Length == 0)
throw new ArgumentException("Feed link required for RSS 1.0 feeds.");
writer.WriteElementString("link", alternateLink); // Write <link>
if (base.Feed.Description == null || string.IsNullOrEmpty(base.Feed.Description.Text))
throw new ArgumentException("Feed title required for RSS 1.0 feeds.");
writer.WriteElementString("description", base.Feed.Description.Text); // Write <description>
writer.WriteStartElement("items"); // Write <items>
writer.WriteStartElement("Seq", this.RdfNamespaceUri);
foreach (SyndicationItem item in base.Feed.Items)
{
string itemAlternateLink = GetAlternateLinkForItem(item);
writer.WriteStartElement("li", this.RdfNamespaceUri);
writer.WriteAttributeString("resource", this.RdfNamespaceUri, itemAlternateLink);
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement(); // Write </channel>
// Write the <item> elements
this.WriteItems(writer, base.Feed.Items);
}
protected virtual void WriteItems(XmlWriter writer, IEnumerable<SyndicationItem> items)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (items != null)
foreach (SyndicationItem item in items)
this.WriteItem(writer, item);
}
protected virtual void WriteItem(XmlWriter writer, SyndicationItem item)
{
if (writer == null)
throw new ArgumentNullException("writer");
string alternateLink = GetAlternateLinkForItem(item);
writer.WriteStartElement("item"); // Write <item>
writer.WriteAttributeString("about", this.RdfNamespaceUri, alternateLink);
this.WriteItemContents(writer, item);
writer.WriteEndElement();
}
protected virtual void WriteItemContents(XmlWriter writer, SyndicationItem item)
{
if (writer == null)
throw new ArgumentNullException("writer");
if (item.Title == null || string.IsNullOrEmpty(item.Title.Text))
throw new ArgumentException("Feed title required for RSS 1.0 feeds.");
writer.WriteElementString("title", item.Title.Text); // Write <title>
string alternateLink = GetAlternateLinkForItem(item);
if (string.IsNullOrEmpty(alternateLink))
throw new ArgumentException("Feed link required for RSS 1.0 feeds.");
writer.WriteElementString("link", alternateLink); // Write <link>
if (base.Feed.Description != null && string.IsNullOrEmpty(item.Summary.Text) == false)
writer.WriteElementString("description", item.Summary.Text); // Write the optional <description>
}
private string GetAlternateLinkForItem(SyndicationItem item)
{
foreach (SyndicationLink lnk in item.Links)
if (lnk.RelationshipType == "alternate")
return lnk.Uri.IsAbsoluteUri ? lnk.Uri.AbsoluteUri : lnk.Uri.ToString();
// If we reach here, return an empty string
return string.Empty;
}
#endregion
#endregion
}
}