Skip to content

Instantly share code, notes, and snippets.

@taylor224
Last active March 22, 2024 12:24
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save taylor224/5eef306afaef7a7a136c66daecba6e41 to your computer and use it in GitHub Desktop.
Save taylor224/5eef306afaef7a7a136c66daecba6e41 to your computer and use it in GitHub Desktop.
티스토리 블로그 백업하기 & 워드프레스로 이사하기

티스토리 블로그 백업하기 & 워드프레스로 이사하기

Tistory 블로그 백업 기능이 삭제 되어 백업할수 없는 분들을 위한 백업 스크립트 및 워드프레스로의 이전 방법입니다.

우선 tistory 의 스킨을 [사용중TickTalk(사용자 수정/업로드) ver.1.0(사용자 수정/업로드)] 으로 변경하시기 바랍니다. 본 스크립트와 설명은 이 스킨을 기반으로 제작되었습니다. 또한 환경설정 - 기본정보 - 블로그 정보 - 주소설정 에서 주소방식을 [숫자 (http://notice.tistory.com/123)] 로 변경하시기 바랍니다. 워드프레스를 미리 설치하신 후 워드프레스 블로그의 url 및 기타 설정을 미리 진행하셔야 합니다. 본 사항을 미리 수행하지 않을경우 스크립트가 동작하지 않을수 있습니다.

진행하기 전 모든 부분에 대해서 읽어보신 후 진행하시기 바랍니다. 사전작업이 필요할 수 있습니다.

티스토리 백업하기

  1. 우선 tistory_backup.py 를 열어 맨 위의 base_url 을 백업할 블로그의 url 로 변경합니다.
  2. 본 스크립트는 이미지 주소를 워드프레스용 주소로 미리 치환합니다. 따라서 워드프레스에 tistory 에서 백업된 사진을 올릴 폴더를 미리 셋팅한 뒤 해당 폴더의 상대주소를 입력해줍니다. 저의 경우는 워드프레스 폴더의 /wp-content/uploads/tistory 폴더를 만들어 업로드 할 예정이었으므로 해당 상대경로를 입력하였습니다. 블로그를 갓 만든 상태에선 uploads 폴더가 없을수 있습니다. 없으면 만들어주셔야 합니다. 향후 첨부파일이 올라갈 디렉터리 입니다.
  3. 파이썬으로 스크립트를 돌립니다. 본 스크립트는 파이썬 2.7 기준으로 제작되었습니다. python tistory_backup.py
  4. 나온 data.xml 파일을 잘 보관합니다.

워드프레스에 글 Import 하기

  1. 앞서 백업한 티스토리 데이터 파일 중 image 폴더에 있는 이미지들을 전체 다 앞서 지정한 서버 폴더에 업로드 합니다. 저의 경우는 /wp-content/uploads/tistory 폴더에 업로드 하였습니다.
  2. WP All Import 라는 plugin 을 다운받아 설치하고 Activate 시킵니다. https://wordpress.org/plugins/wp-all-import/
  3. 관리자 페이지 왼쪽 밑의 All Import 메뉴에서 New Import 를 선택합니다.
  4. Upload a file 버튼을 선택하고 앞서 만들어진 data.xml 파일을 선택하고 업로드 합니다.
  5. 업로드 이후 표시된 화면에서 New Items 박스버튼을 선택한 후 Create new [Posts] 를 선택한 이후 아래의 [Continue to Step 2] 버튼을 누릅니다.
  6. 다음 화면에서 왼쪽의 박스 버튼 리스트에서 item 을 클릭하신 후 아래의 Continue to Step 3 를 클릭합니다.
  7. 다음에 표시된 화면의 오른쪽 XML tree 에서 <title> 을 클릭하여 왼쪽의 제목창으로 drag & drop 합니다.
  8. 같은 방법으로 content 도 왼쪽의 본문 창으로 drag & drop 합니다.
  9. 본문창 바로 아래의 Advanced Options 창을 눌러서 열은 뒤 모두다 체크 해제하고 Decode HTML entities with html_entity_decode 만 활성화 합니다.
  10. 아래의 Images 드롭다운 메뉴를 눌러 열은 뒤 라디오 체크박스에서 Use images currently in Media Library 를 선택합니다.
  11. 그 다음 아래의 Taxonomies, Categories, Tags 드랍다운 창을 열어서 카테고리나 태그를 설정합니다. 일괄로 Tistory_backup 같은 카테고리나 태그를 설정할수도 있고 백업된 파일에서 가져와서 원래의 카테고리를 설정할수도 있습니다. 원래의 카테고리를 설정할 경우 오른쪽 XML tree 에서 category 를 입력창으로 drag & drop 합니다.
  12. 그 다음 아래의 Other Post Options 를 누른 뒤 Post Dates 부분의 입력창에서 now 를 지우고 오른쪽 XML tree 에서 date 를 drag & drop 합니다.
  13. 그 다음 맨 아래의 Continue to Step 4 를 클릭합니다.
  14. Unique Idenfier 부분에 오른쪽 XML Tree 의 id 를 drap & drop 하고 맨 아래의 Continue 를 클릭하고 최종 Import 진행 버튼을 눌러 Import 를 진행합니다.
  15. 게시글의 숫자에 따라 다소 시간이 소요될수 있습니다.

게시글의 id 수정하기

게시글의 id 가 단순히 순차적으로 증가된 값으로 등록되기 때문에 원래의 Tistory 블로그 주소 숫자와 매칭이 되지 않을수 있습니다. 따라서 db 를 수정하여 해당 부분을 바로잡아줄수 있습니다. 기존 블로그에서 Redirect 를 시킬 필요가 없다면 본 작업은 수행하지 않아도 됩니다. 본 작업을 위해서는 워드프레스도 동일하게 블로그 주소 방식을 숫자로 변경해야 합니다. 워드프레스 설정에서 Settings - Permalinks - Common Settings 에서 Custom Structure 를 선택하신 후 "/%post_id%" 라고 입력합니다(따옴표는 제외).

본 작업을 수행하기 전에 wordpress db 를 dump 하여 백업해 놓으신뒤 진행하시기 바랍니다. 본 작업으로 인한 데이터 손실은 책임지지 않습니다. 빈 워드프레스 DB 에서 진행할것을 권장합니다.

또한 기존에 워드프레스 글 데이터가 있을 경우 ID 값이 충돌하여 에러가 발생할수 있습니다. 이 경우 본 작업은 진행하시면 안됩니다.

  1. DB 관리툴을 열어 wp_posts (설정에 따라 앞의 wp 부분 prefix가 다를수 있음) 에서 새로 import 된 글의 guid 부분을 확인하고 복사합니다.
  2. 해당 guid 에서 뒤의 숫자 부분만 지운 url 을 change_index.sql 의 아래 블로그 주소 부분을 지우고 넣습니다.
  3. 자신의 워드프레스 db 테이블 이름에 맞게 wp_posts 와 wp_pmxi_posts 부분을 수정한 후 sql 문을 수행합니다.
  4. sql 문이 잘 수행이 되었는지 확인합니다. 잘 수행이 되었다면 id 가 기존 티스토리 블로그에 매칭되도록 변경됩니다.
  5. 블로그 주소로 접속하여 확인해봅니다.

티스토리를 자동으로 새 블로그로 Redirect 하도록 수정하기

티스토리 관리자 페이지에서 꾸미기 - HTML/CSS 편집에 들어간 뒤 </head> 바로 윗 부분에 tistory_auto_redirect.js 파일에 있는 코드를 붙여넣습니다. 붙여넣을때에 해당 스크립트의 윗 부분에 있는 BLOG_SITE_URL 의 변수를 자신의 새로운 블로그 URL 로 변경합니다.

본 스크립트를 추가하고 저장하면 이 스크립트가 자동으로 블로그의 같은 번호의 글을 체크하여 새로운 블로그에 같은 번호의 글이 있을때만 Redirect 시킵니다.

본 스크립트에 대한 별도의 문의는 받지 않습니다.

by. Taylor Starfield (https://blog.kloa.kr)

UPDATE wp_posts AS t
INNER JOIN wp_pmxi_posts AS tr
ON t.id = tr.post_id
SET t.id = tr.unique_key;
UPDATE wp_posts SET guid = CONCAT("https://blog.domain.com/archives/", CAST(id AS CHAR)) WHERE id > 40;
<script>
var BLOG_SITE_URL = 'https://blog.domain.com'
function isUrlExists(url, page_url)
{
$.getJSON("http://query.yahooapis.com/v1/public/yql?"+
"q=select%20*%20from%20html%20where%20url%3D%22"+
encodeURIComponent(url)+"%22&format=json'&callback=?", function(data){
if(data.results[0]){
window.location.replace(BLOG_SITE_URL + page_url);
}
else{
}
}
);
}
var page_url = window.location.pathname;
if (page_url.length > 1) {
isUrlExists(BLOG_SITE_URL + page_url, page_url);
}
</script>
# -*- coding: utf8 -*-. #
from __future__ import unicode_literals
import urllib2
import BeautifulSoup
import time
import dicttoxml
import sys
import urllib
base_url = 'yourblog.tistory.com'
image_content_url = '/wp-content/uploads/tistory/'
article_data = []
error_count = 0
error_max_count = 50
try:
for index in range(42, 1000):
u = None
try:
u = urllib2.urlopen('http://' + base_url + '/' + str(index))
except urllib2.HTTPError as e:
if e.code == 404:
print('Post ' + str(index) + ' - Not Found')
error_count += 1
if error_count > error_max_count:
print('Error Count is over the max value. Stopping the crawler')
break
time.sleep(3)
continue
else:
raise
error_count = 0
b = BeautifulSoup.BeautifulSoup(u.read())
# Check 404 also
absent_post = b.find('div', {'class': 'absent_post'})
if absent_post:
print('Post ' + str(index) + ' - Not Found')
time.sleep(3)
continue
# Get Title of article
title = b.find('div', {'class': 'tit'}).find('a', {'class': 'link'}).contents[0].encode('utf-8')
category = b.find('div', {'class': 'tit'}).find('a', {'class': 'category'}).contents[0].encode('utf-8')
date = b.find('div', {'class': 'tit'}).find('span', {'class': 'date'}).contents[0].replace('.', '-').replace(' ', 'T') + ':00'
# Get Atricle from page
article = b.find('div', {'class': 'desc'})
# Delete another category article element
another_category = article.find('div', {'class': 'another_category another_category_color_gray'})
if another_category:
another_category.decompose()
# Delete Copyright element
article.find('div', {'style': 'width:100%;margin-top:30px;clear:both;height:30px'}).decompose()
imagelist = article.findAll('img')
for image in imagelist:
if '//cfile' in image['src']:
filename = image['src'].split('/')[-1]
filetype = ''
if 'itistory-photo' in image['filename']:
if image['filemime'] == 'image/jpeg' or image['filemime'] == 'image/jpg':
filetype = 'jpg'
elif image['filemime'] == 'image/png':
filetype = 'png'
elif image['filemime'] == 'image/gif':
filetype = 'gif'
else:
filetype = image['filename'].split('.')[-1].lower()
print(filename + '.' + filetype)
try:
urllib.urlretrieve(image['src'], 'image/' + filename + '.' + filetype)
except Exception:
print('File retrieve error - ' + image['src'])
# Object is soft copy object. It will change root object value.
image['src'] = image_content_url + filename + '.' + filetype
image['onclick'] = None
print('Post ' + str(index) + ' - [' + category.decode('utf-8') + ']' + title.decode('utf-8') + ' - ' + date)
article_data.append({
'id': index,
'title': title,
'date': date,
'category': category,
'content': str(article)
})
time.sleep(3)
except KeyboardInterrupt:
xml = dicttoxml.dicttoxml(article_data, custom_root='article', attr_type=False)
file = open('data.xml', 'w')
file.write(xml)
file.close()
sys.exit(0)
xml = dicttoxml.dicttoxml(article_data, custom_root='article', attr_type=False)
file = open('data.xml', 'w')
file.write(xml)
file.close()
@riverwind46
Copy link

정성껏 작성하신 글 잘 읽었습니다. ^^
혹시 티스토리 블로그 이전 대행 해 주실수는 없는지 알고 싶습니다.
근래에 여러가지로 티스토리 유저의 환경이 나빠진 관계로 이전을 고려하게 되었습니다.
좋은 하루되시고 ..
감사합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment