====== CCleaner 자동 업데이트 스크립트 제작 ======
===== 동기 ===
{{http://static.piriform.com/pf/cc_preview2.png }} '[[http://www.piriform.com/ccleaner|CCleaner (Crap Cleaner)]]'를 아시나요? 윈도우를 사용하다보면 불필요한 레지스트리, 임시파일, 휴지통 파일 등등... 소위 말하는 '찌꺼기'들이 쌓이기 시작합니다. PC 좀 만지시는 분들은 상당히 꺼리는 것들이죠. 이런 것을 관리해주는 최적화 툴이 여럿 있는데, CCleaner도 이런 PC 최적화 툴 중 하나입니다. 간단하고 꽤 안정적이어서 저는 오랫동안 즐겨 사용하고 있습니다.
CCleaner는 개인이 집에서 무료로 이용할 수 있는 '홈(Home)', 좀 더 기능이 강화된 '프로페셔널(Professional)', 그리고 '비즈니스(Business)' 등 몇 가지 버전이 있습니다. 무료로 이용할 수 있는 홈 버전도 사실 상당히 쓸만합니다. 다만 자동 업데이트가 지원되지 않아 매번 홈페이지에서 직접 파일을 다운로드 받아 설치를 해 줘야 한다는 점이 불편합니다.
그런데 쓰다 보면 이 무료 버전의 자동 업데이트 미지원이 은근히 귀찮습니다. 대략 2-3주, 길면 한 두달에 한 번 생각날 때마다 한 번 정도 사용하는데 거의 이 주기가 지나면 적어도 1번쯤은 프로그램의 업데이트가 되어 있죠. 그래서 사실상 CCleaner를 사용할 때마다 먼저 업데이트부터 완료한 후에야 최적화 작업을 하곤 했습니다. 이 푸닥거리를 수회 반복하면 "자동 업데이트가 지원되는 유료를 질러 볼까?" 생각을 해보게 됩니다. 네, 지르셔도 됩니다. 좋다고 생각하시면 하나 지르세요 ;-)
하지만 여전히 무료로 사용하면서, 이 귀찮음을 온전히 감수하시는 분들이 계실 터입니다. 그런 분들을 위해 살짝 반칙 같지만, 그래서 약간 제작사인 Piriform에 미안한 맘이 들지만, 할 수 있는 적합한 선에서 이 귀찮음을 덜 수 있는 방법이 없을까 생각해 보았습니다. 그리고 제 파이썬 잉여력을 좀 쏟아보기로 했습니다.
사실 얼마전부터 CCleaner의 유료 버전인 CCleaner Professional을 사용중입니다. 이 버전은 자동 업데이트가 지원되어 편하게 사용할 수 있지만, 사실 무료 버전의 CCleaner와 별다른 차이가 없습니다. 자동 업데이트가 지원되지 않는 점만 감안하면 무료 버전도 충분히 쓸만합니다. 사실 그렇게 큰 차이도 없는데 돈을 지불한 것이 조금 아깝기까지도 합니다. 이번에도 역시 파이썬 2.7 스크립트를 사용합니다. CCleaner는 윈도우 기반의 소프트웨어이므로 이 스크립트는 윈도우에서만 동작한다고 가정합니다.
===== 작업 방법 =====
크게 아래와 같은 순서로 작업하면 될 것 같습니다.
- 로컬 PC의 CCleaner.exe의 file version을 쿼리한다.
- Piriform에서 CCleaner의 파일 버전을 쿼리한다.
- 로컬 버전의 파일 버전이 낮다면 새로 다운로드 받아 자동설치한다.
세부적으로 각 단계를 구상해 보겠습니다.
==== 로컬 PC 버전 쿼리 ====
CCleaner의 버전은 CCleaner의 파일 속성을 조사해보면 쉽게 알 수 있습니다. 탐색기에서 CCleaner.exe 파일을 선택해 우측 마우스를 클릭해서 '속성'을 클릭하면 나오는 아래 그림과 같은 창에 나오는 정보입니다.
{{ :project:ccleanerautoupdate:ccleaner_property.png?nolink |}}
문제는 이 정보를 우리가 쉽게 프로그램으로 가져올 수 있도록 해야합니다. 이렇게 마우스 클릭 및 메뉴 선택 등의 작업이 들어가면 자동화하기 어렵습니다. 이러한 명령을 CLI 상에서 쉽게 할 수 있는 방법을 찾아야 합니다. 아주 다행히도 이러한 방법은 구글에서 쉽게 검색을 통해 알아냈습니다. 이렇게 하면 됩니다.
wmic DATAFILE WHERE NAME="C:\\Program Files\\CCleaner\\CCleaner.exe" GET version > version.txt
[version.txt 파일]
Version
4.0.0.4064
[[http://msdn.microsoft.com/en-us/library/windows/desktop/aa394531%28v=vs.85%29.aspx|WMIC]]는 MSDN에서 자세하게 설명하고 있습니다. 여러 관리 문서 및 예제등은 검색하면 많이 찾아내실 수 있습니다. 여기서 저는 이 CCleaner의 파일 버전만 알아내고 싶을 뿐이므로 자세한 내용은 지나가도록 하겠습니다.
이렇게 얻어낸 version.txt 파일을 파이썬으로 처리하는 것은 식은 죽 먹기입니다.
==== 온라인 최신 버전 쿼리 ====
최신 버전 정보는 공식 홈페이지의 웹페이지를 크롤링해서 얻어낼 수 있습니다. 이 부분이 사실 민감하다면 민감한 부분입니다. 물론 홈페이지에서 버전 정보를 얻어내는 것이 문제가 되는 내용은 아닙니다만, 홈페이지의 레이아웃은 언제든 변경될 수 있으니까요. 레이아웃이 변경되면 그 전까지는 잘 동작하던 스크립트가 갑자기 먹통이 될 수도 있습니다. 그때는 어쩔 수 없습니다. 직접 레이아웃에 맞추어 수정을 해야 합니다. 무료의 한계입니다. :-( Piriform이 너무 자주 레이아웃을 바꾸지는 말아 주었으면 합니다.
최신 버전은 [[http://www.piriform.com/ccleaner/download|이 곳]]의 'Release notes'를 통해 공지됩니다. 이 부분의 HTML 코드는 다음과 같이 되어 있습니다. (2013년 4월 기준)
Release notes
-
v4.00.4064
(26 Mar 2013)
.....
매우 쉽습니다. ul class="versionHistory"는 이 페이지에서 단 한 번 나옵니다. 그럼 여기서 strong 태그 사이의 버전과 릴리즈된 날짜를 가져오도록 하면 되겠군요.
==== 파일 다운로드 및 설치 ====
이 부분도 Piriform이 디자인을 바꾸면 그에 따라 가야 하는 부분입니다. 다운로드는 [[http://www.piriform.com/ccleaner/download/standard|이 곳]]에서 받을 수 있습니다.
{{ :project:ccleanerautoupdate:ccleaner_download.png?nolink |}}
위 그림처럼 'restart the download' 글자 부근의 a 태그를 따라 가면 ccleaner 설치 파일을 쉽게 받을 수 있을 것입니다.
이 다음은 새롭게 받은 CCleaner를 설치해야합니다. 그런데 여기서 GUI 창을 통해 클릭을 할 수는 없지요. 다운로드가 끝남과 동시에 자동으로 설치가 되어야 합니다. CCleaner 무료 버전도 다행히 명령줄을 통한 자동 설치가 가능합니다. 다음과 같은 명령을 입력하면 됩니다.
ccsetup.exe /S /L=1042 /D=
'/L=1042' 스위치는 한글로 설치하라는 옵션입니다. 타 언어는 [[http://support.microsoft.com/kb/221435|이 페이지]]에서 확인하세요. '/D='는 유저가 원하는 곳에 설치할 때 필요한 옵션입니다. '/S'는 기본값으로 알아서 설치하기 위해 사용됩니다. 크롬과 같은 광고 프로그램들이 이 스위치를 켠다고 해서 같이 설치되지는 않는 것으로 파악하였습니다.
===== 구현 =====
==== 설정 파일 ====
일단 간단한 텍스트 파일로 된 설정 파일을 먼저 만들도록 하겠습니다. 설정 파일의 내용에는 아래 사항이 기록될 것입니다.
* ''ccleaner_path'': CCleaner.exe 파일 경로. 보통 ''C:\Program Files\CCleaner\CCleaner.exe'' 입니다만, 조금씩 다를 수 있습니다.
* ''release_url'': release note가 있는 URL입니다. %%http://www.piriform.com/ccleaner/download%%가 기본입니다.
* ''release_re'': 버전 번호와 배포날짜를 추출할 수 있는 정규표현식을 설정하는 부분입니다. 두 개의 캡쳐를 이용해 하나는 버전, 하나는 날짜를 추출합니다.
* ''download_url'': CCleaner 설치 파일을 다운로드 받을 수 있는 URL입니다. %%http://www.piriform.com/ccleaner/download/standard%%가 기본입니다.
* ''download_re'': 다운로드 링크를 얻을 수 있는 정규표현식을 설정합니다.
* ''install_arg'': 설치 파일 뒤에 붙일 인수를 여기에 적습니다.
ccleaner_path: C:\\Program Files\\CCleaner\\CCleaner.exe
release_url: http://www.piriform.com/ccleaner/download
relaese_re: .+(.+) \((.+?)\)
download_url: http://www.piriform.com/ccleaner/download/standard
download_re: restart the download
install_arg: /S /L=1042
==== 본 구현 ====
# -*- coding: UTF-8 -*-
import sys
import os
import urllib2
import re
def GetHTML(URL):
obj = urllib2.urlopen(URL)
html = obj.read()
obj.close()
return html
# Version 확인을 위한 클래스
class Version:
major = None
minor = None
build = None
# 로컬 버전 문자열 읽음.
def FromLocalVersionString(self, localVersionString):
vers = localVersionString.split('.')
self.major = int(vers[0])
self.minor = int('%s%s' % (vers[1], vers[2]))
self.build = int(vers[3])
# 최신 버전 (웹페이지에서 확인한) 문자열 읽음
def FromCurrentVersionString(self, currentVersionString):
vers = currentVersionString.split('.')
self.major = int(vers[0])
self.minor = int(vers[1])
self.build = int(vers[2])
def __str__(self):
return '%d/%d/%d' % (self.major, self.minor, self.build)
# 로컬 버전 <= 최신버전이므로 !=, == 만 정의.
def __eq__(self, other):
return isinstance(other, self.__class__) and self.__dict__ == other.__dict__
def __ne__(self, other):
return not self.__eq__(other)
def CheckUpdate(config):
# check CCleaner.exe exists!
if os.path.exists(config['ccleaner_path']) == False:
print >> sys.stderr, '%s does not exist!' % config['ccleaner_path']
return 1
# local ccleaner version
print "Checking installed CCleaner's version..."
command = 'wmic DATAFILE WHERE NAME="%s" GET version > version.txt' % config['ccleaner_path']
os.system(command)
# get version
with open('version.txt', 'r') as f:
text = unicode(f.read(), 'utf-16')
iv_txt = text.split(u'\n')[1].strip()
# current release version
print 'Checking current CCleaner version at Piriform...'
release_html = GetHTML(config['release_url'])
release_exp = re.compile(config['release_re'] , re.DOTALL|re.MULTILINE)
release_srch = release_exp.search(release_html).groups()
cv_txt = release_srch[0].strip()[1:] # exclude heading 'v'
reldate = release_srch[1].strip()
# To canonical versino expression.
installed_ver = Version()
current_ver = Version()
installed_ver.FromLocalVersionString(iv_txt)
current_ver.FromCurrentVersionString(cv_txt)
print 'Installed CCleaner version:', installed_ver
print 'Current CCleaner version:', current_ver, reldate
# compare two
if installed_ver == current_ver:
print 'You\'re using current version. Nothing to do.'
return 0
else:
print 'There\'s a new update available!'
# download current release
download_html = GetHTML(config['download_url'])
download_exp = re.compile(config['download_re'])
download_srch = download_exp.search(download_html)
url = download_srch.groups()[0]
filename = url.split('/')[-1]
print 'Begin downloading...'
obj = urllib2.urlopen(url)
with open(filename, 'wb') as f:
f.write(obj.read())
obj.close()
print 'Downloading complete!'
cmd = filename + ' ' + config['install_arg']
print 'Installing...'
os.system(cmd)
print 'Complete!'
return 0
def ParseConfig(configfile):
config = {}
with open(configfile, 'r') as f:
for l in f:
colon = l.find(':')
prop = l[0:colon].strip()
val = l[colon+1:].strip()
config[prop] = val
return config
def main(argv):
# parse config file
configfile = './CCleanerAutoUpdate.cfg'
if len(argv) == 2:
configfile = argv[1]
config = ParseConfig(configfile)
# begin the job
return CheckUpdate(config)
if __name__ == '__main__':
sys.exit(main(sys.argv))
===== 끝마치며 =====
참으로 잉여롭습니다! :-) 혹시나 CCleaner 설치라는 혹을 떼려다가 스크립트 관리라는 혹을 붙인 게 아닌가 싶습니다. 하지만 호기심을 충족했다는 점에서는 대만족입니다. :-) 계속 사용하다보면 ccsetup.exe 파일이 쌓이게 될 수도 있는데, 이것은 설치 후 직접 삭제하시든지, 스크립트를 수정하시면 됩니다. 관리자 권한까지는 완전 자동화하기 어려웠습니다. 이건 사용자가 한 번 확인해 주어야 할 부분이네요. 스크립트는 윈도우 8에서 동작을 확인했습니다.
===== 참고자료 =====
* wmic
* http://superuser.com/questions/363278/is-there-a-way-to-get-file-metadata-from-the-command-line
* http://www.cs.cmu.edu/~tgp/scsadmins/winadmin/WMIC_Queries.txt
* CCleaner: http://www.piriform.com/docs/ccleaner/advanced-usage/command-line-parameters