吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5555|回复: 25
收起左侧

[Python 转载] typora实现多平台发布文章

  [复制链接]
wshuo 发表于 2021-12-5 17:42
本帖最后由 wshuo 于 2022-1-24 03:04 编辑

源码下载

前言

之前写过一片文章,typora 使用CSDN作为图床,用来存储 markdown 文章的图片资源文件。后来发现 typora 还可以自定义导出命令,那么也可以利用这个功能实现直接发布到CSDN平台,或者博客园平台。CSDN平台需要分析一下发布文章的接口,博客园可以直接利用 MetaWeblog API 来发布文章。我之前发布各个平台需要复制粘贴,但是程序员不做重复性质的劳动,哪怕仅仅是复制粘贴。

CSDN发布文章接口

CSDN 接口主要难点就是对请求头上x-ca-signature加了验证,参考我之前写的关于CSDN获取博客内容接口的x-ca-signature签名算法研究。 不过有意思的是,之前验证失败的时候会直接抛出验证错误的响应,现在是直接返回为空,不显示提示信息也是一种防逆向的策略。算法和以前几乎一样,不过有一些细微的差别,例如请求类型是POST,这个请求类型也是构成 x-ca-signature的一部分(加密前)。

csdnArticle.py

import requests
from urllib.parse import urlparse
import http.cookiejar as cookielib
import hashlib
import hmac
from base64 import b64decode,b64encode
import random
import sys
from markdown import markdown
import os
import csdnLogin
requests.packages.urllib3.disable_warnings()

os.chdir(os.path.dirname(os.path.abspath(__file__)))

headers = {
    "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36",
    'Content-Type': "application/json",
    "origin": "https://editor.csdn.net",
    "referer": "https://editor.csdn.net/",
    "sec-ch-ua-platform": "Windows",
}

def createUuid():
    text = ""
    char_list = []
    for c in range(97,97+6):
        char_list.append(chr(c))
    for c in range(49,58):
        char_list.append(chr(c))
    for i in "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx":
        if i == "4":
            text += "4"
        elif i == "-":
            text += "-"
        else:
            text += random.choice(char_list)
    return text

def get_sign(uuid,url):
    s = urlparse(url)
    ekey = "9znpamsyl2c7cdrr9sas0le9vbc3r6ba".encode()
    to_enc = f"POST\n*/*\n\napplication/json\n\nx-ca-key:203803574\nx-ca-nonce:{uuid}\n{s.path}".encode()
    sign = b64encode(hmac.new(ekey, to_enc, digestmod=hashlib.sha256).digest()).decode()
    return sign

def uploadArticle(articles):
    for article in articles:
        url = 'https://bizapi.csdn.net/blog-console-api/v3/mdeditor/saveArticle'
        uuid = createUuid()
        sign = get_sign(uuid,url)
        headers = {}
        headers['x-ca-key'] = "203803574"
        headers['x-ca-nonce'] = uuid
        headers['x-ca-signature'] = sign
        headers['x-ca-signature-headers'] = "x-ca-key,x-ca-nonce"
        session = requests.session()
        session.cookies = cookielib.LWPCookieJar(filename='.cookie/csdn.txt')
        session.cookies.load()

        with open(article,"r",encoding='utf8') as f:
            data = f.read()
        title = os.path.basename(article)[:-3]

        fields = {
            "title": title,
            "markdowncontent": data,
            "content": markdown(data),
            "readType": "public",
            "tags": " ",
            "status": 0,
            "categories": "",
            "type": "original",
            "original_link": "",
            "authorized_status": False,
            "not_auto_saved": "1",
            "source": "pc_mdeditor",
            "cover_images": [],
            "cover_type": 0,
            "is_new": 1,
            "vote_id": 0,
            "pubStatus": "publish"
        }
        data = session.post(url,headers=headers,json=fields,verify=False)
        print(data.json()["msg"])

if __name__ == '__main__':
    uploadArticle(sys.argv[1:])

# x-ca-nonce: 5bbec46a-fae9-45f1-ab12-40e4e92c1901
# x-ca-signature: B2tpU+fIVigDzGba3rJYW6atH/jOZf4G3Ow/K2B64F4=
# 加密算法在app.chunk.12001b99.js 20126行 

csndImage.py

