python 发布文章_一个发布文章到博客园的 Python 脚本

本文介绍了一个Python脚本`cnblogs-post.py`,该脚本能够配置并连接到博客园的MetaWeblog API,实现本地Markdown文件到博客园的文章发布或更新。文章详细讲解了配置步骤、脚本工作原理以及如何使用脚本进行文章的发布、更新和删除操作。
摘要由CSDN通过智能技术生成

copy from https://github.com/DeppWang/cnblogs-post

配置

博客园 -> 管理 -> 设置 -> 允许 MetaWeblog 博客客户端访问

在 cnblogs-post.py 中配置:

config = {

'user_unique_name': 'deppwang', # 你的用户名,用于拼接文章 url

'url': 'https://rpc.cnblogs.com/metaweblog/deppwang', # 你的 MetaWeblog 访问地址

'username': 'DeppWangXQ', # 你的登录用户名,可能跟上面的用户名不一致

'password': '12345678' # 你的登录密码

'local_post_path': '/Users/yanjie/GitHub/HexoBlog/source/_posts/' # 你的本地博文路径

}

在文章开头,添加文章信息块,至少需要包括 title 和 tags,格式如下:

---

title: 一个可编辑与新增博客园文章的 Python 脚本

english_title: a-python-script-to-edit-and-add-cnblogs-posts

date: 2020-06-20 20:48:37

tags: 博客园

categories: Tools

---

正文开始 ...

安装 markdown-it-py,用于将 Markdown 转换为 HTML

pip install markdown-it-py

运行脚本发布或更新

python3 cnblogs-post.py [count] # macOS/Linux

python cnblogs-post.py [count] # Windows

默认只操作最近修改文章,但也可以指定文章数量(count)

脚本根据文章名称来判断是否已经发布,如果已经发布,更新,否则新增。

运行脚本删除

python3 cnblogs-post.py delete # macOS/Linux

python cnblogs-post.py delete # Windows

默认删除最近发布文章

import xmlrpc.client

import ssl

import os

import sys

import logging

# logging.basicConfig(level=logging.INFO)

# 设置 ssl 很重要,否则将报 ssl 错

ssl._create_default_https_context = ssl._create_unverified_context

# dict

config = {

'user_unique_name': '', # 你的用户名,用于拼接文章 url

'url': 'https://rpc.cnblogs.com/metaweblog/', # 你的 MetaWeblog 访问地址

'username': '', # 你的登录用户名,可能跟上面的不一致

'password': '', # 你的登录密码

'local_post_path': '' # 你的本地博文路径

}

class MetaWeblog:

def __init__(self, url, username, password):

self.url, self.username, self.password = url, username, password

self.proxy = xmlrpc.client.ServerProxy(self.url)

def getRecentPosts(self, count):

return self.proxy.metaWeblog.getRecentPosts('', self.username, self.password, count)

def deletePost(self, post_id):

return self.proxy.blogger.deletePost('', post_id, self.username, self.password, True)

def newPost(self, article):

return self.proxy.metaWeblog.newPost('', self.username, self.password,

dict(title=article['title'], description=article['content'],

mt_keywords=article['tags']),

True)

def editPost(self, post_id, article):

return self.proxy.metaWeblog.editPost(post_id, self.username, self.password,

dict(title=article['title'], description=article['content'],

mt_keywords=article['tags']),

True)

def set_article(article_path: str) -> dict:

""" 根据文章路径设置文章(标题、标签和内容)"""

import re

from markdown_it import MarkdownIt

from markdown_it.extensions.front_matter import front_matter_plugin

from markdown_it.extensions.footnote import footnote_plugin

with open(article_path, 'rb') as f:

content = f.read().decode('utf-8')

# 使用分组

reg = r'(---(.|[\n])*?---)'

p = re.compile(reg)

headers = p.findall(content)

logging.info('headers: %s', headers)

if len(headers) < 1:

raise ValueError('文章 %s 不存在 header 信息块「--- ** ---」,请检查!' % article_path)

# headers[0] 为 tuple

header_str = headers[0][0]

lines = header_str.split('\n')

