今天看我各个平台博客同步情况,发现我之前都是在csdn写完,直接复制粘贴到其它平台的,格式什么的完全没有了,而我也急于发布没有做任何处理。想象以后文章多起来,不仅管理起来不方便,而且对其它平台的阅读者很不友好。

顺势观察了一下各平台的导入、导出功能(针对于自己文章):有平台支持批量导入,亦有平台支持批量导出,还有的支持单篇导入,没有一个既可以批量导入又可以批量导出的(嘤嘤嘤)。

而CSDN同样仅支持单篇导入、导出,需要进到每篇文章的编辑页面再进行导出,如果文章超级多的时候,备份、搬迁时候进行此项操作都不是最佳方案。想想我之前那篇Hexo迁移博客的文章:Mac_如何利用Hexo发布、迁移博文?https://blog.csdn.net/pang787559613/article/details/102619683,竟然还是我手动导出的(可能之前文章少,没觉得费事儿吧),还是太年轻啊。

对于可以批量导入的平台,我本人甚是欢喜,就算新注册的账号,也不用一篇一篇去复制粘贴,那么前提是要先把自己已有文章导出为统一格式(如:Markdown),既做一次备份,也便于在其它平台共用。

延伸一下,只能导出自己的文章也不太好,毕竟如果遇到想要收藏的文章时候,存下的格式很乱,就很尴尬了。所以不仅限于导自己的博客,这篇文章的主题是:如何从CSDN批量导出markdown格式的文章?

网上搜了一下有java的脚本用到jsoap、也有python的selenium跟BeautifulSoap两种。相比之下我还是比较熟悉python,但是selenium是基于浏览器模拟导出,仅能在文章编辑页导出自己的文章,有局限性,所以就研究了下BeautifulSoap。基于这位大佬的代码:https://blog.csdn.net/qq_36962569/article/details/100167955,运行下来,仅能拿到前两页前三条(共计6条数据),而且写入报错。对于我非专业python来说有点小烦躁,好在坚持了下来,理清了思路,解决了报错,并成功导出。下面我来记录下我所理解的大佬思路,还有报错的解决方案,贴一下修改过的代码,给需要的人做参考。

一、思路:

1、csdn自带导出功能是如何导出的?

2、每篇文章怎么做区分的?怎么拿到所有文章的id?

3、分页如何处理?

4、拿到内容后解析写入文件?

二、分析过程:

1、csdn自带导出功能是如何导出的?

打开一片文章编辑页面,F12查看数据如何获取的,发现其为这样一个请求https://blog-console-api.csdn.net/v1/editor/getArticle?id=105360586,返回字段中有content和markdowncontent,我们要拿markdown格式,所以Markdowncontent就好了。
迁移、备份博客_从CSDN批量导出markdown格式的文章(Python BeautifulSoap) - 图1
迁移、备份博客_从CSDN批量导出markdown格式的文章(Python BeautifulSoap) - 图2
迁移、备份博客_从CSDN批量导出markdown格式的文章(Python BeautifulSoap) - 图3

2、每篇文章怎么做区分的?怎么拿到所有文章的id?

至于第1步里的id,跟这篇文章详情链接https://blog.csdn.net/pang787559613/article/details/105360586,是一致的,再观察几篇文章详情,仅后轴id不一样, 所以不同文章是以此来区分的。

查看所有文章id:
个人主页,F12打开检查,刷新页面(windows—>>F5,mac—>>command+r):
迁移、备份博客_从CSDN批量导出markdown格式的文章(Python BeautifulSoap) - 图4可以分析以下源码,用BeautifulSoap库解析,能获取id、发布时间等内容:
迁移、备份博客_从CSDN批量导出markdown格式的文章(Python BeautifulSoap) - 图5

3、分页如何处理?

翻到个人主页底部页码出,点开第二页,发现调用接口为:https://blog.csdn.net/pang787559613/article/list/2
迁移、备份博客_从CSDN批量导出markdown格式的文章(Python BeautifulSoap) - 图6

切回第一页时,调用的请求为:https://blog.csdn.net/pang787559613/article/list/1
迁移、备份博客_从CSDN批量导出markdown格式的文章(Python BeautifulSoap) - 图7

4、拿到内容后解析写入文件?

通过调用第3步,拿到所有文章,再用第2步,解析获得文章id以及发布时间,最后调用第1步,解析拿到markdowncontent、文章标题、tags、key等等,保存为hexo格式的markdown(由于我的hexo博客仅有标题+内容,所以代码仅写入了标题跟内容,其余注释了,需要的可自行修改。后来仅保留了内容,做博客迁移)。