import os
import sys

os.chdir(os.path.dirname(os.path.abspath(__file__)))

import re
from threading import Thread
import time
import requests
import http.cookiejar as cookielib
import psutil
from requests_toolbelt import MultipartEncoder
import csdnLogin

requests.packages.urllib3.disable_warnings()

def updatePicture(picList):
    for pic in picList:
        session = requests.session()
        session.cookies = cookielib.LWPCookieJar(filename='.cookie/csdn.txt')
        session.cookies.load()
        # 上传预处理
        url = 'https://imgservice.csdn.net/direct/v1.0/image/upload?watermark=&type=blog&rtype=markdown'
        fields = {
        "watermark":"", 
        "type": "blog",
        "rtype": "markdown",
        }
        headers = {
        "content-type": "application/json",
        "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36",
        "origin": "https://editor.csdn.net",
        "pragma": "no-cache",
        "referer": "https://editor.csdn.net/",
        "x-image-app": "direct_blog",
        "x-image-dir": "direct",
        "x-image-suffix": "png",

        }
        res = session.get(url,headers=headers,verify=False)
        data = res.json()
        #上传图片
        fields = {
        "key": data["data"]["filePath"],
        "policy":data["data"]["policy"],
        "OSSAccessKeyId": data["data"]["accessId"],
        "callback": data["data"]["callbackUrl"],
        "signature" :data["data"]["signature"],
        'file':(os.path.basename(pic),open(pic,'rb'),"image/jpeg"),
        }
        m = MultipartEncoder(fields, boundary='------WebKitFormBoundarynTBa3OWoSMrcVf0F')
        headers = {
            'content-Type': m.content_type,
        }
        url = "https://csdn-img-blog.oss-cn-beijing.aliyuncs.com/"
        res = session.post(url, headers=headers, data=m,verify=False)
        print(res.json()["data"]["imageUrl"])

if __name__ == '__main__':
    updatePicture(sys.argv[1:])
    # updatePicture("C:\\Users\\wshuo\\AppData\\Local\\Temp/typora-icon2.png","C:\\Users\\wshuo\\AppData\\Local\\Temp/typora-icon.png")

在CSDN上发布博客需要登录获取cookie:

csdnLogin.py

import requests
import http.cookiejar as cookielib
import psutil
import os
import re
os.chdir(os.path.dirname(os.path.abspath(__file__)))
requests.packages.urllib3.disable_warnings()

def is_login():
    session = requests.session()
    try:
        session.cookies = cookielib.LWPCookieJar(filename='.cookie/csdn.txt')
        session.cookies.load()

        url = 'https://me.csdn.net/api/user/show'
        response = session.post(url)
        if response.json()['message'] == "成功":
            return True
        else:
            return False
    except Exception as e:
        return False

def login():
    session = requests.session()
    session.cookies = cookielib.LWPCookieJar(filename='.cookie/csdn.txt')
    response = session.get('https://open.weixin.qq.com/connect/qrconnect?appid=wx0ae11b6a28b4b9fc&scope=snsapi_login&redirect_uri=https%3A%2F%2Fpassport.csdn.net%2Fv1%2Fregister%2FpcAuthCallBack%3FpcAuthType%3Dweixin&state=csdn&login_type=jssdk&self_redirect=default&style=white&href=https://csdnimg.cn/release/passport/history/css/replace-wx-style.css',verify=False)
    uuid = re.findall('<img class="qrcode lightBorder" src="(.*?)" />',response.text)[0]
    img_url = 'https://open.weixin.qq.com' + uuid
    imgData = session.get(img_url).content
    with open("qrcode.jpg","wb") as f:
        f.write(imgData)
    os.popen("qrcode.jpg")

    uuid = uuid.split('/')[-1]
    url = 'https://long.open.weixin.qq.com/connect/l/qrconnect?uuid='+uuid
    while True:
        response = session.get(url,verify=False)
        code = re.findall("window.wx_code='(.*?)'",response.text)
        if code != ['']:
            for proc in psutil.process_iter():  # 遍历当前process
                try:
                    if proc.name() == "dllhost.exe":  # 如果你展示图片的进程不是这个,可以对此进行修改
                        proc.kill()  # 关闭该process
                except Exception as e:
                    pass
            break
        time.sleep(1)

    url = 'https://passport.csdn.net/v1/register/pcAuthCallBack?pcAuthType=weixin&code=%s&state=csdn' % code[0]
    session.get(url)
    session.cookies.save()

