グラブルのアイテム一覧抽出用ブックマークレット

物凄く久々の記事ですが・・・

最近はグラブルなんかをしてます。

とりあえずアイテム一覧抽出用のブックマークレット置いておきます。

上が圧縮前、下が圧縮後です。使い方は下に。
gist.github.com


下のをブックマークとして保存
f:id:romer:20170531180215p:plain
アイテムページで、保存したブックマークをクリック

すると下のような感じでクリップボードにコピーされます。xxxは数字です。エクセル貼り付けにでもどうぞ。
f:id:romer:20170531180413p:plain

あ、ご利用は自己責任でお願いします。多分大丈夫だとは思いますが・・・


2017.06.14追記
新UIに一応対応
一部ヒヒイロとか回復素材の数がとれなくなりました。

ていうかテキスト出力付けて欲しいほんと。

laravelの微妙な所

Laravelってなに?PHPのFWの一つ。
公式:http://laravel.com/
日本語情報多いところ:http://kore1server.com/



色々使ってて微妙だと感じる所

  • テンプレートで利用するsectionでの継承。(@parentの部分)
  • テンプレートで@parentなどが置換されるのはLaravel namespace固定な事。
    • これ継承するものがない場合@parentがそのままでます。せめて消して・・・
  • FW内での別クラス利用がほぼLaravel\*とnamespace固定
    • せっかくaliaseがあって拡張しやすいのに固定で内部動作が変えられない。
    • 内部動作を変えたくなる部分はResponseのダウンローダ。FWがマルチバイト未対応なためにちょっと残念。
  • Strクラスに文字列カット系がない。内部でsubstrを利用する部分はそれぞれで記述されている。謎。
  • bundle作成時にbundle名がほぼ固定化される事。bundle名を変更したい場合はclass名の変更やらやる事多い。

あとは大体満足。整理すると意外と少ない。
致命的なのは無く、だいたい回避できる。

まぁLaravel4でどれ程変わるのやら・・・

laravelのbundle内で別bundleの利用の仕方

これ正直ベスト・プラクティスないんですよね。
フォーラムの方でもrequireしろみたいな事かかれてたし。

でもこれ結構スッキリする方法で実装出来そうです。
基本的にはapplication配下がDEFAULT_BUNDLEなので、bundleでも同じ事が出来るはずなのです。
で、考えた結果がこの2つ。

1.start.phpでbundles.phpからの登録を行う方法
ソース

でもこの場合、bundleのインストールをアプリケーション作成者に委ねなきゃいけない。
で、考えたのがこれ。

2.bundlesディレクトリも内包しちゃって、locationを自動設定する。

正直微妙な気がしなくもない。
結局1を選びました。

CIと次のFWのLaravel

最近までは会社で使うFWはCI(Codeigniter)だった。

別に会社で何を使おうが良いんだが、このFWがなぜそんなに流行ったのか自分には謎すぎた。

 

CIの売りは今更書くまでも無く、"軽量な実装"と軽量な実装からくるFWの"習得コストの低さ"だと思うんだが、それを得るためにいろんな物を捨てすぎている。

 

一つCoreを拡張しようとするとメソッドをすべて上書きする必要があったり。

ActiveRecordを利用しないという設定があるにも関わらずSessionをDBで持とうとするとARが必須だったり。 

mysql以外のDB接続系ドライバが微妙だったり。(特にPostgreSQLはPDOでも酷い)

 

まぁとにかく酷い。

 

そこで『FWを変えませんか?』と言う事で色々探し検証した結果

最近日本で流行り?のFuelPHPではなく、Laravelにした。

正直この2つの差はあんまり無いと思う。どちらもいい意味で一長一短。

 

とりあえず使ってみた感想としては良い感じ。

ただ色んな所で無名関数を多様しておりEventListenerとかも持ってるため、一定規模以上になると書き手によっては大いに大変な事になりそう。

dart editorからeclipseのプラグインにしてみたけどイマイチだった

最初は公開できそうかな?と思ったのですが色々だめだったのでやった事だけ書いておきます。

dartエディタから必要な物引っ張ってきてみました。
dartエディタ自体がどうやらeclipse-platform 3.7.1?ベースのようです

なのでフォルダ自体の差分をとってみました
本来ならdiffとか載せたいのですが無理なので必要そうなツリーのみ。

.
└── eclipse
    ├── features
    │   ├── com.google.dart.tools.deploy.feature_0.1.0.201111041746
    │   └── com.google.dart.tools.eclipse.platform.feature_0.1.0.201111041746
    ├── plugins
    │   ├── com.google.dart.compiler.js_0.1.0.201111041746.jar
    │   ├── com.google.dart.indexer_0.1.0.201111041746.jar
    │   ├── com.google.dart.library.core_0.1.0.201111041746
    │   ├── com.google.dart.library.dom_0.1.0.201111041746
    │   ├── com.google.dart.library.html_0.1.0.201111041746
    │   ├── com.google.dart.library.json_0.1.0.201111041746
    │   ├── com.google.dart.tools.core_0.1.0.201111041746.jar
    │   ├── com.google.dart.tools.deploy_1.0.0.201111041746
    │   ├── com.google.dart.tools.search_0.1.0.201111041746.jar
    │   └── com.google.dart.tools.ui_0.1.0.201111041746.jar
    └── samples
        ├── clock
        ├── isolate
        ├── libraries
        ├── slider
        ├── sunflower
        └── total

これらをdropinsにdartフォルダを作ってコピー。
初回起動時はcleanオプションつける事おすすめします。
出来ること

  • その他パースペクティブを開くことが出来る。

不具合っぽい事

  • runボタンをデフォルトでツールバーに出てない -> html右クリから実行できます dartファイル右クリからはdartエディタでも無理でした
  • その他のパースペクティブでも右上のsend feedbackが出ちゃう
  • 起動時にたまにExceptionが発生する -> dartのパースペクティブ開けば大人しくなります
  • workspaceにdartのライブラリっぽいフォルダ(base,observable{n},utilslib{n})が大量に生成されます ->workspace切り替える事おすすめです
  • その他のパースペクティブでもegitが使えなくなった。

起動したらこんな感じです

dartエディターそのままって感じですね


多分featuresやら色々いじくればOKなのでしょうが、私にはわからないので参考にどうぞ。
というかGoogleさん、プラグイン出だしてください。
因みに、dartエディターってまだ型補完とか未実装ですよね・・・?

今更ですが、node.jsでbasic認証にかかったサイトをjqueryでスクレイピングをした。そして困った。

きっかけ

Conkyというデスクトップ上で色々表示できるプログラムで使うスクリプトをNode JSを使って書いてます。
どういった物かというと、ルータ(年代物のCoregaのBAR FX2)から外部IPを取得するもの。
UPnPが使えるルータなので本来ならUPnPで取得したいのですが、色々やってみてもうまくとれない・・・
そこでbasic認証がかかったルータの設定画面(Web)から取得する事にしました。

環境

Node.js -> 0.4.12 // 2011/10/30でstableのやつです
npm -> 1.0.101 // 最初apt-getでinstallしたら0.2.19なんてものが入った。これだと問題が発生したので入れなおした。
jsdom -> 0.2.8 // スクレイピングとかに使います
jquery -> 1.6.4 // 今回指定したバージョン

取得する為にする事

1.Webからデータを取得する方法
2.Basic認証の回避方法
3.取得したHTMLデータをパースする

やってみた

1.APIを見ていたら解決できました。

http - Node.js v0.4.12 Manual & Documentation
設定をオブジェクトで渡してコールバックを指定すると。
よくありがちな方法ですが、やはり楽。

1のサンプル

var http = require('http'),
    routerConf = {
        host : '192.168.0.1', // routerなので。
//        port : '80',     // 省略可能。※1
        path : '/st_dhcp.htm' // これも省略可能。※2
    };

