Railsで携帯版のGoogleAnalyticsを使う方法
ついにGoogle Analyticsが携帯に対応した!というのはうれしい事なんだけど、
当然PHPやPerlのソースはあってもRailsはない・・・
仕方ないので、PHP版ソースをRailsに翻訳して使う事にする。
ちょっと長いけど、コピペで動くはず。
ga.phpに相当する処理をクラスとして用意する
コントローラのアクションに全て書こうとすると、非常に長くなってしまうのでメインの処理はライブラリに分けておく。1x1の透明GIFの出力に関しては、コントローラ側でやる事にする。
2009/11/25追記
元のPHP版のソースはよく見ると、ドコモIDしか取得していないので他キャリアはクッキーで管理している。しかし、実際の携帯はクッキー対応端末が少ないので、他キャリアも端末IDを取得する必要がある。
そこで、jpmobileを使用して端末IDを取得する処理を入れている。なお、request.mobile.identで端末IDを取得していないのは、使っているjpmobileのバージョンが古いため。jpmobileが古いとiモードIDに対応していないので、自前で取得している。たぶん、jpmobile0.0.6ならrequest.mobile.identだけで良いはず。
-- lib/mobile_google_analytics.rb -- require 'digest/md5' require 'net/http' require 'timeout' require 'resolv-replace' require 'jpmobile' Net::HTTP.version_1_2 #== Example # # require 'jpmobile' # class SampleController < ApplicationController # def ga # ga = MobileGoogleAnalytics.new(:request => request, :params => params, :cookies => cookies) # headers["Cache-Control"] = "private, no-cache, no-cache=Set-Cookie, proxy-revalidate" # headers["Pragma"] = "no-cache" # headers["Expires"] = "Wed, 17 Sep 1975 21:32:10 GMT" # send_data( # ga.gif_data, # :disposition => "inline", # :type => "image/gif" # ) # ga.track_page_view # end class MobileGoogleAnalytics VERSION = "4.4sh" COOKIE_NAME = "__utmmobile" COOKIE_PATH = "/" COOKIE_USER_PERSISTENCE = 63072000 GIF_DATA = [ 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x01, 0x00, 0x01, 0x00, 0x80, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x02, 0x02, 0x44, 0x01, 0x00, 0x3b ] attr_reader :request, :params, :cookies, :logger def initialize(attrs) @request = attrs[:request] @params = attrs[:params] @cookies = attrs[:cookies] @logger = Logger.new("#{RAILS_ROOT}/log/mobile_google_analytics.log") end def get_ip return "" unless request.remote_ip # Capture the first three octects of the IP address and replace the forth # with 0, e.g. 124.455.3.123 becomes 124.455.3.0 ip = "" if request.remote_ip =~ /^([^.]+\.[^.]+\.[^.]+\.).*/ ip = "#{$1}0" end return ip end # Get a random number string. def get_random_number rand(0x7fffffff) end # Writes the bytes of a 1x1 transparent gif into the response. def gif_data data = GIF_DATA.map{|m| [m].pack('C')} data.join("") end # Generate a visitor id for this hit. # If there is a visitor id in the cookie, use that, otherwise # use the guid if we have one, otherwise use a random number. def get_visitor_id(guid, account, user_agent, cookie) logger.info "guid=#{guid}, account=#{account}, user_agent=#{user_agent}, cookie=#{cookie}" # If there is a value in the cookie, don't change it. if cookie return cookie; end message = ""; if guid && guid.length > 0 # Create the visitor id using the guid. message = guid + account else message = user_agent + get_random_number.to_s + get_random_string(30) end md5_str = Digest::MD5.hexdigest(message.to_s); return "0x" + md5_str[0, 16] end # Track a page view, updates all the cookies and campaign tracker, # makes a server side request to Google Analytics and writes the transparent # gif byte data to the response. def track_page_view time_stamp = Time.now domain_name = request.host # Get the referrer from the utmr parameter, this is the referrer to the # page that contains the tracking pixel, not the referrer for tracking # pixel. document_referer = params[:utmr] || "-" document_path = params[:utmp] || "" account = params[:utmac] || "" user_agent = request.user_agent || "" # Try and get visitor cookie from the request. cookie = cookies[COOKIE_NAME] guid = get_serial_number visitor_id = get_visitor_id(guid, account, user_agent, cookie); # Always try and add the cookie to the response. cookies[COOKIE_NAME] = { :value => visitor_id, :expiers => Time.now + COOKIE_USER_PERSISTENCE, :path => COOKIE_PATH, :domain => domain_name } utm_gif_location= "http://www.google-analytics.com/__utm.gif"; # Construct the gif hit url. url_params = { "utmwv" => VERSION, "utmn" => get_random_number.to_s, "utmhn" => domain_name, "utmr" => document_referer.to_s, "utmp" => document_path.to_s, "utmac" => account.to_s, "utmcc" => "__utma=999.999.999.999.999.1;", "utmvid" => visitor_id, "utmip" => get_ip } utm_url = utm_gif_location + "?" + url_params.to_query response= send_request_to_google_analytics(utm_url); result = {:response => response, :request_url => utm_url, :body => nil} if response.is_a?(Net::HTTPOK) result[:body] = response.body else logger.error "send request failed!, response=#{response.inspect}, request_url=#{utm_url}" end return result end # Make a tracking request to Google Analytics from this server. # Copies the headers from the original request to the new one. # If request containg utmdebug parameter, exceptions encountered # communicating with Google Analytics are thown. def send_request_to_google_analytics(utm_url) headers = { "User-Agent" => request.user_agent.to_s, "Accept-Language" => request.accept_language.to_s } get_contents(utm_url, :headers => headers) end # ランダムな文字列を返す def get_random_string(length = 8) t = Time.now srand(t.to_i ^ t.usec ^ Process.pid) source = ("a".."z").to_a + (0..9).to_a + ("A".."Z").to_a str = "" length.times{ str += source[rand(source.size)].to_s } return str end # URLの内容を取得し、レスポンスを返す # #+url+:: リクエストURL #戻り値:: Net::HTTPResponse。エラー時はnil def get_contents(url, opts = {}) opts.reverse_merge! :open_timeout => 1, :read_timeout => 3, :headers => {} begin uri = URI.parse(url) Net::HTTP.start(uri.host, uri.port){|http| http.open_timeout = opts[:open_timeout].to_i http.read_timeout = opts[:read_timeout].to_i return http.get(uri.request_uri, opts[:headers]) } rescue TimeoutError => e logger.error "TimeoutError, request_url=#{url}, message=#{e.message}" rescue Exception => e logger.error "Error, request_url=#{url}, message=#{e.message}" end nil end # 携帯の端末番号を返す def get_serial_number sn = nil case request.mobile when Jpmobile::Mobile::Docomo # iモードID if request.env["HTTP_X_DCMGUID"] sn = request.env["HTTP_X_DCMGUID"] end # DoCoMoのエミュレータ case request.user_agent when 'DoCoMo/2.0 ISIM0505(c100;TB;W24H16)' sn = 'ISIM0505' when 'DoCoMo/2.0 ISIM0606(c100;TB;W24H16)' sn = 'ISIM0606' end when Jpmobile::Mobile::Au # EZ番号 sn = request.mobile.subno() when Jpmobile::Mobile::Softbank sn = request.mobile.serial_number() if sn == nil sn = request.mobile.x_jphone_uid() end when Jpmobile::Mobile::Vodafone sn = request.mobile.serial_number() if sn == nil sn = request.mobile.x_jphone_uid() end when Jpmobile::Mobile::Jphone sn = request.mobile.serial_number() if sn == nil sn = request.mobile.x_jphone_uid() end when Jpmobile::Mobile::Emobile # EMnet対応端末ユニークID sn = request.mobile.em_uid() end return sn end end
ga.phpに相当するアクションを作る
とりあえず、適当なコントローラにgaアクションをつくり/gaでアクセスできるようにしておく。
routes.rbの設定は面倒なので省略。
require 'jpmobie' class MobileController < ApplicationController def ga ga = MobileGoogleAnalytics.new(:request => request, :params => params, :cookies => cookies) headers["Cache-Control"] = "private, no-cache, no-cache=Set-Cookie, proxy-revalidate" headers["Pragma"] = "no-cache" headers["Expires"] = "Wed, 17 Sep 1975 21:32:10 GMT" send_data( ga.gif_data, :disposition => "inline", :type => "image/gif" ) ga.track_page_view end end
ビューで使うためのヘルパーを作る
以下のメソッドをApplicationHelperに追加する。
これが完了したら、モバイルサイトのビューで<%= google_analytics_tag %>とすれば作業完了。
-- app/helpers/application_helper.rb -- module ApplicationHelper # Copyright 2009 Google Inc. All Rights Reserved. def google_analytics_tag tracking_code = "MO-xxxxxx-xx" # トラッキングコード ga_account = tracking_code url_params = { "utmac" => ga_account, "utmn" => rand(0x7fffffff).to_s, "utmr" => request.referer || "-", "utmp" => request.request_uri } tmp = [] url_params.keys.sort.each{|key| tmp << "#{key}=" + CGI.escape(url_params[key]) } tmp << "guid=ON" image_url = url_for(:action => :ga) + "?" + tmp.join("&") "<img src=\"#{image_url}\" />" end end