操作系统:windowAll

编程工具:visual studio 2013

编程语言:VC++

最近博文更新的较频繁,为了防止账号异常引起csdn博文丢失,所以花了点时间做了个小工具来导出博文,用做备份。本文将从源码分析整个实现过程。先看个截图:

操作步骤:

  1. 先在博客地址文本框输入博客地址例如:http://blog.csdn.net/yxstars/,http://blog.sina.com.cn/yxstars/,

    http://www.cnblogs.com/yxstars/
  2. 然后点击确定,将显示共有多少篇博文,例如:[19:32:47]博文113篇
  3. 点击文章列表:将显示所有博文,格式:title,href
  4. 点击导出博文:将导出博文,在当前目录下的blog文件夹中。博文格式为html。
  5. 遍历博文:将遍历所有博文并且显示出来。
  6. 刷新:刷新所有博文,不显示。
  7. 图片:导出的博文,图片下载到本地,博文图片链接到本地。
  8. 列表:支持自定义的列表链接博文(当前目录下有个list.ini,可以自定义链接)。
  9. 刷新次数:自定义,循环次数。
  10. 时间间隔:每次循环sleep时间。

源码分析:

1. 获取对应的url页面源代码,实现如下:

bool CBlogExportDlg::GetUrlStr(CString strUrl, CString& UrlData)
{
CInternetSession session;
CHttpFile *file = NULL;
try{
file = (CHttpFile*)session.OpenURL(strUrl);
}
catch (CInternetException *m_pException){
file = NULL;
m_pException->m_dwError;
m_pException->Delete();
session.Close();
ShowMes("网络连接错误...");
return false;
} if (!file){
ShowMes(strUrl + "获取失败...");
return false;
} CString sRecived;
while (file->ReadString(sRecived) != NULL) {
UrlData += sRecived + "\n";
}
session.Close();
file->Close();
delete file;
file = NULL;
return true;
}

2. 获取的html源码为utf8格式,需要转为ansi格式,C++实现代码如下:

int CBlogExportDlg::ConvUtf8ToAnsi(CString& strSource, CString& strChAnsi)
{
if (strSource.GetLength() <= 0)
return 0; CString strWChUnicode; strSource.TrimLeft();
strSource.TrimRight();
strChAnsi.Empty(); int iLenByWChNeed = MultiByteToWideChar(CP_UTF8, 0,
strSource.GetBuffer(0),
strSource.GetLength(), //MultiByteToWideChar
NULL, 0); int iLenByWchDone = MultiByteToWideChar(CP_UTF8, 0,
strSource.GetBuffer(0),
strSource.GetLength(),
(LPWSTR)strWChUnicode.GetBuffer(iLenByWChNeed * 2),
iLenByWChNeed); //MultiByteToWideChar strWChUnicode.ReleaseBuffer(iLenByWchDone * 2); int iLenByChNeed = WideCharToMultiByte(CP_ACP, 0,
(LPCWSTR)strWChUnicode.GetBuffer(0),
iLenByWchDone,
NULL, 0,
NULL, NULL); int iLenByChDone = WideCharToMultiByte(CP_ACP, 0,
(LPCWSTR)strWChUnicode.GetBuffer(0),
iLenByWchDone,
strChAnsi.GetBuffer(iLenByChNeed),
iLenByChNeed,
NULL, NULL); strChAnsi.ReleaseBuffer(iLenByChDone); if (iLenByWChNeed != iLenByWchDone || iLenByChNeed != iLenByChDone)
return 1; return 0;
}

3. 消息文本框显示

void CBlogExportDlg::ShowMes(CString mes)
{
CTime time;
time = CTime::GetCurrentTime();//Get the current time
CString Times = _T("[") + time.Format("%H:%M:%S") + "]";//Conversion time format int len = MesEdit.GetWindowTextLength();
MesEdit.SetSel(len, len);
MesEdit.ReplaceSel(Times + mes + _T("\r\n"));
}

4. 点击确定按钮后,实现代码

