从零开始免费搭建自己的博客(七)——迁移 CSDN 博客到个人博客站点

​ 本文是博客搭建系列文章第六篇,其他文章链接:

  1. 从零开始免费搭建自己的博客(一)——本地搭建 Hexo 框架
  2. 从零开始免费搭建自己的博客(二)——基于 GitHub pages 建站
  3. 从零开始免费搭建自己的博客(三)——基于 Gitee pages 建站
  4. 从零开始免费搭建自己的博客(四)——编写Markdown文章利器 Typora
  5. 从零开始免费搭建自己的博客(五)——Typora + PicGo + GitHub/Gitee图床
  6. 从零开始免费搭建自己的博客(六)——三个站点一键发布博客
  7. 从零开始免费搭建自己的博客(七)——迁移 CSDN 博客到个人博客站点
  8. 从零开始免费搭建自己的博客(八)——博客网站个性化设置及优化

前言

CSDN 没有提供文章导出功能,只有导入功能,我们想把自己以前写的文章迁移到其他平台或者自己的博客网站,还得自己想办法爬取下来。可是那是我写的文章啊,竟然不能想拿就拿回来。。。

我看到需多人的实现思路,是使用文章界面点击编辑按钮的接口,获取自己之前的文章 Markdown 源码。这样还得先登录自己的账号,而且前提是之前文章是用 Markdown 编辑器写的。其实不管是富文本编辑器还是 Markdown 编辑器写的,最终呈现的都是一个 html 网页,不需要登录就可以看到。之前介绍 Typora 时说过, Markdown 语法和 html 语法本来就类似,所以本文思路是直接下载 html 然后转化为 Markdown 格式。

在 CSDN 页面结构不发生改变的情况下,我们可以用这种方法下载 CSDN 上任意文章并保存成 Markdown 格式。只有写过博客的人才知道原创一篇文章要花费多少精力,所以希望大家如果下载别人的文章一定要标明原地址,这是基本节操。

工具选择

语言:Python3

第三方库:requestsparsel tomd

当然可以使用上一篇用到的 pyppeteer,不多对于这个需求来说速度太慢。CSDN 目前没有设置很多反爬虫机制,所以用轻量的 requests 就够了。

parsel Scrapy 框架内置的 html 解析库,后来独立出来。选择 parsel 也是因为够用,且比 BeautifulSoup 更轻。

html 转 Markdown 的库找到两个:tomdhtml2text ,试了一下都挺好用的,美中不足的是两个库转换完的代码块都没有标识语言类别,导致代码无法高亮。我看了 html 源码是有是语言类别信息的,所以需要对库稍作改动才能达到完美的效果。tomd 的原理比较简单粗暴,直接是正则表达式查找替换,源码就一个文件,比较好改,改动点和源码在下面。

实现过程

  1. 获取自己主页文章列表,包括标题和文章地址。
  2. 根据上一步获取的文章地址,获取文章的标题、正文、标签、分类、发布时间。
  3. 根据上一步获取的文章正文,将 html 格式文本转为 md 格式。
  4. 新建.md文件,先添加标题、标签、分类、发布时间,再写入 md 正文。
  5. 使用上一篇文章中实现的一键发布脚本将本地保存的博客发布到自己的博客。

tomd修改

在使用tomd的过程中遇到两个问题,好在源码只有一个文件,原理也很简单,稍微看一下代码逻辑就可以解决。

  1. 无序列表转化后没有换行,导致无序列表只有一行。需要修改tomd.py文件第103行。

    image-20210126224109198

    1
    2
    elif self.tag == 'ul' and tag == 'li':
    self.content = re.sub(pattern, '\n- \g<1>', self.content)

    image-20210126224458389

  2. 代码块没有标识语言类别,无法代码高亮。需要修改tomd.py文件第19行和第50行。各加三行,根据实际用到的语言。

    image-20210126230335050

    image-20210126230422905

    1
    2
    3
    4
    5
    # 'block_code': ('\n```\n', '\n```\n'),
    'block_code_go': ('\n```go\n', '\n```\n'),
    'block_code_py': ('\n```python\n', '\n```\n'),
    'block_code_java': ('\n```java\n', '\n```\n'),
    'block_code_cpp': ('\n```c\n', '\n```\n'),
    1
    2
    3
    4
    5
       # 'block_code': '<pre.*?><code.*?>(.*?)</code></pre>',
    'block_code_go': '<pre.*?><code.*?Go.*?>(.*?)</code></pre>',
    'block_code_py': '<pre.*?><code.*?python.*?>(.*?)</code></pre>',
    'block_code_java': '<pre.*?><code.*?java.*?>(.*?)</code></pre>',
    'block_code_cpp': '<pre.*?><code.*?cpp.*?>(.*?)</code></pre>',

    image-20210126230230535

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
csdn_to_md.py
import os
import re

