最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 十分钟写个博客迁移工具

    正文概述 掘金(Cookieboty)   2021-03-24   418

    前言

    最近不少写博客的朋友跟我反馈博客园的一些文章下架了,这让我联想到去年简书一样,我之前写的博客都被下架不可见了。

    我最开始接触的博客网址是 csdn、思否、简书还有博客园等,但是后期发现,单论博客的生态感觉做的越来越不行,干货虽然很多,但是垃圾、标题党很严重,我自己也有一些博文被莫名的搬走直接标为原创。

    虽然搜问题在上面还是能搜到很多解决方案,但写作的欲望降低了很多。

    综上我从去年入驻掘金,并以掘金作为博客的主平台。个人感觉掘金团队对个人原创的保护是非常好的,同时也在不断的听取用户的建议而去改进。有问题与建议能随时与掘金的同学讨论、沟通,非常方便。

    掘金的成长

    最开始的时候,掘金也是面试、标题党满天飞,但是掘金的运营大佬逐步整顿起来之后,文章的质量有了显著的提高,并且也不断推出有利于新手作者、高质量博文的各种活动,鼓励新人创作、老人分享。

    同样在我入驻掘金之后,作为一个长期用户,新人作者,也是见证了这段时间以来掘金为了社区活跃,博客质量而做的种种努力。

    而最开始使用掘金的 markdown,能吐槽的地方还是很多,但掘金的研发也非常给力,吸纳了用户的建议后,最新升级的 markdown 编辑器也是广受好评,使用过你就知道真相定律是什么了。

    掘金在使用的时候,一直有种特殊的感觉,是一种很纯粹的 coding 情怀。并不仅仅只是一个单纯的博客平台,而是一直致力于社区共建、开源项目、掘金翻译计划等等的建设,为技术社区打造一片纯粹干净的后花园。

    搬家命令行工具

    那么作为程序员,手动搬文章显然是略 low 的

    所以写了一个简单的 python 脚本,有兴趣的同学可以使用它将 cnblogs 上面已有或者创作中的草稿转移到掘金来。

    环境配置

    1. 脚本跑起来需要 python3 环境,所以先安装一下 python 环境

    2. 请在 cookie.json 中补充博客园与掘金的 cookie

    3. 使用 python3 main.py -h 查看使用说明

    使用方法

    十分钟写个博客迁移工具

    请先在 cookie.json 中替换 cookie_cnblogs 与 cookie_juejin 为自己在对应站点上的 cookie

    请自行替换user_name与blog_id
    // 下载单篇文章到默认目录'./cnblogs' 并输出日志到'./log'
    python3 main.py -m download -a https://www.cnblogs.com/{{user_name}}/p/{{blog_id}}.html --enable_log 
    
    // 下载用户所有文章到目录'/Users/cnblogs_t'
    python3 main.py -m download -u https://www.cnblogs.com/{{username}} -p /Users/cnblogs_t
    
    // 上传单篇文章到掘金草稿箱
    python3 main.py -m upload -f ./cnblogs/{{blog_id}}.html
    
    // 上传'./test_blogs'下所有的html文件到掘金草稿箱
    python3 main.py -m upload -d ./test_blogs
    

    main.py

    新建 main.py 文件,将下述 python 代码复制进去

    # coding=utf-8
    import requests
    import os
    import argparse
    import sys
    import json
    from lxml import etree
    from urllib.parse import urlparse
    import logging
    reload(sys)
    sys.setdefaultencoding('utf-8')
    
    parser = argparse.ArgumentParser()
    args_dict = {}
    list_url_tpl = 'https://www.cnblogs.com/%s/default.html?page=%d'
    draft_url = 'https://api.juejin.cn/content_api/v1/article_draft/create_offline'
    jj_draft_url_tpl = 'https://juejin.cn/editor/drafts/%s'
    cnblog_headers = {}
    log_path = './log'
    
    def myget(d, k, v):
        if d.get(k) is None:
            return v
        return d.get(k)
    
    def init_parser():
        parser.description = 'blog move for cnblogs'
        parser.add_argument('-m', '--method', type=str, dest='method', help='使用方式: download下载 upload上传到草稿箱', choices=['upload', 'download'])
        parser.add_argument('-p', '--path', type=str, dest='path', help='博客html下载的路径')
        parser.add_argument('-d', '--dir', type=str, dest='rec_dir', help='制定要上传的博客所在文件夹')
        parser.add_argument('-f', '--file', type=str, dest='file', help='指定上传的博客html')
        parser.add_argument('-u', '--url', type=str, dest='url', help='个人主页地址')
        parser.add_argument('-a', '--article', type=str, dest='article_url', help='单篇文章地址')
        parser.add_argument('--enable_log', dest='enable_log', help='是否输出日志到./log', action='store_true')
        parser.set_defaults(enable_log=False)
    
    def init_log():
        root_logger = logging.getLogger()
        log_formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(pathname)s:%(lineno)s  %(message)s')
        console_handler = logging.StreamHandler(sys.stdout)
        console_handler.setFormatter(log_formatter)
        root_logger.addHandler(console_handler)
        if myget(args_dict, 'enable_log', False):
            if not os.path.exists(log_path):
                os.mkdir(log_path)
            file_handler =  logging.FileHandler('./log/debug.log')
            file_handler.setFormatter(log_formatter)
            root_logger.addHandler(file_handler)
        root_logger.setLevel(logging.INFO)
        
    def download():
        cookies = json.load(open('cookie.json'))
        headers = {'cookie': cookies.get('cookie_cnblogs', '')}
    
        dir_path = myget(args_dict, 'path', './cnblogs')
        if dir_path[len(dir_path)-1] == '/':
            dir_path = dir_path[:len(dir_path)-1]
        if not os.path.exists(dir_path):
            os.mkdir(dir_path)
        
        article_url = myget(args_dict, 'article_url', '-1')
        if article_url != '-1':
            logging.info('article_url=%s', article_url)
            try:
                resp = requests.get(article_url, headers=headers)
                if resp.status_code != 200:
                    logging.error('fail to get blog \'%s\', resp=%s', article_url, resp)
                    return
                tmp_list = article_url.split('/')
                blog_id_str = tmp_list[len(tmp_list)-1]
                with open(dir_path+'/'+blog_id_str, 'w') as f:
                    f.write(resp.text)
                logging.info('get blog \'%s\' success.', article_url)
            except Exception as e:
                logging.error('exception raised, fail to get blog \'%s\', exception=%s.', list_url, e)
            finally:
                return
    
        raw_url = args_dict.get('url')
        rurl = urlparse(raw_url)
        username = (rurl.path.split("/", 1))[1]
        page_no = 1
        while True:
            list_url = list_url_tpl%(username, page_no)
            logging.info('list_url = %s', list_url)
            try:
                resp = requests.get(list_url, headers=headers)
                if resp.status_code != 200:
                    break
            except Exception as e:
                logging.error('exception raised, fail to get list \'%s\', exception=%s.', list_url, e)
                return
            html = etree.HTML(resp.text)
            blog_list = html.xpath('//div[@class=\'postTitle\']/a/@href')
            if len(blog_list) == 0:
                break
            for blog_url in blog_list:
                tmp_list = blog_url.split('/')
                blog_id_str = tmp_list[len(tmp_list)-1]
                blog_resp = requests.get(blog_url, headers=headers)
                if resp.status_code != 200:
                    logging.error('fail to get blog \'%s\', resp=%s, skip.', blog_url, resp)
                    continue
                with open(dir_path+'/'+blog_id_str, 'w') as f:
                    f.write(blog_resp.text)
                logging.info('get blog \'%s\' success.', blog_url)
            page_no += 1
    
    def upload_request(headers, content, filename):
        body = {
            "edit_type": 0,
            "origin_type": 2,
            "content": content
        }
        data = json.dumps(body)
        try:
            resp = requests.post(draft_url, data=data, headers=headers)
            if resp.status_code != 200:
                logging.error('fail to upload blog, filename=%s, resp=%s', filename, resp)
                return
            ret = resp.json()
            draft_id = ret.get('data', {}).get('draft_id', '-1')
            logging.info('upload success, filename=%s, jj_draft_id=%s, jj_draft_url=%s', filename, draft_id, jj_draft_url_tpl%draft_id)
        except Exception as e:
            logging.error('exception raised, fail to upload blog, filename=%s, exception=%s', filename, e)
            return
        
    
    def upload():
        cookies = json.load(open('cookie.json'))
        headers = {
            'cookie': cookies.get('cookie_juejin', ''),
            'content-type': 'application/json'
        }
        filename = myget(args_dict, 'file', '-1')
        if filename != '-1':
            logging.info('upload_filename=%s', filename)
            try:
                with open(filename, 'r') as f:
                    content = f.read()
                    upload_request(headers, content, filename)
                return
            except Exception as e:
                logging.error('exception raised, exception=%s', e)
        
        rec_dir = myget(args_dict, 'rec_dir', '-1')
        if rec_dir != '-1':
            logging.info('upload_dir=%s', filename)
            try:
                g = os.walk(rec_dir)
                for path, dir_list, file_list in g:
                    for filename in file_list:
                        if filename.endswith('.html'):
                            filename = os.path.join(path, filename)
                            with open(filename, 'r') as f:
                                content = f.read()
                                upload_request(headers, content, filename)
            except Exception as e:
                logging.error('exception raised, exception=%s', e)
            return
    
    
    if __name__ == '__main__':
        init_parser()
        args = parser.parse_args()
        args_dict = args.__dict__
        init_log()
    
        empty_flag = True
        for k, v in args_dict.items():
            if k != 'enable_log' and v is not None:
                empty_flag = False
        if empty_flag:
            parser.print_help()
            exit(0)
    
        if args_dict.get('method') == 'upload':
            upload()
        else:
            download()
        pass
    

    cookie.json

    本地新建 cookie.json 文件,与 main.py 同级

    {
        "cookie_cnblogs": "请替换为博客园cookie",
        "cookie_juejin": "请替换为掘金cookie"
    }
    

    github 地址

    最后附上 github 地址,里面除了 demo 的 源码之外也有录制好的一个视频,有兴趣的同学可以下载使用或者研究研究,脚本有问题或者写的不好改进的地方也可以互相探讨下。有意见也可以随时留言反馈


    起源地下载网 » 十分钟写个博客迁移工具

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元