如何批量上传笔记到各大博客平台(支持md、.doc等多种格式)
前言
有时我们并不在博客平台上写笔记,可能是用笔记软件,比如有道、印象笔记等,也有可能放在github,使用hexo搭建等等。万一哪天有想安得广厦千万间,大庇天下寒士俱欢颜的分享精神,想把这些笔记公开到博客园上怎么办?总不能一个个上传把。那么要实现对博客的批量上传则必须使用MetaWeblog的API的功能。 什么是MetaWeblogAPI? MetaWeblog API(MWA)是一个Blog程序接口标准,允许外部程序来获取或者设置Blog的文字和熟悉。他建立在XMLRPC接口之上,并且已经有了很多的实现。 mac电脑的管理软件在文末有所提(MWEB)及但是由于我不喜欢(买不起)苹果
1.metaweblog与blogid(以博客园为例)
要上传到博客的文件大多数通用的文件是html文件,如果你的是markdown文件,请移步我的另一篇文章有详细介绍如何将md文件转换为html文件。如果你用的是word文件,此处以Microsoft Office为例你可以直接在另存为中保存为网页文件格式:
格式.assets\image-20200804204814028.png)
以下为博客园的主要程序接口条目2
对于博客园来说,要注意的是每一链接,都应该加上对应的appKey以及用户名和密码,第一次还需要获得blogid的参数。
以博客园官方的metaweblog api介绍为例:
基本的函数规范有三种:
metaWeblog.newPost (blogid, username, password, struct, publish)
返回一个字符串,可能是Blog的ID。 metaWeblog.editPost (postid, username, password, struct, publish)
返回一个Boolean值,代表是否修改成功。 metaWeblog.getPost (postid, username, password)
返回一个Struct。
1.1如何获得blogid
其中调取api需要的参数为如下,可返回blogid,但是博客园的信息说明是如此的隐晦也有可能是我没有找到地方,毕竟对我等不怎么写代码的人来说并没有这样的天赋能直接看出那个是需要的参数,我将博客园对应的参数标注如下:
知道了参数之后,我们可以使用如下方法来获得我们需要的信息:
a.使用python调取api获取blogid
import xmlrpc.client as xmlrpclib
serviceUrl, appkey = 'http://rpc.cnblogs.com/metaweblog/UserName', 'xxxx'
usr, passwd = 'xxxxx', 'xxxx'
server = xmlrpclib.ServerProxy("http://rpc.cnblogs.com/metaweblog/UserName")
blogInfo = server.blogger.getUsersBlogs(appkey, usr, passwd)
print(blogInfo)
b. 使用postman来获取blogid
再此感谢来自csdn的一篇名为《XML-RPC 简单理解与博客园的MetaWeblog协议》的博文3其中有详细的xml请求与返回样例。
例如blogger.getUsersBlogs请求可以输入以下xml条目
请求样例:
<?xml version="1.0"?>
<methodCall>
<methodName>blogger.getUsersBlogs</methodName>
<params>
<param>
<value><string>appkey</string></value>
</param>
<param>
<value><string>username</string></value>
</param>
<param>
<value><string>password</string></value>
</param>
</params>
</methodCall>
标准返回样例如下:
<?xml version="1.0"?>
<methodResponse>
<params>
<param>
<value>
<array>
<data>
<value>
<struct>
<member>
<name>blogid</name>
<value>
<string>dsafds</string>
</value>
</member>
<member>
<name>url</name>
<value>
<string>http://www.cnblogs.com/caipeiyu/</string>
</value>
</member>
<member>
<name>blogName</name>
<value>
<string>蛀牙</string>
</value>
</member>
</struct>
</value>
</data>
</array>
</value>
</param>
</params>
</methodResponse>
使用postman来复刻操作:
如果有编程的大佬的话就可以根据此来编写一个程序来实现博客编写软件了。其中我在github找到了一款markdownmonster就可以实现部分这样的功能我将在下文说到,此时我就是使用该工具来上传编辑博客的
2.将文档通过metaweblogapi批量上传至博客平台
a. (未亲身实验)使用python来管理将之前转换的html文件上传至博客
import xmlrpc.client as xmlrpclib
import codecs
import os
import sys
import threading
from time import strftime
serviceUrl, appkey = 'http://rpc.cnblogs.com/metaweblog/WeyneChen', 'xxxx'
blogid = 'xxxx'
usr, passwd = 'xxxx', 'xxxx'
def postfile(filepath):
with codecs.open(filepath, 'r', encoding='utf-8', errors='xmlcharrefreplace') as f:
des = f.read()
filename = os.path.basename(filepath)[:-5]
cate_list = ["[随笔分类]python"]
post = dict(description=des, title=filename, dateCreate=strftime(
"%Y%m%dT%H:%M:%S"), categories=cate_list)
server = xmlrpclib.ServerProxy(
"http://rpc.cnblogs.com/metaweblog/WeyneChen")
newPost = server.metaWeblog.newPost(blogid, usr, passwd, post, True)
def getAllFile(path, suffix='.'):
"recursive is enable"
f = os.walk(path)
fpath = []
for root, dir, fname in f:
for name in fname:
if name.endswith(suffix):
fpath.append(os.path.join(root, name))
return fpath
def transferAll(path):
flist = getAllFile(path, ".html")
def post():
if len(flist) != 0:
fname = flist.pop()
postfile(fname)
print("post %s" % fname)
print(strftime("%Y%m%dT%H:%M:%S"))
t = threading.Timer(60, post)
t.start()
else:
exit()
t = threading.Timer(3, post)
t.start()
if __name__ == "__main__":
path = ''
if len(sys.argv) == 1:
path = os.getcwd()
elif len(sys.argv) == 2:
path = sys.argv[1]
else:
print("error parameter")
exit()
transferAll(path)
注意!:这里有个注意点就是,因为博客园的限制,不能连续不断的发表,所以使用threading
间隔60S发一次。
a.2 使用asp.net来调用API (不懂asp亲自临摹失败了)
using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using LitJson;
using metaWeblogTest;
public partial class ceshi : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
M_MetaWeblog m_blog = new M_MetaWeblog();
//m_blog.Url = "http://www.cnblogs.com/webapi/services/metaweblog.aspx";//博客园
m_blog.Url = "http://upload.move.blog.sina.com.cn/blog_rebuild/blog/xmlrpc.php";//sina
Post newPost = new Post();
newPost.dateCreated = DateTime.Now;
newPost.title = "发布测试1";
newPost.description = "发布测试1描述";
//string res=m_blog.newPost("id", "博客园用户名", "博客园密码", newPost, true);//博客园
string res = m_blog.newPost("id", "sina博客用户名", "sina博客密码", newPost, true);//sina
Response.Write(res);
}
}
需要依赖(引用)xmlrpc,以下是我能搜罗找到的一些下载地址:
在你的编辑器中引用dll文件(以visual studio为例):
using System;
using CookComputing.XmlRpc;
namespace metaWeblogTest
{
#region 微软MSN网站 使用的 MetaWeblog API.
/// 这个结构代表用户的博客基本信息
/// </summary>
[XmlRpcMissingMapping(MappingAction.Ignore)]
public struct UserBlog
{
public string url;
public string blogid;
public string blogName;
}
/// <summary>
/// 这个结构代表用户信息
/// </summary>
[XmlRpcMissingMapping(MappingAction.Ignore)]
public struct UserInfo
{
public string url;
public string blogid;
public string blogName;
public string firstname;
public string lastname;
public string email;
public string nickname;
}
/// <summary>
/// 这个结构代表博客分类信息
/// 这后面的getCategories()方法会取到CATEGORY数据。
/// </summary>
[XmlRpcMissingMapping(MappingAction.Ignore)]
public struct Category
{
public string description;
public string title;
}
/// <summary>
/// 这个结构代表博客( 文章 )信息。
/// 这后面的 editPost()方法, getRecentPosts()方法 和 getPost()方法 会取倒POST数据 .
/// </summary>
[XmlRpcMissingMapping(MappingAction.Ignore)]
public struct Post
{
public DateTime dateCreated;
public string description;
public string title;
public string postid;
public string[] categories;
}
#endregion
#region 网站:http://msdn.microsoft.com/en-us/library/aa905670.aspx
///// <summary>
///// 微软MSN网站 使用的 MetaWeblog API.
//// 网站:http://msdn.microsoft.com/en-us/library/aa905670.aspx
///// </summary>
public class M_MetaWeblog : XmlRpcClientProtocol
{
/// <summary>
/// Returns the most recent draft and non-draft blog posts sorted in descending order by publish date.
/// </summary>
/// <param name="blogid"> This should be the string MyBlog, which indicates that the post is being created in the user’s blog. </param>
/// <param name="username"> The name of the user’s space. </param>
/// <param name="password"> The user’s secret word. </param>
/// <param name="numberOfPosts"> The number of posts to return. The maximum value is 20. </param>
/// <returns></returns>
/// TODO:得到最近发布的帖子
[XmlRpcMethod("metaWeblog.getRecentPosts")]
public Post[] getRecentPosts(
string blogid,
string username,
string password,
int numberOfPosts)
{
return (Post[])this.Invoke("getRecentPosts", new object[] { blogid, username, password, numberOfPosts });
}
/// <summary>
/// Posts a new entry to a blog.
/// </summary>
/// <param name="blogid"> This should be the string MyBlog, which indicates that the post is being created in the user’s blog. </param>
/// <param name="username"> The name of the user’s space. </param>
/// <param name="password"> The user’s secret word. </param>
/// <param name="post"> A struct representing the content to update. </param>
/// <param name="publish"> If false, this is a draft post. </param>
/// <returns> The postid of the newly-created post. </returns>
/// TODO:增加一个最新的帖子
[XmlRpcMethod("metaWeblog.newPost")]
public string newPost(
string blogid,
string username,
string password,
Post content,
bool publish)
{
return (string)this.Invoke("newPost", new object[] { blogid, username, password, content, publish });
}
/// <summary>
/// Edits an existing entry on a blog.
/// </summary>
/// <param name="postid"> The ID of the post to update. </param>
/// <param name="username"> The name of the user’s space. </param>
/// <param name="password"> The user’s secret word. </param>
/// <param name="post"> A struct representing the content to update. </param>
/// <param name="publish"> If false, this is a draft post. </param>
/// <returns> Always returns true. </returns>
/// TODO:更新一个帖子
[XmlRpcMethod("metaWeblog.editPost")]
public bool editPost(
string postid,
string username,
string password,
Post content,
bool publish)
{
return (bool)this.Invoke("editPost", new object[] { postid, username, password, content, publish });
}
/// <summary>
/// Deletes a post from the blog.
/// </summary>
/// <param name="appKey"> This value is ignored. </param>
/// <param name="postid"> The ID of the post to update. </param>
/// <param name="username"> The name of the user’s space. </param>
/// <param name="password"> The user’s secret word. </param>
/// <param name="post"> A struct representing the content to update. </param>
/// <param name="publish"> This value is ignored. </param>
/// <returns> Always returns true. </returns>
/// TODO:删除一个帖子
[XmlRpcMethod("blogger.deletePost")]
public bool deletePost(
string appKey,
string postid,
string username,
string password,
bool publish)
{
return (bool)this.Invoke("deletePost", new object[] { appKey, postid, username, password, publish });
}
/// <summary>
/// Returns information about the user’s space. An empty array is returned if the user does not have a space.
/// </summary>
/// <param name="appKey"> This value is ignored. </param>
/// <param name="postid"> The ID of the post to update. </param>
/// <param name="username"> The name of the user’s space. </param>
/// <param name="password"></param>
/// <returns> An array of structs that represents each of the user’s blogs. The array will contain a maximum of one struct, since a user can only have a single space with a single blog. </returns>
/// TODO:得到用户的博客清单
[XmlRpcMethod("blogger.getUsersBlogs")]
public UserBlog[] getUsersBlogs(
string appKey,
string username,
string password)
{
return (UserBlog[])this.Invoke("getUsersBlogs", new object[] { appKey, username, password });
}
/// <summary>
/// Returns basic user info (name, e-mail, userid, and so on).
/// </summary>
/// <param name="appKey"> This value is ignored. </param>
/// <param name="postid"> The ID of the post to update. </param>
/// <param name="username"> The name of the user’s space. </param>
/// <param name="password"></param>
/// <returns> A struct containing profile information about the user.
/// Each struct will contain the following fields: nickname, userid, url, e-mail,
/// lastname, and firstname. </returns>
/// TODO:得到用户信息
[XmlRpcMethod("blogger.getUserInfo")]
public UserInfo getUserInfo(
string appKey,
string username,
string password)
{
return (UserInfo)this.Invoke("getUserInfo", new object[] { appKey, username, password });
}
/// <summary>
/// Returns a specific entry from a blog.
/// </summary>
/// <param name="postid"> The ID of the post to update. </param>
/// <param name="username"> The name of the user’s space. </param>
/// <param name="password"> The user’s secret word. </param>
/// <returns> Always returns true. </returns>
/// TODO:获取一个帖子
[XmlRpcMethod("metaWeblog.getPost")]
public Post getPost(
string postid,
string username,
string password)
{
return (Post)this.Invoke("getPost", new object[] { postid, username, password });
}
/// <summary>
/// Returns the list of categories that have been used in the blog.
/// </summary>
/// <param name="blogid"> This should be the string MyBlog, which indicates that the post is being created in the user’s blog. </param>
/// <param name="username"> The name of the user’s space. </param>
/// <param name="password"> The user’s secret word. </param>
/// <returns> An array of structs that contains one struct for each category. Each category struct will contain a description field that contains the name of the category. </returns>
/// TODO:得到博客分类
[XmlRpcMethod("metaWeblog.getCategories")]
public Category[] getCategories(
string blogid,
string username,
string password)
{
return (Category[])this.Invoke("getCategories", new object[] { blogid, username, password });
}
}
#endregion
}
b. Markdown文件直传博客园(.net core工具之dotnet tool)
小工具地址:https://dotnet.microsoft.com/learn/dotnet/hello-world-tutorial/intro
dotnet tool install -g dotnet-cnblog
进入需要上传的根目录打开命令行
dotnet-cnblog md文件名
第一次使用会输入,账号密码,输入即可,执行完毕生成一个同名md文件,其中图片已经上传到博客园,复制里边内容发布即可。4
c.使用MarkdownMonster(正在使用,自行集成asp调取API代码)
如果熟悉markdown的语法,你可以直接在该编辑器里编写,如果像我一样习惯了typora的编写,可以在该编辑下新建后去typora编写,注意在markdown中的图片链接格式改成如下格式后才能正常的将图片上传到博客平台。
支持Wordrpess 博客、支持 Metaweblog API 的博客服务、Wordpress.com、Evernote 和印象笔记、Blogger、Medium、Tumblr。
这款软件的功能还有很多,比如md文件与html文件互转,以及各种插件,也可以自己写插件获得自己心仪的功能。
MACOS markdown管理软件之 MWEB6
MWeb 功能非常丰富,介绍几个你认为值得强调的功能吧,你如何思考一个需求应不应该实现? 值得强调的功能主要有:
对图片插入的优化很好,比如说可以截图后 CMD + V 直接贴图;插入的本地图片会直接显示,增加易读性;自带图床功能。
支持 LaTeX 数学公式的编辑器内预览,可视化插入和编辑表格。
文档库和外部模式两大模式满足几乎所有 Markdown 使用需求,文档库模式还可以生成静态博客。
发布功能强大,支持发布/更新到 Wordrpess 博客、支持 Metaweblog API 的博客服务、Wordpress.com、Evernote 和印象笔记、Blogger、Medium、Tumblr。
3.博客园代码高亮(没卵用)
如果你使用的是上述python代码来批量上传网页文件到博客园的,其中pygments高亮代码如果需要在博客园网页上体现需要在页面定时css代码中加入样式(以博主的说法来说:这里有一行代码html_text = html_text.replace('highlight', 'cnblogs_code ')
可能会觉得奇怪。原因是mistune + pygments生成的代码块类名是highlight,而博客园默认的是cnblogs_code。所以为了代码能高亮,需要做两步:)
- 将highlight替换成cnblogs_code,即加入上面那行代码
- 进入博客园后台->设置->页面定制css代码,加入下面的样式
/*.cnblogs_code { background-color: #f5f5f5;border:1px}
.cnblogs_code {border-right:gray 1px solid;border-top:gray 1px solid;border-left:gray 1px solid;border-bottom:gray 1px solid;background-color:#fff;padding:2px}*/
.cnblogs_code .hll { background-color: #ffffcc }
.cnblogs_code .c { color: #999988; font-style: italic } /* Comment */
.cnblogs_code .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.cnblogs_code .k { color: #000000; font-weight: bold } /* Keyword */
.cnblogs_code .o { color: #000000; font-weight: bold } /* Operator */
.cnblogs_code .cm { color: #999988; font-style: italic } /* Comment.Multiline */
.cnblogs_code .cp { color: #999999; font-weight: bold; font-style: italic } /* Comment.Preproc */
.cnblogs_code .c1 { color: #999988; font-style: italic } /* Comment.Single */
.cnblogs_code .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
.cnblogs_code .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.cnblogs_code .ge { color: #000000; font-style: italic } /* Generic.Emph */
.cnblogs_code .gr { color: #aa0000 } /* Generic.Error */
.cnblogs_code .gh { color: #999999 } /* Generic.Heading */
.cnblogs_code .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.cnblogs_code .go { color: #888888 } /* Generic.Output */
.cnblogs_code .gp { color: #555555 } /* Generic.Prompt */
.cnblogs_code .gs { font-weight: bold } /* Generic.Strong */
.cnblogs_code .gu { color: #aaaaaa } /* Generic.Subheading */
.cnblogs_code .gt { color: #aa0000 } /* Generic.Traceback */
.cnblogs_code .kc { color: #000000; font-weight: bold } /* Keyword.Constant */
.cnblogs_code .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */
.cnblogs_code .kn { color: #000000; font-weight: bold } /* Keyword.Namespace */
.cnblogs_code .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */
.cnblogs_code .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */
.cnblogs_code .kt { color: #445588; font-weight: bold } /* Keyword.Type */
.cnblogs_code .m { color: #009999 } /* Literal.Number */
.cnblogs_code .s { color: #d01040 } /* Literal.String */
.cnblogs_code .na { color: #008080 } /* Name.Attribute */
.cnblogs_code .nb { color: #0086B3 } /* Name.Builtin */
.cnblogs_code .nc { color: #445588; font-weight: bold } /* Name.Class */
.cnblogs_code .no { color: #008080 } /* Name.Constant */
.cnblogs_code .nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */
.cnblogs_code .ni { color: #800080 } /* Name.Entity */
.cnblogs_code .ne { color: #990000; font-weight: bold } /* Name.Exception */
.cnblogs_code .nf { color: #990000; font-weight: bold } /* Name.Function */
.cnblogs_code .nl { color: #990000; font-weight: bold } /* Name.Label */
.cnblogs_code .nn { color: #555555 } /* Name.Namespace */
.cnblogs_code .nt { color: #000080 } /* Name.Tag */
.cnblogs_code .nv { color: #008080 } /* Name.Variable */
.cnblogs_code .ow { color: #000000; font-weight: bold } /* Operator.Word */
.cnblogs_code .w { color: #bbbbbb } /* Text.Whitespace */
.cnblogs_code .mf { color: #009999 } /* Literal.Number.Float */
.cnblogs_code .mh { color: #009999 } /* Literal.Number.Hex */
.cnblogs_code .mi { color: #009999 } /* Literal.Number.Integer */
.cnblogs_code .mo { color: #009999 } /* Literal.Number.Oct */
.cnblogs_code .sb { color: #d01040 } /* Literal.String.Backtick */
.cnblogs_code .sc { color: #d01040 } /* Literal.String.Char */
.cnblogs_code .sd { color: #d01040 } /* Literal.String.Doc */
.cnblogs_code .s2 { color: #d01040 } /* Literal.String.Double */
.cnblogs_code .se { color: #d01040 } /* Literal.String.Escape */
.cnblogs_code .sh { color: #d01040 } /* Literal.String.Heredoc */
.cnblogs_code .si { color: #d01040 } /* Literal.String.Interpol */
.cnblogs_code .sx { color: #d01040 } /* Literal.String.Other */
.cnblogs_code .sr { color: #009926 } /* Literal.String.Regex */
.cnblogs_code .s1 { color: #d01040 } /* Literal.String.Single */
.cnblogs_code .ss { color: #990073 } /* Literal.String.Symbol */
.cnblogs_code .bp { color: #999999 } /* Name.Builtin.Pseudo */
.cnblogs_code .vc { color: #008080 } /* Name.Variable.Class */
.cnblogs_code .vg { color: #008080 } /* Name.Variable.Global */
.cnblogs_code .vi { color: #008080 } /* Name.Variable.Instance */
.cnblogs_code .il { color: #009999 } /* Literal.Number.Integer.Long */
如下图所示添加后将其保存,我们就能获得代码块样式了。
效果预览:
未做样式前:
使用css样式后:
好吧其实我没感觉到有什么变化,也许是我不得其意,如果有小伙伴知道是什么问题欢迎评论指正,告知于我,感激不尽。
其他方面的博客平台的API详情请移步我的下一篇文章:
《各大博客平台的API调取》 链接