IP address to geolocation

Background

Few months ago I found an interesting website: http://ipinfodb.com/, it provided API which could “translate” any IP Address into a geography location including City/Region/Country as well as latitude/longitude and time zone information, to invoke its API, a registered API key is required (which is free).  Since beforehand I stored visitor’s IP Addresses into my own database, I decided to utilize InfoDB API to store visitor’s GEO locations.

Just few days ago, I casually emitted an idea: summarize those GEO location records and display them on Google Map, hum, it is feasible:)

So, the process is: Track visitor’s IP addresses -> “Translate” them to Geography location -> Show them on Google Map!

(PS, I’ve been using Google Analytics for my Geek Place – http://WayneYe.com for more than two years, it is no double extremely powerful, and it already contains a feature “Map Overlay“, however, due to privacy policy, Google Analytics does NOT display visitor’s IP address, they explained this at: http://www.google.com/support/analytics/bin/answer.py?hl=en&answer=86214).

Implementation

The first task I need to do is track visitor’s IP Address, most of the time, user visits a website in browser submits an HTTP GET request (an HTTP data package) based on Transmission Control Protocol (not all the time) , browser passed the ball to DNS server(s), after several times “wall passes”, the original request delivered to the designation – the web server, during the process, the original Http request was possibly transferred through a number of routers/proxies and many other stuff, the request’s header information might have been updated: Via (Standard HTTP request header) or X-Forwarded-For (non-standard header but widely used), could be the original ISP’s information/IP Address OR possibly one of the proxy’s IP Address.

So, usually the server received the request and saw Via/X-Forwarded-For header information, it got to know visitor’s IP address (NOT all the time, some times ISP’s IP address), in ASP.NET, it is simply to call Request.UserHostAddress, however, we can never simply trust this because of two major reasons:

  1. Malicious application can forge HTTP request, if you are unlucky to trust it and have it inserted into Database, then SQL Injection hole will be utilized by Malicious application.
  2. Not all the visitors are human being, part of them (sometimes majority of them, for example, my website has very few visitors per week while a number of “robot visitors”^_^) could be search engine crawlers, I must distinguish human visitors and crawlers, otherwise I would be happy to see a lot of “visitors” came from “Mountain View, CA” ^_^.

For #1: I use regular expression to validate the string I got from Request.UserHostAddress:

public static Boolean IsValidIP(string ip)
{
    if (System.Text.RegularExpressions.Regex.IsMatch(ip, "[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"))
    {
        string[] ips = ip.Split('.');
        if (ips.Length == 4 || ips.Length == 6)
        {
            if (System.Int32.Parse(ips[0]) < 256 && System.Int32.Parse(ips[1]) < 256
                & System.Int32.Parse(ips[2]) < 256 & System.Int32.Parse(ips[3]) < 256)
                return true;
            else
                return false;
        }
        else
            return false;
    }
    else
        return false;
}

If the result is “0.0.0.0”, I will ignore it.

For #2, so far I haven’t found a “perfect way” to solve this issue (and I guess there might be no perfect solution to identify all the search engines in the world, please correct me if I am wrong); However, I’ve defined two rules to try my best to identify them for general and normal situations:

Rule #1:

Request which contains “Cookie” Header with “ASP.NET_SessionIdAND its value is equal with server side, then it should be a normal user who has just visited my website within the one session.

Notes: there might be two exceptions for rule #1,

  1. If user’s browser has disabled Cookie then this rule will NOT be effective since the client request will never contain a Cookie header since the browser disabled it.
  2. Assume there is a crawler who crawls my website and accept storing cookie, then #1 will not be effective. However, I don’t think a crawler will firstly request a SessionID and then request again using the same SessionID).

Rule #2:

Define a crawler list and analyses whether “User-Agent” header contains one of them, the list should be configurable. Refer more Crawler example at: http://en.wikipedia.org/wiki/Web_crawler#Examples_of_Web_crawlers

Talk is cheap, show me the code, I wrote a method to identify crawlers by applying two rules above.

public static Boolean IsCrawlerRequest()
{
    // Rule 1: Request which contains "Cookie" Header with "ASP.NET_SessionId" and its value is equal with server side, 
    // then it should be a normal user (except maliciously forging, I don't think a crawler will firstly request a sessionID and then request again with the SessionID).
    //if (HttpContext.Current.Request.Cookies["ASP.NET_SessionId"] != null
    //    && HttpContext.Current.Request.Cookies["ASP.NET_SessionId"].Value == HttpContext.Current.Session.SessionID)
    if (HttpContext.Current.Request.Headers["Cookie"] != null
        && HttpContext.Current.Request.Headers["Cookie"].Contains("ASP.NET_SessionId"))
        return false;  // Should be a normal user browsing my website using a browser.

    // Rule 2: define a crawler list and analyses whether "User-Agent" header contains one of them, this should be configurable
    // Refer more Crawler example at: http://en.wikipedia.org/wiki/Web_crawler#Examples_of_Web_crawlers
    var crawlerList = new String[] { "google", "bing", "msn", "yahoo", "baidu", "soso", "sogou", "youdao" };

    if (!String.IsNullOrEmpty(HttpContext.Current.Request.UserAgent))
        foreach (String bot in crawlerList)
            if (HttpContext.Current.Request.UserAgent.ToLower(CultureInfo.InvariantCulture).Contains(bot))
                return true; // It is a crawler

    return false;
}

Please be aware that I commented out HttpContext.Current.Request.Cookies["ASP.NET_SessionId"] != null, since I found that Request.Cookie will ALWAYS contain “ASP.NET_SessionId” EVENT IF the browser disabled Cookie storing, I will do further investigation and double check later!

Ok, now we get normal users’ IP Addresses and filtered search engine crawlers, the next step is invoking InfoDB API to “translate” IP Address to Geolocation, you need register an API KEY here, and then submit an HTTP GET request to:

http://api.ipinfodb.com/v2/ip_query.php?key=%5BAPI KEY]&ip=[IP Address]&timezone=false

It returns XML below, I take IP=”117.136.8.14″ for example:

<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Status>OK</Status>
  <CountryCode>CN</CountryCode>
  <CountryName>China</CountryName>
  <RegionCode>23</RegionCode>
  <RegionName>Shanghai</RegionName>
  <City>Shanghai</City>
  <ZipPostalCode></ZipPostalCode>
  <Latitude>31.005</Latitude>
  <Longitude>121.409</Longitude>
  <Timezone>0</Timezone>
  <Gmtoffset>0</Gmtoffset>
  <Dstoffset>0</Dstoffset>
  <TimezoneName></TimezoneName>
  <Isdst></Isdst>
  <Ip>117.136.8.14</Ip>
</Response>

Wow, looks precise:), I am going to show visitor’s geolocation on Google Map (I know this compromises visitor’s privacy but my personal blog http://WayneYe.com is not a company and I will NEVER earn a cent by doing this:)).

Anyway, I use the latest Google Map JavaScript API V3, and there are two major functionalities:

1. Display visitor’s Geolocation as long as user’s browser support “navigator.geolocation” property (Google Chrome, Mozilla Filefox support it, IE not support), default location will be set to New York City if the browser does not support this W3 recommended standard, a sample below (I used Google Chrome):

VisitorInfo

I am now living in Shanghai

2. Display a specified blog’s visitors’ geolocations on Google Map, screenshot below shows the visitors’ geolocations who visited my blog: My new Dev box – HP Z800 Workstation, by clicking each geolocation, it will show on Google Map.

Visitors of <My new Dev box - HP Z800 Workstation>

Visitors of <My new Dev box - HP Z800 Workstation>

The JavaScript code showing below:

<script type="text/javascript">
    var initialLocation;
    var newyork = new google.maps.LatLng(40.69847032728747, -73.9514422416687);
    var browserSupportFlag = new Boolean();
    var map;
    var myOptions
    var infowindow = new google.maps.InfoWindow();

    function initialize() {
        myOptions = {
            zoom: 6,
            mapTypeId: google.maps.MapTypeId.ROADMAP
        };
        map = new google.maps.Map(document.getElementById("googleMapContainer"), myOptions);

        // Try W3C Geolocation (Preferred)
        if (navigator.geolocation) {
            browserSupportFlag = true;
            navigator.geolocation.getCurrentPosition(function (position) {
                map.setCenter(new google.maps.LatLng(position.coords.latitude, position.coords.longitude));
                infowindow.setContent('Hi, dear WayneYe.com visitor! You are here:)');
                infowindow.setPosition(new google.maps.LatLng(position.coords.latitude, position.coords.longitude));
                infowindow.open(map);
            }, function () {
                handleNoGeolocation(browserSupportFlag);
            });
        } else {
            browserSupportFlag = false;
            handleNoGeolocation(browserSupportFlag);
        }

        function handleNoGeolocation(errorFlag) {
            //contentString = 'Cannot track your location, default to New York City.';

            map.setCenter(newyork);
            //infowindow.setContent(contentString);
            //infowindow.setPosition(newyork);
            infowindow.open(map);
        }
    }

    function setGoogleMapLocation(geoLocation, latitude, longitude) {
        contentString = geoLocation;

        var visitorLocation = new google.maps.LatLng(latitude, longitude);

        map.setCenter(visitorLocation);
        infowindow.setContent(contentString);
        infowindow.setPosition(visitorLocation);
        infowindow.open(map);
    }
</script>

My visitor record page is: http://wayneye.com/VisitRecord.

Advertisements

Atom Syndication Feed – RSS replacer

These days I noticed a new syndication format – Atom, I saw it in a number of web sites I always use: such as MSDN, Twitter, Google Reader and so on, I was thinking whether I need update my life blog (WayneYe.com) to support Atom.

By investigating, I learnt:

The name Atom applies to a pair of related standards. The Atom Syndication Format is an XML language used for web feeds, while the Atom Publishing Protocol (AtomPub or APP) is a simple HTTP-based protocol for creating and updating web resources.

Here is a typical Aom example copied from IETF RFC4287:

<?xml version="1.0" encoding="utf-8"?>
 <feed xmlns="<a href="http://www.w3.org/2005/Atom%22">http://www.w3.org/2005/Atom"</a>>
 <title type="text">dive into mark</title>
 <subtitle type="html">
 A  &lt;em&gt;lot&lt;/em&gt; of effort
 went into making  this effortless
 </subtitle>
 <updated>2005-07-31T12:29:29Z</updated>
 <id>tag:example.org,2003:3</id>
 <link rel="alternate"  type="text/html"
 hreflang="en" href="<a href="http://example.org/%22/">http://example.org/"/</a>>
 <link  rel="self" type="application/atom+xml"
 href="<a href="http://example.org/feed.atom%22/">http://example.org/feed.atom"/</a>>
 <rights>Copyright (c) 2003, Mark Pilgrim</rights>
 <generator uri="<a href="http://www.example.com/%22">http://www.example.com/"</a> version="1.0">
 Example Toolkit
 </generator>
 <entry>
 <title>Atom draft-07 snapshot</title>
 <link  rel="alternate" type="text/html"
 href="<a href="http://example.org/2005/04/02/atom%22/">http://example.org/2005/04/02/atom"/</a>>
 <link rel="enclosure" type="audio/mpeg" length="1337"
 href="<a href="http://example.org/audio/ph34r_my_podcast.mp3%22/">http://example.org/audio/ph34r_my_podcast.mp3"/</a>>
 <id>tag:example.org,2003:3.2397</id>
 <updated>2005-07-31T12:29:29Z</updated>
 <published>2003-12-13T08:29:29-04:00</published>
 <author>
 <name>Mark Pilgrim</name>
 <uri><a href="http://example.org/">http://example.org/</a></uri>
 <email>f8dy@example.com</email>
 </author>
 <contributor>
 <name>Sam  Ruby</name>
 </contributor>
 <contributor>
 <name>Joe Gregorio</name>
 </contributor>
 <content type="xhtml" xml:lang="en"
 xml:base="<a href="http://diveintomark.org/%22">http://diveintomark.org/"</a>>
 <div xmlns="<a href="http://www.w3.org/1999/xhtml%22">http://www.w3.org/1999/xhtml"</a>>
 <p><i>[Update: The Atom draft is  finished.]</i></p>
 </div>
 </content>
 </entry>
 </feed>

Why Atom?

I found a grate comparison article from iis.net – The world of Syndication: Atom 1.0 vs. RSS 2.0?

Atom 1.0 RSS 2.0 Who wins?
Content Model Allows text, escaped HTML, well-formed XHTML, XML, base-64 encoded binary or pointer to web content outside the feed. Text or Escaped HTML This is a major advantage for Atom, writing escaped HTML affects the readability of RSS feeds
Partial Content Has separate <summary> or<content> tags. Has a <description> field. This could contain complete content or just synopsis but has no way of identifying what it contains. This is another win for Atom, helps you have a synopsis and complete view inside a feed reader
Auto Discovery Uses the MIME Type application/atom+xml which is registered with IANA. In addition the feed has a self link to enable auto subscription in readers The MIME Typeapplication/rss+xml is often used but not recognized by IANA Having a registered MIME Type and auto subscription really helps discovery. Another Atom win.
Format Flexibility Atom syndication format allows entries linking to the feed or standalone entries RSS only recognizes a <rss>document. This is also an Atom win, having standalone entries enables scenarios like linking to entries only.
Extensibility Atom defines a well-defined structure to extend the default namespace and has specific guidelines on readers should interpret these. RSS has to fixed namespace, however you could include external XML namespaces. Atom has a slight advantage here as external namespaces extensions in RSS are not easily discoverable. In Atom’s case these extensions have a corresponding namespace tag
Languages Atom uses xml:lang for language specificity RSS has a separate<language> tag for language specificity No one.
Encryption Atom allows the option to encrypt entries using XML Encryption orXML Digital Signature. In addition the feed could be encrypted entire feed using standard web encryption techniques RSS only allows encrypting using standard web encryption techniques. Atom has extra level of encryption possible.
Adoption Most sites that publish both RSS and Atom feeds. Most sites that publish both RSS and Atom feeds. RSS has an advantage of being the first mover, the term RSS has become synonymous with syndication.
Modularity All elements part of Atom’s namespace can be used outside Atom’s context due to way it was designed RSS elements cannot be used outside the context of RSS This has proved to be the one of the major factors of Atom’s popularity and its used in data interactions like those enabled by Google GData

As a conclusion, Atom has a number of advantages compares to RSS format, it has been adopted by Microsoft, Google, Twitter and many other famous companied/organizations, and I believe Atom will be more and more popular in future. I take Twitter search API as an Atom application example, Twitter provided a search API which supports Atom as returned data type:

http://search.twitter.com/search.atom?q={search keyword}

While I try: http://search.twitter.com/search.atom?q=wayne, it returns:

<table>
<tbody>
<tr>
<td></td>
<td><?xml version="1.0" encoding="UTF-8"?></td>
</tr>
<tr>
<td></td>
<td><feed xmlns:google="<a href="http://base.google.com/ns/1.0">http://base.google.com/ns/1.0</a>" xml:lang="en-US" xmlns:openSearch="<a href="http://a9.com/-/spec/opensearch/1.1/">http://a9.com/-/spec/opensearch/1.1/</a>" xmlns="<a href="http://www.w3.org/2005/Atom">http://www.w3.org/2005/Atom</a>" xmlns:twitter="<a href="http://api.twitter.com/">http://api.twitter.com/</a>"></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:search/wayne</id></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://search.twitter.com/search?q=wayne">http://search.twitter.com/search?q=wayne</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><link type="application/atom+xml" href="<a href="http://search.twitter.com/search.atom?q=wayne">http://search.twitter.com/search.atom?q=wayne</a>" rel="self"/></td>
</tr>
<tr>
<td></td>
<td><title>wayne - Twitter Search</title></td>
</tr>
<tr>
<td></td>
<td><link type="application/opensearchdescription+xml" href="<a href="http://search.twitter.com/opensearch.xml">http://search.twitter.com/opensearch.xml</a>" rel="search"/></td>
</tr>
<tr>
<td></td>
<td><link type="application/atom+xml" href="<a href="http://search.twitter.com/search.atom?q=wayne&amp;since_id=29556261324">http://search.twitter.com/search.atom?q=wayne&amp;since_id=29556261324</a>" rel="refresh"/></td>
</tr>
<tr>
<td></td>
<td><twitter:warning>since_id removed for pagination.</twitter:warning></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:44:22Z</updated></td>
</tr>
<tr>
<td></td>
<td><openSearch:itemsPerPage>15</openSearch:itemsPerPage></td>
</tr>
<tr>
<td></td>
<td><link type="application/atom+xml" href="<a href="http://search.twitter.com/search.atom?max_id=29556261324&amp;page=2&amp;q=wayne">http://search.twitter.com/search.atom?max_id=29556261324&amp;page=2&amp;q=wayne</a>" rel="next"/></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556261324</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:44:22Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/luuanaguerra/statuses/29556261324">http://twitter.com/luuanaguerra/statuses/29556261324</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>lollipop - lil wayne #np</title></td>
</tr>
<tr>
<td></td>
<td><content type="html">lollipop - lil &lt;b&gt;wayne&lt;/b&gt; &lt;a href=&quot;<a href="http://search.twitter.com/search?q=%23np&quot;">http://search.twitter.com/search?q=%23np&quot;</a> onclick=&quot;pageTracker._setCustomVar(2, 'result_type', 'recent', 3);pageTracker._trackPageview('/intra/hashtag/#np');&quot;&gt;#np&lt;/a&gt;</content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:44:22Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a1.twimg.com/profile_images/1158340569/niver_de_lu4_normal.jpg">http://a1.twimg.com/profile_images/1158340569/niver_de_lu4_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://blackberry.com/twitter&quot;">http://blackberry.com/twitter&quot;</a> rel=&quot;nofollow&quot;&gt;Twitter for BlackBerry®&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>nl</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>luuanaguerra (Luana Guerra)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/luuanaguerra">http://twitter.com/luuanaguerra</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556257642</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:44:17Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/TreyAG/statuses/29556257642">http://twitter.com/TreyAG/statuses/29556257642</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>RT @tashabeee: hi @birdman5star please stop making songs with wayne. im tired of skipping your verse. thanks bye</title></td>
</tr>
<tr>
<td></td>
<td><content type="html">RT &lt;a href=&quot;<a href="http://twitter.com/tashabeee&quot;&gt;@tashabeee&lt;/a&gt;:">http://twitter.com/tashabeee&quot;&gt;@tashabeee&lt;/a&gt;:</a> hi &lt;a href=&quot;<a href="http://twitter.com/birdman5star&quot;&gt;@birdman5star&lt;/a&gt;">http://twitter.com/birdman5star&quot;&gt;@birdman5star&lt;/a&gt;</a> please stop making songs with &lt;b&gt;wayne&lt;/b&gt;. im tired of skipping your verse. thanks bye</content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:44:17Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a3.twimg.com/profile_images/1138081399/992a2800-f56e-42e4-a23c-59416c48a0e1_normal.png">http://a3.twimg.com/profile_images/1138081399/992a2800-f56e-42e4-a23c-59416c48a0e1_normal.png</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://twitter.com/&quot;&gt;web&lt;/a&gt;">http://twitter.com/&quot;&gt;web&lt;/a&gt;</a></twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>TreyAG (Tremaine Garrison )</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/TreyAG">http://twitter.com/TreyAG</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556251186</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:44:09Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/EventsTBrox/statuses/29556251186">http://twitter.com/EventsTBrox/statuses/29556251186</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>Big Media USA Announces the Addition of Wayne Gurnick's Wedding Planning ... <a href="http://bit.ly/94NlG3">http://bit.ly/94NlG3</a></title></td>
</tr>
<tr>
<td></td>
<td><content type="html">Big Media USA Announces the Addition of &lt;b&gt;Wayne&lt;/b&gt; Gurnick&amp;apos;s Wedding Planning ... &lt;a href=&quot;<a href="http://bit.ly/94NlG3&quot;&gt;http://bit.ly/94NlG3&lt;/a&gt;">http://bit.ly/94NlG3&quot;&gt;http://bit.ly/94NlG3&lt;/a&gt;</a></content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:44:09Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a3.twimg.com/profile_images/1094782987/T-Brox_logo_-_less_than_30kb_normal.jpg">http://a3.twimg.com/profile_images/1094782987/T-Brox_logo_-_less_than_30kb_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://twitterfeed.com%26quot%3B/">http://twitterfeed.com&quot;</a> rel=&quot;nofollow&quot;&gt;twitterfeed&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>EventsTBrox (Events T-Brox)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/EventsTBrox">http://twitter.com/EventsTBrox</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556250780</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:44:09Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/cnsrvativeNurse/statuses/29556250780">http://twitter.com/cnsrvativeNurse/statuses/29556250780</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>Gov. Rick Perry set to take next step, onto national stage: By WAYNE SLATER / The Dallas Morning News The Republic... <a href="http://bit.ly/aflxjk">http://bit.ly/aflxjk</a></title></td>
</tr>
<tr>
<td></td>
<td><content type="html">Gov. Rick Perry set to take next step, onto national stage: By &lt;b&gt;WAYNE&lt;/b&gt; SLATER / The Dallas Morning News The Republic... &lt;a href=&quot;<a href="http://bit.ly/aflxjk&quot;&gt;http://bit.ly/aflxjk&lt;/a&gt;">http://bit.ly/aflxjk&quot;&gt;http://bit.ly/aflxjk&lt;/a&gt;</a></content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:44:09Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a3.twimg.com/profile_images/993100283/090626-005422-030010_normal.jpg">http://a3.twimg.com/profile_images/993100283/090626-005422-030010_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://twitterfeed.com%26quot%3B/">http://twitterfeed.com&quot;</a> rel=&quot;nofollow&quot;&gt;twitterfeed&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>cnsrvativeNurse (jt)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/cnsrvativeNurse">http://twitter.com/cnsrvativeNurse</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556247353</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:44:05Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/enricop/statuses/29556247353">http://twitter.com/enricop/statuses/29556247353</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>RT @mustbeloaded: if i end up like ti...wayne...gucci...and boosie......just make sure you get one of the black t's.....#FREETRELL</title></td>
</tr>
<tr>
<td></td>
<td><content type="html">RT &lt;a href=&quot;<a href="http://twitter.com/mustbeloaded&quot;&gt;@mustbeloaded&lt;/a&gt;:">http://twitter.com/mustbeloaded&quot;&gt;@mustbeloaded&lt;/a&gt;:</a> if i end up like ti...&lt;b&gt;wayne&lt;/b&gt;...gucci...and boosie......just make sure you get one of the black t&amp;apos;s.....&lt;a href=&quot;<a href="http://search.twitter.com/search?q=%23FREETRELL&quot;">http://search.twitter.com/search?q=%23FREETRELL&quot;</a> onclick=&quot;pageTracker._setCustomVar(2, 'result_type', 'recent', 3);pageTracker._trackPageview('/intra/hashtag/#FREETRELL');&quot;&gt;#FREETRELL&lt;/a&gt;</content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:44:05Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a3.twimg.com/profile_images/678943055/ENRICOP_normal.jpg">http://a3.twimg.com/profile_images/678943055/ENRICOP_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://twitter.com/&quot;&gt;web&lt;/a&gt;">http://twitter.com/&quot;&gt;web&lt;/a&gt;</a></twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>enricop (Kellogg Lieberbaum)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/enricop">http://twitter.com/enricop</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556236691</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:52Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/JulesStreething/statuses/29556236691">http://twitter.com/JulesStreething/statuses/29556236691</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>dwayne wayne shades .... Minus the frames <a href="http://plixi.com/p/54700453">http://plixi.com/p/54700453</a></title></td>
</tr>
<tr>
<td></td>
<td><content type="html">dwayne &lt;b&gt;wayne&lt;/b&gt; shades .... Minus the frames &lt;a href=&quot;<a href="http://plixi.com/p/54700453&quot;&gt;http://plixi.com/p/54700453&lt;/a&gt;">http://plixi.com/p/54700453&quot;&gt;http://plixi.com/p/54700453&lt;/a&gt;</a></content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:52Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a0.twimg.com/profile_images/435091824/juice_normal.jpg">http://a0.twimg.com/profile_images/435091824/juice_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://www.ubertwitter.com/bb/download.php&quot;">http://www.ubertwitter.com/bb/download.php&quot;</a> rel=&quot;nofollow&quot;&gt;ÜberTwitter&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>JulesStreething (Julian Loh)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/JulesStreething">http://twitter.com/JulesStreething</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556236309</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:51Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/Sheila464/statuses/29556236309">http://twitter.com/Sheila464/statuses/29556236309</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>I have, twice, and will be attending in Fort Wayne, IN on Dec. 1st.  Awesome show!!  Thx Sharon!</title></td>
</tr>
<tr>
<td></td>
<td><content type="html">I have, twice, and will be attending in Fort &lt;b&gt;Wayne&lt;/b&gt;, IN on Dec. 1st.  Awesome show!!  Thx Sharon!</content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:51Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a0.twimg.com/profile_images/1105408424/IMG_0404_normal.JPG">http://a0.twimg.com/profile_images/1105408424/IMG_0404_normal.JPG</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;/devices&quot; rel=&quot;nofollow&quot;&gt;txt&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>Sheila464 (Sheila Harting)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/Sheila464">http://twitter.com/Sheila464</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556233419</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:47Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/musicheadlines/statuses/29556233419">http://twitter.com/musicheadlines/statuses/29556233419</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>President Clinton Comments On Lil Wayne's Pending Release From Prison <a href="http://bit.ly/bpZYqg">http://bit.ly/bpZYqg</a></title></td>
</tr>
<tr>
<td></td>
<td><content type="html">President Clinton Comments On Lil &lt;b&gt;Wayne&lt;/b&gt;&amp;apos;s Pending Release From Prison &lt;a href=&quot;<a href="http://bit.ly/bpZYqg&quot;&gt;http://bit.ly/bpZYqg&lt;/a&gt;">http://bit.ly/bpZYqg&quot;&gt;http://bit.ly/bpZYqg&lt;/a&gt;</a></content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:47Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a0.twimg.com/profile_images/1117218416/lmh_normal.jpg">http://a0.twimg.com/profile_images/1117218416/lmh_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://www.zepan.org/software/tweetly-updater/&quot;">http://www.zepan.org/software/tweetly-updater/&quot;</a> rel=&quot;nofollow&quot;&gt;Tweetly Updater&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>musicheadlines (LatestMusicHeadlines)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/musicheadlines">http://twitter.com/musicheadlines</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556229269</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:42Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/ruthlessblogs/statuses/29556229269">http://twitter.com/ruthlessblogs/statuses/29556229269</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>Lil Wayne Has Another Legal Issue To Address Post-Prison <a href="http://f.ast.ly/f65yS">http://f.ast.ly/f65yS</a></title></td>
</tr>
<tr>
<td></td>
<td><content type="html">Lil &lt;b&gt;Wayne&lt;/b&gt; Has Another Legal Issue To Address Post-Prison &lt;a href=&quot;<a href="http://f.ast.ly/f65yS&quot;&gt;http://f.ast.ly/f65yS&lt;/a&gt;">http://f.ast.ly/f65yS&quot;&gt;http://f.ast.ly/f65yS&lt;/a&gt;</a></content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:42Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a3.twimg.com/profile_images/1094935523/RuthlessBlogs_Logo_no_Title2_normal.png">http://a3.twimg.com/profile_images/1094935523/RuthlessBlogs_Logo_no_Title2_normal.png</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://www.linksalpha.com/&quot;">http://www.linksalpha.com/&quot;</a> rel=&quot;nofollow&quot;&gt;LinksAlpha&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>ruthlessblogs (RuthlessBlogs)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/ruthlessblogs">http://twitter.com/ruthlessblogs</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556228815</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:42Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/michanwoo7/statuses/29556228815">http://twitter.com/michanwoo7/statuses/29556228815</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>RT @san_e: lil wayne's 'im not a human being'...dang this album makes me wanna rap so bad! lyrics are just crazy</title></td>
</tr>
<tr>
<td></td>
<td><content type="html">RT &lt;a href=&quot;<a href="http://twitter.com/san_e&quot;&gt;@san_e&lt;/a&gt;:">http://twitter.com/san_e&quot;&gt;@san_e&lt;/a&gt;:</a> lil &lt;b&gt;wayne&lt;/b&gt;&amp;apos;s &amp;apos;im not a human being&amp;apos;...dang this album makes me wanna rap so bad! lyrics are just crazy</content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:42Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a1.twimg.com/profile_images/1154790897/184359626_normal.gif">http://a1.twimg.com/profile_images/1154790897/184359626_normal.gif</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://twitter.com/&quot;&gt;web&lt;/a&gt;">http://twitter.com/&quot;&gt;web&lt;/a&gt;</a></twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>michanwoo7 (Michelle Anne Kam)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/michanwoo7">http://twitter.com/michanwoo7</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556223216</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:35Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/bobo_16/statuses/29556223216">http://twitter.com/bobo_16/statuses/29556223216</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>RT @iamorezi: OK ...LIL WAYNE WE R  WAITING FOR U OH!!!! ....IF U LOVE WEEZY ..LET ME HEAR U SAY OH OH OH OH!!! LOLZ ...ON TOP FORM DIS MORNING</title></td>
</tr>
<tr>
<td></td>
<td><content type="html">RT &lt;a href=&quot;<a href="http://twitter.com/iamorezi&quot;&gt;@iamorezi&lt;/a&gt;:">http://twitter.com/iamorezi&quot;&gt;@iamorezi&lt;/a&gt;:</a> OK ...LIL &lt;b&gt;WAYNE&lt;/b&gt; WE R  WAITING FOR U OH!!!! ....IF U LOVE WEEZY ..LET ME HEAR U SAY OH OH OH OH!!! LOLZ ...ON TOP FORM DIS MORNING</content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:35Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a2.twimg.com/profile_images/1143033238/IMG01062-20100919-1240_normal.jpg">http://a2.twimg.com/profile_images/1143033238/IMG01062-20100919-1240_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://blackberry.com/twitter&quot;">http://blackberry.com/twitter&quot;</a> rel=&quot;nofollow&quot;&gt;Twitter for BlackBerry®&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>bobo_16 (Bolanle Muda Lawal)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/bobo_16">http://twitter.com/bobo_16</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556215635</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:25Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/presidentclint/statuses/29556215635">http://twitter.com/presidentclint/statuses/29556215635</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>I favorited a YouTube video -- Lil Wayne - Boom (Zoom Remix) <a href="http://youtu.be/yhKfKV-H9FY?a">http://youtu.be/yhKfKV-H9FY?a</a></title></td>
</tr>
<tr>
<td></td>
<td><content type="html">I favorited a YouTube video -- Lil &lt;b&gt;Wayne&lt;/b&gt; - Boom (Zoom Remix) &lt;a href=&quot;<a href="http://youtu.be/yhKfKV-H9FY?a&quot;&gt;http://youtu.be/yhKfKV-H9FY?a&lt;/a&gt;">http://youtu.be/yhKfKV-H9FY?a&quot;&gt;http://youtu.be/yhKfKV-H9FY?a&lt;/a&gt;</a></content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:25Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a2.twimg.com/profile_images/1144930262/Green_Sea_Turtle_normal.jpg">http://a2.twimg.com/profile_images/1144930262/Green_Sea_Turtle_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://www.google.com/support/youtube/bin/answer.py?hl=en&amp;answer=164577&quot;">http://www.google.com/support/youtube/bin/answer.py?hl=en&amp;answer=164577&quot;</a> rel=&quot;nofollow&quot;&gt;Google&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>presidentclint (Clint Rodefer)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/presidentclint">http://twitter.com/presidentclint</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556215496</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:25Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/Lyddongirl/statuses/29556215496">http://twitter.com/Lyddongirl/statuses/29556215496</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>&gt; lol “@WayneG1988: I should be at work running late in true wayne style! If I turnt up on time to anything (cont) <a href="http://tl.gd/6pm2qm">http://tl.gd/6pm2qm</a></title></td>
</tr>
<tr>
<td></td>
<td><content type="html">&amp;gt; lol “@WayneG1988: I should be at work running late in true &lt;b&gt;wayne&lt;/b&gt; style! If I turnt up on time to anything (cont) &lt;a href=&quot;<a href="http://tl.gd/6pm2qm&quot;&gt;http://tl.gd/6pm2qm&lt;/a&gt;">http://tl.gd/6pm2qm&quot;&gt;http://tl.gd/6pm2qm&lt;/a&gt;</a></content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:25Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a0.twimg.com/profile_images/1080680292/image_normal.jpg">http://a0.twimg.com/profile_images/1080680292/image_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://twitter.com/&quot;">http://twitter.com/&quot;</a> rel=&quot;nofollow&quot;&gt;Twitter for iPhone&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>Lyddongirl (Nicky Full of hope..)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/Lyddongirl">http://twitter.com/Lyddongirl</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556202617</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:08Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/presidentclint/statuses/29556202617">http://twitter.com/presidentclint/statuses/29556202617</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>I favorited a YouTube video -- Lil Wayne-&quot;Duffle Bag Boy&quot; &amp; &quot;Fireman&quot;-Uncut <a href="http://youtu.be/ZE0Xs-6kVLE?a">http://youtu.be/ZE0Xs-6kVLE?a</a></title></td>
</tr>
<tr>
<td></td>
<td><content type="html">I favorited a YouTube video -- Lil &lt;b&gt;Wayne&lt;/b&gt;-&amp;quot;Duffle Bag Boy&amp;quot; &amp;amp; &amp;quot;Fireman&amp;quot;-Uncut &lt;a href=&quot;<a href="http://youtu.be/ZE0Xs-6kVLE?a&quot;&gt;http://youtu.be/ZE0Xs-6kVLE?a&lt;/a&gt;">http://youtu.be/ZE0Xs-6kVLE?a&quot;&gt;http://youtu.be/ZE0Xs-6kVLE?a&lt;/a&gt;</a></content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:08Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a2.twimg.com/profile_images/1144930262/Green_Sea_Turtle_normal.jpg">http://a2.twimg.com/profile_images/1144930262/Green_Sea_Turtle_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://www.google.com/support/youtube/bin/answer.py?hl=en&amp;answer=164577&quot;">http://www.google.com/support/youtube/bin/answer.py?hl=en&amp;answer=164577&quot;</a> rel=&quot;nofollow&quot;&gt;Google&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>en</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>presidentclint (Clint Rodefer)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/presidentclint">http://twitter.com/presidentclint</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td><entry></td>
</tr>
<tr>
<td></td>
<td><id>tag:search.twitter.com,2005:29556200416</id></td>
</tr>
<tr>
<td></td>
<td><published>2010-11-03T09:43:05Z</published></td>
</tr>
<tr>
<td></td>
<td><link type="text/html" href="<a href="http://twitter.com/Xizou/statuses/29556200416">http://twitter.com/Xizou/statuses/29556200416</a>" rel="alternate"/></td>
</tr>
<tr>
<td></td>
<td><title>RT @OfficialGuzzy: Nao entendo a galera q fala q o Lil Wayne,Drake,Eminem nao mandam bem na rima...se vc e um,ou nao entende Ingles ou nao entende RAP #rapBR</title></td>
</tr>
<tr>
<td></td>
<td><content type="html">RT &lt;a href=&quot;<a href="http://twitter.com/OfficialGuzzy&quot;&gt;@OfficialGuzzy&lt;/a&gt;:">http://twitter.com/OfficialGuzzy&quot;&gt;@OfficialGuzzy&lt;/a&gt;:</a> Nao entendo a galera q fala q o Lil &lt;b&gt;Wayne&lt;/b&gt;,Drake,Eminem nao mandam bem na rima...se vc e um,ou nao entende Ingles ou nao entende RAP &lt;a href=&quot;<a href="http://search.twitter.com/search?q=%23rapBR&quot;">http://search.twitter.com/search?q=%23rapBR&quot;</a> onclick=&quot;pageTracker._setCustomVar(2, 'result_type', 'recent', 3);pageTracker._trackPageview('/intra/hashtag/#rapBR');&quot;&gt;#rapBR&lt;/a&gt;</content></td>
</tr>
<tr>
<td></td>
<td><updated>2010-11-03T09:43:05Z</updated></td>
</tr>
<tr>
<td></td>
<td><link type="image/png" href="<a href="http://a2.twimg.com/profile_images/535869450/twitterProfilePhoto_normal.jpg">http://a2.twimg.com/profile_images/535869450/twitterProfilePhoto_normal.jpg</a>" rel="image"/></td>
</tr>
<tr>
<td></td>
<td><twitter:geo></td>
</tr>
<tr>
<td></td>
<td></twitter:geo></td>
</tr>
<tr>
<td></td>
<td><twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:result_type>recent</twitter:result_type></td>
</tr>
<tr>
<td></td>
<td></twitter:metadata></td>
</tr>
<tr>
<td></td>
<td><twitter:source>&lt;a href=&quot;<a href="http://www.ubertwitter.com/bb/download.php&quot;">http://www.ubertwitter.com/bb/download.php&quot;</a> rel=&quot;nofollow&quot;&gt;ÜberTwitter&lt;/a&gt;</twitter:source></td>
</tr>
<tr>
<td></td>
<td><twitter:lang>pt</twitter:lang></td>
</tr>
<tr>
<td></td>
<td><author></td>
</tr>
<tr>
<td></td>
<td><name>Xizou (XIzou)</name></td>
</tr>
<tr>
<td></td>
<td><uri><a href="http://twitter.com/Xizou">http://twitter.com/Xizou</a></uri></td>
</tr>
<tr>
<td></td>
<td></author></td>
</tr>
<tr>
<td></td>
<td></entry></td>
</tr>
<tr>
<td></td>
<td></feed></td>
</tr>
</tbody>
</table>

Coding it

Now I realized that Atom should be a trend, so I decide to support Atom in my life blog (WayneYe.com) developed by myself.  I found there are already some great libraries doing such kind of things such as Atom.NET, however, the pity is most of them seemed seriously out of date… So I decide do it myself as usualSmile

I supposed to manually created an “Atom.xml” file and expose it to web site, in additional, I hope it is RESTful, I use this:
http://WayneYe.com/Atom

I wrote C# code snippet below to generate and CRUD the Atom.xml, Blog is an O/R Mapping entity class written by me, it is nothing special with some properties:
BlogClass

Constants definition:

        public const String AtomTitle = "Wayne的自由空间";
        public const String AtomSubtitle = "Wayne's Atom Syndication Feed";
        public const String AtomNamespace = "http://www.w3.org/2005/Atom";
        public const String AtomUri = "http://wayneye.com/Atom";
        public const String AtomAlternateUri = "http://wayneye.com/Feeds/Atom.xml";
        public const int MaximumEntryCount = 15;

Regenerate Atom Xml Feed:

        public static void RegenerateAtom()
        {
            if (File.Exists(AtomHandler.AtomFilePath)) File.Delete(AtomHandler.AtomFilePath);

            XNamespace atomXmln = AtomNamespace; // Declare the Atom XML namespace first

            XDocument atomXml = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
                new XElement(atomXmln + "feed", new XAttribute(XNamespace.Xml + "lang", "zh-CN"),
                    new XElement(atomXmln + "title", new XAttribute("type", "html"), AtomTitle),
                    new XElement(atomXmln + "subtitle", new XAttribute("type", "html"), AtomSubtitle),
                    new XElement(atomXmln + "id", AtomUri),
                    new XElement(atomXmln + "link", new XAttribute("rel", "alternate"), new XAttribute("type", "text/html"), new XAttribute("href", Constants.WayneYeUri)),
                    new XElement(atomXmln + "link", new XAttribute("rel", "self"), new XAttribute("type", "application/atom+xml"), new XAttribute("href", AtomAlternateUri)),
                    new XElement(atomXmln + "updated", DateTime.Now.ToUniversalTime())));

            var entries = new List();
            var entryCount = 0;
            foreach (var blog in Blog.LoadAllBlogs(BlogVisibility.Public, false))
            {
                blog.HyperLink = String.Format("{0}ViewBlog.aspx?BlogID={1}", WayneBlogMaster.ServerRoot, blog.Id);

                if (entryCount == MaximumEntryCount) break; // Maximum entry count is 15 by default

                entries.Add(ConvertBlogToAtomEntry(blog, false));

                entryCount++;
            }

            atomXml.Root.Add(entries);

            atomXml.Save(AtomHandler.AtomFilePath);
        }

