前言
有時我們並不在博客平台上寫筆記,可能是用筆記軟件,比如有道、印象筆記等,也有可能放在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調取》 鏈接