[티스토리] Python Open API 활용 블로그 RSS 생성 소스



예전엔 티스토리에서 블로그 전체 백업기능이 있었는데 지금은 없어졌습니다.


RSS 기능으로 백업은 가능한데 최대 50개까지만 가능합니다.


그래서 파이썬으로 전체 게시글을 RSS 로 만들어 주는 프로그램을 만들어서 공개합니다.


RSS 만 있으면 워드프레스로 플러그인을 이용하면 블로그 이전이 가능합니다.


물론 이미지 파일은 티스토리가 존재해야 이미지가 보여집니다~


이미지까지 백업하지 않는 이상 완전한 탈출은 힘듭니다.



# yum install python python-pip

# pip install requests pytz


사전에 python 관련해서 미리 설치를 하여야 합니다.



▶ Python 소스코드


# coding=utf-8

import json

import math

import requests

import urlparse

import xml.etree.ElementTree as xml

from datetime import datetime

from pytz import timezone


headers = { ### 헤더 필요시 requests.post(URL, headers=headers)

 'Referer':'https://www.tistory.com/auth/login',

 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'

}

URL_0 = 'https://www.tistory.com/auth/login'       ### 티스토리 로그인 URL

URL_1 = 'https://www.tistory.com/oauth/authorize'  ### 인증 요청 및 Authentication code 발급 URL

URL_2 = 'https://www.tistory.com/apis/blog/info'   ### 블로그 정보 URL

URL_3 = 'https://www.tistory.com/apis/post/list'   ### 블로그 리스트 URL

URL_4 = 'https://www.tistory.com/apis/post/read'   ### 블로그 상세보기 URL

loginParams = {                                    ### 로그인 Parameters ( 블로그주소, 이메일계정, 비밀번호 )

 'redirectUrl':'http://ivps.tistory.com',

 'loginId':'이메일계정',

 'password':'비밀번호'

}

tokenParams = {                                    ### 토큰값을 받아오기 위한 Parameters ( App ID, CallBack, 'token' )

 'client_id':'Open API App ID',

 'redirect_uri':'Open API CallBack',

 'response_type':'token'

}

def params_2(access_token):                        ### 블로그 정보 Parameters

  return {'access_token':access_token, 'output':'json'}

def params_3(access_token, blogName, page, count): ### 블로그 리스트 Parameters

  return {'access_token':access_token, 'output':'xml', 'targetUrl':blogName, 'page':page, 'count':count}

def params_4(access_token, blogName, post_id):     ### 블로그 상세보기 Parameters

  return {'access_token':access_token, 'output':'xml', 'targetUrl':blogName, 'postId':post_id}


blogName = 'ivps' ### 블로그명

page = 0          ### 1 페이지 부터 시작

count = 30        ### 최대값 30

post_id = 0       ### 아래에서 데이터를 추출

access_token = '' ### 아래에서 데이터를 추출


rs = requests.session()