三、完整代码:

  1. # encoding:utf-8
  2. # author:笑胖仔
  3. import json
  4. import uuid
  5. import time
  6. import requests
  7. import datetime
  8. from bs4 import BeautifulSoup
  9. def request_blog_list(page):
  10. """获取博客列表
  11. 主要包括博客的id以及发表时间等
  12. """
  13. url = f'https://blog.csdn.net/pang787559613/article/list/{page}'
  14. reply = requests.get(url)
  15. parse = BeautifulSoup(reply.content, "lxml")
  16. spans = parse.find_all('div', attrs={'class':'article-item-box csdn-tracking-statistics'})
  17. blogs = []
  18. for span in spans[:40]:
  19. try:
  20. href = span.find('a', attrs={'target':'_blank'})['href']
  21. read_num = span.find('span', attrs={'class':'num'}).get_text()
  22. date = span.find('span', attrs={'class':'date'}).get_text()
  23. blog_id = href.split("/")[-1]
  24. blogs.append([blog_id, date, read_num])
  25. except:
  26. print('Wrong, ' + href)
  27. return blogs
  28. def request_md(blog_id, date):
  29. """获取博客包含markdown文本的json数据"""
  30. url = f"https://blog-console-api.csdn.net/v1/editor/getArticle?id={blog_id}"
  31. headers = {
  32. "cookie":"uuid_tt_dd=10_20621362900-1586421086666-163599; dc_session_id=10_1586421086666.420623; Hm_lvt_6bcd52f51e9b3dce32bec4a3997715ac=1586421618; dc_sid=d4ceee41911ac755c162110ff811aee3; __gads=ID=608336bee91baf3d:T=1586421689:S=ALNI_MZozulzITWLw3Hxzo3jrnu5fmz8CA; c_ref=https%3A//blog.csdn.net/pang787559613/article/list/2; c-toolbar-writeguide=1; SESSION=3b5e7c88-b27d-4fcc-a2d5-4e97c1438a3c; UN=pang787559613; Hm_ct_6bcd52f51e9b3dce32bec4a3997715ac=6525*1*10_20621362900-1586421086666-163599!5744*1*pang787559613; announcement=%257B%2522isLogin%2522%253Atrue%252C%2522announcementUrl%2522%253A%2522https%253A%252F%252Fblog.csdn.net%252Fblogdevteam%252Farticle%252Fdetails%252F105203745%2522%252C%2522announcementCount%2522%253A0%252C%2522announcementExpire%2522%253A3600000%257D; UserName=pang787559613; UserInfo=604f13922dc04f2d8071fe0834e95db3; UserToken=604f13922dc04f2d8071fe0834e95db3; UserNick=%E7%AC%91%E8%83%96%E4%BB%94; AU=5FE; BT=1586422795507; p_uid=U000000; Hm_lpvt_6bcd52f51e9b3dce32bec4a3997715ac=1586422842; TY_SESSION_ID=ffc735f4-f5ae-40ed-9b98-ff01e337bf76; dc_tos=q8ijsv",
  33. "user-agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.120 Safari/537.36"
  34. }
  35. data = {"id": blog_id}
  36. reply = requests.get(url, headers=headers, data=data)
  37. try:
  38. write_hexo_md(reply.json(), date)
  39. except Exception as e:
  40. print("***********************************")
  41. print(e)
  42. print(url)
  43. # print(reply.json())
  44. def write_hexo_md(data, date):
  45. """将获取的json数据解析为hexo的markdown格式"""
  46. title = data["data"]["title"]
  47. title = title.replace("[", "【")
  48. title = title.replace("]", "】")
  49. tags = data["data"]["tags"]
  50. # 页面唯一标识符,用于统计系统和评论系统
  51. key = "key" + str(uuid.uuid4())
  52. name = f"{date[0]}-{date[1]}-{date[2]}-{title}"
  53. tag = "tags:\n - " + "\n - ".join(tags.split(","))
  54. # #hexo内容顶部拼接:title、tag、key
  55. # header = "---\n" + f"title: {title}\n" + tag + "\n" + f"key: {key}\n" + "---\n\n"
  56. # #hexo内容顶部拼接:仅title
  57. # header = "---\n" + f"title: {title}\n" + "---\n\n"
  58. content = data["data"]["markdowncontent"].replace("", "")
  59. # hexo格式markdown
  60. # with open(f"blogs/{name}.md", "w", encoding="utf-8") as f:
  61. # f.write(header + content)
  62. # print(f"写入 {name}")
  63. # 用来博客迁移。遂仅保留内容
  64. with open(f"blogs/{title}.md", "w", encoding="utf-8") as f:
  65. f.write(content)
  66. print(f"写入 {title}")
  67. def main(total_pages=2):
  68. """
  69. 获取博客列表,包括id,时间
  70. 获取博客markdown数据
  71. 保存hexo格式markdown
  72. """
  73. blogs = []
  74. for page in range(1, total_pages + 1):
  75. blogs.extend(request_blog_list(page))
  76. for blog in blogs:
  77. blog_id = blog[0]
  78. date = blog[1].split()[0].split("-")
  79. request_md(blog_id, date)
  80. time.sleep(1)
  81. if __name__ == '__main__':
  82. main()