Rev. 2.73

'갑자기 내 블로그의 RSS는 몇명이나 구독하고 있을까?'하는 의문이 생겨 구글링하다가 재미있는 사실을 발견 했습니다. 구글은 RSS를 크롤링 할 때 UA(User-Agent)에 다음과 같은 정보를 던집니다.

User-Agent: Feedfetcher-Google; (+http://www.google.com/feedfetcher.html; 4 subscribers; feed-id=1794595805790851116)

크롤러의 이름과 URL, 구독자 수, 그리고 피드ID를 알려 주는 것이죠. 여기서 피드ID는 블로그 운영 중에 RSS URI를 변경한 경우에 여러개가 생겨날 수 있는데 이를 구분하기 위한 것입니다. 아무튼, 혹시나 다른 RSS 크롤러들도 위 룰을 따르고 있는지 살펴보니, 구독자 수를 남기고 퍼가는 크롤러들이 상당수 있었습니다. Feedfetcher-Google(구글 리더), HanRSS, Bloglines, Fastladder 등이 subscribers 정보를 가지고 있더군요. 그래서 자동으로 갱신되는 구독자 수 모니터링 위젯을 만들어 보기로 마음먹었습니다. 막상 저지르고 보니 대부분 서버단 작업이여서 상당히 곤욕스러웠어요. 작업 내용을 요약하면 다음과 같습니다.

1. 종전의 rss로 접근 URI를 모두 rss.php로 포워드한다. 주의, 리다이렉트한 경우 UA값을 손실 함.
2. UA 정보를 가져오기 위해 파싱된 rss.xml 파일을 rss.php 파일에서 불러온다.
3. rss.php에서 가로챈 UA를 DB에 로깅한다. 하루동안의 데이터만 쌓이도록 쿼리.
4. MySQL에 새 테이블을 추가한다.(index, useragent, regdate 로 구성된 간단한 테이블)
5. Ajax로 접근할 subscribe.php파일을 생성하고 필요한 파일들을 include한다.
6. DB에서 긁어온 UA정보를 토대로 다양한 필터링을 통해 여러가지 통계자료를 산출 한다.
7. 산출한 자료를 JSON으로 파싱한다.
8. JSON을 불러와 아래와 같은 출력 결과물을 만든다.

AjaxSubscription

위 결과물은 하루간 쌓인 실제 데이터(약 3-4000행)를 산출한 것입니다. Ajax를 통해 호출하고 30초마다 자동 갱신됩니다. subscribers 정보를 제공하는 크롤러는 10여종이며, 나머지는 "알 수 없는 구독기"로 분류했습니다. 로봇을 제외하고도 자그마치 50여종이나 되는군요. 어쨋거나 완벽한 RSS 구독자 수를 알아내는 것은 불가능 하겠지만 매우 신빙성 있는 일부분을 한눈에 알아볼 수 있게 되었습니다. UA에 subscribers를 정보를 작성하는 것은 RFC에서 권고하고 있는 내용이라고 하네요. 국내 RSS서비스 중에는 HanRSS밖에 없습니다. 앞으로 이 권고를 따르는 RSS 리더 웹 서비스가 더욱 많아졌으면 하는 바램입니다.

덧, 여기저기 퍼다 쓴 것이 너무 많아 php소스를 공개하지 못하는 점 양해 바랍니다.

Comments

mobileme.png

지난 WWDC에서 스티브씨가 애플의 새 서비스인 모바일미(me.com)를 소개했었죠. 모바일미는 메일, 연락처, 캘린더, 갤러리, 문서 등 여러가지 데이터를 다양한 디바이스에서 동시에 사용할 수있게 하는 클라우드 컴퓨팅 컨셉의 인터넷 서비스입니다. 모바일미는 사실상 기존 닷맥(.Mac)의 서비스 대상이 확대된 것입니다. 지금까지는 주로 애플 컴퓨터 사용자를 대상으로 서비스 하던 것에 스토리지 용량을 늘리고 여러 푸시 서비스 추가한 것입니다. 여기서 주목해야 할 부분은 바로 me.com 웹서비스입니다. 마치 데스크탑 애플리케이션을 사용하는 것 처럼 웹상에서 데이터들을 손쉽게 다룰 수 있게 합니다. 구글 Apps처럼 말이죠, 아래 투어 동영상에서는 메일과 연락처, 캘린더, 갤러리 웹서비스를 사용하는 모습을 볼 수 있습니다. 놀라울 정도로 직관적인 UI와 애플 특유의 그래픽 디자인은 실제 맥용 iCal과 iPhoto를 방불케 하는 군요.

Click to Play!

▶ Click to Play!

7월에 중순에 출시되는 모바일미는 112,900원(개인 1년 기준)을 지급하고 사용하는 유료 서비스로써 한국에서도 서비스되며, 미리 주문할 수 있습니다. 기존 닷맥 사용자들은 자동 업그레이드 된다고 하네요.

Comments


Dion Almaer씨의 구글 AJAX Language API를 이용한 다국어 번역 북마크릿을 보고 한글로 번역되는 것도 있으면 좋겠다 싶어 만들어 보았습니다. 구글의 AJAX Language API는 총 39개국의 언어를 지원하며, 그중에서도 한국을 포함한 23개국 언어간 쌍방향 번역기능이 포함된 쓸만한 번역 엔진입니다. 번역하고자 하는 문자 영역을 드래그하여 선택한 후 북마크릿을 클릭하면 위 화면과 같은 번역 결과물을 출력합니다. 파이어폭스2, 3 RC에서 테스트 되었으며, 아쉽게도 IE계열 브라우저는 지원하지 않습니다. 자세한 특징은 다음과 같습니다.

  • 39개국 언어를 지원하는 구글 AJAX Language API를 사용함
  • 영어, 일본어, 프랑스어, 독일어, 중국어 등 총 23개국 언어 번역 지원
  • 번역 대상의 언어를 자동검출함으로 별도로 지정할 필요가 없음
  • 어느 사이트에서나 잘 보일수 있는 출력화면 최적화
  • 빠른 속도를 위해 별도의 자바스크립트 라이브러리(프레임웍)을 사용치 않음
  • 512바이트 단위로 분할 전송하여 장문 번역 가능(추가)
  • 장문인 경우 단어 단위로 계산하여 오역 발생 확률 감소(추가)
  • 줄바꿈 인식 및 HTML코드 탈출(추가)
  • 창의 크기가 윈도 크기를 넘긴 경우 스크롤바 활성(추가)
  • 상황별 오류 출력(추가)
  • 상황별 처리중 알리미 출력(추가)
  • 창 드래그 앤 드롭(추가 예정)

다국어 번역 북마크릿 - 이 링크를 브라우저의 북마크 도구모음에 추가하거나 북마크에 추가 하십시오.

if (!window.apiLoaded) {
  window.apiLoaded = function(text) {
    var link = false, next = null, language = 'ko';
    if (!text) {
      text = window.getSelection().toString().replace(/&/g, '&');
      text = text.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\n/g, '<br/>');
    } else link = true;

    if (text.length > 511) {
      var count = 0, words = text.split(' ');
      for (var i = 0; i < words.length; i++) {
        var l = words[i].length + 1;
        if (count + l > 500) {
          if (count > 511) count -= words[i-1].length + 1;
          break;
        }
        count += l;
      }
      next = text.substring(count + 1, text.length);
      text = text.substring(0, count);
    }

    var win = document.getElementById('__translationWindow');
    var img = document.getElementById('__translationWindowIcon');
    if (!link && win && win.style.display != 'none')
      win.childNodes[1].innerHTML = 'Translating...';
    else if (!img) {
      img = document.createElement('img');
      img.id = '__translationWindowIcon';
      img.src = "http://www.google.com/favicon.ico";
      img.setAttribute('style', 'top:10px;right:10px;position:fixed;z-index:999998');
      document.body.appendChild(img);
    } else img.style.display = 'block';

    var getLang = function(lang) {
      for (var l in google.language.Languages)
        if (google.language.Languages[l] == lang) return l.toLowerCase();
      return lang;
    };

    var error = function(text, lang) {
      if (lang) lang = 'guessing the language: ' + getLang(lang);
      text = 'No translation found for "' + text + '" ' + lang;
      return translationWindow(text, null, link, img, win);
    };

    if (!text) return error('');
    google.load('language', '1', {'callback': function() {
      google.language.detect(text, function(d) {
        if (!d.error && d.language) {
          google.language.translate(text, d.language, language, function(t) {
            if (t.translation) {
              translationWindow(t.translation, getLang(d.language), link, img, win);
              if (next) apiLoaded(next);
            } else error(text, language);
          });
        } else error(text, '');
      });
    }});
  };
}