http.get(routerConf,function (response) {

    var chunks = {length : 0, items : []};

    response.on('data', function (chunk) {
        // ここでブツ切れデータを持ったBufferとして渡ってきます。※3
        // ブツ切れなので全データ取得できるまでとりあえずどこかに放り込む。
        chunks.length += chunk.length;
        chunks.items.push(chunk);
    });
    response.on('end', function () {
        // 普段読める文字への変換。
        var allText = '',
            allBuffer = new Buffer(chunks.length)
            liveLength = 0,
            真面目 = function (chunk) {
                chunk.copy(allBuffer,liveLength);
                liveLength += chunk.length;
            },
            手抜き = function (chunk) {
                allText += chunk.toString('utf8');
            };

        chunks.items.forEach(真面目);
        chunks.items.forEach(手抜き);

        delete chunks,preLength,真面目,手抜き;

        // 日本語ですが動きます。どちらでも良いと思います。
        // allBuffer使ったほうが本来のやり方な気がします。
        // ここでは書きませんが文字コード変換(node-iconv)もBufferのままの方が楽です。
        console.log(allBuffer.toString('utf8'));
        console.log(allText);
    });
}).end(); // request終了をする。error処理をわざとしていないのでこういった書き方ができます。
          // 本来はon('error')などを使ってエラー処理すべきです。

※1 lib/http.jsの880行目あたりからの推測ですが。
※2 lib/http.jsの883行目こちらもこのあたりからの推測です。
※3 Buffer

2.ぐぐりました。サイト見つけました。APIも見ました。解決しました。

node.jsでbasic認証を使ってhttps接続する - @blog.justoneplanet.info
ぐぐると一番上にありました。

参考にしたのに否定的になるかもしれませんが・・・
確かに記事に書かれてる方法だと出来るのですが、APIを探してもcreateClientというAPIがない。
変更点の過去ログ等を調べると0.2系から0.3系に上がる時に廃止される方向になったとか。Node.js0.2から0.3への変更点
で、httpsの方法としてマニュアルhttps.requestソッドを使った方法が紹介されてましたが、単純にはうまく行っていなかった模様。
しかし、このサイトのお陰で、Basic認証の仕様を思い出す。
HeaderにAuthorizationを含めれば良いんだと。

関連API調べました。

http.request() は http.ClientRequest クラスのインスタンスを返します。

とマニュアルにあったので、マニュアルhttp.ClientRequestを眺めていると、

ヘッダは setHeader(name, value), getHeader(name), removeHeader(name) API によってまだ可変のままです。

とあったので試して見ましたがうまくいかず。ようやく見つけたのがheadersオプションでした。
最終的に以下のようなコードに。

2のサンプル

var http = require('http'),
    routerConf = {
        host : '192.168.0.1',
        path : 'st_dhcp.htm',
        headers : { // 追加
            'Authorization': 'Basic ' + new Buffer( 'id:pass').toString('base64')
        }
    };

    // 以下は1のサンプルと同じ。
http.get(routerConf,function (response) {
    var chunks = { length : 0, items : []};
    response.on('data', function (chunk) {
       chunks.length += chunk.length;
       chunks.items.push(chunk);
    });
    response.on('end', function () {
        var allBuffer = new Buffer(chunks.length)
            liveLength = 0;

        chunks.items.forEach(function (chunk) {
            chunk.copy(allBuffer,liveLength);
            liveLength += chunk.length;
        });

        delete chunks,preLength;

        console.log(allBuffer.toString('utf8'));
   });
}).end();
3.jQueryで取得する方法ぐぐりました。だけど古かった。jsdomの公式みました。方法わかりました。いけました。

node.jsとjQueryでスクレイピングするウェブアプリの作り方 | さくらたんどっとびーずを参考にしました。
上記のサイトではjsdomというライブラリとjqueryを使ってます。
しかし、半年以上前の記事なので結構APIが変わってました。

まずはインストール周りから。
環境を書いた時にnpmで問題が発生した為入れなおしましたと書いたのですが、
それは3で使うjsdomライブラリでpath解決の問題が発生した為です。
古いnpmの場合ファイルの置き方が違うらしく、うまくrequireできないという問題があります。
これはnpmのversionが1.0以上であれば解決します。

次にjsdomのAPI。もの凄く単純になってました。
以前はdocument作って、window作って、jquery使うのであれば専用関数読んで・・・とちょっと面倒だったのですが
今は1つのメソッドで良いようになってます。