title, tags_categories = '', []

for line in lines:

# 不使用':', 再替换所有空格为 null,因为可能存在中英文空格

line_ele = line.split(': ')

if line_ele[0] == 'title':

title = line_ele[1].rstrip()

elif line_ele[0] == 'tags':

tags = line_ele[1].rstrip()

tags = tags.replace('[', '')

tags = tags.replace(']', '')

tags_categories.append(tags)

# 将分类也作为标签

elif line_ele[0] == 'categories':

categories = line_ele[1].rstrip()

tags_categories.insert(0, categories)

separator = ', '

tags = separator.join(tags_categories)

logging.info("tags: %s", tags)

# 只替换第一个

content = content.replace(header_str, '', 1)

md = (

MarkdownIt()

.use(front_matter_plugin)

.use(footnote_plugin)

.enable('image')

.enable('table')

)

# markdown 转换为 HTML

content = md.render(content)

# cnblogs-markdown 属性用于代码块样式

content = '

%s
' % content

if title == '' or content == '' or tags == '':

raise ValueError('文章 title、content、tags 均不能为空;\': \'后有空格。请检查!')

article = {'title': title, 'content': content, 'tags': tags}

return article

def get_local_modified_file(file_count: int):

""" 获取指定数量的最近修改文章 """

import stat

import datetime as dt

modified = []

for root, sub_folders, files in os.walk(config['local_post_path']):

for file in files:

try:

unix_modified_time = os.stat(os.path.join(root, file))[stat.ST_MTIME]

human_modified_time = dt.datetime.fromtimestamp(unix_modified_time).strftime('%Y%m%dT%H:%M:%S')

filename = os.path.join(root, file)

if os.path.splitext(filename)[1] == '.md':

modified.append((human_modified_time, filename))

except:

pass

modified.sort(key=lambda a: a[0], reverse=True)

return modified[0: file_count]

def judge_str_equal(str1, str2):

""" 判断两个字符串是否相等 """

str1 = str1.replace(' ', '')

str2 = str2.replace(' ', '')

return str1 == str2

def edit_or_new(article_path: str) -> None:

""" 编辑或新增 """

blog = MetaWeblog(config['url'], config['username'], config['password'])

posts = blog.getRecentPosts(100)

article = set_article(article_path)

title = article['title']

# 如果存在,更新,否则新增。接口不提供最近修改时间,所以不能跳过

exist_flag = 0

for post in posts:

# 判断是否含有相同的字符,不直接用等号

if judge_str_equal(post['title'], title):

status = blog.editPost(post['postid'], article)

if status is True:

print("更新文章「%s」成功,文章地址 https://www.cnblogs.com/%s/p/%s.html" % (title, config['user_unique_name'], post['postid']))

exist_flag = 1

break

if exist_flag == 0:

id = blog.newPost(article)

if id is not None:

print("发布文章「%s」成功,文章地址 https://www.cnblogs.com/%s/p/%s.html" % (title, config['user_unique_name'], id))

def post(count: int) -> None:

""" 发布指定数量的最新修改文章 """

modified_files = get_local_modified_file(count)

for modified_file in modified_files:

edit_or_new(modified_file[1])

def delete() -> None:

""" 默认删除最新文章 """

metaWeblog = MetaWeblog(config['url'], config['username'], config['password'])

posts = metaWeblog.getRecentPosts(100)

postid = posts[0]['postid']

title = posts[0]['title']

status = metaWeblog.deletePost(postid)

if status is True:

print('删除「%s」成功!' % title)

else:

print('删除「%s」失败,文章不存在。文章 id 为:%s' % (title, postid))

def main():

try:

if len(sys.argv) > 1 and sys.argv[1] == 'delete':

delete()

elif len(sys.argv) > 1 and isinstance(int(sys.argv[1]), int):

print('正在发布 ...')

logging.info("sys.argv[1]: %s", sys.argv[1])

post(int(sys.argv[1]))

else:

post(1)

except Exception as err:

print("错误提示:%s", format(err))

print('发布失败')

sys.exit(-1)

if __name__ == '__main__':

main()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值