if not os.path.exists(".cookie"):
    os.mkdir(".cookie")
if not os.path.exists(os.path.join(".cookie","csdn.txt")):
    with open(os.path.join(".cookie","csdn.txt"),"w") as f:
        f.write("")

if not is_login():
    login()

这里依然采用扫码登录的方式。

博客园平台

如果要使用博客园的  MetaWeblog API, 要在设置中进行简单的设置。  

image-20211205170715395

然后才可以使用,接口定义 很全面了,使用起来非常方便。  

1. 上传图片

cnblogsImage.py

import xmlrpc.client
import ssl
import os
import json
import sys
ssl._create_default_https_context = ssl._create_unverified_context

rootPath = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(rootPath,"cnblogs.json"),"rb") as f:
    config = json.loads(f.read())

def uploadImage(images):
    for image in images:
        with open(image,"rb") as f:
            imageData = f.read()

        baseName = os.path.basename(image)
        suffix = baseName.split(".")[-1]
        file = dict(
            bits = imageData,
            name = baseName,
            type = f"image/{suffix}"
        )
        proxy = xmlrpc.client.ServerProxy(config["url"])
        s = proxy.metaWeblog.newMediaObject('', config['username'], config['password'],file)
        print(s["url"])

if __name__ == '__main__':
    uploadImage(sys.argv[1:])

2.发布文章

cnblogsArticle.py

import xmlrpc.client
import ssl
import os
import json
import sys
ssl._create_default_https_context = ssl._create_unverified_context

rootPath = os.path.abspath(os.path.dirname(__file__))
with open(os.path.join(rootPath,"cnblogs.json"),"rb") as f:
    config = json.loads(f.read())

def uploadArticle(articles):
    for article in articles:
        with open(article,"r",encoding="utf8") as f:
            data = f.read()

        title = os.path.basename(article)[:-3]
        post = dict(
            dateCreated = xmlrpc.client.DateTime(),
            description = data,
            title = title,
            categories = ['[Markdown]'],
        )
        proxy = xmlrpc.client.ServerProxy(config["url"])
        s = proxy.metaWeblog.newPost('', config['username'], config['password'],post,True)
        # 输出文章连接
        userName = config["url"].split("/")[-1]
        print(f"https://www.cnblogs.com/{userName}/p/{s}.html")

if __name__ == '__main__':
    uploadArticle(sys.argv[1:])

使用

这里我使用的都是直接脚本调用,我不想进行打包,总感觉打包后执行效率变低。

如果要使用上传文章到博客圆,或者使用博客园图床,首先要对 cnblogs.json 进行修改:

{
    "url": "https://rpc.cnblogs.com/metaweblog/Hellowshuo",  //设置中的那个链接
    "username": "Hello_wshuo",  //用户名
    "password": "12345678" //密码
}

配置使用typora图床:

image-20211205171747126

命令:

D:/Python36/python.exe D:/software/Typora/command/cnblogsImage.py

这里最好都使用绝对路径进行配置,否则可能会有找不到脚本的问题。


上传文章到博客园:

image-20211205172039656

命令:

D:/Python36/python.exe D:/software/Typora/command/cnblogsArticle.py "${currentPath}"

${currentPath} 表示的是完整的markdown文件路径。


CSDN 图床配置:

命令:

D:/Python36/python.exe D:/software/Typora/command/csdnImage.py

CSDN 上传文章:

image-20211205172555478

命令:

D:/Python36/python.exe D:/software/Typora/command/csdnArticle.py "${currentPath}"

第一次上传CSDN文章或者使用CSDN图床,会提示扫码登录(登录成功后会自动关闭窗口),登录后自动保存cookie,下次登录直接调用cookie,等到cookie过期后会重新提示扫码登录。

免费评分

参与人数 5吾爱币 +8 热心值 +4 收起 理由
Psycho-L + 1 + 1 谢谢@Thanks!
Witheredead + 1 我很赞同!
冥界3大法王 + 4 + 1 这下方便了,明天测试!
柒呀柒 + 2 + 1 谢谢@Thanks!
Pandolar + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| wshuo 发表于 2021-12-25 00:23
本帖最后由 wshuo 于 2021-12-25 00:25 编辑

