1. 程式人生 > WINDOWS開發 >使用metaweblog API實現通用部落格釋出 之 API測試

使用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編寫,請配置好執行環境。

  1. 第一次使用前先把./hooks/commit-msg檔案複製到./.git/hooks/中。
  2. 執行cnblogs.py
    1. 程式有一個可選引數。
      • config設定部落格資訊。
      • download下載文章。
    2. 第一次執行cnblogs.py時預設選擇config引數,設定部落格資訊。
    3. 此後每次執行程式時,./articles/*.md將被上傳到部落格併發布;./unpublished/*.md將被上傳到部落格,但不釋出(並標註分類“unpublished”)。文章均以檔名為題,且不釋出的文章。如果部落格中已經存在同名文章,將替換其內容!
  3. 編輯./articles/./unpublished/中markdown檔案,在本地git倉庫commit更改,自動執行./cnblogs.py(需要使用終端命令才能檢視返回資訊)。

注意事項/已知Bug

  1. 本程式不保證穩定性,為防止資料丟失,建議使用前預先備份部落格。
  2. clone倉庫不能下載.git資料夾,因此需要手動複製呼叫cnblogs.py的指令碼./hooks/commit-msg.git
  3. 由於metaWeBlog本身沒有提供檢視文章是否已釋出的介面,所有使用“unpublished”分類標註未釋出文章。也就是說,當執行python cnblogs.py download命令時,部落格中沒有釋出也沒有“unpublished”分類的文章也會存到./articles/,下次執行時將被自動釋出。
  4. 由於介面不允許將已經發布的文章設定為未釋出,所以若./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

出處:https://gist.github.com/sooop/4986757