前言
我們每個猿都有一個搭建自己獨立博客的夢,我也不例外。以前想 現在想 以后也想。之所以一直遲遲沒有着手,是因為難以跨出第一步。每次心里想着,等我以后技術好了再說,然后就沒有然后了。以前用過wordpress,雖然插件很多,不過有時候想改改自己想要的效果很難,因為 我壓根就不會php。也看過.net的一些開源博客,代碼量多,看得頭暈,沒那個耐心。
再說,別人的始終是別人的。得魚不如得漁。與其花時間去研究php還不如自己寫個.net版的。有人說博客園已經很好了啊,我承認確實,而且還可以后台定制自己想要的樣式和js。不過始終還是不如自己開發的來得隨心所欲。最重要自己開發還可以當作練手 對一個網站的各環節 做一次練習,用以發現自己的不足,並加以提升。那我以后的博客寫哪里呢?當然還是會繼續發博客園,誰叫這里人氣旺呢。
這次開發的博客主要功能或特點:
第一:可以兼容各終端,特別是手機端。
第二:到時會用到大量html5,炫啊。
第三:導入博客園的精華文章,並做分類。(不要封我)
第四:做個插件,任何網站上的技術文章都可以轉發收藏 到本博客。
所以打算寫個系類:《一步步搭建自己的博客》
- 一步步開發自己的博客 .NET版(1、頁面布局、blog遷移、數據加載)
- 一步步開發自己的博客 .NET版(2、評論功能)
- 一步步開發自己的博客 .NET版(3、注冊登錄功能)
- 一步步開發自己的博客 .NET版(4、文章發布功能)
- 一步步開發自己的博客 .NET版(5、搜索功能)
- 一步步開發自己的博客 .NET版(6、手機端的兼容)
關於域名和空間
在以前我們學C#的想要搭一個免費的博客,要不只能用國外的免費空間要么在linux下用php。用起來都是各種坑,網速各種卡。然后,現在我們學C#的時代來了,這里要感謝阿里雲(免費主機)。當然域名還是要自己買的。萬網 和 新網 可以對比下 哪里便宜買哪里,都可以用。(注意:最好買 .com .net .cc .org 因為有些域名不能在阿里備案)。買好域名之后 然后就是備案了,備案也沒什么復雜的,阿里自動備案。中間除了 找阿里 要一塊免費的布 照個相 寄過去,就是等了,其他的什么也不用做。大概半個月的樣子吧。建議 買域名的時候最好一次性買久一點,不然后期再續費要比第一次買貴。(如果您實在不想花這個錢,這個我最后給您支個招吧,你申請好免費的主機后,你把主機ip給我,我免費給你二級域名。誰叫我是活雷鋒)
開發環境
域名和主機都搞定以后,就開始選擇開發環境了。我選的是 vs2013 mvc4 ef6.0 mssql 。
博客遷移
然后就是博客遷移,之前也老想着搭建博客,可一直沒有行動。這就是從0到1 的難。只要你走出了第一步 后面就 順暢得多了。那么 我們搭博客 沒有測試數據 總還是感覺沒什么動力。所以,我就寫了個程序,把我在博客園發表的文章扒過去。
那么我們需要哪些數據呢?現在大概想到的有:博客正文、tag標簽、文章分類、創建時間、博客標題
好了,那我們就正式開始扒吧。(可以參考我以前的博客備份小工具3)
首先是從/mvc/blog/sidecolumn.aspx頁面取得 文章分類。然后根據 每個類型 的鏈接 取得這個類型下的所有文章。然后在取正文的時候發現 文章所屬tag標簽和分類是異步的到頁面的, http://www.cnblogs.com/mvc/blog/CategoriesTags.aspx?blogApp=用戶名&postId=文章id。(也許博客園有api,我也沒去看沒去找。)
1.首先建一個實體數據模型
我這里采用的是 model first(之前搞錯概念了,謝謝園友指正) 。這里要說明的是 tag標簽和文章是多對多的關系,文章類型和文章也是多對多的關系。
2.然后根據模型生成數據庫
個人覺得這里非常爽,自動幫我建好了 主外鍵 和索引,免除了我們自己手動去在數據庫里面建。
3.從博客園扒數據
模型和數據庫建好了,那么我們現在就開始遷移吧~下面是全部代碼,其中有存數據庫的部分可以自己改改。