CSDN上传图片接口失效,源码下载的压缩包我就不改了,现在改为:
import os
import sys

os.chdir(os.path.dirname(os.path.abspath(__file__)))

import re
from threading import Thread
import time
import requests
import http.cookiejar as cookielib
import psutil
from requests_toolbelt import MultipartEncoder
import csdnLogin

requests.packages.urllib3.disable_warnings()

def updatePicture(picList):
    for pic in picList:
        session = requests.session()
        session.cookies = cookielib.LWPCookieJar(filename='.cookie/csdn.txt')
        session.cookies.load()
        # 上传预处理
        url = 'https://imgservice.csdn.net/direct/v1.0/image/upload?watermark=&type=blog&rtype=markdown'
        fields = {
        "watermark":"", 
        "type": "blog",
        "rtype": "markdown",
        }
        headers = {
        "content-type": "application/json",
        "user-agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36",
        "origin": "https://editor.csdn.net",
        "pragma": "no-cache",
        "referer": "https://editor.csdn.net/",
        "x-image-app": "direct_blog",
        "x-image-dir": "direct",
        "x-image-suffix": "png",

        }
        res = session.get(url,headers=headers,verify=False)
        data = res.json()
        #上传图片
        fields = {
        "key": data["data"]["filePath"],
        "policy":data["data"]["policy"],
        "OSSAccessKeyId": data["data"]["accessId"],
        "callback": data["data"]["callbackUrl"],
        "signature" :data["data"]["signature"],
        'file':(os.path.basename(pic),open(pic,'rb'),"image/jpeg"),
        }
        m = MultipartEncoder(fields, boundary='------WebKitFormBoundarynTBa3OWoSMrcVf0F')
        headers = {
            'content-Type': m.content_type,
        }
        url = "https://csdn-img-blog.oss-cn-beijing.aliyuncs.com/"
        res = session.post(url, headers=headers, data=m,verify=False)
        print(res.json()["data"]["imageUrl"])

if __name__ == '__main__':
    updatePicture(sys.argv[1:])
冥界3大法王 发表于 2021-12-5 20:01
@wshuo
图片支持外链不?不会挂?
那个破CSDN限制很多,当年文章随便发,后来20篇限制,后来只有10篇
最后发个屁儿都要审核,最后我再也不发了。
冥界3大法王 发表于 2021-12-6 10:05
Witheredead 发表于 2021-12-6 01:51
限制多的很,不如博客园了

看了楼主帖,太好了,免费存图白嫖吧。

免费评分

参与人数 1热心值 +1 收起 理由
Witheredead + 1 我很赞同!

查看全部评分

 楼主| wshuo 发表于 2021-12-7 00:27
Psycho-L 发表于 2021-12-7 00:25
请问遇到带有空格的文件名时
使用with open打开会报错怎么办
会提示No such file or directory

引号引起来,是我写的不完善
"${currentPath}"
侃遍天下无二人 发表于 2021-12-5 18:01
我改天试试能不能用go语言也写一个,go的效率很高
Onlyfans 发表于 2021-12-5 20:04
感谢分享!
柒呀柒 发表于 2021-12-5 20:15
学习学习!!
 楼主| wshuo 发表于 2021-12-5 20:46
冥界3大法王 发表于 2021-12-5 20:01
@wshuo
图片支持外链不?不会挂?
那个破CSDN限制很多,当年文章随便发,后来20篇限制,后来只有10篇

你仔细看我文章中图片链接,就是在csdn上,52自动去掉一些参数就实现了去水印。
weliong 发表于 2021-12-5 21:34
能发 带音频的文章么?
AiChiShuCai 发表于 2021-12-5 21:58
感谢分享!
dragonjelly 发表于 2021-12-5 23:54
妈妈再也不怕我的博客要一个个发了,感谢大佬分享源码,学习了
Witheredead 发表于 2021-12-6 01:51
冥界3大法王 发表于 2021-12-5 20:01
@wshuo
图片支持外链不?不会挂?
那个破CSDN限制很多,当年文章随便发,后来20篇限制,后来只有10篇

限制多的很,不如博客园了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-24 00:29

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表