以下公式サンプルです。

var jsdom = require('jsdom');
jsdom.env({
  html: 'http://news.ycombinator.com/',         // 対象となるurlかHTMLソース
  scripts: [
    'http://code.jquery.com/jquery-1.5.min.js' // 追加で必要となる外部のjavascript。scriptタグで指定されてる分については自動で読み込み等出来る模様。
  ],
  done: function(errors, window) {        // domが構築された後呼ばれる
    var $ = window.$;
    console.log('HN Links');
    $('td.title:not(:last) a').each(function() {
      console.log(' -', $(this).text());
    });
  }
});

これ見た時びっくりしました。1,2でやった事はなんだったのだと・・・。
しかしこのままだと、basic認証のサイトをどうやればいいのか解りません。
公式読んでも

config.html : see html above
config.scripts : see scripts above
config.src : An array of javascript strings that will be evaluated against the resulting document. Similar to scripts, but it accepts javascript instead of paths/urls.
config.done : see callback above
config.document :
referer : the new document will have this referer
cookie : manually set a cookie value i.e. 'key=value; expires=Wed, Sep 21 2011 12:00:00 GMT; path=/'
config.features : see Flexibility section below. Note: the default feature set for jsdom.env does not include fetching remote javascript and executing it. This is something that you will need to carefully enable yourself.

としか書かれていない。そこでjsdomのソース読みました。
lib/jsdom.js内に怪しいところありました。

      request({ uri: url,
                encoding: config.encoding || 'utf8',
                headers: config.headers || {}
              },
              function(err, request, body) {
                processHTML(err, body);
      });

あやしい・・・。どうみても怪しい。このファイルの先頭行でrequestというモジュールをrequireしてたので調べたら”Simplified HTTP request client.”とでてきました。
という事でheadersに追加しました。

3.で出来上がったコード。

var Jsdom = require('jsdom'),
    jquery_js = 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js',

    routerConf = {     // http.getでも使いやすいように別けておく。無駄なオプションあると怒られる。
        'host' : '192.168.0.1',
        'path' : 'st_dhcp.htm',
        'headers' : {
            'Authorization': 'Basic ' + new Buffer('id:pass').toString('base64')
        }
    },
    httpConf = {
        'html' : 'http://'+routerConf.host+'/'+routerConf.path,
        'scripts' : [
            jquery_js
        ],
        'headers' : routerConf.headers,
        'done' : function (err,window) {
            var $ = jQuery = window.jQuery,             // $,jQueryで呼び出しやすくするため
                document = window.document;             // 同じ理由でdocumentも
            (function () {
                console.log($(document.body).text());
            }).apply(window);                           // this == windowにするため
        },
        'features' : {
            // scriptタグで指定されてるjsを読み込んだ後実行するかどうか。未指定だと実行。null等のfalsyな値を入れると自動実行されない。
            ProcessExternalResources : null
            // その他はデフォルト
            // MutationEventをどうするかや外部ファイルを読み込むか等がある。
        }
    };
Jsdom.env(httpConf);

文字コード変換が出来ませんがものすごく短くなりました。

感想

思ってたより楽!

以下余談と困った事

jsdomにはsrcオプションを使った場合、

バージョン0.2.9で修正されました
srcを使った分だけdoneに設定したcallbackが呼ばれるという不具合があります。
lib/jsdom 175行目

    totalDocs  = config.scripts.length,

とあるのですが、このtotalDocsはその192行目付近で

    var scriptComplete = function() {
      docsLoaded++;
      if (docsLoaded >= totalDocs) {
        window.document.implementation._features = features;
        callback(errors, window);
      }
    }

として使われています。このscriptCompleteはsrc,scriptsで指定したもののloadイベントが完了するたびによばれるものです。
jsdom自体にはpullリクエスト付きでバグ報告?として上がってました。
最近なのかと思ったら結構前から・・・?単純にこれは以下のように修正すれば良いです。

    totalDocs  = config.scripts.length + config.src.length,


