C#把博客園編譯成CHM文檔閱讀

 

使用CHM文檔 閱讀隨筆

背景

  咱們在開發的過程當中,經常都會想記錄下來一些東西,能夠成文的,則以隨筆的形式發佈,那些不能成文的,例如某bug的解決方案,或者開發中的注意事項,甚至是某個SQL語句,以隻言片語的形式記錄在文章、日記裏,這樣,本身就能在不一樣的設備、終端上查看本身記錄的東西。css

  博客園的文章,若是不設置在首頁顯示的話,我的以爲查看起來不是很方便。想到本身曾作了一個數據庫CHM文檔生成工具,因而,不論是隨筆,仍是文章,可否也經過CHM文檔的形式查看呢。想到這裏,個人需求就產生了。html

效果預覽

資源下載

  可執行程序正則表達式

  示例CHM文檔數據庫

  源代碼json

開發思路

  1.獲得博客內容。以前想過經過url請求的方式獲取到博客正文部分,可是後臺的文章或日記處理起來相對麻煩,因而採用了博客備份獲得的xml文件,按照xml文件結構,定義數據結構,讀取xml數據。怎麼讀取,這裏就不細講,若有須要,移步至《以讀取博客園隨筆備份爲例 將xml 序列化成json,再序列化成對象》數據結構

  2.遍歷博客的博文,而後將博文的html內容,以html形式的存儲。可是xml裏存儲的僅僅是博客的正文部份內容,整個頁面顯示框架是沒有的。此時,我打開任意個人任意一篇博客,而後將網頁文件所有下載到本地,對應的js或圖片會存儲在files文件夾中。打開下載的html,去掉頁眉,側邊欄等,最後獲得咱們須要的模版,將關鍵地方使用特殊字符串佔位,替換後的模版效果如圖:mvc

  利用上述模版,替換後的網頁效果,僅僅包含正文部分了。框架

  3.獲得模板化的html博客正文以後,就能夠輕鬆的將其編譯成CHM文件了。具體的編譯方式很簡單,下面貼一下代碼:ide

CHM編譯封裝類
#region 版權信息
/* ==============================================================================
   * 文 件 名: ChmHelp
   * 功能描述:Chm編譯封裝類
   * Copyright (c) 2013 武漢經緯視通科技有限公司
   * 創 建 人: alone
   * 建立時間: 2012/12/05 20:53:29
   * 修 改 人: alone
   * 修改時間: 2013/3/01 18:22:03
   * 修改描述: 使用hha.dll接口方法
   * 版    本: v1.0.0.0
   * ==============================================================================*/
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Chen.Ext;
/* 變量含義 rootPath:待編譯的文件夾 rootParent:rootPath的父目錄
 * 1.將rootPath目錄下的文件編譯成CHM。
 * 2.將編譯過程當中產生的hhp、hhc、hhk文件均放在rootParent下。
 * 3.基於rootParent,獲取待編譯文件的相對路徑(相對rootParent)。
 */
namespace Chen.Common
{
    /// <summary>
    /// Chm輔助類
    /// </summary>
    public class ChmHelp
    {
        #region 成員定義
        //Chm文件保存路徑
        public string ChmFileName { get; set; }
        //Chm文件Titie
        public string Title { get; set; }
        //編譯文件夾路徑
        public string RootPath
        {
            get { return rootPath; }
            set
            {
                rootPath = Path.GetFullPath(value);
                //獲取上級目錄的完整路徑
                //指定文件夾的父目錄是做爲關鍵字被文件的完整路徑替換的,此時目錄必須攜帶\\
                DirectoryInfo di = new DirectoryInfo(rootPath);
                rootParent = di.Parent.FullName + "\\";
            }
        }
        //默認頁面 相對編譯文件夾的路徑
        public string DefaultPage
        {
            get//編譯時路徑是相對rootParent
            {
                var rootName = Path.GetFileName(rootPath);
                return rootName + "\\" + defaluePage;
            }
            set //賦值時路徑是相對rootPath。
            {
                defaluePage = value;
            }
        }
        public int FileCount { get { return fileCount; } }
        //私有變量
        private string rootParent;
        private string rootPath;
        private string defaluePage;
        private int fileCount = 0;
        //CHM相關文件內容
        private StringBuilder hhcBody = new StringBuilder();
        private StringBuilder hhpBody = new StringBuilder();
        private StringBuilder hhkBody = new StringBuilder();
        private bool deleteTmp = true;
        //日誌信息
        private StringBuilder sbMessage = new StringBuilder();
        public event Action<string> logHandle;
        #endregion

