読者です 読者をやめる 読者になる 読者になる

風柳メモ

ソフトウェア・プログラミング関連の覚書が中心。

気が付いたら jQuery 用の highlight プラグインを書いていた

先日、自分のココログを全文検索するブログパーツ: 暴想の改修を行っていたのだが、furyu.tea-nifty.com
元のスクリプトのハイライト処理だと、TEXTAREA 内まで置換されてしまうのが難点だったため、せっかくなので jQuery を用いて実装してみた。github.com

$.setHighlightColor('yellow'); // .highlight要素に付けるハイライト(背景)色を指定(引数を指定しない場合、デフォルト(#ffcc33)になる)

$('p').highlight('キーワード'); // 全 p 要素内で、"キーワード" というテキストをハイライトする(引数として配列を指定することで、複数のキーワード指定も可能)

$('p').unhighlight(); // 全 p 要素内のハイライトを取り消す

style・script・textarea・iframe・frame 要素内は避けてハイライトするようになっている。
highlight()のオプションはjQuery Highlight Plugin | bartaz @ GitHub互換っぽくなっているはず。

裏話

最初はjq-cocolog_ajax_search.js内にベタ書きしていたのだが、せっかく jQuery 化したことだし、プラグインとして独立させることに。

例によって、ひととおり実装したところで、ふと検索してみると、
highlight: JavaScript text higlighting jQuery plugin
とか、
jQuery Highlight Plugin | bartaz @ GitHub
といったプラグインが見つかったので、オプションは後者のものと互換っぽくしておいた。
すなおに最初から調べて使っておけ、という話。

Google ChromeにてDOMノード追加に異様に時間がかかる現象が発生して悩む→拡張機能が原因だった

JavaScriptでページ遷移用のナビゲーションを作っていたのだが、リンクと半角スペース(テキストノード)とを交互に挿入するような実装にしていると、なぜか異様に時間がかかってしまう(数百個程度のノード挿入に、数秒要する)。
どうやら、ナビゲーションのノードがDOMツリー上にあるとこうなるようで、いったんDOMツリーから切り離した状態で作成し、最後にDOMツリーに戻すようにすると、ほとんど時間はかからない。でもなぜ……?


さんざん悩んだが、結局はとある拡張機能が原因だった、というオチ(今回の場合ははちまバスター)。
もっと早くに、シークレットウィンドウで調べておくべきだった……。
【追記】なお、早々にはちまバスターは改修された

テスト

function test_add_nodes( options ) {
    if ( ! options ) {
        options = {}
    }
    var parent_node = ( options.parent_node ) ? options.parent_node : document.body,
        before_processing = !! ( options.before_processing ),
        node_number = ( 0 < options.node_number ) ? options.node_number : 300,
        exclude_link = !! ( options.exclude_link ),
        exclude_text = !! ( options.exclude_text );
    
    var container = document.createElement('div'),
        link_node_template = document.createElement('a');
        text_node_template = document.createTextNode('');
        before_time = new Date().getTime();
    
    if ( parent_node && before_processing ) {
        parent_node.insertBefore( container, parent_node.firstChild );
    }
    for ( var ci=0; ci < node_number; ci++ ) {
        if ( ! exclude_link ) {
            var link_node = link_node_template.cloneNode( true );
            link_node.href = '#';
            link_node.innerHTML = ci;
            container.appendChild( link_node );
        }
        if ( ! exclude_text ) {
            var text_node = text_node_template.cloneNode( true );
            text_node.textContent = '*';
            container.appendChild( text_node );
        }
    }
    if ( parent_node && ( ! before_processing ) ) {
        parent_node.insertBefore( container, parent_node.firstChild );
    }
    var after_time = new Date().getTime(),
        elapsed_time = ( after_time - before_time );
    
    return elapsed_time;
}

function log_result( test_name, elapsed_time) {
	console.log( test_name + ' : ' + ( elapsed_time / 1000.0) + ' sec' );
}

// DIV要素にリンクやテキストノードを一通り追加してから、DOMツリーに挿入
log_result('after , text only', test_add_nodes( { before_processing : false, exclude_link : true, exclude_text : false } ) );
log_result('after , link only', test_add_nodes( { before_processing : false, exclude_link : false, exclude_text : true } ) );
log_result('after , both     ', test_add_nodes( { before_processing : false, exclude_link : false, exclude_text : false } ) );

// DIV要素をDOMツリーに予め挿入してから、リンクやテキストノードを追加
log_result('before, text only', test_add_nodes( { before_processing : true, exclude_link : true, exclude_text : false } ) );
log_result('before, link only', test_add_nodes( { before_processing : true, exclude_link : false, exclude_text : true } ) );
log_result('before, both     ', test_add_nodes( { before_processing : true, exclude_link : false, exclude_text : false } ) );
拡張機能有効時
after , text only : 0.003 sec
after , link only : 0.057 sec
after , both      : 0.054 sec
before, text only : 0.04 sec
before, link only : 0.04 sec
before, both      : 6.856 sec // これは酷い
拡張機能無効時
after , text only : 0.002 sec
after , link only : 0.005 sec
after , both      : 0.01 sec
before, text only : 0.027 sec
before, link only : 0.045 sec
before, both      : 0.05 sec

【覚書】Google Fusion Tables に Python でアクセスする手順

Google Fusion Tables Python

Google Fusion Tables の更新は、実際には、自前のLinuxサーバー等から自動的に行うことを考えているため、まずは Linux 端末上から Fusion Tables にアクセスできる方法を調べる必要がある。
この場合、Linux 端末側は、OAuth 2.0 クライアントとして Fusion Tables API を使用することになるらしい。developers.google.com
で公開されている各プログラミング言語用のライブラリを用いることで、アクセスできるのだと思われる。
とりあえず、ある程度使い慣れている Python 2.7 によるアクセスを行うことを試みた。
いろいろと調べつつ、手探りで試してとりあえずアクセスは出来たが、これが正しい方法なのかどうかは自信がない。もっとスマートなやり方がありそうな気がするが……。

OAuth 2.0 クライアントID&シークレットの取得

Google Developers Consoleを開き、Fusion Tables へのアクセス用に、新しいプロジェクトを作成する。
f:id:furyu-tei:20150819212036p:plain
f:id:furyu-tei:20150819212043p:plain
f:id:furyu-tei:20150819212048p:plain


作成したプロジェクトを開き、
f:id:furyu-tei:20150819212051p:plain
左のメニューの
APIと認証 → API
から、Fusion Table API を選んで
f:id:furyu-tei:20150819212054p:plain
[APIを有効にする]を押す。
f:id:furyu-tei:20150819212100p:plain


次に、同じく
APIと認証 → 認証情報
から、
[認証情報を追加] → OAuth 2.0 クライアント ID
を選択し、
f:id:furyu-tei:20150819212111p:plain
[同意画面を設定]ボタンを押して
f:id:furyu-tei:20150819212117p:plain
サービス名を適当につけて入力し、[保存]を押す。
f:id:furyu-tei:20150819212123p:plain


「アプリケーションの種類」は「○ その他」を選択し、[作成]ボタンを押すと、
f:id:furyu-tei:20150819212128p:plain
OAuth 2.0 のクライアントID及びクライアントシークレットが発行されるので、[OK]を押す。
これは後からでも確認できるため、特にコピーしておく必要などはない。
f:id:furyu-tei:20150819212142p:plain

OAuth 2.0 クライアントID情報は、右側にあるダウンロードボタンにて、JSON形式でダウンロードできる。
f:id:furyu-tei:20150819212153p:plain


Linux 端末(OAuth 2.0 クライアント)側の準備

Installation  |  API Client Library for Python  |  Google Developers
に従い、

