SNSのシェアボタンを自前でトラッキングしながらＡリンクで作ってみる

#はじめに
Facebookのいいね等の数は標準のタグでわかりますが、シェア数等はわかりません。

しかし、wrapperを作ることによって、完全なシェア数はわからないものの、とりあえずクリックカウンター or アクセスログを取得することができます。

また、LINEは新たにPCにも対応したものの、モバイル用のタグのがメッセージ性により良いこともあります。

それらを対策したスクリプトを perl CGI で作成してみました。

なお、参考コードのため、このコードが完全に動くとは限りません。

##ディレクトリ構成
-/script/share.pl
-/s/index.cgi

##HTMLの内容
SNSのアイコン素材は、以下のものを使用させていただきました。
https://www.iscle.com/web-it/sns-speed.html

```HTML:test.html
<li>
<a class="share share-twitter" href="/s?p=twitter&u=%エンコードしたURL%" target="_blank">
<span class="icon-twitter"></span>
</a>
</li>

<li>
<a class="share share-facebook" href="/s?p=facebook&u=%エンコードしたURL%" target="_blank">
<span class="icon-facebook"></span>
</a>
</li>

<li>
<a class="share share-google-plus" href="/s?p=googleplus&u=%エンコードしたURL%" target="_blank">
<span class="icon-google-plus"></span>
</a>
</li>

<li>
<a class="share share-hatebu" href="/s?p=hatebu&u=%エンコードしたURL%" target="_blank">
<span class="icon-hatebu"></span>
</a>
</li>

<li>
<a class="share share-pocket" href="/s?p=pocket&u=%エンコードしたURL%" target="_blank">
<span class="icon-pocket"></span>
</a>
</li>

<li>
<a class="share share-line" href="/s?p=line&u=%エンコードしたURL%" target="_blank">
<span class="icon-line"></span>
</a>
</li>
</ul>
```

##/s/index.cgiの内容

```perl:/s/index.cgi
#!/usr/bin/env perl

BEGIN {
# 必要であれば何か書く
}
srand;

$::script="./script";
chdir("..");
require "$::script/share.pl";
&share;
```

