[티스토리] Python Open API 이용 블로그 및 이미지 백업하기



앞에서는 블로그의 내용만 RSS 로 만들었는데 이번에는 사진 이미지까지 백업하는 파이썬 소스코드를 공개합니다.


티스토리에서 워드프레스로 완전히 이전을 원하시는 경우에 사용하시면 됩니다.


이미지는 본문 내용에 포함할 수도 있고 다운로드한 사진은 img src 의 url 을 치환할 수도 있습니다.


IMG_SRC_TYPE 값이 1 이면 URL 만 치환, 2 이면 본문에 이미지 삽입


본문에 포함되는 방식이 아닌 1의 경우는 아래 코드에서 저장된 이미지 파일을 해당 경로에 수동으로 업로드 하셔야 합니다.



▶ Python 소스코드


# coding=utf-8

# pip install beautifulsoup4

# pip install python-magic

# pip install pytz

# pip install requests

# pip install urllib3

import base64

import json

import magic

import math

import os

import requests

import urllib2

import urlparse

import xml.etree.ElementTree as xml

from bs4 import BeautifulSoup

from datetime import datetime

from pytz import timezone


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

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

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

count = 30        ### 최대값 30

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

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

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}

IS_IMG_TO_SAVE = 1  ### 이미지 저장

IMG_SRC_TYPE = 1    ### 1:이미지 URL 변경, 2:이미지를 본문 내용에 포함

def image_save_from_html(html):

  html_obj = BeautifulSoup(html, 'html.parser')

  img_data = html_obj.find_all('img')

  print('img count : ' + str(len(img_data)))

  for image in img_data:

    if '//cfile' in image['src']:

      print('img src : ' + image['src'])

      try:

        imgUrl = image['src']

        filename = image['src'].split('/')[-1]

        imgData = urllib2.urlopen(image['src']).read()

        f = open(filename, 'wb')

        f.write(imgData)

        f.close()

        if(IMG_SRC_TYPE == 1):

          imgSrc = image_url_change(filename)

          html = html.replace(imgUrl, imgSrc)

        elif(IMG_SRC_TYPE == 2):

          imgSrc = image_to_rawdata(filename)

          html = html.replace(imgUrl, imgSrc)

        else:

          print('@@@ imgUrl : ' + imgUrl)

      except:

        print('@@@ HTMLparse Error : ' + str(image))

    else:

      print('@@@ Pass img src : ' + image['src'])

  return html

def image_to_rawdata(filename):

  mime_type = magic.from_file(filename, mime=True)

  print('mime_type : ' + mime_type)

  f = open(filename, 'rb')

  image = f.read()

  f.close()

  rawData = base64.b64encode(image).decode('utf-8')

  imgSrc = 'data:'+ mime_type +';base64,' + rawData

  return imgSrc

def image_url_change(filename):

  mime_type = magic.from_file(filename, mime=True)

  print('mime_type : ' + mime_type)

  if '.' not in filename: ### 이미지 파일 확장자가 없으면 추가

    ext = mime_type.split('/')[-1]

    os.rename(filename, filename+'.'+ext)

    filename = filename+'.'+ext

  imgSrc = '/wp-content/uploads/tistory/' + filename

  return imgSrc



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'): ### 상세내용 추출

                      if(IS_IMG_TO_SAVE == 1):

                        html = image_save_from_html(desc.find('content').text)

                      else:

                        html = desc.find('content').text

                      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 = html

                      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') # 적당한 위치에 저장


색깔이 다른 부분만 수정해서 사용하시면 됩니다.


이미지를 본문 내용에 포함하는 경우에 이미지가 많이 들어간 경우는 걸러주는 작업이 필요해 보입니다.


양이 많으니깐 메모리 부족 현상 때문인지 Killed 가 발생하더군요~



티스토리가 요즘 방문통계가 영 이상하네요~


블로그 이미지

영은파더♥

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

,

[Python] 이미지 파일의 MIME TYPE 알아내는 방법



티스토리 같은 경우 이미지를 첨부하면 아래와 같이 확장자명이 생략이 됩니다.


<img src="https://t1.daumcdn.net/cfile/tistory/C81EA46998F0401423" style="cursor: pointer;max-width:100%;height:auto" width="820"/>