try:

  r0 = rs.post(URL_0, data=loginParams)

  try:

    r1 = rs.get(URL_1, params=tokenParams)

    access_token = str( urlparse.parse_qs( r1.url.split('#')[1] )['access_token'][0] )

    print('### access_token : ' + access_token)

    try:

      r2 = rs.get(URL_2, params=params_2(access_token))

      print('### Open API, Blog Info Url : ' + str(r2.url))

      #print(r2.text)

      item = json.loads(r2.text)

      item_size = len(item['tistory']['item']['blogs'])

      ### RSS XML Create

      rss = xml.Element('rss')

      rss.set('version', '2.0')

      x1_ch = xml.SubElement(rss, 'channel')

      for i in range(item_size): ### 0 ~ 5, 없거나 최대 5개의 블로그

        blog_name = item['tistory']['item']['blogs'][i]['name']

        if(blog_name == blogName): # 일치하는 블로그만

          print('### Find blog : ' + str(blog_name))

          ### ==> 필요는 없지만 티스토리 rss 에 나온는 형식에 맞춰줌

          x1_ch_ti = xml.SubElement(x1_ch, 'title').text             = item['tistory']['item']['blogs'][i]['title']

          x1_ch_li = xml.SubElement(x1_ch, 'link').text              = item['tistory']['item']['blogs'][i]['url']

          x1_ch_de = xml.SubElement(x1_ch, 'description').text       = item['tistory']['item']['blogs'][i]['description']

          x1_ch_la = xml.SubElement(x1_ch, 'language').text          = 'ko'

          x1_ch_pu = xml.SubElement(x1_ch, 'pubDate').text           = datetime.now(timezone('Asia/Seoul')).strftime('%a, %d %b %Y %H:%M:%S %z')

          x1_ch_ge = xml.SubElement(x1_ch, 'generator').text         = 'ivps.kr'

          x1_ch_ma = xml.SubElement(x1_ch, 'managingEditor').text    = item['tistory']['item']['blogs'][i]['nickname']

          x1_ch_im = xml.SubElement(x1_ch, 'image')

          x1_ch_im_ti = xml.SubElement(x1_ch_im, 'title').text       = item['tistory']['item']['blogs'][i]['title']

          x1_ch_im_ur = xml.SubElement(x1_ch_im, 'url').text         = item['tistory']['item']['blogs'][i]['profileImageUrl']

          x1_ch_im_li = xml.SubElement(x1_ch_im, 'link').text        = item['tistory']['item']['blogs'][i]['url']

          x1_ch_im_de = xml.SubElement(x1_ch_im, 'description').text = item['tistory']['item']['blogs'][i]['description']

          ### <==

          nickname = item['tistory']['item']['blogs'][i]['nickname']

          totalCnt = item['tistory']['item']['blogs'][i]['statistics']['post']

          print('### post : ' + totalCnt) ### 포스팅 갯수

          pages = int ( math.ceil ( float(totalCnt) / float(count) ) )

          for j in range(pages): ### 총 페이지 만큼 반복

            page = j+1

            print('### Page : ' + str(page) + ' of ' + str(pages) + ' ###')

            try:

              r3 = rs.get(URL_3, params=params_3(access_token, blogName, page, count))

              print('### Open API, Blog List Url : ' + str(r3.url))

              xmlList = xml.fromstring(r3.text.encode(r3.encoding))

              #xml.dump(xmlList)

              for parent in xmlList.getiterator('post'): ### 목록에서 postId 추출

                post_id = int( parent.find('id').text )

                visibility = int( parent.find('visibility').text )

                if(visibility in (2,3)): ### 2:??, 3:발행 게시글

                try:

                  r4 = rs.get(URL_4, params=params_4(access_token, blogName, post_id))

                  print('### Open API, Blog Desc Url, postId(' + str(post_id) + ') : ' + str(r4.url))

                  xmlDesc = xml.fromstring(r4.text.encode(r4.encoding))

                  #print(xml.dump(xmlDesc))

                  for desc in xmlDesc.getiterator('item'): ### 상세내용 추출

                      x2_it = xml.SubElement(x1_ch, 'item')

                      x3_ti = xml.SubElement(x2_it, 'title').text       = parent.find('title').text

                      x3_li = xml.SubElement(x2_it, 'link').text        = parent.find('postUrl').text

                      x3_de = xml.SubElement(x2_it, 'description').text = desc.find('content').text

                      for tag in desc.find('tags').findall('tag'): ### 카테고리 갯수 만큼 반복

                        x3_ca = xml.SubElement(x2_it, 'category').text  = tag.text

                      x3_au = xml.SubElement(x2_it, 'author').text      = nickname

                      x3_gu = xml.SubElement(x2_it, 'guid').text        = parent.find('postUrl').text

                      x3_pu = xml.SubElement(x2_it, 'pubDate').text     = parent.find('date').text

                except:

                  print('@@@ Error : ' + str(r4.url))

                else:                    ### 0:비공개, 1:보호

                  print('### Pass PostId(' + str(post_id) + ') visibility : ' + str(visibility))

            except:

              print('@@@ Error : ' + str(r3.url))

        else:

          print('### Pass blog : ' + str(blog_name))

    except:

      print('@@@ Error : ' + str(r2.url))

  except:

    print('@@@ Error : ' + str(r1.url))

except:

  print('@@@ Error : ' + str(r0.url))

  

#xml.dump(rss)

xml.ElementTree(rss).write('/var/www/html/rss.xml') # 적당한 위치에 저장


색깔로 표시한 부분만 수정하면 됩니다.


제대로 안되면 중간 중간에 있는 주석 풀어서 디버깅 해보세요~


그리고 너무 빈번한 Open API 호출을 하면 트래픽 부하로 차단이 될 수도 있습니다.


그런 경우엔 해당 url 을 건너 뛰고 진행하시면 됩니다.


블로그 이미지

영은파더♥

가상서버호스팅 VPS 리눅스 서버관리 윈도우 IT

,