/// <summary> /// 根據用戶導入cnblog數據 /// </summary> /// <param name="userName"></param> /// <returns></returns> public string Import(string userName, string iszf = "false") { int blosNumber = 0; JavaScriptSerializer jss = new JavaScriptSerializer(); string url = "http://www.cnblogs.com/" + userName + @"/mvc/blog/sidecolumn.aspx"; HtmlAgilityPack.HtmlWeb htmlweb = new HtmlAgilityPack.HtmlWeb(); var docment = htmlweb.Load(url); string userid = GetCnblogUserId(userName); var liS = docment.DocumentNode.SelectNodes("//*[@id='sidebar_categories']/div[1]/ul/li"); foreach (var item in liS) { var tXPath = item.XPath; var href = item.SelectSingleNode(tXPath + "/a").Attributes["href"].Value; var blogtype = htmlweb.Load(href); //var entrylistItem = blogtype.DocumentNode.SelectNodes("//*[@id='mainContent']/div/div[2]/div[@class='entrylistItem']"); var entrylistItem = blogtype.DocumentNode.SelectNodes("//div[@class='entrylistItem']"); if (null == entrylistItem)//做兼容 entrylistItem = blogtype.DocumentNode.SelectNodes("//div[@class='post post-list-item']"); // if (null == entrylistItem) { continue; } foreach (var typeitem in entrylistItem) { var typeitemXPath = typeitem.XPath; var typeitemhrefObj = typeitem.SelectSingleNode(typeitemXPath + "/div/a"); if (null == typeitemhrefObj) //做兼容 typeitemhrefObj = typeitem.SelectSingleNode(typeitemXPath + "/h2/a"); var typeitemhref = typeitemhrefObj.Attributes["href"].Value; if (IsAreBlog(typeitemhref)) continue;//說明這篇文章已經備份過了的 var bloghtml = htmlweb.Load(typeitemhref); var blogcontextobj = bloghtml.DocumentNode.SelectSingleNode("//*[@id='cnblogs_post_body']");//.InnerHtml; if (blogcontextobj == null) continue;//有可能是加密文章 var blogcontext = blogcontextobj.InnerHtml; var blogtitle = bloghtml.DocumentNode.SelectSingleNode("//*[@id='cb_post_title_url']").InnerText; var blogurl = bloghtml.DocumentNode.SelectSingleNode("//*[@id='cb_post_title_url']").Attributes["href"].Value; var blogtypetagurl = "http://www.cnblogs.com/mvc/blog/CategoriesTags.aspx?blogApp=" + userName + "&blogId=" + userid + " =" + typeitemhref.Substring(typeitemhref.LastIndexOf('/') + 1, typeitemhref.LastIndexOf('.') - typeitemhref.LastIndexOf('/') - 1); var blogtag = Blogs.Common.Helper.MyHtmlHelper.GetRequest(blogtypetagurl); var jsonobj = jss.Deserialize<Dictionary<string, string>>(blogtag); if (null == jsonobj) continue;//如果沒有 則返回 (這里只能去 數字.html 不能取那種自定義的url) var tagSplit = jsonobj["Tags"].Split(','); var blogtagid = new List<int>(); for (int i = 0; i < tagSplit.Length; i++) { if (tagSplit[i].Length >= 1 && tagSplit[i].LastIndexOf('<') >= 1) { var blogtagname = tagSplit[i].Substring(tagSplit[i].IndexOf('>') + 1, tagSplit[i].LastIndexOf('<') - tagSplit[i].IndexOf('>') - 1); blogtagid.Add(this.GetTagId(blogtagname, userName)); } } var categoriesSplit = jsonobj["Categories"].Split(','); var blogtypeid = new List<int>(); for (int i = 0; i < categoriesSplit.Length; i++) { if (categoriesSplit[i].Length >= 1 && categoriesSplit[i].LastIndexOf('<') >= 1) { var blogtypename = categoriesSplit[i].Substring(categoriesSplit[i].IndexOf('>') + 1, categoriesSplit[i].LastIndexOf('<') - categoriesSplit[i].IndexOf('>') - 1); blogtypeid.Add(this.GetTypeId(blogtypename, userName)); } } var blogtimeobj = bloghtml.DocumentNode.SelectSingleNode("//*[@id='post-date']"); var blogtime = ""; if (null != blogtimeobj) blogtime = blogtimeobj.InnerText; BlogsBLL blog = new BlogsBLL(); var myBlogTags = new BlogTagsBLL().GetList(t => blogtagid.Contains(t.Id), isAsNoTracking: false).ToList();//.ToList(); var myBlogTypes = new BLL.BlogTypesBLL().GetList(t => blogtypeid.Contains(t.Id), isAsNoTracking: false).ToList();//.ToList(); try { var modelMyBlogs = new ModelDB.Blogs() { BlogContent = blogcontext, BlogCreateTime = blogtime, BlogTitle = blogtitle, BlogUrl = blogurl, IsDel = false, BlogTags = myBlogTags, BlogTypes = myBlogTypes, UsersId = GetUserId(userName), BlogForUrl = blogurl, IsForwarding = iszf == "checked" }; blog.Add(modelMyBlogs); blog.save(); blosNumber++; } catch (Exception) { throw; } } } if (blosNumber > 0) return "成功導入" + blosNumber + "篇Blog"; return "ok"; } private int GetTagId(string tagname, string userName) { BlogTagsBLL blogtag = new BlogTagsBLL(); try { var blogtagmode = blogtag.GetList(t => t.TagName == tagname); if (blogtagmode.Count() >= 1) return blogtagmode.FirstOrDefault().Id; else { blogtag.Add(new ModelDB.BlogTags() { TagName = tagname, IsDel = false, UsersId = GetUserId(userName) }); blogtag.save(); return GetTagId(tagname, userName); } } catch (Exception) { return -1; } } private int GetTypeId(string typename, string userName) { BlogTypesBLL blogtype = new BlogTypesBLL(); var blogtagmode = blogtype.GetList(t => t.TypeName == typename); if (blogtagmode.Count() >= 1) return blogtagmode.FirstOrDefault().Id; else { blogtype.Add(new ModelDB.BlogTypes() { TypeName = typename, CreateTime = DateTime.Now, IsDel = false, UsersId = GetUserId(userName) }); blogtype.save(); return GetTypeId(typename, userName); } } /// <summary> /// 獲取haojima用戶id /// </summary> /// <param name="userName"></param> /// <returns></returns> private int GetUserId(string userName) { BlogUsersBLL user = new BlogUsersBLL(); var blogtagmode = user.GetList(t => t.UserName == userName); if (blogtagmode.Count() >= 1) return blogtagmode.FirstOrDefault().Id; else { user.Add(new ModelDB.BlogUsers() { UserName = userName, IsDel = false, UserPass = "admin", UserNickname = userName }); user.save(); return GetUserId(userName); } } /// <summary> /// 檢查 這個 url地址 是否被添加過 /// </summary> /// <param name="url"></param> /// <returns></returns> private bool IsAreBlog(string url) { BLL.BlogsBLL blog = new BLL.BlogsBLL(); var blogs = blog.GetList(t => t.BlogUrl == url); return blogs.Count() >= 1; } /// <summary> /// 獲取cnblog用戶id /// </summary> /// <param name="url"></param> /// <returns></returns> private string GetCnblogUserId(string url) { HtmlAgilityPack.HtmlWeb htmlweb = new HtmlAgilityPack.HtmlWeb(); var docment = htmlweb.Load("http://www.cnblogs.com/" + url); var list = docment.DocumentNode.SelectNodes("//link[@rel='stylesheet']"); foreach (var item in list) { if (null != item.Attributes && item.Attributes.Contains("href")) { var href = item.Attributes["href"].Value; href = href.Substring(href.LastIndexOf("/") + 1, href.IndexOf(".") - href.LastIndexOf("/") - 1); int userid = -1; if (int.TryParse(href, out userid)) return userid.ToString(); } } return ""; }
頁面布局
關於頁面布局 ,怎樣簡單怎樣來。我是分成了 頭、尾、中間。中間二八分。這個不重要,現在暫時這么遭。以后再考慮 多終端的兼容。
數據加載
現在數據都已經遷移過來的,需要展示在我們自己搭建的博客,我想對於大家來書應該沒什么難度。從學編程開始,老師就教我們 增刪改查。只是美與丑的問題。
這里有一點要注意,因為正文內容保存到數據庫的都是html代碼,而我們要在首頁展示文章列表只顯示小部分內容,那怎么截取字符串呢?你不能保證剛好是在html標簽結尾后截取啊。我這里用到了HtmlAgilityPack取InnerText 的屬性。就像jqeruy中 .html() 和 .text()區別,如果截圖斷了html標簽 顯示 將會很混亂。
最后總結
這個博客我也才剛開始做,現有也僅僅只是實現了博客的展示功能,連分頁都還沒有去實現。所以本系列博客更新會比較慢。我也需要邊做邊學邊更新,工作中還沒用過MVC。
到最后等我做完了,我會放git上開源,到時候大家有興趣的可以一起來完善和定制自己想要的效果。
說了這么多來張效果圖吧。
如果您對本篇文章感興趣,那就麻煩您點個贊,您的鼓勵將是我的動力。 當然您還可以加入QQ群:
討論。
原文鏈接:http://www.cnblogs.com/zhaopei/p/4737958.html
一步步開發自己的博客 .NET版系列:http://www.cnblogs.com/zhaopei/tag/Hi-Blogs/