        #region hha 方法引入
        [DllImport("hha.dll")]
        private extern static void HHA_CompileHHP(string hhpFile, CompileLog g1, CompileLog g2, int stack);
        delegate bool CompileLog(string log);
        //編譯信息
        private bool CompileLoging(string log)
        {
            if (logHandle != null) logHandle(log);
            return true;
        }
        private bool CompileProcess(string log)
        {
            return true;
        }
        #endregion

        #region 構造所須要的文件
        private void Create(string path)
        {
            //獲取文件
            var strFileNames = Directory.GetFiles(path);
            //獲取子目錄
            var strDirectories = Directory.GetDirectories(path);
            //給該目錄添加UL標記
            if (strFileNames.Length > 0 || strDirectories.Length > 0)
                hhcBody.AppendLine("    <UL>");
            //處理獲取的文件
            foreach (string filename in strFileNames)
            {
                var fileItem = new StringBuilder();
                fileItem.AppendLine("    <LI> <OBJECT type=\"text/sitemap\">");
                fileItem.AppendLine("        <param name=\"Name\" value=\"{0}\">".FormatString(Path.GetFileNameWithoutExtension(filename)));
                fileItem.AppendLine("        <param name=\"Local\" value=\"{0}\">".FormatString(filename.Replace(rootParent, string.Empty)));
                fileItem.AppendLine("        <param name=\"ImageNumber\" value=\"11\">");
                fileItem.AppendLine("        </OBJECT>");
                //添加文件列表到hhp
                hhpBody.AppendLine(filename);
                hhcBody.Append(fileItem.ToString());
                hhkBody.Append(fileItem.ToString());
                //記錄待編譯文件總數
                fileCount++;
            }
            //遍歷獲取的目錄
            foreach (string dirname in strDirectories)
            {
                if (dirname.Contains("content") || dirname.Contains("image")) continue;
                hhcBody.AppendLine("    <LI> <OBJECT type=\"text/sitemap\">");
                hhcBody.AppendLine("        <param name=\"Name\" value=\"{0}\">".FormatString(Path.GetFileName(dirname)));
                hhcBody.AppendLine("        <param name=\"ImageNumber\" value=\"1\">");
                hhcBody.AppendLine("        </OBJECT>");
                //遞歸遍歷子文件夾
                Create(dirname);
            }
            //給該目錄添加/UL標記
            if (strFileNames.Length > 0 || strDirectories.Length > 0)
            {
                hhcBody.AppendLine("    </UL>");
            }
        }
        private void CreateHHC()
        {
            var code = new StringBuilder();
            code.AppendLine("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">");
            code.AppendLine("<HTML>");
            code.AppendLine("<HEAD>");
            code.AppendLine("<meta name=\"GENERATOR\" content=\"EasyCHM.exe  www.zipghost.com\">");
            code.AppendLine("<!-- Sitemap 1.0 -->");
            code.AppendLine("</HEAD><BODY>");
            code.AppendLine("<OBJECT type=\"text/site properties\">");
            code.AppendLine("    <param name=\"ExWindow Styles\" value=\"0x200\">");
            code.AppendLine("    <param name=\"Window Styles\" value=\"0x800025\">");
            code.AppendLine("    <param name=\"Font\" value=\"MS Sans Serif,9,0\">");
            code.AppendLine("</OBJECT>");
            //遍歷文件夾 構建hhc文件內容
            code.Append(hhcBody.ToString());
            code.AppendLine("</BODY></HTML>");
            //File.WriteAllText(Path.Combine(SourcePath, "chm.hhc"), code.ToString(), Encoding.GetEncoding("gb2312"));
            File.WriteAllText(".//chm.hhc", code.ToString(), Encoding.GetEncoding("gb2312"));
        }
        private void CreateHHP()
        {
            var code = new StringBuilder();
            code.AppendLine("[OPTIONS]");
            code.AppendLine("CITATION=Made by Chen");//製做人
            code.AppendLine("Compatibility=1.1 or later");//版本
            code.AppendLine(@"Compiled file=" + ChmFileName);//生成chm文件路徑
            code.AppendLine("Contents file=chm.HHC");//hhc文件路徑
            code.AppendLine("COPYRIGHT=www.jinwin.com");//版權全部
            code.AppendLine(@"Default topic={1}");//CHM文件的首頁
            code.AppendLine("Default Window=Main");//目標文件窗體控制參數,這裏跳轉到Windows小節中,與其一致便可
            code.AppendLine("Display compile notes=Yes");//顯示編譯信息
            code.AppendLine("Display compile progress=Yes");//顯示編譯進度
            //code.AppendLine("Error log file=error.Log");//錯誤日誌文件
            code.AppendLine("Full-text search=Yes");//是否支持全文檢索信息
            code.AppendLine("Index file=chm.HHK");//hhk文件路徑
            code.AppendLine("Title={0}");//CHM文件標題
            //code.AppendLine("Flat=NO");//編譯文件不包括文件夾
            code.AppendLine("Enhanced decompilation=yes");//編譯文件不包括文件夾
            code.AppendLine();
            code.AppendLine("[WINDOWS]");
            //例子中使用的參數 0x20 表示只顯示目錄和索引
            code.AppendLine("Main=\"{0}\",\"chm.hhc\",\"chm.hhk\",\"{1}\",\"{1}\",,,,,0x63520,180,0x104E, [0,0,745,509],0x0,0x0,,,,,0");
            //Easy Chm使用的參數 0x63520 表示目錄索引搜索收藏夾
            //code.AppendLine("Main=\"{0}\",\"chm.HHC\",\"chm.HHK\",\"{1}\",\"{1}\",,,,,0x63520,271,0x304E,[0,0,745,509],,,,,,,0");
            code.AppendLine();
            code.AppendLine("[MERGE FILES]");
            code.AppendLine();
            code.AppendLine("[FILES]");
            code.Append(hhpBody.ToString());

            // File.WriteAllText(Path.Combine(SourcePath, "chm.hhp"), code.ToString().FormatString(Title, DefaultPage), Encoding.GetEncoding("gb2312"));
            File.WriteAllText(".//chm.hhp", code.ToString().FormatString(Title, DefaultPage), Encoding.GetEncoding("gb2312"));
        }
        private void CreateHHK()
        {
            var code = new StringBuilder();
            code.AppendLine("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">");
            code.AppendLine("<HTML>");
            code.AppendLine("<HEAD>");
            code.AppendLine("<meta name=\"GENERATOR\" content=\"EasyCHM.exe  www.zipghost.com\">");
            code.AppendLine("<!-- Sitemap 1.0 -->");
            code.AppendLine("</HEAD><BODY>");
            code.AppendLine("<OBJECT type=\"text/site properties\">");
            code.AppendLine("    <param name=\"ExWindow Styles\" value=\"0x200\">");
            code.AppendLine("    <param name=\"Window Styles\" value=\"0x800025\">");
            code.AppendLine("    <param name=\"Font\" value=\"MS Sans Serif,9,0\">");
            code.AppendLine("</OBJECT>");
            code.AppendLine("<UL>");
            //遍歷文件夾 構建hhc文件內容
            code.Append(hhkBody.ToString());
            code.AppendLine("</UL>");
            code.AppendLine("</BODY></HTML>");
            //File.WriteAllText(Path.Combine(SourcePath, "chm.hhk"), code.ToString(), Encoding.GetEncoding("gb2312"));
            File.WriteAllText(".//chm.hhk", code.ToString(), Encoding.GetEncoding("gb2312"));
        }
        #endregion

        /// <summary>
        /// 編譯
        /// </summary>
        /// <returns></returns>
        public void Compile()
        {
            //準備hhp hhc hhk文件
            Create(RootPath);
            CreateHHC();
            CreateHHK();
            CreateHHP();
            var path = ".//chm.hhp";
            HHA_CompileHHP(path, CompileLoging, CompileProcess, 0);
            DeleteTmpFile();
        }
        /// <summary>
        /// 使用hhc.exe進行編譯 暫時不使用該方式 侷限性較大
        /// </summary>
        /// <param name="hhpPath"></param>
        private void CompileByHHC(string hhpPath)
        {
            var hhcPath = string.Empty;
            var program = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
            if (File.Exists(".//hhc.exe"))
            {
                hhcPath = ".//hhc.exe";
            }
            else if (File.Exists(program + @"\HTML Help Workshop\hhc.exe"))
            {
                hhcPath = program + @"\HTML Help Workshop\hhc.exe";
            }
            else if (File.Exists(program + @" (x86)\HTML Help Workshop\hhc.exe"))
            {
                hhcPath = program + @" (x86)\HTML Help Workshop\hhc.exe";
            }
            else
            {
                throw new Exception("未找到編譯核心文件hhc.exe");
            }
            var process = new Process();//建立新的進程,用Process啓動HHC.EXE來Compile一個CHM文件
            try
            {
                ProcessStartInfo processInfo = new ProcessStartInfo();
                processInfo.WindowStyle = ProcessWindowStyle.Hidden;
                processInfo.FileName = hhcPath;  //調入HHC.EXE文件 
                processInfo.Arguments = hhpPath;
                processInfo.UseShellExecute = false;
                processInfo.CreateNoWindow = false;
                process.StartInfo = processInfo;
                process.Start();
                process.WaitForExit(); //組件無限期地等待關聯進程退出
                if (process.ExitCode == 0)
                {
                    throw new Exception("編譯發生異常!");
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                process.Close();
            }
        }
        /// <summary>
        /// 反編譯
        /// </summary>
        /// <returns></returns>
        public bool DeCompile()
        {
            //反編譯時,Path做爲CHM文件路徑
            //獲得chm文件的絕對路徑
            string ExtportPath = Path.GetDirectoryName(ChmFileName);
            //命令參數含義
            //Path:導出的文件保存的路徑
            //ChmPath:Chm文件所在的路徑
            string cmd = " -decompile " + ExtportPath + " " + ChmFileName;//反編譯命令
            Process p = Process.Start("hh.exe", cmd);//調用hh.exe進行反編譯
            p.WaitForExit();
            return true;
        }
        //刪除臨時文件
        private void DeleteTmpFile()
        {
            if (deleteTmp)
            {
                var arr = new string[] { ".//chm.hhc", ".//chm.hhp", ".//chm.hhk" };
                foreach (var a in arr)
                {
                    if (File.Exists(a))
                    {
                        File.Delete(a);
                    }
                }
            }
        }
    }
}
調用示例
            //編譯CHM文檔
            ChmHelp chm = new ChmHelp();
            chm.RootPath = ".//cnblogs";
            chm.ChmFileName =Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop ),channel.title+".chm");
            chm.DefaultPage = "博文目錄.html";
            chm.Title = channel.title;
            chm.Compile();