sudo pip install --upgrade google-api-python-client

のようにして、google-api-python-client をインストールしておく。


Python で Fusion Tables にアクセスするサンプルコード(sample_fusiontables.py)

上記でダウンロードしたJSON(OAuth 2.0 クライアントID情報)ファイルを、
client_secrets.json
という名前にして同じディレクトリに配置した状態で実行すると、アクセス可能な Fusion Tables の最初のテーブルIDを取得し、当該テーブルの行列を表示するサンプル。

# -*- coding: utf-8 -*-

# OAuth 2.0 for Devices (sample code)
# https://github.com/google/oauth2client/blob/master/samples/oauth2_for_devices.py

# Using OAuth 2.0 for Devices
# https://developers.google.com/accounts/docs/OAuth2ForDevices

# Fusion Tables REST API
# https://developers.google.com/fusiontables/docs/v2/reference/

from __future__ import print_function

import sys
import json
import time
import datetime
import httplib2
import traceback
from pprint import pprint
from oauth2client import client
from googleapiclient.discovery import build

def get_credentials(scopes, client_secret_file = None, credential_file_in = None, credential_file_out = None): #{
  def load_credential_file(credential_file_in):
    if not credential_file_in:
      return None
    try:
      fp = open(credential_file_in, 'rb')
      credentials = client.OAuth2Credentials.new_from_json(fp.read())
      fp.close()
      if credential_file_in != credential_file_out:
        save_credential_file(credential_file_out, credentials)
      return credentials
    except Exception, error:
      #print(traceback.format_exc())
      pass
    return None
    
  def save_credential_file(credential_file_out, credentials):
    if not credential_file_out:
      return
    fp = open(credential_file_out, 'wb')
    fp.write(credentials.to_json())
    fp.close()
  
  credentials = load_credential_file(credential_file_in)
  if credentials:
    return credentials
  
  client_secrets = json.load(open(client_secret_file, 'rb'))
  client_id = client_secrets['installed']['client_id']
  client_secret = client_secrets['installed']['client_secret']
  
  while not credentials:
    flow = client.OAuth2WebServerFlow(client_id, client_secret, ' '.join(scopes))
    
    # Step 1: get user code and verification URL
    # https://developers.google.com/accounts/docs/OAuth2ForDevices#obtainingacode
    flow_info = flow.step1_get_device_and_user_codes()
    
    print('Verification URL: {0}'.format(flow_info.verification_url));
    print('User code: {0}'.format(flow_info.user_code));
    
    user_code_expiry = flow_info.user_code_expiry
    interval = flow_info.interval
    
    while datetime.datetime.now() < user_code_expiry:
      print('\r{0:>4} seconds remaining'.format( (user_code_expiry - datetime.datetime.now()).seconds ), end = '')
      sys.stdout.flush()
      # Step 2: get credentials
      # https://developers.google.com/accounts/docs/OAuth2ForDevices#obtainingatoken
      try:
        credentials = flow.step2_exchange(device_flow_info = flow_info)
        if credentials:
          break
      except Exception, error:
        #print(traceback.format_exc())
        credentials = None
      time.sleep(interval)
    print('')
  
  save_credential_file(credential_file_out, credentials)
  
  return credentials
#} // end of get_credentials()


if __name__ == '__main__': #{
  # https://console.developers.google.com/project/<project name>/apiui/credential (Download JSON)
  CLIENT_SECRET_FILE = 'client_secrets.json'
  
  # Allowed scopes
  # https://developers.google.com/identity/protocols/OAuth2ForDevices#allowedscopes
  SCOPES = ('https://www.googleapis.com/auth/fusiontables',)
  
  CREDENTIAL_FILE = 'credentials.json'
  
  credentials = get_credentials(
    client_secret_file = CLIENT_SECRET_FILE,
    scopes = SCOPES,
    credential_file_in = CREDENTIAL_FILE,
    credential_file_out = CREDENTIAL_FILE,
  )
  
  print('Access token: {0}'.format(credentials.access_token))
  print('Refresh token: {0}'.format(credentials.refresh_token))
  
  # Get Fusion Tables service
  # https://developers.google.com/accounts/docs/OAuth2ForDevices#callinganapi
  fusiontables = build(
    serviceName = 'fusiontables',
    version = 'v2',
    http = credentials.authorize( httplib2.Http() ),
  )
  
  # Fusion Tables REST API
  # https://developers.google.com/fusiontables/docs/v2/reference/
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Table
  Table = fusiontables.table()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Column
  Column = fusiontables.column()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Template
  Template = fusiontables.template()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Style
  Style = fusiontables.style()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Query
  Query = fusiontables.query()
  
  # https://developers.google.com/fusiontables/docs/v2/reference/#Task
  Task = fusiontables.task()
  
  # ===== Samples
  print('***** Tables')
  table_list = Table.list().execute()
  pprint(table_list)
  tableId = table_list['items'][0]['tableId']
  
  print('***** Columns & Rows')
  rows = Query.sql(
    sql = 'SELECT * FROM {0}'.format(tableId)
  ).execute()
  pprint(rows)

Shell 上から、

$ ls
client_secrets.json  sample_fusiontables.py
$ python ./sample_fusiontables.py

のように実行すると、初回には、標準出力上に

Verification URL: https://www.google.com/device
User Code: XXXX-XXXX
xxxx seconds remaining

のように表示されて待ち状態に入る。

手動で「Verification URL」に示された URL にブラウザでアクセスすると、(場合によってはアカウント選択・ログイン画面の後で)端末に表示されるコードの入力を促されるため、「User Code」で示されたコードを入力し、[続行]を押す。
f:id:furyu-tei:20150819212158p:plain
Fusion Tables の管理の許可を求められるため、[承認する]を押す。
f:id:furyu-tei:20150819212207p:plain


すると、しばらくしてサンプルプログラム側が承認を認識し(Access token・Refresh tokenを取得し)、結果が表示される。


なお、Access token・Refresh tokenを含む情報は、カレントディレクトリ上の
credentials.json
というファイルに書き込まれ、

$ ls
client_secrets.json  credentials.json  sample_fusiontables.py

次回実行時にはこれを読み込んで使用するため、基本的には、上記のブラウザによる手動の承認手順は初回のみでよい。
この動作の詳細は、上記ソースコード中の get_credentials() 関数の定義及び呼び出し箇所を参照のこと。

【覚書】Google Fusion Tables に Google Apps Script でアクセスする手順

Google Fusion Tables Apps Script

ひとまず Google Fusion Tables が使えるようになったが、何らかのプログラムからアクセスする方法を調べないと……ということで、Google Apps Scriptなるもの(実はこれも初めていじる)で試してみる。

Google ドライブに Google Fusion Tables のアプリを追加(接続)(初回のみ)

Googleドライブのマイドライブから、
[新規] → その他 → +アプリを追加
とたどって、
f:id:furyu-tei:20150819211106p:plain
By Google から
f:id:furyu-tei:20150819211111p:plain
Google Apps Script を探し出し、
f:id:furyu-tei:20150819211607p:plain
f:id:furyu-tei:20150819211616p:plain
接続する。
f:id:furyu-tei:20150819211619p:plain
この手順は一度実施するだけで良い。


Google Apps Scriptのスクリプトを作成し、Fusion Tables にアクセスできるよう設定

Googleドライブのマイドライブから、
[新規] → その他 → Google Apps Script
と選択し、
f:id:furyu-tei:20150819211624p:plain
「空のプロジェクト」を選んで[閉じる]を押すと、
f:id:furyu-tei:20150819211629p:plain
プロジェクトが作成される。

ここで、メニューの
リソース → Google の拡張サービス...
を選ぶと、
f:id:furyu-tei:20150819211640p:plain
プロジェクト名を決めていないときはここで尋ねられるので、適当に名前をつけて[OK]を押す。
f:id:furyu-tei:20150819211645p:plain
Google の拡張サービスの中から、Fusion Tables を探して、
f:id:furyu-tei:20150819211649p:plain
右側の□をクリックし、ON(緑)の状態にする。
f:id:furyu-tei:20150819211653p:plain
ここで、[OK]はまだ押さずに、続けて

これらのサービスは Google デベロッパー コンソールでも有効になっている必要があります。

のリンクをクリックし、Google Developers Consoleを開く。
すると、APIと認証 → API の API ライブラリが開いているはずなので、「Fusion Tables API」を探し、
f:id:furyu-tei:20150819211658p:plain
「Fusion Tables API」をクリックした画面で、[APIを有効にする]ボタンを押す。
f:id:furyu-tei:20150819211702p:plain
その後、Google Apps Scriptの画面に戻ると、そのスクリプトでは Fusion Tables API が使用できるようになる。

簡単なスクリプトの実行

Googleドキュメント上の Fusion Tables のテーブルにアクセスして、行内容を表示するだけのサンプルスクリプト。

var tableId = 'your-table-id';

function getRows(request) {
  var sql = 'SELECT * FROM ' + tableId + ' LIMIT 10';
  
  var result = FusionTables.Query.sqlGet(sql);
  for (var row_number=0; row_number < result.rows.length; row_number++) {
    var columns = [];
    for (var column_number=0; column_number < result.columns.length; column_number++) {
      columns.push(result.columns[column_number] + '=' + result.rows[row_number][column_number]);
    }
    Logger.log(columns.join(', '));
  }
}

ここで、"your-table-id" の部分には、自分で作成した Fusion Tables のテーブルの画面右上にある[Share]ボタンを押して表示される共有リンク
f:id:furyu-tei:20150819211724p:plain
の、"……?docid=~" の~部分をコピーして貼り付ける。

これを実行すると、
f:id:furyu-tei:20150819211707p:plain
承認を求められるので、[続行]を押し、
f:id:furyu-tei:20150819211712p:plain
許可のリクエスト画面にて、[承認]を押す。
f:id:furyu-tei:20150819211717p:plain
実行が終わり、メニューから
表示 → ログ
を選んで、
f:id:furyu-tei:20150819211720p:plain
実行結果が表示されていれば、無事 Fusion Tables のテーブルへのアクセスが成功している。
f:id:furyu-tei:20150819211728p:plain

【覚書】Google Fusion Tables を使えるようにする手順

Google Fusion Tables

データベースと地図とのお手軽な連携方法を調べていて、Google Fusion Tables と Google Maps の組み合わせが使えないかなあ、と思ったので、試してみることに。
……が、実は前提となる Google Drive 自体をほぼ使ったことが無かったこともあって、とりあえずテーブルを作成する状態まで持ってくるのにも少々とまどったため、忘れないうちにメモ。

Google ドライブに Google Fusion Tables のアプリを追加(接続)(初回のみ)

Googleドライブのマイドライブから、
[新規] → その他 → +アプリを追加
とたどって、
f:id:furyu-tei:20150819211106p:plain
By Google から
f:id:furyu-tei:20150819211111p:plain
Fusion Tables を探し出し、
f:id:furyu-tei:20150819211114p:plain
接続する。
f:id:furyu-tei:20150819211118p:plain
この手順は一度実施するだけで良い。


テーブルの作成

Googleドライブのマイドライブから、
[新規] → その他 → Google Fusion Tables
を選択し、
f:id:furyu-tei:20150819211122p:plain

のいずれかを選択して[Next]を押すと、
f:id:furyu-tei:20150819211125p:plain
テーブルが作成される。
f:id:furyu-tei:20150819211130p:plain

【付記】Two column location の設定方法

Edit → Change columns
にて、カラムを追加・削除したり、名前や型(Type)等を編集したりできる。
f:id:furyu-tei:20150819211134p:plain

カラムの Type で "Location" を選んだ場合には、地名や住所、緯度・経度の組み合わせ(カンマ区切り)などを入れることができる(これは Map の方にも反映される)。
このとき、緯度・経度しか使用しない場合には、「□ Two column location」にチェックを入れて、緯度(Latitude)、経度(Longitude)として使用するカラム名をそれぞれ指定すればよいのだが、最初これを行おうと、Latitude カラムを追加してみると、
f:id:furyu-tei:20150819211139p:plain
Type を Location に変更しても、「□ Two column location」が選択できない(灰色表示)。

いろいろ試してみたところ、先に Longitude に該当するカラムを Type = Number で作成してから、
f:id:furyu-tei:20150819211149p:plain
一旦[Save]し、再び、Edit → Change columns から開くと、
f:id:furyu-tei:20150819211154p:plain
今度は選択できるようになっているので、チェックし、Latitude/Longitudeにそれぞれ対応するカラム名を選択して(ついでに、不要になったLocationカラムは削除して)、再度[Save]する。


このようにカラムを設定してから、実際に緯度・経度を指定したデータ(行)を作成してみると、
f:id:furyu-tei:20150819211158p:plain
"Map of Location" タブで、当該データがプロットされているのを確認できる。
f:id:furyu-tei:20150819211203p:plain


BIGLOBE光ネクスト(大阪)の通信速度調査 (2015/08/12~18)

承前。furyu.hatenablog.com

測定結果(2015/08/12~08/18)

f:id:furyu-tei:20150819204144p:plain
前回

  • さくらのレンタルサーバにおける帯域制限の影響は考慮していない。
    ただし傾向としては、04時~08時頃の測定時には頭打ちになっているように思えることから、これはさくら側の帯域制限の影響だと推測している(1TCPセッションにつき、40~50Mbpsあたり、とか?)。

ということで、比較として、

  • Google App Engine(GAE) 上に、同様に20MBのファイルを設置
  • GAE→さくらレンタルサーバ、GAE→自宅のそれぞれの転送速度も、10分毎に記録

してみた。

これを見ると、

  • ISPによる制限がないGAE→さくらでは、大体50~70Mbpsくらいで推移しており、一日の間で大きな変化はない(一週間の中間値は59Mbpsくらい*1)。
  • 同じ GAE からダウンロードしていても、GAE→自宅はGAE→さくらと比較して一部時間帯を除いて速度が明確に遅いことから、ISPがボトルネックとなっているのは明らか。
  • GAE→自宅、さくら→自宅はだいたい同じような傾向を示している。
  • 02時~09時頃は GAE→自宅がさくら→自宅を明確に上回っていることから、さくらのレンタルサーバ側の速度制限(上限:40~50Mbps)はやはり存在する模様。
  • ↑以外の、さくら→自宅が頭打ちになっていない時間帯では、GAE→自宅(RTT~40ms)よりもさくら→自宅(RTT~20ms)の方が多少転送速度が高い傾向にある。

*1:少し不思議なのは、Quotas - App Engine — Google Cloud Platformによると Free Limit だと1GB/日まで・最大56MB/分、となっているところ。(20MB×24×6)×2~5.3GBで、一日分も転送レートも上回っているのだけれども、静的ファイルだからか?

Radish Network Speed Testing の通信速度測定結果を用いて、プロバイダごとの傾向を調べてみた

BIGLOBEからの乗り換えを検討するにあたり、現時点での通信速度に関して、プロバイダ(ISP)毎のおおまかな傾向などがわかるかと、netspeed.studio-radish.com
蓄積されている測定結果をあたってみた。

集計方法