if (!window.translationWindow) {
  window.translationWindow = function(translation, language, link, img, win) {
    img.style.display = 'none';
    if (!win) {
      var ws = [
        'font-family:AppleGothic,Malgun Gothic,Gulim,Dotum', 'background:#eee',
        'width:300px', 'opacity:0.9', 'padding:1px', 'text-align:left', 'color:#333',
        'top:10px', 'right:10px', 'font-size:12px', 'overflow:auto', 'position:fixed',
        'border:3px solid #aaa', 'line-height:1.5', 'z-index:999999', 'display:block'
      ], ds = ['background:#bbb','padding:2px 4px'];

      win = document.createElement('div');
      win.id = '__translationWindow';
      win.onmouseover = function() { win.style.opacity='1'; };
      win.onmouseout  = function() { win.style.opacity='0.9'; };
      win.setAttribute('style', ws.join(';'));
      win.innerHTML = [
        '<div style="font-size:13px;font-weight:bold;margin-bottom:3px;'+ds.join(';')+'">', 
          '<img style="vertical-align:text-bottom;" src="' + img.src + '"/>',
          'Google Translate',
        '</div>',
        '<span></span>',
        '<div style="text-align:right;font-size:11px;margin-top:3px;'+ds.join(';')+'">',
        '</div>'].join('');

      var a = document.createElement('a');
      a.setAttribute('style', 'position:absolute;top:2px;right:7px;cursor:pointer;');
      a.innerHTML = 'x';
      a.onclick = function(){ win.style.display = 'none'; };
      win.firstChild.appendChild(a);

      document.body.appendChild(win);
    } else win.style.display = 'block';

    var span = win.childNodes[1];
    if (!link){
      if (!language) translation = '<span style="color:#f30">' + translation + "</span>";
      else win.lastChild.innerHTML = 'detected as ' + language;
      span.innerHTML = translation;
      win.style.bottom = '';
    } else {      
      span.innerHTML += ' ' + translation;
      if (window.innerHeight < win.offsetHeight) win.style.bottom = '10px';
    }
    return true;
  };
}

if (!window.google || (google && !google.language)) {
  var script = document.createElement('script');
  script.src = 'http://www.google.com/jsapi?callback=apiLoaded';
  script.type = 'text/javascript';
  document.getElementsByTagName('body')[0].appendChild(script);
} else apiLoaded();

void(0);

Javascript to URL Bookmarklet Encoder

만들다 보니 자바스크립트 코드를 URL 형태로 만들어주는 녀석이 필요해서 간단한 엔코더도 만들었습니다. 상단의 TEXTAREA에 자바스크립트 코드를 넣고 버튼을 누르면 북마크릿에 사용할 수 있는 URL형태로 변환해 줍니다. 좌측 버튼은 최소한의 코드 변환을 실행하고 우측 버튼은 웹 표준에 부합하는 링크용 코드로 변환합니다.

Comments