티스토리 블로그를 2차 개인 도메인으로 사용하다가 다시 기존 tistory.com 도메인으로 원위치 했습니다.

2차 도메인 주소에서 1차 도메인으로 리디렉션까지 시켰지만 그 이후로 블로그 방문통계를 보니 유입 수치가 엄청 줄어들었습니다.

언젠간 다시 회복되겠지 하면서 거의 1년이 흘렀지만 아직 여전히 회복이 되지 않더군요~ ㅋ

그래서 Google Search Console 메뉴를 여기저기 둘러봤더니 설정에 주소 변경이라는 항목이 있네요~

속성 설정에 주소 변경을 클릭합니다.

리디렉션은 예전에 아파치 .htaccess 파일에 걸어두었습니다.

RewriteEngine On

RewriteCond %{HTTP_HOST} ^blog\.ivps\.kr$ [NC]
RewriteRule ^(.*)$ https://ivps.tistory.com%{REQUEST_URI} [R=301,L]

blog.ivps.kr 요청이 오면 ivps.tistory.com 으로 리디렉션 하도록 한 설정입니다.

새 사이트 선택에서 변경할 사이트를 선택하고 유효성 검사 및 업데이트를 누릅니다.

유효성 검사 통과가 되면 이동 확인을 누릅니다.

이렇게 해놓고 몇일을 더 기다려 봐야겠습니다.

도움말은 여기를 참고하세요~

 

블로그 이미지

영은파더♥

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

,

티스토리는 rss 만 제공되는데 sitemap.xml 도 제공되면 좋겠네요~

무료로 사이트맵을 작성해주는 사이트가 있긴 하지만 불필요한 url 까지 생기는 문제가 있어서,

파이썬으로 한번 만들어 소스코드 공개합니다.

포스팅 주소가 숫자로 된 티스토리 블로그만 해당됩니다.

 

# coding=utf-8
from urllib2 import urlopen
from bs4 import BeautifulSoup
import xml.etree.ElementTree as xml
import time

BLOG_URL = 'https://ivps.tistory.com/' ### 블로그 주소
if ( BLOG_URL[len(BLOG_URL)-1] != '/' ):
  BLOG_URL += '/'
post_end = 0
html = urlopen(BLOG_URL)
soup = BeautifulSoup(html, "html.parser")
for hr in soup.findAll('a', {'class':'link_post'}):
  tmp = hr.get('href')[1:]
  if ( tmp != '' ):
    max = int( tmp )
    if ( post_end < max ):
      post_end = max
print "post_end " + str(post_end)

### SITEMAP XML Create
sitemap = xml.Element('urlset')
sitemap.set('xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9')
print ('<?xml version=\'1.0\' encoding=\'utf-8\'?>')
print ('<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">')
x1_1st = xml.SubElement(sitemap, 'url')
x2_lo = xml.SubElement(x1_1st, 'loc').text        = BLOG_URL
x2_ch = xml.SubElement(x1_1st, 'changefreq').text = 'always'
x2_pr = xml.SubElement(x1_1st, 'priority').text   = '1.0'
print ('<url><loc>' + BLOG_URL + '</loc><changefreq>always</changefreq><priority>1.0</priority></url>')
for i in range(post_end):
  url = BLOG_URL + str(i)
  try:
    html = urlopen(url)
    soup = BeautifulSoup(html, "html.parser")
    #print (soup.head.find("meta", {"name":"description"}).get('content'))
    x2_ur = xml.SubElement(sitemap, 'url')
    x3_lo = xml.SubElement(x2_ur, 'loc').text        = url
    x3_ch = xml.SubElement(x2_ur, 'changefreq').text = 'daily'
    x1_pr = xml.SubElement(x2_ur, 'priority').text   = '0.9'
    print ('<url><loc>' + url + '</loc><changefreq>daily</changefreq><priority>0.9</priority></url>')
  except:
    print (url + ' is not found')
  time.sleep(0.01)
print ('</urlset>')

#xml.dump(sitemap)
xml.ElementTree(sitemap).write('./sitemap.xml', encoding='utf-8', xml_declaration=True)

사용해 보시고 문제점이 있다면 댓글 남겨주세요~

생성된 파일을 공지사항에 첨부해놓고 해당 링크 주소를 웹마스터 도구에 제출하면 됩니다.

 

블로그 이미지

영은파더♥

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

,

티스토리에 2차 도메인으로 개인 도메인이 연결되어 있었는데 보안사이트가 적용되지 않아서 과감히 연결을 해제했습니다.

1차와 2차 모두 네이버 웹마스터 도구에 등록이 되어 있는데도 하나를 지웠더니 방문수가 급격하게 줄어들었네요~

유입 감소를 해결하기 위한 방법에 대해서 알아보겠습니다.

티스토리 개인 도메인 연결 해제 후 검색유입 감소

검색 유입다음에서만 되는 것 같습니다.

며칠 좀 더 지켜봐야겠네요~

 

개인도메인 삭제한지 딱 7일이 지났는데, 네이버, 구글 실망입니다~ ㅋ

중복 사이트 패널티 먹은건지 잘 모르겠지만 1차 도메인 주소로 검색 색인현황을 보니 좀 더딘 것 같습니다.