みんなの測定結果 - Radish Network Speed Testing -より、各絞り込み条件における三ヶ月間(2015/05/14~08/13)の測定データを取得し(1頁表示件数は100件固定)、時間帯での傾向を捉えやすいように、測定時刻毎の下り通信速度をプロットした。
これらのデータは株式会社Studio Radish様によりクリエイティブ・コモンズ「表示-継承ライセンス」として公開されており、これをもとに集計した下記のグラフについてもに準ずるものとする。
データの取得には、後述のブックマークレットを使用した。

グラフの縦軸は、均等にすると値の差が大きくなりすぎて判り難いため、対数目盛にしてある(下限1Mbps・上限1Gbpsとみなし、実測1Mbps未満は1Mbps、1Gbps以上は1Gbpsとしてプロット)。

プロバイダごとの傾向

測定サーバ 評価 回線種別 測定地
すべて すべて 光ファイバ(FTTH)・NTTフレッツ・100M超 指定なし

の条件で、プロバイダのみ変更して測定。

データの数が多いものから順に、以下に示す。

ブロードバンド回線事業者の加入件数調査(2015年3月末時点) - 株式会社 MM総研を見ると、2015年3月末時点でのFTTH契約数シェアは
OCN>Yahoo!BB>BIGLOBE>So-net>plala>eo>au one net>@nifty
となっており、当然ながら加入者が多いところは測定データも多い傾向がある。

■ OCN

service.ocn.ne.jp
f:id:furyu-tei:20150814191140p:plain

■ Yahoo! BB

bbpromo.yahoo.co.jp
f:id:furyu-tei:20150814191150p:plain

■ ぷらら

www.plala.or.jp
f:id:furyu-tei:20150814191154p:plain

■ @nifty

setsuzoku.nifty.com
f:id:furyu-tei:20150814191205p:plain

■ BB.excite

bb.excite.co.jp
f:id:furyu-tei:20150814191209p:plain

傾向
  • いずれの場合にも(ベストエフォートであるためやむを得ないとはいえ)通信速度のばらつきが非常に大きい。
    個人レベルでの「○○は速いよ(遅いよ)」という話は参考程度にとどめておく方が無難かも知れない。自分の地域で快適かどうかは運次第か、と感じる。
  • 一般的な傾向として、21~25時の時間帯は特に(利用者が集中することによる輻輳を反映してか)低速を示すデータ数が増加している。
  • 輻輳の発生する時間帯であっても、十分高速に通信できている方も一定数存在する(全国のデータなので同じ地域・時間帯であっても差が極端に出ているのかどうかはこれだけではわからないが)。
  • @niftyとASAHIネットは、輻輳時間帯でも低速を示すデータの割合が少なく、比較的余裕があるように思える。
    取得できたサンプル数が十分とは言えないため、断言できるものではないが。

本来なら、測定地でフィルタをかけて自分の地域の傾向を知りたいところだが、そうなると現状では十分なサンプル数が取れない。
例えば、上記のOCNのデータに「神奈川県」でフィルタをかけると、
f:id:furyu-tei:20150816005316p:plain
のような分布になるため、
OCN 光ネクスト(神奈川)の速度問題について
で述べられている拡張工事による品質改善が功を奏している、という結論に短絡したくなるが、サンプル数が少なく、輻輳時間帯のデータがあまり無いこともあり、確かなことは言えない。


回線種別(通信事業者)の違いによる傾向

通信網の違いも気になってきたので、ついでに調べてみる。
住んでいるところの関係で、事実上変更できないのだが。

測定サーバ 評価 回線種別 プロバイダ 測定地
すべて すべて 光ファイバ(FTTH)・100M超 指定なし 指定なし

の条件で、回線種別のキャリアのみを変えて測定。

データ数の多い物から順に、以下に示す。

■ NTT フレッツ

flets.com
flets-w.com
f:id:furyu-tei:20150814195716p:plain

■ au ひかり

www.au.kddi.com
f:id:furyu-tei:20150814195810p:plain

■ K-OPTI.com eo光ネット

eonet.jp
f:id:furyu-tei:20150814195822p:plain

■ So-net NURO光

www.nuro.jp
f:id:furyu-tei:20150814195826p:plain

傾向
  • NTT フレッツ以外のサービスは全般的に比較的中~高速域のデータが多いし、夜間の輻輳も今のところは目立ったものではなさそうなので(NURO光は若干多い?)、通信速度のみに着目する場合、自宅が対応しているのであれば乗り換えを検討する価値はありそう。
  • NTT フレッツは(当然ながら)上記のプロバイダの結果をそのまま重ね合わせた傾向となっている。加入者が圧倒的に多く、その分輻輳も発生しやすいということか。
    自分の経験や知人の話、ネット上の口コミ等をみても、往々にして(プロバイダを介さない)フレッツ網自体の速度は十分、という場合が多いようだし、プロバイダにがんばってもらうしかない……。

付録:Radish Network Speed Testingの測定データを CSV 形式でダウンロードするブックマークレット

みんなの測定結果 - Radish Network Speed Testing -のデータ取得には、以下のブックマークレットを使用。

bookmarklet:Radish Network Speed Testing の測定結果をダウンロード

Radish Network Speed Testing の測定結果をダウンロード - Hatena::Let
使用方法

  1. みんなの測定結果 - Radish Network Speed Testing -で、1頁表示件数を設定(推奨:100・後述)。
  2. 絞り込み条件を設定し、検索。
  3. ブックマークレットを実行(取得データ数がカウントされていく)。
  4. データが取得し終わると、CSVファイルがダウンロード可能となるリンクができ、自動的にダウンロード開始。
    ただし、HTML5・A要素(リンク)のdownload属性がサポートされているブラウザとIE10以降のみ。Safari等はNG。


[2015/08/17] [検索]ボタンを押す手順および1頁表示件数を設定する手順を省略(取得時の1頁表示件数は100固定に)。

  1. みんなの測定結果 - Radish Network Speed Testing -で、絞り込み条件を設定([検索]ボタンを押す必要はない)。
  2. ブックマークレットを実行し、基準日と期間を入力(データの取得が開始され、取得データ数がカウントされていく)。
  3. データが取得し終わると、CSVファイルがダウンロード可能となるリンクができ、自動的にダウンロード開始。
    ただし、HTML5・A要素(リンク)のdownload属性がサポートされているブラウザとIE10以降のみ。Safari等はNG。

CSVファイルはBOM付のUTF-8形式になっている。
【参考】

ブラウザだけで Excel 向け日本語 CSV ファイルを作る方法 - do_akiの徒然想記

Windows版Excelだと文字化けせずに開けるはず。Mac版はNGらしい。

みんなの測定結果 - Radish Network Speed Testing -では1頁表示件数により得られるデータ数が異なる

例えば、1頁表示件数を 100 にした場合のデータ数は、1 にしてさかのぼって取得したデータと比較すると、かなり少なくなる。
この件を株式会社Studio Radish様に問い合わせたところ、

  • 同一のユーザーによるデータは、1ページの中で最初のデータのみを代表として表示(グループ化)。
    当該ユーザーのその他のデータには、"この人の測定履歴(件数)"で表示可能)。
    非常にアクティブな(測定登録回数の多い)ユーザーがいると、多様な環境での結果を比較する上で妨げとなるため。
  • グループ化は 1ページ内でのみ実施され、ページが変わると再度最初のデータのみが表示される。
    時系列での比較のため。

という仕様になっているためとのこと。

今回の記事でも、なるべく多様な環境でのデータを取得したかったため、グループ化が最大の効果を得られるように、1頁表示件数を 100 にして取得している。
また、1頁表示件数が少ないとその分取得ページ数が多くなり、サーバーに負荷をかけてしまう、という理由もある。