Main Page
 The gatekeeper of reality is
 quantified imagination.

Stay notified when site changes by adding your email address:

Your Email:

Bookmark and Share
Email Notification
Project "Facebook Wall Feed 2"
Purpose
The purpose of this project is to demonstrate how to create a feed from a public group wall on Facebook in ASP.NET (unfortunately I've not had time to write up equivalents in Perl or PHP). This particular version compensates the new Facebook timeline so the crawler can retrieve content.

Introduction
This page will show you how to generate an RSS (or simple XML) feed from the wall of a public group on Facebook WITHOUT using the Graph API offered by Facebook.

If you would like to see how to generate an RSS (or simple XML) feed from the wall of a public group on Facebook WITH the Graph API, see:
Introduction to Graph API
Using JSON-based Graph API to get Wall Posts

ASP.NET 3.5 Code:
<%@ Import Namespace="System" %>
<%@ Import Namespace="System.IO" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.Text" %>

<script runat="server">
    ' Call this page as: somepage.aspx?name=Group1&feedNumber=6
    ' name = the server cache object name
    ' feedNumber = number of posts on wall to accept

    ' Feed Customizaiton
    Dim discussionGroupPage As String = "http://www.facebook.com/PUBLIC-GROUP?sk=wall"		' URL of the public group wall
    Dim rssDescriptionLen As Integer = 60							' Number of characters to allow in the poster's post (description)
    Dim numberOfPostsShow As Integer = 6							' Number of posts on the wall to show
    Dim rssIncludeImg As Integer = 1								' 0 = do not include the thumbnail image of posters in description, 1 = include
    Dim rssIncludeHeader As Integer = 0                             				' 0 = do not include RSS provider data (below), 1 = include
    Dim rssShowErrors As Integer = 0								' 0 = do not show server-side errors with wall data retrieval, 1 = show
    Dim feed_title As String = "Your Feed Title"
    Dim feed_link As String = "http://www.facebook.com/PUBLIC-GROUP/"
    Dim feed_description As String = "Public Group Wall Feed"
    Dim feed_language As String = "en-us"
    Dim feed_pubdate As String = "20 Apr 2007 9:40:00 GMT"
    Dim feed_copyright As String = String.Empty
    Dim feed_webmaster As String = "you@yoursite.com"
    ' Maintenance
    Dim overrideCache As Integer = 0						    ' 1 = override referring to cache when testing (useful when you have more than one instance of this code running referring to the same cache object and others are loading a different instance), 0 = do not override
    Dim rssCacheClear As Integer = 0                                                ' 1 = clear server-cache, 0 = do not clear server-cache
    Dim rawDump As Integer = 0							    ' 1 = show fetched content without reorganization/formatting, 0 = do not show fetched content
    ' Do Not Use
    Dim crawlAgent As String = String.Empty
    Dim crawlError As String = String.Empty
    ' TRAP CONNECT ERRORS IN CRAWLPAGE
    Private Function crawlPage(ByVal URL As String) As String
	Dim buffSize As Integer = 2048
	Dim crawlOutput As String = String.Empty
	Dim crawlMethod As String = "GET"
	Dim crawlURL As String = URL
	Try
		Dim myRequest As HttpWebRequest = CType(WebRequest.Create(crawlURL), HttpWebRequest)
		myRequest.UserAgent = crawlAgent
		myRequest.Method = crawlMethod
		Dim myResponse As HttpWebResponse = CType(myRequest.GetResponse(), HttpWebResponse)
		Dim streamResponse As Stream = myResponse.GetResponseStream()
		Dim streamRead As New StreamReader(streamResponse)
		Dim readBuff(buffSize) As [Char]
		Dim lineStep As Integer = streamRead.Read(readBuff, 0, buffSize)
		While lineStep > 0
			Dim outputData As New [String](readBuff, 0, lineStep)
			crawlOutput = crawlOutput & outputData
			lineStep = streamRead.Read(readBuff, 0, buffSize)
		End While
		streamRead.Close()
		streamResponse.Close()
		myResponse.Close()
	Catch ex As Exception
		crawlError = Server.HtmlEncode(ex.Message)
	End Try
	Return (crawlOutput)
    End Function
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs)
	Dim feedContent As String = String.Empty
	Dim rssFeed As String = String.Empty
	' Querystring Data
	Dim feedName As String = CStr(Request.QueryString("name"))
	If Len(feedName) = 0 Then : feedName = "default" : End If
	' Handle Manual Cache Clearing
	If rssCacheClear = 0 Then
		Dim maxFeedsTotalAccept As Integer = CInt(Request.QueryString("feedNumber"))
		If maxFeedsTotalAccept <= 0 Then : maxFeedsTotalAccept = numberOfPostsShow : End If
		' Retrieve Page Since Cache Does Not Exist
		If HttpContext.Current.Cache(feedName) Is Nothing OR overrideCache = 1 OR rawDump = 1 Then
        		' Get User's Agent Data
			crawlAgent = Request.UserAgent
			' Load Wall Page
			Dim discussionPageData As String = crawlPage(discussionGroupPage)
			' Process Page
			if Len(crawlError) = 0 Then
				' Filter Content
				discussionPageData = Replace(discussionPageData, vbCrLf, "")
				' Break-up Content
				Dim elemLinesRaw() As String = Split(discussionPageData, ">")
				' Extract Useful Content
				Dim elemLines(,) As String
				Dim elemLinesCount As Integer = 0
				Dim contentAStartFlag As Integer = 0
				Dim contentBStartFlag As Integer = 0
				Dim contentCStartFlag As Integer = 0
				Dim contentCOrigin As Integer = 0
				Dim contentCStartSubSet1 As Integer = 0
				Dim contentCVariant1 As Integer = 0
				Dim contentStartedParse As Integer = 0
				Dim contentRecord As Integer = 0
				Dim storyPhoto As String = String.Empty
				Dim storyAuthor As String = String.Empty
				Dim storyStatement As String = String.Empty
				For S As Integer = 0 To UBound(elemLinesRaw)
					if rawDump = 1 Then
						Response.Write(elemLinesRaw(S) & vbcrlf)
					Else
						' Read Content
						if contentStartedParse = 0 Then
							' Determine content layout style
							if elemLinesRaw(S).indexOf("class=""storyContent""") > 0 Then ' uiUnifiedStory uiStreamStory
								contentAStartFlag = 1 : contentStartedParse = 1
							Elseif elemLinesRaw(S).indexOf("uiUfiComment comment") > 0 Then
								contentBStartFlag = 1 : contentStartedParse = 1
							Elseif elemLinesRaw(S).indexOf("timelineUnitCustomBackground") > 0 Or elemLinesRaw(S).indexOf("pvm uiListItem") > 0 Or elemLinesRaw(S).indexOf("ptm uiListItem") > 0 Or elemLinesRaw(S).indexOf("pbm uiListItem") > 0 Then
								contentCStartFlag = 1 : contentStartedParse = 1
							End if
						Else
							' Parse A Content
							if contentAStartFlag = 8 Then
								if (elemLinesRaw(S).indexOf("</") > 0) Then
									storyStatement = Split(elemLinesRaw(S), "</")(0)
								Else
									storyStatement = elemLinesRaw(S)
								End if
								contentRecord = 1
							Elseif contentAStartFlag = 7 Then
								if elemLinesRaw(S).indexOf("</") > -1 Then
									contentAStartFlag = 8
								End if
							Elseif contentAStartFlag = 6 Then
								if elemLinesRaw(S).indexOf("class=""") > 0 Then
									contentAStartFlag = 7
								Else
									if elemLinesRaw(S).indexOf("</") > 0 Then
										storyStatement = Split(elemLinesRaw(S), "</")(0)
										contentRecord = 1
									Elseif elemLinesRaw(S).indexOf("<br /") > 0 Then
										storyStatement = Split(elemLinesRaw(S), "<br /")(0)
										contentRecord = 1
									End if
								End if
							Elseif contentAStartFlag = 5 Then
								if elemLinesRaw(S).indexOf("<a href") > -1 And elemLinesRaw(S).indexOf("event, bagof(") > 0 Then
									contentAStartFlag = 6
								End if
								' Catch that variant from contentAStartFlag = 3 earlier
								if elemLinesRaw(S).indexOf("class=""messageBody"" data-ft=""") > 0 Then
									contentAStartFlag = 6
								End if
							Elseif contentAStartFlag = 4 Then
								storyAuthor = Split(elemLinesRaw(S), "</a")(0)
								contentAStartFlag = 5
							Elseif contentAStartFlag = 3 Then
								' Catch variant of a closing tag that is not there under other circumstances
								if elemLinesRaw(S).indexOf("</") > 0 Then
									storyAuthor = Split(elemLinesRaw(S), "</")(0)
									contentAStartFlag = 5
								Else
									contentAStartFlag = 4
								End if
							Elseif contentAStartFlag = 2 Then
								if elemLinesRaw(S).indexOf("actorName") > 0 Then
									contentAStartFlag = 3
								End if
							Elseif contentAStartFlag = 1 Then
								if contentCOrigin = 1 Then
									contentCOrigin = 0 : contentAStartFlag = 2
								Elseif elemLinesRaw(S).indexOf("uiProfilePhoto") > 0 Then
									storyPhoto = Split(Split(elemLinesRaw(S), "src=""")(1), """")(0)
									contentAStartFlag = 2
								End if
							End if

							' Parse B Content
							if contentBStartFlag = 5 Then
								storyStatement = Split(elemLinesRaw(S), "</span")(0)
								contentRecord = 1
							Elseif contentBStartFlag = 4 Then
								if elemLinesRaw(S).indexOf("commentBody") > 0 Then
									contentBStartFlag = 5
								End if
							Elseif contentBStartFlag = 3 Then
								storyAuthor = Split(elemLinesRaw(S), "</")(0)
								contentBStartFlag = 4
							Elseif contentBStartFlag = 2 Then
								if elemLinesRaw(S).indexOf("actorName") > 0 Then
									contentBStartFlag = 3
								End if
							Elseif contentBStartFlag = 1 Then
								if elemLinesRaw(S).indexOf("uiProfilePhoto") > 0 Then
									storyPhoto = Split(Split(elemLinesRaw(S), "src=""")(1), """")(0)
									contentBStartFlag = 2
								End if
							End if

							' Parse C Content
							if contentCStartFlag = 2 Then
								' Start Variants
								if contentCStartSubSet1 = 6 Then
									if elemLinesRaw(S).indexOf("</") > 0 Then
										storyStatement = Split(elemLinesRaw(S), "</")(0)
										contentRecord = 1
									End if
								Elseif contentCStartSubSet1 = 5 Then
									if elemLinesRaw(S).indexOf("""messageBody""") > 0 Then
										contentCStartSubSet1 = 6
									End if
								Elseif contentCStartSubSet1 = 4 Then
									storyAuthor = Split(elemLinesRaw(S), "</")(0)
									contentCStartSubSet1 = 5
								Elseif contentCStartSubSet1 = 3 Then
									if contentCVariant1 = 1 Then
										storyStatement = Split(elemLinesRaw(S), "</div")(0)
										contentRecord = 1
									End if
									if elemLinesRaw(S).indexOf("class=""tipOnelineStory""") > 0 Then
										contentCVariant1 = 1
									Elseif elemLinesRaw(S).indexOf("class=""tlTxFe""") > 0 Then
										contentCVariant1 = 1
									Elseif elemLinesRaw(S).indexOf("class=""fsm fwn fcg""") > 0 Then
										contentCStartFlag = 0 : contentStartedParse = 0
									Elseif elemLinesRaw(S).indexOf("class=""storyContent""") > 0 Then
										contentCStartFlag = 0 : contentCStartSubSet1 = 0
										contentAStartFlag = 1 : contentCOrigin = 1
									End if
								Elseif contentCStartSubSet1 = 2 Then
									storyAuthor = Split(elemLinesRaw(S), "</a")(0)
									contentCStartSubSet1 = 3
								Elseif contentCStartSubSet1 = 1 Then
									contentCStartSubset1 = 2
								End if
								if elemLinesRaw(S).indexOf("<span class=""fwb""") > -1 And contentCStartSubset1 = 0 Then
									contentCStartSubset1 = 1
								End if
								' Catch New Variant
								if elemLinesRaw(S).indexOf("data-hovercard=") > 0 And contentCStartSubSet1 = 0 Then
									contentCStartSubset1 = 4
								End if
							Elseif contentCStartFlag = 1 Then
								if elemLinesRaw(S).indexOf("timelinePageMostRecentLabel") > 0 Then
									' Skip top entry of link to recent posts by others
									contentCStartFlag = 0 : contentStartedParse = 0
								Elseif elemLinesRaw(S).indexOf("uiProfilePhoto") > 0 Then
									storyPhoto = Split(Split(elemLinesRaw(S), "src=""")(1), """")(0)
									contentCStartFlag = 2
								End if
							End if
							' Record Content
							if contentRecord = 1 Then
								' Save Data
								if Len(storyPhoto) > 0 AND Len(storyAuthor) > 0 AND Len(storyStatement) > 0 Then
									' Filter breaking
									storyStatement = Replace(Replace(storyStatement, vbCr, ""), vbLf, "")
									' Continue
									storyAuthor = Server.HtmlDecode(storyAuthor)
									storyStatement = Server.HtmlDecode(storyStatement)
									' Filter out HTML inserted by the poster
									if storyStatement.indexOf("<") > -1 OR storyStatement.indexOf(">") > -1 Then
										Dim tmp_Filtered As String = String.Empty
										Dim tmp_Flip As Integer = 0
										For T As Integer = 0 TO Len(storyStatement)
											Dim tmp_contentChar As String = Mid(storyStatement, T + 1, 1)
											if tmp_contentChar = "<" Then : tmp_Flip = 1 : End if
											if tmp_Flip = 0 Then : tmp_Filtered = tmp_Filtered & tmp_contentChar : End if
											if tmp_contentChar = ">" Then : tmp_Flip = 0 : End if
										Next
										storyStatement = tmp_Filtered
									End if
									' Limit size of statement
									if Len(storyStatement) > rssDescriptionLen Then
										storyStatement = Mid(storyStatement, 1, rssDescriptionLen) & "..."
									End if
									ReDim Preserve elemLines(3, elemLinesCount)
									elemLines(0, elemLinesCount) = storyPhoto
									elemLines(1, elemLinesCount) = storyAuthor
									elemLines(2, elemLinesCount) = storyStatement
									elemLines(3, elemLinesCount) = feed_pubdate
									elemLinesCount = elemLinesCount + 1
								End if
								contentAStartFlag = 0 : contentBStartFlag = 0 : contentCStartFlag = 0 : contentCOrigin = 0
								contentCStartSubSet1 = 0 : contentCVariant1 = 0 : contentStartedParse = 0 : contentRecord = 0
								storyPhoto = String.Empty : storyAuthor = String.Empty : storyStatement = String.Empty
							End if
						End if
					End if
				Next
				if rawDump = 0 Then
					' Assemble Data into RSS
					if elemLinesCount > 0 Then
						' Content returned
						Dim feedStep As Integer = 0
						For R As Integer = 0 To elemLinesCount - 1
							if R < maxFeedsTotalAccept Then
								feedContent = feedContent & "<item>" & vbCrLf
								feedContent = feedContent & "<title>" & feed_title & "</title>" & vbCrLf
								feedContent = feedContent & "<link>" & feed_link & "</link>" & vbCrLf
								if rssIncludeImg = 1 Then
									feedContent = feedContent & "<description>" & Server.HtmlEncode("<img align=""left"" src=""" & elemLines(0, R) & """  border=""0"" /> ") & Server.HtmlEncode(Replace(elemLines(2, R), "'", "'")) & "</description>" & vbCrLf
								Else
									feedContent = feedContent & "<description>" & Server.HtmlEncode(Replace(elemLines(2, R), "'", "'")) & "</description>" & vbCrLf
								End if
								feedContent = feedContent & "<author>" & elemLines(1, R) & "</author>" & vbCrLf
								feedContent = feedContent & "<date>" & elemLines(3, R) & "</date>" & vbCrLf
								feedContent = feedContent & "</item>" & vbCrLf
							End if
						Next
					Else
						' No content found; target is reachable but the source code of the wall has possibly changed
						feedContent = feedContent & "<item>" & vbCrLf
						feedContent = feedContent & "<title>Page Found But Data Not Recognized</title>" & vbCrLf
						feedContent = feedContent & "<link></link>" & vbCrLf
						feedContent = feedContent & "<description>Page Found But Data Not Recognized</description>" & vbCrLf
						feedContent = feedContent & "<author>Server</author>" & vbCrLf
						feedContent = feedContent & "<date>" & feed_pubdate & "</date>" & vbCrLf
						feedContent = feedContent & "</item>" & vbCrLf
					End if
				End if
			Else
				' No page found; target is unreachable; error
				feedContent = feedContent & "<item>" & vbCrLf
				feedContent = feedContent & "<title>Page Not Found/Error</title>" & vbCrLf
				feedContent = feedContent & "<link></link>" & vbCrLf
				if rssShowErrors = 0 Then
					feedContent = feedContent & "<description>Either the page specified could not be found or an error has occurred.</description>" & vbCrLf
				Else
					feedContent = feedContent & "<description>" & Server.HtmlEncode(crawlError) & "</description>" & vbCrLf
				End if
				feedContent = feedContent & "<author>Server</author>" & vbCrLf
				feedContent = feedContent & "<date>" & feed_pubdate & "</date>" & vbCrLf
				feedContent = feedContent & "</item>" & vbCrLf
			End if ' End Len(crawlError) = 0
			if rawDump = 0 And overrideCache = 0 Then
				' Add RSS Envelope, if specified
				if rssIncludeHeader = 1 Then
					rssFeed = rssFeed & "<rss version=""2.0"">" & vbCrLf
				End if
				rssFeed = rssFeed & "<channel>" & vbCrLf
				If rssIncludeHeader = 1 Then
					rssFeed = rssFeed & "<title>" & feed_title & "</title>" & vbCrLf
					rssFeed = rssFeed & "<link>" & feed_link & "</link>" & vbCrLf
					rssFeed = rssFeed & "<description>" & feed_description & "</description>" & vbCrLf
					rssFeed = rssFeed & "<language>" & feed_language & "</language>" & vbCrLf
					rssFeed = rssFeed & "<date>" & feed_pubdate & "</date>" & vbCrLf
					rssFeed = rssFeed & "<copyright>" & feed_copyright & "</copyright>" & vbCrLf
					rssFeed = rssFeed & "<webmaster>" & feed_webmaster & "</webmaster>" & vbCrLf
				End If
				rssFeed = rssFeed & feedContent
				rssFeed = rssFeed & "</channel>" & vbCrLf
				if rssIncludeHeader = 1 Then
					rssFeed = rssFeed & "</rss>"
				End if
				' Add RSS to Server Cache
				HttpContext.Current.Cache.Add(feedName, rssFeed, Nothing, DateTime.Now.AddDays(1), System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, Nothing)
			End if
		Else
			' Get Feed From Server Cache
			rssFeed = CType(HttpContext.Current.Cache(feedName), String)
		End if ' End HttpContext.Current.Cache(feedName) Is Nothing
	Else
		' Clear Server Cache
		HttpContext.Current.Cache.Remove(feedName)
		' Compile RSS Data
		feedContent = "<item>" & vbCrLf
		feedContent = feedContent & "<title>Server Cache Cleared</title>" & vbCrLf
		feedContent = feedContent & "<link></link>" & vbCrLf
		feedContent = feedContent & "<description>Server Cache Cleared</description>" & vbCrLf
		feedContent = feedContent & "<author>Server</author>" & vbCrLf
		feedContent = feedContent & "<date>" & feed_pubdate & "</date>" & vbCrLf
		feedContent = feedContent & "</item>" & vbCrLf
		if rssIncludeHeader = 1 Then
			rssFeed = rssFeed & "<rss version=""2.0"">" & vbCrLf
		End if
		rssFeed = rssFeed & "<channel>" & vbCrLf
		If rssIncludeHeader = 1 Then
			rssFeed = rssFeed & "<title>" & feed_title & "</title>" & vbCrLf
			rssFeed = rssFeed & "<link>" & feed_link & "</link>" & vbCrLf
			rssFeed = rssFeed & "<description>" & feed_description & "</description>" & vbCrLf
			rssFeed = rssFeed & "<language>" & feed_language & "</language>" & vbCrLf
			rssFeed = rssFeed & "<date>" & feed_pubdate & "</date>" & vbCrLf
			rssFeed = rssFeed & "<copyright>" & feed_copyright & "</copyright>" & vbCrLf
			rssFeed = rssFeed & "<webmaster>" & feed_webmaster & "</webmaster>" & vbCrLf
		End If
		rssFeed = rssFeed & feedContent
		rssFeed = rssFeed & "</channel>" & vbCrLf
		if rssIncludeHeader = 1 Then
			rssFeed = rssFeed & "</rss>"
		End if
	End if ' End rssCacheClear = 0
	' Generate Output
	if rawDump = 0 And overrideCache = 0 Then
		Response.Buffer = False
		If rssIncludeHeader = 1 Then
			Response.ContentType = "application/rss+xml"
		Else
			Response.ContentType = "text/xml"
		End If
		Response.Write("<" & "?" & "xml version=""1.0"" encoding=""utf-8""" & "?" & ">" & vbCrLf)
		Response.Write(rssFeed)
	End if
    End Sub
</script>
About Joe