본문 바로가기
IT

Python Open API 활용 블로그 RSS 생성 소스

by Oh.mogilalia 2021. 7. 29.

예전엔 티스토리에서 블로그 전체 백업기능이 있었는데 지금은 없어졌습니다.
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 을 건너 뛰고 진행하시면 됩니다.



출처: https://ivps.tistory.com/650 [iVPS 가상서버호스팅]

'IT' 카테고리의 다른 글

티스토리 추출  (0) 2021.07.30
Tistory Wordpress 연동  (0) 2021.07.30
.htaccess 파일  (0) 2021.07.29
wordpress .htaccess  (0) 2021.07.29
windisk32imager SD 용량 줄이기  (0) 2021.07.28