I see posts here and there about how to get Twitter data using their API, which seems to work well. I'm not crazy about the fact that it requires authentication and potentially extra work to secure the application, though. While this may not be anything new (I really don't know if anyone else has touched on this yet), there is another way to consume Twitter that doesn't require any user authentication.

The way to do this is through Twitter's search, located here: http://search.twitter.com. This is a fairly straightforward search page, not unlike Google. Type in "silverlight" and hit search, and you'll get back a listing of posts in chronological order. See the link in the top righthand corner? "Feed for this query" - click that and you'll be rewarded with an Atom feed.

I've put together an example project that shows how to download and utilize the data provided by Twitter's search engine. The example project described in this post can be download here. When you open the project, right-click the HTML file and pick "Set As Start Page" so you can see the app when you run it.

Inside the project, you'll find the usual Page.xaml.cs file, and you'll also see a user control called PostTemplate.xaml. This file is the template that will be populated with the data from the RSS feed. As shown in the following image, it looks like a rectangular thought bubble, and contains some default placeholder text. It also has drag and drop code in it so the bubbles can be manipulated once they are generated.

PostTemplate User Control

PostTemplate User Control

In the Page.xaml.cs file, I've declared several lists to store the parsed data, as well as a list to store the feeds. By making use of a list to store feeds, it is possible to add multiple different feeds from Twitter. You can see where the feeds were declared prior to the Page() constructor. Here's the code that initializes the lists inside the Page() constructor.

// initialize lists
rssFeeds = new List string();
rssPublished = new List string();
rssPostLink = new List string();
rssAvatar = new List string();
rssPostTitle = new List string();
rssAuthor = new List string();
rssAuthorUri = new List string();
 
// list of feeds to fetch
rssFeeds.Add("http://search.twitter.com/search.atom?lang=en&q=silverlight");

The code then calls a function called fetchData(), which is shown here:

private void fetchData()
{
    // request each feed in the feed list
    foreach (string feed in rssFeeds)
    {
        WebClient rest = new WebClient();
        rest.DownloadStringCompleted +=
            new DownloadStringCompletedEventHandler(rest_DownloadStringCompleted);
        rest.DownloadStringAsync(new Uri(feed));
    }
}

This code uses a REST service to get the data back. The data being returned was the tricky part. Luckily, Rob Houweling was willing to help me out with some parsing questions. The trick is that when using an Atom feed, you have to also provide a namespace, otherwise the parser will return empty strings. The parsing code goes inside the rest_DownloadStringCompleted function, which starts out like this:

XElement elements = XElement.Parse(e.Result);

The next line sets up a string that will contain the namespace from the XML:

string entryNameSpace = "{" + elements.Attribute("xmlns").Value + "}";

From there, the block of code to parse the data looks like the following listing.

// parse the xml
var entries = from element in elements.Elements(string.Format("{0}entry", entryNameSpace))
      select new
      {
          published  = element.Element(string.Format("{0}published", entryNameSpace))
            != null ? (string)element.Element(string.Format("{0}published", entryNameSpace)) : "",
          link = new XElement("link", element.Elements(string.Format("{0}link", entryNameSpace))),
          title = element.Element(string.Format("{0}title", entryNameSpace))
             != null ? (string)element.Element(string.Format("{0}title", entryNameSpace)) : "",
          authorName = element.Element(string.Format("{0}author", entryNameSpace)).
             Element(string.Format("{0}name", entryNameSpace)).Value
             != null ? (string)element.Element(string.Format("{0}author", entryNameSpace)).
             Element(string.Format("{0}name", entryNameSpace)).Value : "",
           authorUri  = element.Element(string.Format("{0}author", entryNameSpace)).
              Element(string.Format("{0}uri", entryNameSpace)).Value
              != null ? (string)element.Element(string.Format("{0}author", entryNameSpace)).
              Element(string.Format("{0}uri", entryNameSpace)).Value : "",
       };

Once all the data has been parsed, it can be stored in the various lists.

foreach (var entry in entries)
{
    // how many items have been parsed?
    numElements++;
 
    // add the parsed data to the list objects
    rssPublished.Add(entry.published);
    foreach (var link in entry.link.Elements())
    {
        // direct link to post
        if (link.Attribute("type").Value == "text/html")
            rssPostLink.Add(link.Attribute("href").Value);
 
        // direct link to author's avatar
        if (link.Attribute("type").Value == "image/png")
            rssAvatar.Add(link.Attribute("href").Value);
    }
    rssPostTitle.Add(entry.title);
    rssAuthor.Add(entry.authorName);
    rssAuthorUri.Add(entry.authorUri);
}

The application then calls a method called populateCanvas() that takes the data from the lists and creates instances of the PostTemplate user control on the main canvas. The example project just does the first four posts, so it is controlled by a simple "for" loop that creates an instance of PostTemplate called newPost, populates the template, and adds it to the main canvas.

for (int i = 0; i < 4; i++)
{
    PostTemplate newPost = new PostTemplate();
    newPost.msgTitle.Text = rssPostTitle[i];
    newPost.msgNameLink.Content = rssAuthor[i];
    newPost.msgNameLink.NavigateUri = new Uri(rssAuthorUri[i], UriKind.Absolute);
    newPost.msgNameLink.TargetName = "_blank";
 
    DateTime date_dt = DateTime.Parse(rssPublished[i]);
    newPost.msgDate.Text = date_dt.ToLongDateString() + " " + date_dt.ToLongTimeString();
 
    string avatarUri = rssAvatar.ElementAt(i);
 
    match = Regex.Match(avatarUri, ".gif", RegexOptions.IgnoreCase);
    if (!match.Success)
    {
        BitmapImage bmi = new BitmapImage(new Uri(avatarUri, UriKind.Absolute));
        newPost.profileImage.Source = bmi;
    }
 
    Canvas.SetLeft(newPost, rng.Next(1, (int)(LayoutRoot.Width - newPost.Width)));
    Canvas.SetTop(newPost, rng.Next(1, (int)(LayoutRoot.Height - newPost.Height)));
    LayoutRoot.Children.Add(newPost);
}

When the application runs, you get something like what is shown in the follow image. As you can see, the application displays the data in a friendly format, avatar and link to a list of the poster's tweets included.

The TwitterRSS Application

The TwitterRSS Application

There's a lot of room to expand here, but you can see that grabbing the data and working with it is fairly straightforward once you get past the Atom namespace issue.

One Response to “Getting Tweets from Twitter without the API”

  1. Silverlight Cream for September 16, 2008 -- #369 says:

    [...] Getting Tweets from Twitter without the API [...]

Leave a Reply