그래서 일단 해당 url 의 이미지를 로컬에 저장한 다음에 아래의 소스코드로 알아내면 됩니다.


import magic


mime_type = magic.from_file(filename, mime=True)

print('mime_type : ' + mime_type)


사전에 pip install python-magic 명령으로 해당 라이브러리를 먼저 설치하면 됩니다.


'Python' 카테고리의 다른 글

[Python] requests.get 한글깨짐  (0) 2019.08.19
[CentOS] Python PIP 설치  (0) 2019.08.19
[CentOS] 7.x Apache + Python 연동 방법  (0) 2018.12.21
[Python] sitemap.xml 생성 방법  (0) 2018.12.20
[Python] XML Create and Write  (0) 2018.12.18
블로그 이미지

영은파더♥

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

,

[CentOS] 7.x Apache + Python 연동 방법



아파치에서 파이썬도 동작이 가능합니다.


# vi /etc/httpd/conf.d/python.conf


<Directory /var/www/html/python>

  Options +ExecCGI

  AddHandler cgi-script .py

</Directory>



# systemctl restart httpd


# mkdir /var/www/html/python


# vi /var/www/html/python/index.py


#!/usr/bin/env python


print "Content-type: text/html\n"

print "Python test page"


# chmod 755 /var/www/html/python/index.py


이제 브라우저에서 한번 열어보세요~


블로그 이미지

영은파더♥

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

,

[워드프레스] All in One SEO 플러그인



워드프레스로 만들 블로그를 구글이나 네이버 웹마스터 도구에 등록하려면 sitemap.xml 과 rss feed 가 있으면 자신의 블로그 게시글의 URL 을 등록해서 검색 노출에 도움이 됩니다.


워드프레스 자체에는 티스토리 처럼 피드 기능은 제공하는데 사이트맵 기능을 제공하지 않네요~


플러그인 이용해서 사이트맵을 만들면 됩니다.


Yoast SEO 와 All in One SEO 가 인기가 좋던데 제가 볼때는 아이콘이 더 가미된 올인원SEO 를 더 추천 하고 싶네요~ ㅎ



[워드프레스] All in One SEO 플러그인


원하는 기능을 활성화 시켜서 사용하면 될 것 같습니다.




블로그 이미지

영은파더♥

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

,

[티스토리] Python 으로 sitemap.xml 만들기



티스토리 블로그는 RSS 는 제공하지만 사이트맵은 제공하지 않습니다.


그래서 sitemap 만들어주는 사이트에서 만들고 편집해서 등록하였었는데 엄청 귀찮습니다.


파이썬으로 티스토리 블로그의 사이트맵을 생성해주는 프로그래밍을 해서 공개합니다.



▶ Python Create Sitemap 소스코드


# 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'])

      ### SITEMAP XML Create

      sitemap = xml.Element('urlset')

      sitemap.set('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')

      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))

          ### ==> 블로그 주소 부분

          x1_1st = xml.SubElement(sitemap, 'url')

          x2_lo = xml.SubElement(x1_1st, 'loc').text        = item['tistory']['item']['blogs'][i]['url']

          x2_ch = xml.SubElement(x1_1st, 'changefreq').text = 'always'

          x2_pr = xml.SubElement(x1_1st, 'priority').text   = '1.0'

          ### <==

          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:발행 게시글

                  x2_ur = xml.SubElement(sitemap, 'url')

                  x3_lo = xml.SubElement(x2_ur, 'loc').text        = parent.find('postUrl').text

                  x3_ch = xml.SubElement(x2_ur, 'changefreq').text = 'daily'

                  x1_pr = xml.SubElement(x2_ur, 'priority').text   = '0.9'

                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(sitemap)

xml.ElementTree(sitemap).write('/var/www/html/sitemap.xml', encoding='utf-8', xml_declaration=True) # 적당한 위치에 저장


RSS 만들어주는 코드에서 응용해서 만들었습니다.


색깔이 들어간 부분만 수정하시면 됩니다.


티스토리에서 제공하는 Open API 를 이용한 코드입니다.


오픈API 등록은 https://ivps.tistory.com/645 여기를 참고하세요~


블로그 이미지

영은파더♥

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

,

[Python] sitemap.xml 생성 방법