あと、私個人の環境のせいなのか全く解ってない問題が。
その3で作ったソースの先頭に

process.on('uncaughtException', function (err) {
  console.dir(err)
});

var Jsdom = require('jsdom'),

としてもエラーが取得できないexceptionが・・・。周辺でtry-catchとしてもキャッチできない・・・。こまったw

何をしたらそうなったかというと
読み込み対象のhtmlのbodyタグにonloadのattributeがあった時に
onloadに設定されている関数で不具合やらがあった時そうなる。
詳細は不明。というかまだ未調査。

解決するかも?と思い、onloadで呼ばれる関数と同名の関数をsrcで用意し、
featuresオプションのfetchExternalResourcesをfalsyに設定しても解決しない。
うーん・・・どうしたものか。

文字コードを変換しても問題が発生するので文字コードは関係なさげ。

Ubuntu 11.10を入れなおした。

一昨日HDD関連のエラーがでてCUIしか起動しなくなった。
何時もなら調べてどうにかするんだけど、Node.js弄りたくてめんどくさかったのと、
64bit版から32bit版に変えたかったので入れなおす事にした。

次再インストールする事になっても良いようにメモ。
バックアップするもの

/etc/fstab
/etc/X11/xorg.conf
/etc/apt/*
/home/username/.Skype/*
/home/username/.bash_aliases
/home/username/.bash_history
/home/username/.bash_logout
/home/username/.bashrc
/home/username/.calc_history
/home/username/.bash_aliases
/home/username/.config/autostart/*
/home/username/.config/banshee-1/*
/home/username/.config/gedit/*
/home/username/.config/google-chrome/*
/home/username/.config/vlc/*
/home/username/.eclipse/*
/home/username/.gitconfig
/home/username/.local/share/applications/eclipse-original.desktop
/home/username/.mozc/*
/home/username/.mozilla/*
/home/username/.npm/*
/home/username/.npmrc
/home/username/.opera/*
/home/username/.profile
/home/username/.selected_editor
/home/username/.ssh/*
/home/username/.subversion/*
/home/username/.vim/*
/home/username/.viminfo
/home/username/.vimrc
/home/username/.xvindkeysrc
/home/username/* #.無しフォルダとファイル

こんなところか。goolge-chromeは全部の設定がのこってた。
他何かあったら追加するとしよう。
削除したもの。

sudo apt-get remove \
casper:i386 \
language-pack-pt-base:i386 \
language-pack-zh-hans:i386 \
lupin-casper:i386 \
libdmraid1.0.0.rc16:i386 \
ubiquity-frontend-gtk:i386 \
language-pack-gnome-zh-hans-base:i386 \
ubiquity-slideshow-ubuntu:i386 \
gir1.2-timezonemap-1.0:i386 \
jfsutils:i386 \
gir1.2-gstreamer-0.10:i386 \
lzma:i386 \
archdetect-deb:i386 \
gparted:i386 \
language-pack-gnome-pt-base:i386 \
language-pack-de-base:i386 \
language-pack-gnome-zh-hans:i386 \
python-pyicu:i386 \
python-argparse:i386 \
ubiquity:i386 \
libdebian-installer4:i386 \
user-setup:i386 \
ubiquity-casper:i386 \
language-pack-gnome-de-base:i386 \
cryptsetup:i386 \
libecryptfs0:i386 \
reiserfsprogs:i386 \
rdate:i386 \
xfsprogs:i386 \
python-xklavier:i386 \
unionfs-fuse:i386 \
language-pack-es-base:i386 \
btrfs-tools:i386 \
libgtkmm-2.4-1c2a:i386 \
localechooser-data:i386 \
apt-clone:i386 \
libtimezonemap1:i386 \
language-pack-gnome-es-base:i386 \
ecryptfs-utils:i386 \
ubiquity-ubuntu-artwork:i386 \
dpkg-repack:i386 \
cifs-utils:i386 \
firefox-locale-de:i386 \
firefox-locale-es:i386 \
libdebconfclient0:i386 \
language-pack-xh-base:i386 \
language-pack-zh-hans-base:i386 \
firefox-locale-pt:i386 \
firefox-locale-zh-hans:i386 \
dmraid:i386 \
language-pack-de:i386 \
language-pack-es:i386 \
language-pack-pt:i386 \
language-pack-xh:i386 \
libnss3-1d:i386 \
language-pack-gnome-de:i386 \
keyutils:i386 \
language-pack-gnome-es:i386 \
language-pack-gnome-xh-base:i386 \
language-pack-gnome-pt:i386 \
language-pack-gnome-xh:i386 \
deja-dup:i386 \
tomboy:i386 \
ubuntu-desktop:i386 \
gwibber:i386 \
empathy:i386 \
vino:i386 \
xul-ext-ubufox:i386 \
transmission-gtk:i386 \
thunderbird-globalmenu:i386 \
thunderbird:i386 \
thunderbird-gnome-support:i386 \
thunderbird-locale-en-gb:i386 \
thunderbird-locale-en-us:i386 \
thunderbird-locale-en:i386 \
thunderbird-locale-ja:i386 \
libreoffice-math:i386 \
libreoffice-impress:i386 \
eog:i386 \
simple-scan:i386 \
aisleriot:i386 \
gbrainy:i386 \
gnome-mahjongg:i386 \
totem-plugins:i386 \
totem:i386 \
totem-mozilla:i386 \
gnome-user-share:i386 \
system-config-printer-gnome:i386 \
gnome-orca:i386 \
libssl-dev:i386 \
libssl-doc:i386 \
zlib1g-dev:i386 \
libev4:i386 \
libev-dev:i386 \
vinagre:i386 \
gcalctool:i386 \
ubuntuone-control-panel-gtk:i386 \
ubuntuone-installer:i386 \
pidgin-musictracker:i386 \
hplip-cups:i386 \
bluez-cups:i386 \
hplip:i386 \
cups-driver-gutenprint:i386 \
cups:i386 \
libcupscgi1:i386 \
cups-client:i386 \
cups-bsd:i386 \
ptouch-driver:i386 \
c2esp:i386 \
libcupsdriver1:i386 \
hpijs:i386 \
libcupsmime1:i386 \
cups-ppdc:i386 \
libcupsppdc1:i386 \
foomatic-db-engine:i386 \
pnm2ppa:i386 \
libspectre1:i386 \
ghostscript-x:i386 \
rastertosag-gdi:i386 \
gs-cjk-resource:i386 \
splix:i386 \
libevince3-3:i386 \
libgs9:i386 \
libcupsimage2:i386 \
ghostscript:i386 \
ghostscript-cups:i386 \
cups-common:i386 \
foomatic-db-compressed-ppds:i386 \
pxljr:i386 \
foo2zjs:i386 \
foomatic-filters:i386 \
cups-pk-helper:i386 \
min12xxw:i386 \
libgutenprint2:i386 \
system-config-printer-udev:i386 \
system-config-printer-common:i386 \
python-cups:i386 \
python-cupshelpers:i386 \
gcalctool:i386 \
pnm2ppa:i386 \
libcupscgi1:i386 \
libspectre1:i386 \
libcupsmime1:i386 \
hplip:i386 \
ghostscript-x:i386 \
vinagre:i386 \
cups-driver-gutenprint:i386 \
libcupsppdc1:i386 \
gs-cjk-resource:i386 \
libgutenprint2:i386 \
libevince3-3:i386 \
system-config-printer-common:i386 \
cups:i386 \
libcupsdriver1:i386 \
libgs9:i386 \
cups-bsd:i386 \
cups-pk-helper:i386 \
libcupsimage2:i386 \
foomatic-filters:i386 \
python-cupshelpers:i386 \
laptop-detect

言語パックとcups系と初期からインストールされてないものいくつか
消したくても消せない物。zeitgeist
ログ採取用のものでそれなりにPCパワー食う奴。
でもこれ消すとubuntu-lens-*系まで消さないといけなくなる。
そしてそれらを消すとDashがまともに動かない。

次インストールしたものといきたかったけど数が多すぎた。のでパッケージ単位で。

とりあえずこんなものか。
あとなにか見つかれば追加してこ。