네이버 웹마스터도구 색인현황

 

▶ 1차 조치사항 ( 7일이 지난 후 )

  ☞ 기존 2차 도메인 호스트를 가상서버에 웹서비스를 구축하고 .htaccess 작성해서 1차 도메인으로 리다이렉트 적용

      한번 지켜봐야겠네요~

      효과가 있네요~ 예전만 못하지만 기존 2차 도메인 주소 타고 리디렉션 되어서 오는 것 같습니다.

 

▶ 검색유입 감소 해결 방법

  ☞ 1차 도메인 네이버 및 구글 웹마스터도구에 사이트 주소 등록

  ☞ 기존 2차 도메인 주소를 1차로 리디렉션 하기

RewriteEngine On

RewriteCond %{HTTP_HOST} ^blog\.ivps\.kr$ [NC]
RewriteRule ^(.*)$ https://ivps.tistory.com%{REQUEST_URI} [R=301,L]

제 블로그 주소를 예로 들었는데 본인의 블로그 주소에 맞게 수정하시면 됩니다.

 

블로그 이미지

영은파더♥

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

,

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

,

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

,

[티스토리] 오픈 API 로 블로그 목록 백업하기


예전엔 블록그 백업 기능을 지원했었는데 지금은 없어졌네요~ 유일하게 RSS 최대 50개까지 백업이 가능하지만 더 많은 포스팅이라면 Open API 를 이용할 수 밖에 없습니다.

백업하는 방법에 대해서만 언급하고 있으니 보시고 참고해서 프로그래밍은 직접하셔야 합니다.

인터넷에 찾아보시면 파이썬으로 되어 있는게 있더군요~


Open API 등록 : https://www.tistory.com/guide/api/manage/register


앱등록을 하면 앱관리에서 정보를 수정도 가능합니다.

[티스토리] 오픈 API 로 블로그 목록 백업하기

▶ 토큰 값 받기 ( 도움말 : https://tistory.github.io/document-tistory-apis/auth/authorization_code.html )

https://www.tistory.com/oauth/authorize?client_id={App ID}&redirect_uri={CallBack}&response_type=token

도움말을 보면 이해가 쉬울겁니다.

브라우저에서 위 주소를 치면 아래처럼 리다이렉트 되어 access_token 값이 날아옵니다.

모자이크 처리된 부분을 복사해서 아래 기능의 {access token} 값으로 이용하면 됩니다.

받고나서 1시간이 지나면 안된다는 군요~


▶ 블로그 정보 가져오기 ( 도움말 : https://tistory.github.io/document-tistory-apis/apis/v1/blog/list.html )

https://www.tistory.com/apis/blog/info?access_token={access token}&output=xml

output 은 json 또는 xml 둘 중에 하나입니다.


▶ 블로그 목록 가져오기 ( 도움말 : https://tistory.github.io/document-tistory-apis/apis/v1/post/list.html )

https://www.tistory.com/apis/post/list?access_token={access token}&output=xml&count=30&page=1&targetUrl=ivps

위에서 블로그 정보에서 총 게시글 수를 계산해서 page 와 count 프로그래밍 하시면 됩니다.

count 는 최대 30입니다.

targetUrl 은 티스토리 계정으로 총 5개의 블로그를 개설할 수 있는데 그 중에 하나를 입력하면 됩니다.


▶ 블로그 내용 가져오기 ( 도움말 : https://tistory.github.io/document-tistory-apis/apis/v1/post/read.html )

https://www.tistory.com/apis/post/read?access_token={access token}&output=xml&targetUrl=ivps&postId=3


이제 위의 3개의 API 를 조합해서 프로그래밍을 하면 됩니다.

나중에 여유가 되면 Python 으로 한번 만들어 보도록 하겠습니다.



▶ Python 으로 access_token 값 가져오기 : https://ivps.tistory.com/648

▶ Python 블로그 백업용 RSS 파일생성 : https://ivps.tistory.com/650



블로그 이미지

영은파더♥

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

,

티스토리 보안서버 SSL 적용


Tistory 가 http 에서 https 로 보안서버가 적용된다고 합니다.

*.tistory.com 블로그 및 2차 도메인까지 모두 적용된다고 하네요~

티스토리 보안서버 SSL 적용

인증서는 티스토리에서 알아서 적용해주는가 봅니다.


준비사항 :

HTML 스킨편집에서 외부 리소스 가져오는 부분에 http 로 되어 있는 부분만 수정해 주면 될 것 같습니다.

미리 바뀔 것에 대비해서 http:// 부분을 // 이렇게 변경해 주면 됩니다.


참고로 https:// 로 변경해도 되지만 // 이렇게 해주면 현재 열린게 http 면 http 로 https 면 https 로 호출을 합니다.


ps : 오늘자로 적용이 완료되었네요~

아래 링크 참고해서 적용시키면 됩니다.

https://notice.tistory.com/2444

개인도메인은 적용이 엄청 오래걸리네요~

5일째 아직 적용준비중으로 나오는군요~ ㅋ




블로그 이미지

영은파더♥

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

,