void CBlogExportDlg::OnBnClickedButtonOk()
{
GetDlgItemText(IDC_EDIT_ADDRESS, blogAdr);
ShowBlogAdr();
//blogAdr = ("http://blog.csdn.net/yxstars/");
int pos = blogAdr.Find("http://blog.csdn.net/");
if (pos == -1){
ShowMes("csdn blog地址不对...");
}
blogAdrs = blogAdr; CString urlData;
if (!GetUrlStr(blogAdr, urlData)){
return;
} CFile fs;
if (!fs.Open(strDirPath + "temp", CFile::modeCreate | CFile::modeWrite)){
return;
} fs.Write(urlData, urlData.GetLength());
fs.Close(); CString ansiUrlData;
ConvUtf8ToAnsi(urlData, ansiUrlData);
GetBlogInfo(ansiUrlData); }

5. 根据博客地址,获取源代码后分析,查找博文数目,和博文列表页数。

<!--显示分页 -->

<div id="papelist" class="pagelist">
<span> 113条数据 共6页</span><strong>1</strong> <a href="/yxstars/article/list/2">2</a> <a href="/yxstars/article/list/3">3</a> <a href="/yxstars/article/list/4">4</a> <a href="/yxstars/article/list/5">5</a> <a href="/yxstars/article/list/6">...</a> <a href="/yxstars/article/list/2">下一页</a> <a href="/yxstars/article/list/6">尾页</a>
</div>

从上面的代码中可以获取信息如下:

<span> 113条数据  共6页</span>, 共有113篇博文,共有6页。

<a href="/yxstars/article/list/3">,页面链接地址为/yxstars/article/list/
+ 要显示的页数。

C++代码实现如下:

void CBlogExportDlg::GetBlogInfo(CString& urlData)
{
int pos = urlData.Find("<div id=\"papelist\" class=\"pagelist\">");
if (pos == -1){
ShowMes("获取列表数目失败...");
return;
}
urlData = urlData.Mid(pos + 44);
pos = urlData.Find("条数据");
if (pos == -1){
ShowMes("获取列表条数失败...");
return;
} CString blogListNum = urlData.Left(pos); pos = urlData.Find("条数据 共");
int poss = urlData.Find("页</span>");
if ((poss == -1) || (pos == -1)){
ShowMes("获取列表页数失败...");
return;
} CString listPage = urlData.Mid(pos + 10, poss - pos - 10);
blogListPage = StrToInt(listPage);
ShowMes("博文" + blogListNum + "篇");
}

6. 当点击显示列表时,根据之前的页面地址获取信息。

void CBlogExportDlg::OnBnClickedButtonList()
{
clearMes();
CString urlData, ansiUrlData, listPage;
//http://blog.csdn.net/yxstars/article/list/1
FileListMap.clear();
listNum = 1; for (int i = 1; i < blogListPage + 1; i++){
urlData.Empty();
ansiUrlData.Empty();
listPage.Format("%d", i);
blogAdr = blogAdrs + "/article/list/" + listPage;
ShowBlogAdr();
if (!GetUrlStr(blogAdr, urlData)){
return;
} ConvUtf8ToAnsi(urlData, ansiUrlData);
GetFileList(ansiUrlData);
} }

7. 在每个页面获取文章列表和页面地址。

    <h1>
<span class="link_title"><a href="/yxstars/article/details/38469431">
<font color="red">[置顶]</font>
金融系列12《双币电子现金方案》
</a></span>
</h1>

从上面源码可以看出:

<span class="link_title">后面就是博文链接地址。

</a>前面的就是博文标题。

如果有置顶操作,会多出这部分<font color="red">[置顶]</font>

C++获取源码实现如下:

void CBlogExportDlg::GetFileList(CString& urlData)
{
CString strListNum;
int posF = urlData.Find("<span class=\"link_title\">");
while (posF != -1){
urlData = urlData.Mid(posF + 34);
int posE = urlData.Find("\"");
if (posE == -1){
ShowMes("获取列表失败...");
return;
} CString href = urlData.Left(posE);
posF = urlData.Find("</a>");
if (posF == -1){
ShowMes("获取列表失败...");
return;
} CString title = urlData.Mid(posE+2, posF-posE-2);
posF = title.ReverseFind('>');
if (posF != -1){
title = title.Mid(posF + 1);
}
title.Trim("\n").Trim();
href = "http://blog.csdn.net" + href;
FileListMap[title] = href;
strListNum.Format("%03d", listNum++);
strListNum = (strListNum + ":" + title + " ").Left(45);
ShowMes(strListNum + href);
posF = urlData.Find("<span class=\"link_title\">");
}
}