  到這裏基本上就已經完成了,可是有一點須要改進。博客中插入的圖片,是url地址,若是閱讀該chm文件時,電腦沒有聯網,此時是影響閱讀的。此時咱們的工做就應該是下載博客正文所使用的圖片,存儲到本地,編譯到CHM文件中。工具

  4.首先提取博客正文的url連接,並下載圖片。圖片分兩種,手動插入的圖片和博客園顯示代碼時使用的圖片(展開代碼,摺疊代碼,複製代碼的圖標),前者下載時需防止圖片命名重複致使覆蓋,後者若是已經下載,則無需重複下載。下載成功後,對博客正文進行替換,將引用圖片的url連接替換成本地的相對途徑。

複製代碼
        /// <summary>
        /// 提取網頁文件中的圖片連接
        /// </summary>
        /// <param name="sHtmlText">html</param>
        /// <returns></returns>
        public static string[] GetHtmlImageUrls(string sHtmlText)
        {
            // 定義正則表達式用來匹配 img 標籤     
            Regex regImg = new Regex(@"<img\b[^<>]*?\bsrc[\s\t\r\n]*=[\s\t\r\n]*[""']?[\s\t\r\n]*(?<imgUrl>[^\s\t\r\n""'<>]*)[^<>]*?/?[\s\t\r\n]*>", RegexOptions.IgnoreCase);
            // 搜索匹配的字符串      
            MatchCollection matches = regImg.Matches(sHtmlText);
            int i = 0;
            string[] sUrlList = new string[matches.Count];
            // 取得匹配項列表        
            foreach (Match match in matches)
                sUrlList[i++] = match.Groups["imgUrl"].Value;
            return sUrlList;
        }
複製代碼

