65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 将 Blogspot 博客导出到 HTML/GitHub

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.58/5 (5投票s)

2014年8月19日

LGPL3

2分钟阅读

viewsIcon

21487

LINQ-to-XML 示例。

我终于做到了:我购买了 LINQPad 的代码补全功能,这样我就可以轻松地编写 C# 脚本了。现在,当然,我本可以使用 C# 脚本 免费使用,但是……等等,我为什么没有这样做呢?

无论如何,在我的 上一篇文章中,我详细解释了如何在 GitHub 上设置博客。现在是时候将我旧的 Blogger 博客转换为一个闪亮的新 GitHub 格式了。

要从 Blogger 导出您的博客,请登录 Blogger,进入您的博客控制面板,进入 设置 | 其他 选项卡,然后单击“导出博客”。

您将获得一个 XML 文件,基本上是 Atom 格式。它很难理解,因为它没有任何换行符(除了您的博客模板中的换行符,这些换行符只是为了分散您的注意力)。它是一个 <feed> 根元素,包含许多 <entry> 元素,其中一些包含您的帖子,另一些包含元数据。

这是我编写的代码,用于导出到 GitHub。只需将其粘贴到 LINQPad 或其他工具中,将 filepath 更改为指向您的 xml 文件,然后运行它!

void Main()
{
    string filepath = @"C:\Downloads\Blog.xml";
    string text = File.ReadAllText(filepath);
    XDocument doc = XDocument.Parse(text);

    // Use XNamespaces to deal with those pesky "xmlns" attributes.
    // The underscore represents the default namespace.
    var _ = XNamespace.Get("http://www.w3.org/2005/Atom");
    var app = XNamespace.Get("http://purl.org/atom/app#");

    var posts = doc.Root.Elements(_+"entry")
        // An <entry> is either a post, or some bit of metadata no one cares about.
        // Exclude entries that don't have a child like <category term="...#post"/>
        .Where(entry => entry.Element(_+"category").Attribute("term").ToString().Contains("#post"))
        // Exclude any entries with an <app:draft> element except <app:draft>no</app:draft>
        .Where(entry => !entry.Descendants(app+"draft").Any(draft => draft.Value != "no"));

    var outfolder = Path.Combine(Path.GetDirectoryName(filepath), Path.GetFileNameWithoutExtension(filepath));
    Directory.CreateDirectory(outfolder);

    foreach (var entry in posts)
    {
       // Extract data from XML
        DateTime published = DateTime.Parse(entry.Element(_+"published").Value);
        DateTime updated = DateTime.Parse(entry.Element(_+"updated").Value);
        string title = entry.Element(_+"title").Value;
        string content = entry.Element(_+"content").Value;
        string type = entry.Element(_+"content").Attribute("type").Value ?? "html";
        XElement empty = new XElement("empty");
        XAttribute emptA = new XAttribute("empty","");
        string originalLink = ((entry.Elements(_+"link")
            .FirstOrDefault(e => e.Attribute("rel").Value == "alternate") ?? empty)
            .Attribute("href") ?? emptA).Value;
        string outFileName = string.Format("{0:yyyy-MM-dd}-{1}.{2}", published,
               Path.GetFileNameWithoutExtension(originalLink), type);
        var outPath = Path.Combine(outfolder, outFileName);

        if (content.Count(c => c == '\n') <= 3)
            content = AddLineBreaks(content); // optional

        // Write output file (partial HTML for Jekyll)
        using (StreamWriter output = File.CreateText(outPath)) {
            output.WriteLine("---");
            output.WriteLine("title: \"{0}\"", title);
            output.WriteLine("layout: post");
            output.WriteLine("# Pulled from Blogger. Last updated there on: {0:yyyy-MM-dd}", updated);
            output.WriteLine("---");
            if (originalLink != "")
                output.WriteLine("<small><p><i>This post was imported from "+
                 "<a href='{0}'>blogspot</a>.</i></p></small>", originalLink);
            output.WriteLine(""); // Disable Jekyll/Liquid
            output.Write(content);
            output.WriteLine("");
        }
    }
}

它将创建一个以 xml 文件命名的文件夹,并在该文件夹内为每个帖子创建一个 html 文件,如下所示

2007-09-03-hello-no-one.html
2011-07-05-why-wpf-sucks.html
2012-06-07-smart-tabs.html
2013-05-28-onward.html

这些文件名是 Jekyll 的正确格式,因此,如果您要迁移到 GitHub,只需将所有这些文件移动到您的 /_posts 文件夹,提交,就完成了!如果您想要“正确的”HTML 文件,请修改上面的代码以生成正确的代码,例如 <html><head>...</head>... 而不是 Jekyll 前置信息。

哦,顺便说一下,Blogger 导出的 HTML 在您的帖子中根本不包含任何换行符。所以我编写了这个小方法,在适当的位置添加一些换行符

string AddLineBreaks(string content)
{
    var sb = new StringBuilder(content.Length + 100);

    bool pre = false, fail;
    for (UString rest = content; !rest.IsEmpty;) {
        if (rest.StartsWith("<pre")) pre = true;
        if (rest.StartsWith("</pre")) pre = false;
        bool s;
        if ((s = rest.StartsWith("<br />")) || rest.StartsWith("<br/>")) {
            sb.Append(pre ? "\n" : "<br/>\n");
            rest = rest.Substring(s ? 6 : 5);
            continue;
        }
        if (rest.StartsWith("<li>") || rest.StartsWith("<p>") || rest.StartsWith("<tr>") || rest.StartsWith("<pre>") || rest.StartsWith("<blockquote>") || rest.StartsWith("<img"))
            sb.Append('\n');
        if (rest.StartsWith("</ul>") || rest.StartsWith("</ol>") || rest.StartsWith("</blockquote>"))
            sb.Append('\n');
        char c = (char)rest.PopFront(out fail);
        if (!fail) sb.Append(c);
    }

    return sb.ToString();
}

这依赖于我的 Loyc.Essentials.dll 库中的 UString(它是一种字符串切片)。如果您想使用此函数,请从 NuGet 下载 LoycCore

这段代码对我来说足够好了,希望对您来说也足够好……但我不知道图片是否有效(Blogger 肯定不在导出文件中包含图片)。注意:默认情况下,blogspot 中的 HTML 具有“隐式”换行符功能,其中换行符会自动转换为 <br/>。我关闭了该功能,因为它经常会破坏非平凡帖子的格式;如果您启用了该选项,我不确定 Blogger 在 XML 文件中提供的 HTML 是否包含这些自动插入的 <br/>

© . All rights reserved.