8. 当点击导出博文时,我们只需把源代码保存为html格式即可,采用多线程实现:

void CBlogExportDlg::OnBnClickedButtonExport()
{
clearMes();
unsigned tid;
unsigned long thd = _beginthreadex(NULL, 0, CBlogExportDlg::WriteCycle, this, 0, &tid);
if (thd != NULL)
{
CloseHandle((HANDLE)thd);
} } unsigned __stdcall CBlogExportDlg::WriteCycle(void* p)
{
CBlogExportDlg* dlg = (CBlogExportDlg*)p;
CString blogFolderPath = dlg->strDirPath + "Blog\\";
if (!PathIsDirectory(blogFolderPath))
{
if (!CreateDirectory(blogFolderPath, NULL))
{
dlg->ShowMes(blogFolderPath + "创建失败...");
return 1;
}
} dlg->stopRun = false;
CString urlData, strList;
int iList = 1;
CFile cf;
std::map<CString, CString>::iterator iter;
for (iter = dlg->FileListMap.begin(); iter != dlg->FileListMap.end(); iter++){
//dlg->blogAdr = iter->second;
//dlg->ShowBlogAdr();
urlData.Empty();
if (!dlg->GetUrlStr(iter->second, urlData)){
return 1;
}
strList.Format("%3d", iList++);
dlg->ShowMes("正在导出第" + strList + "篇博文:" + iter->first);
CString blogPath(iter->first);
blogPath.Replace('\\', '_');
blogPath.Replace('/', '_');
blogPath = blogFolderPath + blogPath + ".html";
if (!cf.Open(blogPath, CFile::modeCreate | CFile::modeWrite)){
dlg->ShowMes("创建文件失败" + blogPath);
return 2;
}
cf.Write(urlData, urlData.GetLength());
cf.Close(); if (dlg->stopRun){
return 1;
} }
return 0;
}

9. 遍历博文时,只需依次访问之前保存的链接即可,实现如下:

void CBlogExportDlg::OnBnClickedButtonRead()
{
clearMes();
unsigned tid;
unsigned long thd = _beginthreadex(NULL, 0, CBlogExportDlg::ReadCycle, this, 0, &tid);
if (thd != NULL)
{
CloseHandle((HANDLE)thd);
}
} unsigned __stdcall CBlogExportDlg::ReadCycle(void* p)
{
CBlogExportDlg* dlg = (CBlogExportDlg*)p;
dlg->stopRun = false;
std::map<CString, CString>::iterator iter;
for (iter = dlg->FileListMap.begin(); iter != dlg->FileListMap.end(); iter++){
dlg->blogAdr = iter->second;
dlg->ShowBlogAdr();
dlg->ShowMes("正在遍历博文:" + iter->first);
Sleep(3000);
if (dlg->stopRun){
return 1;
} }
return 0;
}

CSDN免积分下载地址:

2014.08.01更新: http://download.csdn.net/detail/yxstars/7786309

2014.09.05更新:http://download.csdn.net/detail/yxstars/7867583

文/yanxin8原创,获取更多信息请访问http://yanxin8.com/222.html