  到這裏,基本上所有完成了。

  程序使用的模版文件,全在content文件夾下,若是有朋友使用到自定義css,能夠手動的更改模版。

下節預告

  咱們既然能將本身的博客備份獲得的xml文件,轉換成chm文件,咱們也一樣能夠將某我的的博客隨筆備份成chm文件,參考《一鍵構造你的博客目錄》,它能夠獲得某個博客下的全部隨筆連接,既然可以獲得連接,咱們就能夠等到博客的正文,所以咱們一樣將其轉換成CHM文件。

  以啊漢的博客爲例,示例文檔下載,效果圖預覽:

  

 

 

出處:https://www.cnblogs.com/codealone/archive/2013/04/19/3029055.html

=======================================================================================

使用CHM文檔 採集隨筆(續)

背景

  上篇說到咱們能夠將本身的博客內隨筆/文章/日記備份獲得的xml 轉換成CHM文檔,若是咱們但願將某個大牛的博客隨筆所有導出,這個能不能實現呢?寫在這裏算是廢話了,既然有了這篇博客,那麼這個問題,必定是能夠解決的。

資源下載

  可執行程序

  源代碼

  示例文檔截圖(路過秋天):

      

開發思路

  1.根據博客園ID獲得隨筆類別,如地址爲 http://www.cnblogs.com/cyq1162/,則博客園ID爲cyq1162,請求頁面http://www.cnblogs.com/cyq1162/mvc/blog/sidecolumn.aspx。請求結果以下:

     

