前言

我们每个猿都有一个搭建自己独立博客的梦,我也不例外。以前想 现在想 以后也想。之所以一直迟迟没有着手,是因为难以跨出第一步。每次心里想着,等我以后技术好了再说,然后就没有然后了。以前用过wordpress,虽然插件很多,不过有时候想改改自己想要的效果很难,因为 我压根就不会php。也看过.net的一些开源博客,代码量多,看得头晕,没那个耐心。

再说,别人的始终是别人的。得鱼不如得渔。与其花时间去研究php还不如自己写个.net版的。有人说博客园已经很好了啊,我承认确实,而且还可以后台定制自己想要的样式和js。不过始终还是不如自己开发的来得随心所欲。最重要自己开发还可以当作练手 对一个网站的各环节  做一次练习,用以发现自己的不足,并加以提升。那我以后的博客写哪里呢?当然还是会继续发博客园,谁叫这里人气旺呢。

这次开发的博客主要功能或特点:
    第一:可以兼容各终端,特别是手机端。
    第二:到时会用到大量html5,炫啊。
    第三:导入博客园的精华文章,并做分类。(不要封我)
    第四:做个插件,任何网站上的技术文章都可以转发收藏 到本博客。

所以打算写个系类:《一步步搭建自己的博客

关于域名和空间

在以前我们学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 = ;
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('/') + , typeitemhref.LastIndexOf('.') - typeitemhref.LastIndexOf('/') - );
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 = ; i < tagSplit.Length; i++)
{
if (tagSplit[i].Length >= && tagSplit[i].LastIndexOf('<') >= )
{
var blogtagname = tagSplit[i].Substring(tagSplit[i].IndexOf('>') + , tagSplit[i].LastIndexOf('<') - tagSplit[i].IndexOf('>') - );
blogtagid.Add(this.GetTagId(blogtagname, userName));
}
}
var categoriesSplit = jsonobj["Categories"].Split(',');
var blogtypeid = new List<int>();
for (int i = ; i < categoriesSplit.Length; i++)
{
if (categoriesSplit[i].Length >= && categoriesSplit[i].LastIndexOf('<') >= )
{
var blogtypename = categoriesSplit[i].Substring(categoriesSplit[i].IndexOf('>') + , categoriesSplit[i].LastIndexOf('<') - categoriesSplit[i].IndexOf('>') - );
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 > )
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() >= )
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 -;
}
} private int GetTypeId(string typename, string userName)
{
BlogTypesBLL blogtype = new BlogTypesBLL();
var blogtagmode = blogtype.GetList(t => t.TypeName == typename);
if (blogtagmode.Count() >= )
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() >= )
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() >= ;
} /// <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("/") + , href.IndexOf(".") - href.LastIndexOf("/") - );
int userid = -;
if (int.TryParse(href, out userid))
return userid.ToString();
}
}
return "";
}

页面布局

关于页面布局 ,怎样简单怎样来。我是分成了 头、尾、中间。中间二八分。这个不重要,现在暂时这么遭。以后再考虑 多终端的兼容。

数据加载

现在数据都已经迁移过来的,需要展示在我们自己搭建的博客,我想对于大家来书应该没什么难度。从学编程开始,老师就教我们 增删改查。只是美与丑的问题。

这里有一点要注意,因为正文内容保存到数据库的都是html代码,而我们要在首页展示文章列表只显示小部分内容,那怎么截取字符串呢?你不能保证刚好是在html标签结尾后截取啊。我这里用到了HtmlAgilityPack取InnerText 的属性。就像jqeruy中 .html() 和 .text()区别,如果截图断了html标签 显示 将会很混乱。

最后总结

这个博客我也才刚开始做,现有也仅仅只是实现了博客的展示功能,连分页都还没有去实现。所以本系列博客更新会比较慢。我也需要边做边学边更新,工作中还没用过MVC。

到最后等我做完了,我会放git上开源,到时候大家有兴趣的可以一起来完善和定制自己想要的效果。

说了这么多来张效果图吧。

如果您对本篇文章感兴趣,那就麻烦您点个赞,您的鼓励将是我的动力。 当然您还可以加入QQ群:讨论。

演示地址:http://blog.haojima.net/

原文链接:http://www.cnblogs.com/zhaopei/p/4737958.html

一步步开发自己的博客 .NET版系列:http://www.cnblogs.com/zhaopei/tag/Hi-Blogs/

开源地址:http://git.oschina.net/zhaopeiym/Hi-Blogs