博客导出工具(C++实现,支持sina,csdn,自定义列表)的更多相关文章

  1. 用Python编写博客导出工具

    用Python编写博客导出工具 罗朝辉 (http://kesalin.github.io/) CC 许可,转载请注明出处   写在前面的话 我在 github 上用 octopress 搭建了个人博 ...

  2. CSDN博客导出工具 Mac By Swift

    写这篇文章的主要目的是了解Swift语言本身,如何以及Objc和第三方交互框架 必须先用CSDN帐户登录.您可以导出所有的博客文章,加入YAML当首标信息,包括对应标签和分类在头制品信息,和底座式(原 ...

  3. BlogPublishTool - 博客发布工具

    BlogPublishTool - 博客发布工具 这是一个发布博客的工具.本博客使用本工具发布. 本工具源码已上传至github:https://github.com/ChildishChange/B ...

  4. 多平台博客发布工具OpenWrite的使用

    1 介绍 OpenWrite官网 OpenWrite是一款便捷的多平台博客发布工具,可以在OpenWrite编写markdown文档,然后发布到其他博客平台,目前已经支持CSDN.SegmentFau ...

  5. Mac端博客发布工具推荐

    引子 推荐一款好用的 Mac 端博客发布工具. 下载地址 echo 博客对接 这里以cnblog为例.接入类型为metawebblog,access point可以在cnblog的设置最下边找到,然后 ...

  6. Golang拼接字符串的5种方法及其效率_Chrispink-CSDN博客_golang 字符串拼接效率 https://blog.csdn.net/m0_37422289/article/details/103362740

    Different ways to concatenate two strings in Golang - GeeksforGeeks https://www.geeksforgeeks.org/di ...

  7. MetaWeblog博客客户端工具之Windows Live Writer

    吐槽&注意的坑: 刚听说了有这么一个东西,据说Windows Live Writer开源之后就改名为Open Live Writer,我以为Open Live Writer就要比Windows ...

  8. 【转】如何使用离线博客发布工具发布CSDN的博客文章

    目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...

  9. 将Medium中的博客导出成markdown

    Medium(https://medium.com)(需要翻墙访问)是国外非常知名的一个博客平台.上面经常有很多知名的技术大牛在上面发布博客,现在一般国内的搬运的技术文章大多数都是来自于这个平台. M ...

随机推荐

  1. python-appium练习编写脚本时遇到问题

    遇到问题: 1.安卓4.2及以下系统无法识别resource-id属性 只能用text属性识别 2.输入中文无法识别 脚本最顶部增加#coding=utf-8 3.对象无法识别resource-id属 ...

  2. Windows环境Mycat数据库分库分表中间件部署

    下载地址MYCAT官方网站 jdk安装配置 首先去oracle官网下载并安装jdk8,添加环境变量,JAVA_HOME设置为D:\Worksoftware\Java\jdk1.8 CLASSPATH设 ...

  3. 【练习】创建私有的dblink

    1.创建dblink第一种方法,是在本地数据库tnsnames.ora文件中配置了要远程访问的数据库. .设置监听: ①[root@host02 ~]# vi /etc/hosts 添加:[IP和名字 ...

  4. x86_64 Ubuntu 14.04 LST安装gcc4.1.2 转载

    由于编译源码软件需要用到gcc4.1.2版本,但是本机已经安装有gcc4.8.4,下载gcc4.1.2源码编译总会出现运行找不到库文件错误,或者i386和x86_64不兼容问题,在http://ask ...

  5. Ajax.BeginForm 上传文件

    在 Mvc 中上传文件时通常使用 Html.BeginForm 标签,同时对Form 添加属性 enctype = "multipart/form-data",前端代码如下: @H ...

  6. break 和 continue

    break 和 continue 相同点: 都 用在循环体内,如 switch.for.while.do while的程序块中,用于控制程序循环语句的执行 不同点: break可以离开当前switch ...

  7. 洛谷P2264 情书

    P2264 情书 88通过 971提交 题目提供者lin_toto 标签字符串 难度提高+/省选- 提交该题 讨论 题解 记录 最新讨论 yyy快把题目改回来 噫 这题的题目好逗啊... 情书std ...

  8. Android中各组件的生命周期

    1.Activity生命周期图 二.activity三种状态 (1)active:当Activity运行在屏幕前台(处于当前任务活动栈的最上面),此时它获取了焦点能响应用户的操作,属于活动状态,同一个 ...

  9. TCP/IP详解学习笔记(14)-- TCP可靠传输的实现

    1.概述      为方便描述可靠传输原理,假定数据传输只在一个方向上进行,即A发送数据,B给出确认 2.以字节为单位的滑动窗口      TCP的滑动窗口是以字节为单位的.为了便于说明,字节编号取得 ...

  10. php 调用.net的webservice 需要注意的

    首先 SoapClient类这个类用来使用Web services.SoapClient类可以作为给定Web services的客户端.它有两种操作形式:* WSDL 模式* Non-WSDL 模式在 ...