파이썬으로 사이트맵 파일을 만드는 방법입니다.

<?xml version='1.0' encoding='utf-8'?>

<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

  <url>

    <loc>https://ivps.tistory.com</loc>

    <changefreq>always</changefreq>

    <priority>1.0</priority>

  </url>

</urlset>


위와 같은 파일을 파이썬으로 코딩하면 아래와 같습니다.

import xml.etree.ElementTree as xml


sitemap = xml.Element('urlset')

sitemap.set('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')

x1_1st = xml.SubElement(sitemap, 'url')

x2_lo = xml.SubElement(x1_1st, 'loc').text = 'https://ivps.tistory.com'

x2_ch = xml.SubElement(x1_1st, 'changefreq').text = 'always'

x2_pr = xml.SubElement(x1_1st, 'priority').text = '1.0'

xml.ElementTree(sitemap).write('/var/www/html/sitemap.xml', encoding='utf-8', xml_declaration=True)

칼라 부분이 키포인트이네요~


블로그 이미지

영은파더♥

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

,

[티스토리] 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

,

[Python] XML Create and Write


파이썬에서 XML 을 작성하는 방법입니다.

<rss version="2.0">

<channel>

<item>

<title>subject</title>

<title>description</title>

</item>

</channel>

</rss>


위와 같은 XML 을 만드는 코드입니다.

import xml.etree.ElementTree as xml


rss = xml.Element('rss')

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

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

it = xml.SubElement(ch, 'item')

ti = xml.SubElement(it, 'title')

de = xml.SubElement(it, 'title')

ti.text = str('subject')

de.text = str('description')

xml.dump(rss)

xml.ElementTree(rss).write('rss.xml')

이 외에도 만드는 방법이 더 있습니다.


블로그 이미지

영은파더♥

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

,

[Python] 티스토리 Open API 활용 access_token 값 가져오기


티스토리 블로그를 백업 받기 위해서 Open API 를 활용하는 방법이 있습니다.

제일 먼저 해야되는게 access_token 값을 알아야 합니다.

토큰값은 브라우저에서 받아와도 되지만 1시간이 지나면 다시 받아야 하는 번거로움이 있습니다.

그리고 백업을 자동화 하려면 아무래도 Python 으로 프로그래밍을 하는게 나아보입니다.


제대로 설명된 곳이 없어서 저처럼 한참 헤매실 분들을 위해서 포스팅합니다.


redirectUrl 은 CallBack 값이고, client_id 는 App ID 값을 입력하면 됩니다.

import requests

import urlparse


URL_0 = 'https://www.tistory.com/auth/login'

URL_1 = 'https://www.tistory.com/oauth/authorize'

loginParams = {

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

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

 'password':'비밀번호'

}

tokenParams = {

 'client_id':'App ID',

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

 'response_type':'token'

}


rs = requests.session()

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

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

print(r2.url)

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

print(access_token)


파이썬이 처음이라면 아래와 같이 미리 설치를 해주어야 합니다.

# yum install python python-pip

# pip install requests


access_token 값이 알아내면 그 다음 부터는 하나씩 풀어나가면 될 것 같습니다.

다음에는 블로그 리스트를 가져오는 소스를 공개하도록 하겠습니다.

블로그백업 RSS 파일 생성 소스 : https://ivps.tistory.com/650


블로그 이미지

영은파더♥

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

,

[Python] 한글이 있는 xml 파싱 UnicodeEncodeError


한글이 있는 경우 파싱할 때 아래와 같은 에러가 나는 경우 처리 방법입니다.

Traceback (most recent call last):

  File "example.py", line 44, in <module>

    xmlR = xml.fromstring(r3.text)

  File "/usr/lib64/python2.7/xml/etree/ElementTree.py", line 1300, in XML

    parser.feed(text)

  File "/usr/lib64/python2.7/xml/etree/ElementTree.py", line 1640, in feed

    self._parser.Parse(data, 0)

UnicodeEncodeError: 'ascii' codec can't encode characters in position 266-267: ordinal not in range(128)


encode('utf-8') 을 붙여주면 됩니다.

xmlR = xml.fromstring(r3.text.encode('utf-8'))


블로그 이미지

영은파더♥

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

,