一步步开发自己的博客 .NET版(1、基本显示)的更多相关文章

  1. 一步步开发自己的博客 .NET版(3、注册登录功能)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  2. 一步步开发自己的博客 .NET版(4、文章发布功能)百度编辑器

    前言 这次开发的博客主要功能或特点: 第一:可以兼容各终端,特别是手机端. 第二:到时会用到大量html5,炫啊. 第三:导入博客园的精华文章,并做分类.(不要封我) 第四:做个插件,任何网站上的技术 ...

  3. 一步步开发自己的博客 .NET版(5、Lucenne.Net 和 必应站内搜索)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  4. 一步步开发自己的博客 .NET版 剧终篇(6、响应式布局 和 自定义样式)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

  5. 一步步开发自己的博客 .NET版(11、Web.config文件的读取和修改)

    Web.config的读取 对于Web.config的读取大家都很属性了.平时我们用得比较多的就是appSettings节点下配置.如: 我们对应的代码是: = ConfigurationManage ...

  6. 一步步开发自己的博客 .NET版(10、前端对话框和消息框的实现)

    关于前端对话框.消息框的优秀插件多不胜数.造轮子是为了更好的使用轮子,并不是说自己造的轮子肯定好.所以,这个博客系统基本上都是自己实现的,包括日志记录.响应式布局.评论功能等等一些本可以使用插件的.好 ...

  7. 一步步开发自己的博客 .NET版(9、从model first替换成code first 问题记录)

    为什么要改用code first 用过code first的基本上都不会再想用回model first或是db first(谁用谁知道).不要问我为什么不一开始就直接使用code first,因为那个 ...

  8. ASP.NET 一步步开发自己的博客 .NET版(11、Web.config文件的读取和修改)

    原文:http://www.cnblogs.com/zhaopei/p/5677053.html

  9. 一步步搭建自己的博客 .NET版(2、评论功能)

    前言 这次开发的博客主要功能或特点:    第一:可以兼容各终端,特别是手机端.    第二:到时会用到大量html5,炫啊.    第三:导入博客园的精华文章,并做分类.(不要封我)    第四:做 ...

随机推荐

  1. 从中间件的历史来看移动App开发的未来

    在移动开发领域我们发现一个很奇怪的现象:普通菜鸟新手经过3个月的培训就可以拿到 8K 甚至上万的工作:在北京稍微有点工作经验的 iOS 开发,就要求 2 万一个月的工资.不知道大家是否想过:移动应用开 ...

  2. C#异步编程(二)

    async和await结构 序 前篇博客异步编程系列(一) 已经介绍了何谓异步编程,这篇主要介绍怎么实现异步编程,主要通过C#5.0引入的async/await来实现. BeginInvoke和End ...

  3. MAC Osx PHP安装指导

    php.ini的位置 Mac OS X中没有默认的php.ini文件,但是有对应的模版文件php.ini.default,位于/private/etc/php.ini.default 或者说 /etc ...

  4. Spring中Bean的实例化

                                    Spring中Bean的实例化 在介绍Bean的三种实例化的方式之前,我们首先需要介绍一下什么是Bean,以及Bean的配置方式. 如果 ...

  5. C++ 拷贝构造函数和赋值运算符

    本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义 ...

  6. 安装angular-cli

    最近在学习angular2,并尝试用这个框架来做公司的一个新项目. 终于要开始开发了,等了1个多月. 因为第一次用这个新框架做项目,不太熟悉,就找了angular-cli这个脚手架来搭建项目. 安装了 ...

  7. PHP设计模式(一)简单工厂模式 (Simple Factory For PHP)

    最近天气变化无常,身为程序猿的寡人!~终究难耐天气的挑战,病倒了,果然,程序猿还需多保养自己的身体,有句话这么说:一生只有两件事能报复你:不够努力的辜负和过度消耗身体的后患.话不多说,开始吧. 一.什 ...

  8. 【干货分享】流程DEMO-离职流程

    流程名: 离职申请   流程相关文件: 流程包.xml WebService业务服务.xml WebService.asmx WebService.cs   流程说明: 流程中集成了webservic ...

  9. [AlwaysOn Availability Groups] 健康模型 Part 2 ——扩展

    健康模型扩展 第一部分已经介绍了AlwayOn健康模型的概述.现在是创建一个自己的PBM策略,然后设置为制定的归类.创建这些策略,创建之后修改一下配置,dashboard就会自动评估这些策略. 场景, ...

  10. 两个变量交换的四种方法(Java)

    对于两种变量的交换,我发现四种方法,下面我用Java来演示一下. 1.利用第三个变量交换数值,简单的方法. (代码演示一下) class TestEV //创建一个类 { public static ...