├── README.md └── main.py /README.md: -------------------------------------------------------------------------------- 1 | # 迁移Hexo到typecho 2 | 3 | 这是一个能解析_posts文件夹中的所有符合 `Hexo YAML` 格式的 markdown 文件解析,并且能远程连接 typecho 数据库,导入文章,标签,分类。 4 | 5 | ## 准备工作 6 | 7 | 安装 Python3.x,以及依赖 pymysql 8 | 9 | ```bash 10 | pip3 install pymysql 11 | ``` 12 | 13 | 14 | 15 | ## 使用方法 16 | 17 | 下载或克隆此项目,打开 `main.py` ,进行编辑,在 `HexoToTypecho(host='', user='', database='', files, passwd='')` (位于最后一行),填入相关参数,分别是 typecho数据库,用户名,数据库名,密码。 18 | 19 | 默认处理md文件夹位于 `_posts` 文件夹。使用前把 `main.py` 放入 `你的hexo目录/source` 然后使用 Python3 运行。 20 | 21 | ## 常见问题 22 | 23 | * 出现 NoneType 报错 24 | - 文档没有按照标准 markdown 格式或 YAML 格式编写 25 | * 正文提取出现 NoneType 报错 26 | - `---`内容后加两个 `\n` 再写正文 -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import re 2 | import pymysql 3 | import time 4 | import os 5 | 6 | 7 | class HexoToTypecho(): 8 | def __init__(self, host, user, database, files, passwd=''): 9 | self.host = host 10 | self.user = user 11 | self.passwd = passwd 12 | self.port = 3306 13 | self.database = database 14 | self.files = files 15 | self.connectSQL() 16 | # self.parse_hexo_md() 17 | for i in files: 18 | self.insert_post(i) 19 | self.insert_tags_category() 20 | self.relationships() 21 | self.cur.execute( 22 | 'ALTER TABLE typecho_relationships DROP PRIMARY KEY') 23 | self.db.close() 24 | 25 | def connectSQL(self): 26 | try: 27 | sql = pymysql.connect(host=self.host, user=self.user, password=self.passwd, database=self.database, 28 | charset='utf8') 29 | except pymysql.Error as e: 30 | print(e) 31 | quit(1) 32 | else: 33 | print('连接成功') 34 | self.cur = sql.cursor() 35 | self.db = sql 36 | self.cur.execute('ALTER TABLE typecho_metas ADD UNIQUE KEY(name,type)') 37 | 38 | def parse_hexo_md(self, file): 39 | # TODO 文件名可以作为缩略名 40 | with open('_posts/' + file, encoding='utf-8') as f: 41 | s = f.read() 42 | print('当前处理 ---> ', file) 43 | 44 | # 标题提取 45 | title = re.search(r'title: (.*?)\n', s, re.S).group(1) 46 | # 时间转化时间截 47 | date = re.search(r'date: (.*?)\n', s, re.S).group(1) 48 | date = time.strptime(date, "%Y-%m-%d %H:%M:%S") 49 | date = int(time.mktime(date)) 50 | try: 51 | if not re.search(r'tags:[ ]*(.*?)\n', s).group(1): 52 | if re.search(r'tags:[ ]*\n(.*?)\nca', s, re.S): 53 | items = re.search(r'tags:[ ]*\n(.*?)\nca', s, re.S).group(1) 54 | tags = re.findall(r'- (.*?)\n', items) 55 | else: 56 | tags = '' 57 | else: 58 | tags = re.search(r'tags:[ ]*(.*?)\n', s).group(1) 59 | except AttributeError as e: 60 | print(e) 61 | tags = '' 62 | 63 | try: 64 | if not re.search(r'categories:[ ]*(.*?)\n', s).group(1): 65 | if re.search(r'categories:[ ]*\n(.*?)\n---', s, re.S): 66 | items = re.search(r'categories:[ ]*\n(.*?)\n---', s, re.S).group(1) 67 | categories = re.findall(r'- (.*?)\n', items) 68 | else: 69 | categories = '' 70 | else: 71 | categories = re.search(r'categories:[ ]*(.*?)\n', s).group(1) 72 | except AttributeError as e: 73 | print(e) 74 | categories = '' 75 | # 正文提取 76 | post = re.search(r'---\n\n(.*?)$', s, re.S).group(1) 77 | 78 | # print((title, date, tags, categories,post)) 79 | return (title, date, tags, categories, '' + post) 80 | 81 | def insert_post(self, file): 82 | data = self.parse_hexo_md(file) 83 | self.data = data 84 | db = self.db 85 | cur = self.cur 86 | modified = int(time.mktime(time.localtime(os.stat('_posts/' + file).st_mtime))) 87 | sql = ''' 88 | INSERT INTO typecho_contents(title,slug, created,modified, text,type,status,allowComment,allowFeed,allowPing,authorId) VALUES (%s,%s,%s,%s,%s,'post','publish',1,1,1,1) 89 | ''' 90 | 91 | try: 92 | cur.execute(sql, (data[0], file.split('.md')[0], data[1], modified, data[4])) 93 | db.commit() 94 | except Exception as e: 95 | print(e) 96 | db.rollback() 97 | 98 | def insert_tags_category(self): 99 | data = self.data 100 | cur = self.cur 101 | # cur.execute('ALTER TABLE typecho_metas ADD UNIQUE KEY(name,type)') 102 | sql = ''' 103 | INSERT INTO typecho_metas(name,slug,type,count) VALUES (%s,%s,'tag',1) ON DUPLICATE KEY UPDATE count = count + 1 104 | ''' 105 | # tags导入 106 | try: 107 | # (title, date, tags, categories, '' + post) 108 | if isinstance(data[2], list): 109 | for i in data[2]: 110 | cur.execute(sql, (i, i)) 111 | self.db.commit() 112 | else: 113 | if data[2]: 114 | cur.execute(sql, (data[2], data[2])) 115 | self.db.commit() 116 | except pymysql.DatabaseError as e: 117 | print(e) 118 | self.db.rollback() 119 | 120 | # category 导入 121 | sql = ''' 122 | INSERT INTO typecho_metas(name,slug,type,count) VALUES (%s,%s,'category',1) ON DUPLICATE KEY UPDATE count = count + 1 123 | ''' 124 | try: 125 | # (title, date, tags, categories, '' + post) 126 | if isinstance(data[3], list): 127 | for i in data[3]: 128 | cur.execute(sql, (i, i)) 129 | self.db.commit() 130 | else: 131 | if data[3]: 132 | cur.execute(sql, (data[3], data[3])) 133 | self.db.commit() 134 | except pymysql.DatabaseError as e: 135 | print(e) 136 | self.db.rollback() 137 | 138 | def relationships(self): 139 | db = self.db 140 | cur = self.cur 141 | data = self.data 142 | print('tag = ', data[2], 'type = ', type(data[2]), 'cet = ', data[3]) 143 | # 映射 tags 144 | select_mid = ''' 145 | SELECT mid FROM typecho_metas WHERE name = %s AND type = %s 146 | ''' 147 | select_cid = ''' 148 | SELECT cid FROM typecho_contents WHERE title = %s 149 | ''' 150 | add_relationship = ''' 151 | INSERT INTO typecho_relationships(cid,mid) VALUES (%s,%s) 152 | ''' 153 | 154 | try: 155 | cur.execute(select_cid, (data[0])) 156 | 157 | cid = cur.fetchall()[0][0] # 获取 cid 158 | 159 | if isinstance(data[2], list): 160 | for i in data[2]: 161 | cur.execute(select_mid, (i, 'tag')) 162 | tu = cur.fetchall() 163 | # print('mid = ', tu[0][0]) # mid 获取 164 | mid = tu[0][0] 165 | 166 | cur.execute(add_relationship, (cid, mid)) 167 | else: 168 | cur.execute(select_mid, (data[2], 'tag')) 169 | tu = cur.fetchall() 170 | print('mid = ', tu) # mid 获取 171 | mid = tu[0][0] 172 | cur.execute(add_relationship, (cid, mid)) 173 | except pymysql.DatabaseError as e: 174 | print(e) 175 | db.rollback() 176 | except IndexError as e: 177 | print('不能建立关系', data[2]) 178 | return 179 | 180 | # categories 181 | # (title, date, tags, categories, '' + post) 182 | try: 183 | if isinstance(data[3], list): 184 | for i in data[3]: 185 | cur.execute(select_mid, (i, 'category')) 186 | tu = cur.fetchall() 187 | # print('mid = ', tu[0][0]) # mid 获取 188 | mid = tu[0][0] 189 | 190 | cur.execute(add_relationship, (cid, mid)) 191 | else: 192 | cur.execute(select_mid, (data[3], 'category')) 193 | tu = cur.fetchall() 194 | # print(tu) # mid 获取 195 | mid = tu[0][0] 196 | cur.execute(add_relationship, (cid, mid)) 197 | except pymysql.DatabaseError as e: 198 | print(e) 199 | db.rollback() 200 | except IndexError as e: 201 | print('不能建立关系', data[3]) 202 | return 203 | 204 | 205 | if __name__ == '__main__': 206 | files = [f for f in os.listdir('_posts') if not f.startswith('.')] 207 | print('总有', files) 208 | HexoToTypecho(host='', user='', database='', files='', passwd='') 209 | --------------------------------------------------------------------------------