使用metaweblog API實現通用部落格釋出 之 API測試
使用metaweblog API實現通用部落格釋出 之 API測試
使用部落格比較少,一則是文筆有限,怕寫出的東西狗屁不通,有礙觀瞻, 二則是懶,很討厭要登入到網站上寫東西,也沒有那麼多時間(藉口)。個人最喜歡用於記錄的工具是Zim https://zim-wiki.org/ ,記錄東西超級方便,可惜只支援PC版本, 記錄的東西可以到處為MarkDown 格式,非常方便(你現在看到的這篇就是用Zim寫的)。
無意間看到Vs Code上有部落格園的外掛,作為程式設計師,順手google/百度了一下,原來通用部落格都支援使用metaweblog API來訪問,還支援直接釋出markdown 格式,簡直不要太好。 找了找2年前註冊的部落格源賬號,用來測試一下。
發揮典型中國程式設計師的拿來主義精神,經過goolgle/百度一番搜尋,參考以下文件進行API測試,在此表示感謝!!
https://www.cnblogs.com/caipeiyu/p/5475761.html
https://github.com/1024th/cnblogs_githook
1 在哪裡找API說明
在部落格設定最的最末端,有MetaWeblog 的訪問地址連結
點選進入頁面,有metaweblog API 的詳細說明
具體內容不贅述了。
2 測試API
使用python3 進行API測試,直接上程式碼:
#encoding = utf-8
#!/bin/sh python3
import xmlrpc.client as xmlrpclib
import json
‘‘‘
配置字典:
type | description(example)
str | metaWeblog url,部落格設定中有(‘https://rpc.cnblogs.com/metaweblog/1024th‘)
str | appkey,Blog地址名(‘1024th‘)
str | blogid,這個無需手動輸入,通過getUsersBlogs得到
str | usr,登入使用者名稱
str | passwd,登入密碼
str | rootpath,博文存放根路徑(新增git管理)
‘‘‘
‘‘‘
POST:
dateTime dateCreated - Required when posting.
string description - Required when posting.
string title - Required when posting.
array of string categories (optional)
struct Enclosure enclosure (optional)
string link (optional)
string permalink (optional)
any postid (optional)
struct Source source (optional)
string userid (optional)
any mt_allow_comments (optional)
any mt_allow_pings (optional)
any mt_convert_breaks (optional)
string mt_text_more (optional)
string mt_excerpt (optional)
string mt_keywords (optional)
string wp_slug (optional)
‘‘‘
class MetablogClient():
def __init__(self,configpath):
‘‘‘
@configpath: 指定配置檔案路徑
‘‘‘
self._configpath = configpath
self._config = None
self._server = None
self._mwb = None
def createConfig(self):
‘‘‘
建立配置
‘‘‘
while True:
cfg = {}
for item in [("url","metaWeblog url,部落格設定中有 (‘https://rpc.cnblogs.com/metaweblog/blogaddress‘)"),("appkey","Blog地址名(‘blogaddress‘)"),("usr","登入使用者名稱"),("passwd","登入密碼"),("rootpath","博文字地儲存根路徑")]:
cfg[item[0]] = input("輸入"+item[1])
try:
server = xmlrpclib.ServerProxy(cfg["url"])
userInfo = server.blogger.getUsersBlogs(
cfg["appkey"],cfg["usr"],cfg["passwd"])
print(userInfo[0])
# {‘blogid‘: ‘xxx‘,‘url‘: ‘xxx‘,‘blogName‘: ‘xxx‘}
cfg["blogid"] = userInfo[0]["blogid"]
break
except:
print("發生錯誤!")
with open(self._configpath,"w",encoding="utf-8") as f:
json.dump(cfg,f,indent=4,ensure_ascii=False)
def existConfig(self):
‘‘‘
返回配置是否存在
‘‘‘
try:
with open(self._configpath,"r",encoding="utf-8") as f:
try:
cfg = json.load(f)
if cfg == {}:
return False
else:
return True
except json.decoder.JSONDecodeError: # 檔案為空
return False
except:
with open(self._configpath,encoding="utf-8") as f:
json.dump({},f)
return False
def readConfig(self):
‘‘‘
讀取配置
‘‘‘
if not self.existConfig():
self.createConfig()
with open(self._configpath,encoding="utf-8") as f:
self._config = json.load(f)
self._server = xmlrpclib.ServerProxy(self._config["url"])
self._mwb = self._server.metaWeblog
def getUsersBlogs(self):
‘‘‘
獲取部落格資訊
@return: {
string blogid
string url
string blogName
}
‘‘‘
userInfo = self._server.blogger.getUsersBlogs(self._config["appkey"],self._config["usr"],self._config["passwd"])
return userInfo
def getRecentPosts(self,num):
‘‘‘
讀取最近的博文資訊
‘‘‘
return self._mwb.getRecentPosts(self._config["blogid"],self._config["passwd"],num)
def newPost(self,post,publish):
‘‘‘
釋出新博文
@post: 釋出內容
@publish: 是否公開
‘‘‘
while True:
try:
postid = self._mwb.newPost(self._config[‘blogid‘],self._config[‘usr‘],self._config[‘passwd‘],publish)
break
except:
time.sleep(5)
return postid
def editPost(self,postid,publish):
‘‘‘
更新已存在的博文
@postid: 已存在博文ID
@post: 釋出內容
@publish: 是否公開發布
‘‘‘
self._mwb.editPost(postid,publish)
def deletePost(self,publish):
‘‘‘
刪除博文
‘‘‘
self._mwb.deletePost(self._config[‘appkey‘],publish)
def getCategories(self):
‘‘‘
獲取博文分類
‘‘‘
return self._mwb.getCategories(self._config[‘blogid‘],self._config[‘passwd‘])
def getPost(self,postid):
‘‘‘
讀取博文資訊
@postid: 博文ID
@return: POST
‘‘‘
return self._mwb.getPost(postid,self._config[‘passwd‘])
def newMediaObject(self,file):
‘‘‘
資原始檔(圖片,音訊,視訊...)上傳
@file: {
base64 bits
string name
string type
}
@return: URL
‘‘‘
return self._mwb.newMediaObject(self._config[‘blogid‘],file)
def newCategory(self,categoray):
‘‘‘
新建分類
@categoray: {
string name
string slug (optional)
integer parent_id
string description (optional)
}
@return : categorayid
‘‘‘
return self._server.wp.newCategory(self._config[‘blogid‘],categoray)
```
#以上是對API的簡單封裝,萬事具備,開始測試
### 2.1 獲取分類
```python
import core.metablogclient as blogclient
client = blogclient.MetablogClient(‘blog_config.json‘)
client.readConfig()
catLst = client.getCategories()
print(catLst)
[{‘description‘: ‘[釋出至部落格園首頁]‘,‘htmlUrl‘: ‘‘,‘rssUrl‘: ‘‘,‘title‘: ‘[釋出至部落格園首頁]‘,‘categoryid‘: ‘0‘},{‘description‘: ‘[Markdown]‘,‘title‘: ‘[Markdown]‘,‘categoryid‘: ‘-5‘}...]
獲取了所有的分類資訊,其中我在網站上自建了一個隨筆分類,也可以獲取到
2.2 新建分類
import core.metablogclient as blogclient
client = blogclient.MetablogClient(‘blog_config.json‘)
client.readConfig()
catid = client.newCategory({
"name": "[隨筆分類]測試分類","slug": "","parent_id": 0,"description": "測試建立一個隨筆子分類"
})
print("新建分類:",catid)
新建分類: 1536823
但是在部落格園網站上無法看到這個分類,使用獲取分類再次測試,也無法獲取到該分類,使用該分類釋出部落格,也是無
效的,所以我想__根據年月自動分類__的想法就泡湯啦
2.3 拉取現有博文
import core.metablogclient as blogclient
client = blogclient.MetablogClient(‘blog_config.json‘)
client.readConfig()
posts = client.getRecentPosts(9999)
print(posts)
[{‘dateCreated‘: <DateTime ‘20190829T11:21:00‘ at 0x2a80990>,‘description‘: ‘<p>測試</p>‘,‘title‘: ‘測試‘,‘enclosure‘: {‘length‘: 0},‘link‘: ‘https://www.cnblogs.com/robert-9/p/11428668.html‘,‘permalink‘: ‘https://www.cnblogs.com/robert-9/p/11428668.html‘,‘postid‘: ‘11428668‘,‘source‘: {},‘userid‘: ‘-2‘}]
正確拉取現有博文,通過API文件,發現無法獲取博文是否處於釋出狀態,這是一個遺憾
2.4 釋出博文
import core.metablogclient as blogclient
import datetime
client = blogclient.MetablogClient(‘blog_config.json‘)
client.readConfig()
postid = client.newPost({
"time": datetime.datetime.now(),"title": "metaweblog API隨筆釋出","description": "##metaweblog API隨筆釋出\n測試\n","categories": ["[Markdown]"],"mt_keywords": "metaweblog;python"
},False)
print(‘釋出隨筆:‘,postid)
測試釋出成功,並能在網站上看到該隨筆, 如果想釋出為文章,日誌或新聞,加入必要的分類即可。
2.5 上傳圖片
import datetime
import base64
import core.metablogclient as blogclient
client = blogclient.MetablogClient(‘blog_config.json‘)
client.readConfig()
with open(‘abc.png‘,‘rb‘) as f:
bs64_str = base64.b64encode(f.read())
url = client.newMediaObject({
"bits": bs64_str,"name": "abc.png","type": "image/png"
})
print(url)
{‘url‘: ‘https://img2018.cnblogs.com/blog/1211514/201908/1211514-20190829114435333-814710358.png‘}
測試成功, 這樣就可以在上傳Markdown 格式之前,自動將本地的圖片上傳到伺服器上了。
出處:https://www.cnblogs.com/robert-9/p/11428982.html
======================================================
上面的github中的cnblogs_githook 我也一起貼到這裡,方便檢視:
cnblogs_githook
基於rpcxml協議,利用githook,在commit時自動釋出本地markdown文章到部落格園。
使用說明
本指令碼用python3
編寫,請配置好執行環境。
- 第一次使用前先把
./hooks/commit-msg
檔案複製到./.git/hooks/
中。 - 執行
cnblogs.py
:- 程式有一個可選引數。
config
設定部落格資訊。download
下載文章。
- 第一次執行
cnblogs.py
時預設選擇config
引數,設定部落格資訊。 - 此後每次執行程式時,
./articles/*.md
將被上傳到部落格併發布;./unpublished/*.md
將被上傳到部落格,但不釋出(並標註分類“unpublished”)。文章均以檔名為題,且不釋出的文章。如果部落格中已經存在同名文章,將替換其內容!
- 程式有一個可選引數。
- 編輯
./articles/
,./unpublished/
中markdown檔案,在本地git倉庫commit
更改,自動執行./cnblogs.py
(需要使用終端命令才能檢視返回資訊)。
注意事項/已知Bug
- 本程式不保證穩定性,為防止資料丟失,建議使用前預先備份部落格。
- clone倉庫不能下載
.git
資料夾,因此需要手動複製呼叫cnblogs.py
的指令碼./hooks/commit-msg
到.git
。 - 由於metaWeBlog本身沒有提供檢視文章是否已釋出的介面,所有使用“unpublished”分類標註未釋出文章。也就是說,當執行
python cnblogs.py download
命令時,部落格中沒有釋出也沒有“unpublished”分類的文章也會存到./articles/
,下次執行時將被自動釋出。 - 由於介面不允許將已經發布的文章設定為未釋出,所以若
./unpublished/
內的文章在部落格內有同名文章時不會被上傳。
下面是cnblog.py 檔案
#! /usr/bin/env python # coding=utf-8 # 使用python xmlrpc 傳送內容到部落格園 # http://rpc.cnblogs.com/metaweblog/WeyneChen 從連結可以看到支援的metaweblog API import xmlrpc.client as xmlrpclib import glob import os import sys import json import time import datetime # 釋出文章路徑(article path) art_path = "./articles/" # 不釋出文章路徑(unpublished article path) unp_path = "./unpublished/" # 部落格配置路徑(config path) cfg_path = "blog_config.json" # 備份路徑(backup path) bak_path = "./backup/" # 獲取文章篇數 recentnum = 99999 # 建立路徑 for path in [art_path,unp_path,bak_path]: if not os.path.exists(path): os.makedirs(path) # -----配置讀寫操作----- ‘‘‘ 配置字典: type | description(example) str | metaWeblog url,部落格設定中有(‘https://rpc.cnblogs.com/metaweblog/1024th‘) str | appkey,Blog地址名(‘1024th‘) str | blogid,這個無需手動輸入,通過getUsersBlogs得到 str | usr,登入使用者名稱 str | passwd,登入密碼 ‘‘‘ def exist_cfg(): ‘‘‘ 返回配置是否存在 ‘‘‘ try: with open(cfg_path,"r",encoding="utf-8") as f: try: cfg = json.load(f) if cfg == {}: return False else: return True except json.decoder.JSONDecodeError: # 檔案為空 return False except: with open(cfg_path,"w",encoding="utf-8") as f: json.dump({},f) return False def create_cfg(): ‘‘‘ 建立配置 ‘‘‘ while True: cfg = {} for item in [("url","metaWeblog url,部落格設定中有 (‘https://rpc.cnblogs.com/metaweblog/blogaddress‘)"),("appkey","Blog地址名(‘blogaddress‘)"),("usr","登入使用者名稱"),("passwd","登入密碼")]: cfg[item[0]] = input("輸入"+item[1]) try: server = xmlrpclib.ServerProxy(cfg["url"]) userInfo = server.blogger.getUsersBlogs( cfg["appkey"],cfg["usr"],cfg["passwd"]) print(userInfo[0]) # {‘blogid‘: ‘xxx‘,‘blogName‘: ‘xxx‘} cfg["blogid"] = userInfo[0]["blogid"] break except: print("發生錯誤!") with open(cfg_path,encoding="utf-8") as f: json.dump(cfg,indent=4,ensure_ascii=False) url = appkey = blogid = usr = passwd = "" server = None mwb = None title2id = {} def get_cfg(): global url,appkey,blogid,usr,passwd,server,mwb,title2id with open(cfg_path,encoding="utf-8") as f: cfg = json.load(f) url = cfg["url"] appkey = cfg["appkey"] blogid = cfg["blogid"] usr = cfg["usr"] passwd = cfg["passwd"] server = xmlrpclib.ServerProxy(cfg["url"]) mwb = server.metaWeblog # title2id[title]=postid 儲存部落格中文章標題對應的postid recentPost = mwb.getRecentPosts( cfg["blogid"],cfg["passwd"],recentnum) for post in recentPost: # 1.把datetime轉成字串 dt = post["dateCreated"] # post["dateCreated"] = dt.strftime("%Y%m%dT%H:%M:%S") post["dateCreated"] = dt.__str__() # 2.把字串轉成datetime # datetime.datetime.strptime(st,"%Y%m%dT%H:%M:%S") # datetime.datetime.fromisoformat(str) title2id[post["title"]] = post["postid"] # 格式化成20160320-114539形式 filename = time.strftime("%Y%m%d-%H%M%S",time.localtime()) with open(bak_path+filename+".json","w",encoding="utf-8") as f: json.dump(recentPost,indent=4) # server = xmlrpclib.ServerProxy(url) # userInfo = server.blogger.getUsersBlogs(appkey,passwd) # recentPost = mwb.getRecentPosts(blogid,9) def newPost(blogid,publish): while True: try: postid = mwb.newPost(blogid,publish) break except: time.sleep(5) return postid def post_art(path,publish=True): title = os.path.basename(path) # 獲取檔名做部落格文章標題 [title,fename] = os.path.splitext(title) # 去除副檔名 with open(mdfile,"r",encoding="utf-8") as f: post = dict(description=f.read(),title=title) post["categories"] = ["[Markdown]"] # 不釋出 if not publish: # 對於已經發布的文章,直接修改為未釋出會報錯: # xmlrpc.client.Fault: <‘published post can not be saved as draft‘> # 所以先刪除這個文章 # if title in title2id.keys(): # server.blogger.deletePost( # appkey,title2id[title],True) if title not in title2id.keys(): post["categories"].append(‘[隨筆分類]unpublished‘) # 標記未釋出 # post["postid"] = title2id[title] postid = newPost(blogid,publish) print("New:[title=%s][postid=%s][publish=%r]" % (title,publish)) # 釋出 else: if title in title2id.keys(): # 部落格裡已經存在這篇文章 mwb.editPost(title2id[title],publish) print("Update:[title=%s][postid=%s][publish=%r]" % (title,publish)) else: # 部落格裡還不存在這篇文章 postid = newPost(blogid,publish)) def download_art(): recentPost = mwb.getRecentPosts(blogid,recentnum) for post in recentPost: if "categories" in post.keys(): if ‘[隨筆分類]unpublished‘ in post["categories"]: with open(unp_path+post["title"]+".md",encoding="utf-8") as f: f.write(post["description"]) else: with open(art_path+post["title"]+".md",encoding="utf-8") as f: f.write(post["description"]) if __name__ == "__main__": if not exist_cfg(): create_cfg() get_cfg() if len(sys.argv) > 1: if sys.argv[1] == "download": download_art() elif sys.argv[1] == "config": create_cfg() get_cfg() for mdfile in glob.glob(art_path+"*.md"): post_art(mdfile,True) for mdfile in glob.glob(unp_path+"*.md"): post_art(mdfile,False)
剛剛還在網上看到一個metaWeblog.py 檔案,具體如下:
#!/usr/local/bin/python #filename:metaWeblog ‘‘‘ MetaWeblog API wrapper for python Copyright 2013. All right reserved to sooop. ‘‘‘ import xmlrpclib class Post: def __init__(self): self.keys = ["username","permaLink","guid","description","pubDate","author","title","dateCreated","tags","link","postid","categories"] self.categories = [] self.postid = "" self.link = "" self.tags = [] self.dateCreated = "" self.title = "" self.author = "" self.pubDate = "" self.description = "" self.guid = "" self.permaLink = "" self.username = "" self.publish = True def struct(self): struct = {} for k in self.keys: struct[k] = getattr(self,k) return struct def addCategory(self,cats): if type(cats) == list: self.categories.extend(cats) else: self.categories.append(cats) def addTags(self,*args,**kwargs): if type(self.tags) == str and self.tags.strip() == "": self.tags = [] for i in args: print i self.tags.append(i) @staticmethod def postFromStructs(structs): if type(structs) == list: result = [] for elem in structs: a = Post() for key in a.keys: setattr(a,key,elem[key]) result.append(a) if len(result) > 1: return result else: return result[0] else: result = Post() for key in structs: setattr(result,structs[key]) return result class Weblog: def __init__(self,service_url,blog_id=None,user_id=None,password=None): self.blog_id = blog_id self.user_id = blog_id self.password = password self.server_url = service_url self.server = xmlrpclib.ServerProxy(self.server_url) self.categories = [] self.lastPostID = None; self.getCategories() def getCategories(self,refresh=False): cats = self.server.metaWeblog.getCategories(self.blog_id,self.user_id,self.password) if refresh or self.categories == []: for elem in cats: self.categories.append(elem[‘title‘]) return cats def getPost(self,post_id): result = self.server.metaWeblog.getPost(post_id,self.password) if result: pass return result def getRecentPosts(self,count=1): result = self.server.metaWeblog.getRecentPosts(self.blog_id,self.password,count) if result: self.lastPostID = result[len(result)-1][‘postid‘] return result def newPost(self,aPost): newPostID = self.server.metaWeblog.newPost(self.blog_id,aPost.struct(),aPost.publish) self.lastPostID = newPostID return newPostID def editPost(self,aPost,post_id=None): if post_id == None: post_id = aPost.postid result = self.server.metaWeblog.editPost(post_id,aPost.publish) if result: self.lastPostID = post_id return result