Create new blog entry

        public static void AddBlogEntry(Blog b)
        {
                XNamespace atomXmln = AtomNamespace;
                XDocument atomXml = XDocument.Load(AtomFilePath);

                XElement newEntry = ConvertBlogToAtomEntry(b, false);

                atomXml.Root.LastNode.Remove(); // Remove oldest blog entry

                // Insert this new blog entry at top position
                // NOTES: while querying, I need explicitly specify the element's namespace
                atomXml.Root.Elements(atomXmln + "entry").First().AddBeforeSelf(newEntry);

                atomXml.Save(AtomFilePath);
        }

Update an existing blog entry

        public static void UpdateBlogEntry(Blog b)
        {
                XNamespace atomXmln = AtomNamespace;
                var atomXml = XDocument.Load(AtomFilePath);

                var newEntry = ConvertBlogToAtomEntry(b, true);

                if (atomXml.Root.Descendants(atomXmln + "entry").Any(
                    xe => xe.Element(atomXmln + "id").Value.Equals(b.HyperLink)))  // Check entry existense
                {
                    var blogEntry =
                        atomXml.Root.Descendants(atomXmln + "entry").Single(
                            xe => xe.Element(atomXmln + "id").Value.Equals(b.HyperLink));

                    blogEntry.ReplaceWith(newEntry);  // Replace the entried entry node

                    atomXml.Save(AtomFilePath);
                }
                else
                {
                    // Do nothing since this blog which supposed to be editted does not exist in the Atom.xml
                }
        }