  經過正則匹配到該頁面的連接,以某個連接爲例,http://www.cnblogs.com/cyq1162/category/268820.html,其中包含cyq1162/category,基於這樣的規則,咱們能夠獲得全部的隨筆分類連接。

  2.獲得隨筆分類連接以後,則請求該連接的內容,獲得該隨筆下的全部文章連接。文章連接,以某個連接爲例,http://www.cnblogs.com/cyq1162/archive/2013/03/17/2964746.html,其中包含cyq1162/archive,基於這樣的規則,咱們能夠獲得全部的文章連接。

  3.獲得文章的連接,這樣就能獲得文章正文。咱們要獲取的有文章標題、文章正文、發佈時間。這裏沒有去嘗試獲取文章做者,很差獲取。前面指的須要獲取的3個內容,在某個明確id的節點裏。這裏使用了HtmlAgilityPack進行HTML解析,感受很是方便,能夠直接根據ID獲得元素,而後獲取它的內容。解析代碼以下:

複製代碼
                            //下載隨筆內容 替換後保存本地
                            var contentCode = GetContent(articleUrl);//獲取隨筆內容
                            HtmlDocument htmlCode = new HtmlDocument();
                            htmlCode.LoadHtml(contentCode);
                            var titleNode = htmlCode.GetElementbyId("cb_post_title_url");
                            var postBody = htmlCode.GetElementbyId("cnblogs_post_body");
                            var postDate = htmlCode.GetElementbyId("post-date");
                            //var topics = htmlCode.GetElementbyId("topics");
                            var localHtml = template
                       .Replace("{channelTitle}", titleNode.InnerText)//博文標題
                       .Replace("{preContent}", DownImage(postBody.InnerHtml))//博文內容
                       .Replace("{channelHref}", titleNode.GetAttributeValue("href", "#"))//博文地址
                       .Replace("{channelLink}", userId + ".cnblogs.com")//博客地址
                       .Replace("{channelAuthor}", userId);//博文做者
複製代碼

 

  4.下一步則是編譯CHM了,這裏就不重複介紹了。

 

  其中參考啊漢的博文 《一鍵構造你的博客目錄》 構造了隨筆目錄。

  代碼邏輯就這麼多,比較簡單,但願大夥喜歡。若是設置首頁不顯示隨筆分類的話,是沒法採集的,如果博客引用了自定義樣式,須要手動添加該樣式在。有其餘的問題,能夠向我反饋,也能夠本身下載代碼調試看看。

 

 

出處:https://www.cnblogs.com/codealone/archive/2013/04/23/3037780.html

相關文章
相關標籤/搜索