##/script/share.plの内容
```perl:/script/share.pl
use strict;

use CGI qw(:standard);
use CGI::Carp qw(fatalsToBrowser);
use Encode;

$::hashtaglist="拡散希望,見たらRT,共感したらRT,らぶりつください,RTで私を有名にして下さい";
$::twitter_max=140;
$::twitter_url_length=24;	# 1つのURLは24文字消費するようです

sub share {
	%::form=&init_form;
	if($::form{p} eq "" || $::form{u} eq "") {
		location("http://google.co.jp");
		exit;
	}

	my $url=$::form{u};
	my $platform=$::form{p};

#
# ここでURLを解析して下さい。
#

	my $mode;
	my $value;
	my $attr;
	if($url=~/^https?\:\/\/([^\/]+?)\/(.+?)\/(.+?)\.html$/) {
		$mode="list";
		$attr=$2;
		$value=$3;
	} elsif($url=~/^https?\:\/\/([^\/]+?)\/(.+?)\.html$/) {
		$mode="file";
		$value=$2;
	} else {
		$mode="file";
		$value="index"
	}
	if($mode eq "list") {
		&count("share-$platform-list-$mode-$value");
	} else {
		&count("share-$platform-$mode-$value");
	}

	# 以下は何も入ってません。URLを解析して、データベース等から内容を取得して下さい。
	my $title;			# ページタイトル
	my $twittertitle;	# Twitter用のタイトルは短めのを用意しましょう。
	my $desc;			# ページの説明
	my $addhashtag;		# Twitter用追加ハッシュタグ

	if($platform eq "twitter") {
		&share_twitter($url,$twittertitle eq "" ? $title : $twittertitle ,$addhashtag);
	} elsif($platform eq "facebook") {
		&share_facebook($url,$title,$desc);
	} elsif($platform eq "hatebu") {
		&share_hatebu($url,$title,$desc);
	} elsif($platform eq "googleplus") {
		&share_googleplus($url,$title,$desc);
	} elsif($platform eq "pocket") {
		&share_pocket($url,$title,$desc);
	} elsif($platform eq "line") {
		&share_line($url,$title);
	} else {
		location("http://www.google.co.jp");
	}
	exit;
}

sub share_line {
	my($url,$title,$desc)=@_;
	my $locationurl;

	if($ENV{HTTP_USER_AGENT}=~/Android|iPhone|iPad|Windows\sPhone|BlackBerry/) {
		# スマホの場合、従来の共有方法でメッセージ＆URLを送る
		$locationurl=sprintf(
			"http://line.me/R/msg/text/?%s%s%s"
			, &encode($title)
			, "%0D%0A"
			, &encode($url)
		);
	} else {
		# PCの場合、新しい共有方法でURLのみ送る
		$locationurl=sprintf(
			"https://timeline.line.me/social-plugin/share?url=%s"
			, &encode($url)
		);
	}
	&location($locationurl);
}

sub share_pocket {
	my($url,$title,$desc)=@_;
	my $locationurl=sprintf(
		"http://getpocket.com/edit?url=%s"
		, $url
	);
	location($locationurl);
}

sub share_facebook {
	my($url,$title,$desc)=@_;
	my $locationurl=sprintf(
		"https://www.facebook.com/sharer/sharer.php?u=%s&title=%s&description=%s"
		, $url
		, &encode($title)
		, &encode($desc)
	);
	location($locationurl);
}

sub share_hatebu {
	my($url,$title,$desc)=@_;
	my $locationurl=sprintf(
		"http://b.hatena.ne.jp/add?mode=confirm&url=%s&title=%s"
		, $url
		, &encode($title)
	);
	location($locationurl);
}

sub share_googleplus {
	my($url,$title,$desc)=@_;
	my $locationurl=sprintf(
		"https://plus.google.com/share?url=%s"
		, &encode($url)
	);
	location($locationurl);
}

sub share_twitter {
	my($url,$title,$addhashtag)=@_;
	my $hashtag=&twitter_hashtag($title,$addhashtag);
	my $locationurl="http://twitter.com/home?status="
		. &encode($title . "\r\n" . $url . "\r\n" . $hashtag);	$locationurl=~s/%0D%0A%0D%0A/%0D%0A/g;
	location($locationurl);
}

# Twitterのハッシュタグを140文字に収まるように作成する

sub twitter_hashtag {
	my (@args)=@_;
	my $title=shift @args;
	my @hash;
	my @body;
	foreach(@args) {
		push(@hash,$_);
	}

	foreach(split(/,/,$::hashtaglist)) {
		push(@hash,$_);
	}

	my $test=$title;
	my $testd;
	my $bodykey=1;
	foreach my $hash(@hash) {
		$test.="\r\n#" . $hash if($hash ne "");
		$testd=Encode::decode("utf-8", $test);
		return $body[$bodykey-1] if(length($testd) >= $::twitter_max - $::twitter_url_length);
		$body[$bodykey].=$body[$bodykey-1] . "\r\n#" . $hash if($hash ne "");
#print "$bodykey $body[$bodykey] " . length($testd) . "\n";
		$bodykey++;
	}
	$bodykey--;
	return $body[$bodykey];
}

sub location {
	my($url)=@_;
	print <<EOM;
Location: $url

EOM
}

sub init_form {
	my $qCGI=new CGI;
	my %form;
	if ($qCGI->param()) {
		foreach my $var ($qCGI->param()) {
			$form{$var} = $qCGI->param($var);
		}
	}

	my $query = $ENV{QUERY_STRING};
	if ($query =~ /&/) {
		my @querys = split(/&/, $query);
		foreach (@querys) {
			$_ = &decode($_);
			$form{$1} = $2 if (/([^=]*)=(.*)$/);
		}
	}
	%form;
}

sub encode {
	my ($s) = @_;
	$s =~ s/(\W)/'%' . uc unpack('H2', $1)/eg;
	$s =~ s/\%20/+/g;
	$s;
}

sub count {
# カウンター等は自作して下さい。
# 1バイトずつappendするタイプが一番軽量に実装でき、かつ、データ消失がありません。
}
1;
```