Important Notes

1. You might noticed that in my generating Atom code I explicitly specify XML Namespace to every child nodes, actually this is NOT redundant, if I don’t do that, the generated XML would like below:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<feed xml:lang="zh-CN" xmlns="http://www.w3.org/2005/Atom">
  <title type="html" xmlns="">Wayne的自由空间</title>
  <subtitle type="html" xmlns="">Wayne's Atom Syndication Feed</subtitle>
  <id xmlns="">http://wayneye.com/Atom</id>
... ...

2. Similar tricky like #1 above, while doing Linq query, I MUST specify XML Namespace explicitly too, otherwise I couldn’t get any result.
3. I noticed that most Atom feeds contain ONLY 15 entries, so far I didn’t find out the reason for this, I just simply follow them, i.e. I also let my Atom feed contains 15 entries, that’s why I raised:
public const int MaximumEntryCount = 15;  // Of course I can store this value inside config file

Updated on Nov 3rd 2010, I also noticed that WCF 4.0 is officially supporting Atom, it is called Syndication Service Library: http://msdn.microsoft.com/en-us/library/bb412203.aspx

In Windows Communication Foundation (WCF), syndication feeds are modeled as another type of service operation, one where the return type is one of the derived classes of SyndicationFeedFormatter. The retrieval of a feed is modeled as a request-response message exchange. A client sends a request to the service and the service responds. The request message is set over an infrastructure protocol (for example, raw HTTP) and the response message contains a payload that consists of a commonly understood syndication format (RSS 2.0 or Atom 1.0). Services that implement these message exchanges are referred to as syndication services.

Hum, I will study it some day..

References

Atom Wiki page:
http://en.wikipedia.org/wiki/Atom_(standard)

IETF – The Atom Syndication Format
http://tools.ietf.org/html/rfc4287

W3C Feed Validation Service
http://validator.w3.org/feed/

A Short Introduction to the Atom Publishing Protocol
http://exist.sourceforge.net/atompub.html

Google provided URL to convert RSS to Atom
http://www.google.com/reader/atom/feed/{url-to-convert}

RSS/Atom libraries
RSS.NET http://www.rssdotnet.com/
Atom.NET http://atomnet.sourceforge.net/
ASP.Net RSS Toolkit http://aspnetrsstoolkit.codeplex.com/