import parsel
import requests
import tomd


def get_article_info(url):
html = requests.get(url, headers=headers).text
selector = parsel.Selector(html)
urls = selector.css('#articleMeList-blog > div.article-list > div > h4 > a').xpath('.//@href').getall()
print('共找到%d篇文章...' % len(urls))
return urls


def get_html_from_csdn(url):
html = requests.get(url, headers=headers).text
selector = parsel.Selector(html)
title = selector.css('div.article-title-box > h1::text').get()
article = selector.css('div.article_content').get()
category = selector.css('div.blog-tags-box > div > a::text').getall()[0]
tags = selector.css('div.blog-tags-box > div > a[data-report-click*="mod"]::text').getall()
time_stamp = selector.css('div > span.time::text').get()
author = selector.css('#uid > span.name::text').get()
origin = url
return title, article, category, tags, time_stamp, author, origin


def html_to_md(title, article, category, tags, time_stamp, author, origin):
md = tomd.convert(article)
# 图片url标准化
url_pattern = re.compile(r'<img.*?(https://.*?\.gif|https://.*?\.png).*?">')
for src_url in url_pattern.finditer(md):
img_name = src_url.group(1).split('/')[-1]
md = md.replace(src_url.group(0), '![%s](%s)' % (img_name, src_url.group(1)))
print('正在下载 %s' % title)
text = "---\ntitle: %s\ndate: %s\ntags: [%s]\ncategories: %s\n---\n\n> 作者: %s\n> 原文链接: %s\n%s" % (
title, time_stamp, ', '.join(tags), category, author, origin, md)
# Windows下文件名字不能包含特殊符号
file_name = re.sub(r'[\\/:*?"<>|]', ' ', title)
with open('articles/%s.md' % file_name.strip(), 'w', encoding='utf-8') as f:
f.write(text)


def main(url):
if not os.path.exists('articles'):
os.mkdir('articles')
article_urls = get_article_info(url)
for article_url in article_urls:
title, article, category, tags, time_stamp, author, origin = get_html_from_csdn(article_url)
html_to_md(title, article, category, tags, time_stamp, author, origin)
print('完成%d篇文章的下载' % len(article_urls))


if __name__ == '__main__':
headers = {
'Host': 'blog.csdn.net',
'Referer': 'https://blog.csdn.net',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3542.0 Safari/537.36'
}
start_url = "https://blog.csdn.net/用户名"
main(start_url)

后续

本文解决了将 CSDN 上使用富文本编辑器写的文章下载到本地转化成 Markdown 格式的问题,然后再配合上一篇中实现的一键发布脚本,就可以实现三个博客站点的同步了。到这里,已经实现了我自认为很完美的效果,以后就可以开开心心安安静静的写博客了。

我们看别人的独立博客,包括一些博主自己定制的博客园页面时,经常会看到一些有意思的特效,比如切换页签时原标题会变成调皮搞笑的文字,页面点击出现文字或爱心,页面出现跟随鼠标的随机线条,页面动态下雨或雪花,还有可以点击互动的小老鼠、二次元妹子等等,这些效果有的 Hexo 主题就内置了只需要把开关打开,有的需要自己手动加代码实现,因为它们本质上就是一段 js 代码(突然想起在以前公司有一次被安排到前端帮忙,曾经“沉迷”于在系统登录页面添加这些特效,hh)。还有一些针对 Hexo 的优化内容,比如提升页面加载速度,seo 优化,全文搜索插件等。这两项内容准备在最后一篇文章介绍。

写本系列文章第一篇之前,我并没有接触过 Hexo 这些东西,不知道 GitHub Pages 还能这样用,更不知道原来搭建博客里面有这么大的名堂,甚至一直没用 Markdown 写博客。决定自己搭建博客后,我先大概查了一下资料,然后把 8 篇文章的标题确定下来,形成一个大纲。其实在写前面几篇文章的时候,我还没把自己的博客搭起来,是后面一边学习一边操作一边记录,从小白的角度去弄懂每个步骤的原理,将过程记录下来,希望可以帮助更多的小伙伴。还有个意外收获,那就是发现了这是一个学习新东西的好方法。

  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2020-2021 杰克小麻雀
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信