金利0無利息キャッシング – キャッシングできます

2010-02-03

Page2FeedっていうAPIを作った件

17:37 | Page2FeedっていうAPIを作った件 - 金利0無利息キャッシング – キャッシングできます を含むブックマーク はてなブックマーク - Page2FeedっていうAPIを作った件 - 金利0無利息キャッシング – キャッシングできます

http://ic.edge.jp/page2feed/

別の大きめの機能のリリース準備中なんですが、片手間に作ってみました。フレーム使ってるサイトとか今のところ動きませんが、おおむね上手く動くと思います。有志が作ってくれた野良フィード、と同程度のクオリティのものを自動で作れます。

リリースが早かったので「前々から作ってたのかな」みたいな反応もいくつか見ましたが、そういうわけではないです。ただ、過去に似たようなコードを書いたことがあるのですんなり出来ました。

アルゴリズムはこれに改良を加えたものです http://la.ma.la/blog/diary_200705301424.htm

実装開始 http://twitter.com/bulkneets/status/8240819145

リリース http://twitter.com/bulkneets/status/8358993519

twitterでフィードを吐いてないサイトのURLを募集して参考にしました。協力してくれた方に感謝します。よく考えたらなんでもRSSで購読されてるサイトを使えば良いので必要なかったかもしれない。

とりあえず動くものは3時間ぐらいで出来たと思います(面接試験に調度良い)、あとは2日ぐらいかけて精度の調整をやりました。既存のサービスと何が違うの、みたいなのがFAQだと思うのでいくつか比較してみます。

livedoor専用のAPIではない

既存の似たようなサービスは沢山あると思うのですが、

  • ユーザー登録が必要
  • URLからfeedのURLが求められなかったり(サービス独自のidを付ける)
  • なんか勝手に広告とか入ったりしそう or 有料サービスに誘導したがる
  • アクが強い(サービスのロゴとかクレジットとかデカデカと表示する)
  • いつサービス終了するか分からない
  • 予告なく止まったリBANされたりしそう
  • そもそもサービスの品質が微妙

というような具合で、タダ乗りしても良さそうなのが無かった。タダ乗りしても良さそうなサービスはこれらと逆の条件を満たしているサービスです。既存のサービスでタダ乗りしても良さげなのがあるならリンク張って終わりというのも考えたのですが、上記と逆の条件を全て満たすようなサービスが無かったので、片手間に作れそうだしせっかくなのでこの際作っておくか、ということになりました。

単体で収益挙げられるような要素が無いですけども、readerの付加価値が上がるので利益が出なくてもまあ問題ないでしょう。ラボですし。

Google Readerのと何が違うの

日本語のサイトでも変換出来ます。

http://googlereader.blogspot.com/2010/01/follow-changes-to-any-website.html

http://blogs.itmedia.co.jp/saito/2010/01/google-reader-6.html

any websiteって言うので、なんかインパクトが大きかったみたいで「LDRにも似たような機能を希望」とか要望が飛んできた。それで作ったので、後追いといえば後追いですが、

http://www.google.com/support/reader/bin/answer.py?hl=en&answer=172963

"Currently, only English-language content in HTML format is supported."としっかり書いてあります。英語のサイトに関しても、生成されたフィードが読みやすいとは思えなかった。実際に試さないで「これはすごい」とか「これは便利」とか記事を書くのは止めませんか。

現状英語のサイトにしか対応してないのを知らないで試して残念な思いをしている人がチラホラいる。

http://d.hatena.ne.jp/Imamura/20100202/googlereaderRSS

http://dullneko.typepad.com/blog/2010/02/%E3%83%A9%E3%82%A4%E3%83%96%E3%83%89%E3%82%A2rss%E3%83%95%E3%82%A3%E3%83%BC%E3%83%89%E3%82%92%E8%87%AA%E5%8B%95%E7%94%9F%E6%88%90%E3%81%99%E3%82%8Bpage2feed-api%E5%85%AC%E9%96%8B--internet-watch.html

Page2FeedはGoogle Readerでも上手く動くみたいです。独立したAPIなのでタダ乗り歓迎です

http://bardiche.posterous.com/bbrss

なんでもRSSでは無い

なんでもRSSの問題点をいくつか挙げます。

  • フィードのURLと記事のURLが汚いです。
  • フィードのURLに解析した抽出すべき要素のXPathが含まれている
    • HTMLの構造が変わると以前に生成したフィードのURLが無効になります。
  • 日付情報が含まれていないと変換出来ません。
    • 名前のインパクトは大きいですが、全てのページを変換出来るわけではないです。
  • url#guid形式の独自のPermalinkを作ります。
    • Page2Feedでは要素中にhrefかidがあるならば、なるべくそれを使います。

フィードリーダーから記事をそのまま開いたり、ソーシャルブックマークの情報を参照しやすくするために、なるべく記事のリンクを抽出します。実用性を重視してます。

逆になんでもRSSとは違って日付情報を解析してないので、記事の更新時刻が正確ではありません。フィードリーダーが取得した時刻になるでしょう。

クロールして差分を記録するタイプでは無い

クロールして差分を記録するタイプ(page2rss.comやGoogle)は

  • ページが更新されるまで生成されるフィードの質が確認出来ない(プレビュー出来ない)
  • 過去のデータを蓄積しないと行けないので、ストレージが必要になる(著作権等への配慮も)

過去記事の蓄積と新着部分の判別はRSSリーダー側に任せて、HTMLをフィードに変換する処理は切り分けてしまった方が良いだろうと判断しました。必要なのは帯域とCPUパワーなので、もしアクセスが異常に増えても対応しやすい。1世代分は保存して学習に使った方が精度が上がるかな、とは思ってます。

本文抽出ではないです

スコアが高いのだけを出力すれば本文のみのフィードも作れるのですが、意図的に本文以外も入れています。生成されたフィードは未読管理機能があるフィードリーダーで使う前提です。本文以外の要素もフィードに入る場合がありますが、一度既読になってしまえば現れないからです。なので、例えばティッカーやFirefoxのライブブックマークには適さないでしょう。

学習やサイトごとの定義を(今のところ)使ってません

今のところしてません。例えばサイトごとの定義を作るとメンテナンスコストがかかります。ベースとなるアルゴリズムが優秀であれば、上手くいかないサイトに対してだけパッチを作れば良いことになります。定義作るとしてもタイプ(ニュース、日記、掲示板、など)に応じて各種閾値を調整するみたいな感じになると思います。学習や人力による調整を行わなくても、デフォルト状態でとりあえず良い感じのフィードを出力するのを目的としてます。

今後

元のコードがJavaScriptなんで今度はPerlからJavaScriptに移植すれば何かと役に立つかもしれない。JS実行したDOM構築後のフィード作ったり認証が必要なページをブラウザで解析して送ったりとか。

もう少し技術よりの解説を書くかもしれない。

2010-01-14

AnyEvent::Handleで永続的にコネクション張る際にメモリ食いまくり

17:45 | AnyEvent::Handleで永続的にコネクション張る際にメモリ食いまくり - 金利0無利息キャッシング – キャッシングできます を含むブックマーク はてなブックマーク - AnyEvent::Handleで永続的にコネクション張る際にメモリ食いまくり - 金利0無利息キャッシング – キャッシングできます

一つのAnyEvent::Handleをコネクション切らないで使いまわして、JSONRPCなんかでリクエスト送りまくったりした場合に、書き込みバッファが際限なく溜まりすぎてサイズが大きく確保されたまま500MBとかになってしまって困ったのでメモする。

事前知識

  • 変数が使用しているメモリ使用量はDevel::Sizeなんかで確認できる。
  • 文字列の伸び縮みではメモリは開放されない。undefされれば開放される。
  • ただしコンパイルオプションによってはそもそも開放されない。
  • Uusemymallocでググる
    • $Config{usemymalloc}がy => perlmallocを使う。
    • $Config{usemymalloc}がn => システムのmallocを使う。
  • 手元の環境では$Config{usemymalloc}がnで、メモリを開放してくれた。

AnyEvent::Handleで何とかするケース

書き込みバッファが掃けたタイミングで、新しいイベントを発火させるようにする。

  • $hdl->on_drain(sub{ ... })を定義すると、wbufが掃けたタイミングでコールバックが呼ばれる。
  • $hdl->low_water_mark($bytes)はon_drainが発火する閾値。書き込みバッファが1000KB以下になったらon_drainが呼ばれる、ということが出来る。

際限なくwbufを確保してもいいけどメモリ開放して欲しいケース

AE::timerとか使って適当なタイミングでこういうコードを呼ぶ。

if($hdl->{wbuf} eq ""){
    delete $hdl->{wbuf}; $hdl->{wbuf}="";
}

もしくは適当なタイミングでコネクション張りなおせば良い。ものすごい勢いで書き込んでいたらどの道メモリを食いつぶすのでオススメできない。

Coro使うケース

こういうコード挟んで書き込みイベントにwaitを入れる。wbufが8MB以上だったらsleepする。

while(1024 * 1024 * 8 < length $hdl->{wbuf}) {
    warn "wait for write";
    Coro::Timer::sleep 0.05;
}

まとめ

  • AnyEvent::Handleで書き込みまくるときはon_drainを使うか適当にwait入れた方が良い。

2009-12-29

LDRの未読件数を表示するGoogle Chrome拡張機能作った

03:52 | LDRの未読件数を表示するGoogle Chrome拡張機能作った - 金利0無利息キャッシング – キャッシングできます を含むブックマーク はてなブックマーク - LDRの未読件数を表示するGoogle Chrome拡張機能作った - 金利0無利息キャッシング – キャッシングできます

誰か作ってるだろと思ってギャラリー検索したけど見つからなかったので作った。

https://chrome.google.com/extensions/detail/ifekfcjbnkflfndoahjigdhlhgndkncb

os0xさんが技評の連載のサンプルで作ってたやつの改造、あとGoogle Mail Checkerのソースなども参考にした。

http://gihyo.jp/dev/serial/01/chrome-extensions/0001

変更点は

  • id入力 → 表示の際にスクレイピングしてlocalStorageに保存
  • 指数表示 → Kの方が親しみやすいだろうと思ったのでKにした。10000は10K

一方的にスクリプトを適用するのは簡単で、bookmarkletGreasemonkeyスクリプト相当の拡張は簡単に作れそう。

  • 特定URLに対してスクリプト実行はcontent_scriptで可能
  • ブックマークレット相当のことはchrome.tabs.executeScriptで可能

ただし、拡張側から現在表示中のタブの中身のDOMを直接参照したりは出来ないっぽかった。

tabオブジェクトのプロパティからURLやtitleは取れるが、DOMは取れない。

http://code.google.com/chrome/extensions/tabs.html#type-Tab

これはセキュリティ上の制約と言うことだろうから

  • livedoor Readerのドメインにcontent_scriptを適用、メッセージ送信を受け取れるようにする。
  • livedoor Readerを表示していたら拡張機能がメッセージを送る、content_scriptが表示中のDOMからlivedoor idを取得して返す。
  • 拡張機能がcontent_scriptからのレスポンスを受信してlivedoor idをlocalStorageに保存

と言う方法を取った。

os0xos0x2009/12/30 07:22こんにちはー。再利用ありがとうございます。
拡張側とページ側はプロセスが分かれているので、直接アクセスすることができません。これはセキュリティ上の理由でもあり、実装上の都合でもあり。
chrome.tabs.executeScriptはcontent_script相当の処理を拡張コンテキストからプログラマブルに実行するAPIなので、ブックマークレットとはコンテキストが異なります(ページ側のコンテキストと分かれている)。
ブックマークレット(ページ側のコンテキスト)としてスクリプトを実行したい場合は、chrome.tabs.update({url:'javascript:/**/'}); のようにすると実行できます。
もう少し詳しい話はこちらに書きました http://groups.google.co.jp/group/chromium-extensions-japan/browse_frm/thread/eefc287888862afb

2009-12-16

ウェブサービスにおける正しい退会ページのあり方

14:35 | ウェブサービスにおける正しい退会ページのあり方 - 金利0無利息キャッシング – キャッシングできます を含むブックマーク はてなブックマーク - ウェブサービスにおける正しい退会ページのあり方 - 金利0無利息キャッシング – キャッシングできます

トークンの確認があるべき

  • いわゆるCSRF対策。
  • サービスにログインした状態のユーザーを退会フォームにPOSTするような罠を仕込んだページに誘導するだけで退会出来てしまうから
  • 退会ページでJavaScriptでconfirm出して「本当によろしいですか?」とか確認してもフォーム直接POSTしてしまえば意味が無い

パスワードの確認があるべき

  • もしサービスにXSS脆弱性があったら、そのユーザー自身に退会ページにアクセスさせて正しいトークンを含んだ正規のformを自動でsubmitできてしまうので
  • あるいはログインされたまま放置されたPCを誰かが勝手に操作できる状態だったら、セッションが維持されている限り本人でなくても退会できてしまう

パスワードの入力フォームがautocomplete=offであるべき

  • ユーザーがパスワードを保存していて、サービスにXSSが存在する場合、パスワードが自動入力された退会ページのsubmitボタンを押すJavaScriptを仕込むことが出来る
    • OperaはCtrl+Enter押すまで自動入力されない(DOMで参照できない)ので無理、最初から入力された状態になるブラウザが対象
  • XSSが存在しなくても、クリックジャッキングによって(パスワードが自動入力された)退会ページを開いて退会ボタンを押すことが出来る
    • JavaScriptでconfirmを出すのはこういう攻撃に対して一定の効果があるかもしれない。

退会処理が500エラーを返すべき

  • 落ち着け、よく考えてから退会しろ
  • わざわざサポート宛にメールを送って本当に退会したいと言ってくるやつだけ本当の退会ページに案内すべきだ

2009-11-19

FeedBurner「より強力なアクセス解析機能が提供されたよ、デフォルトでオンにしておいたから!」

22:22 | FeedBurner「より強力なアクセス解析機能が提供されたよ、デフォルトでオンにしておいたから!」 - 金利0無利息キャッシング – キャッシングできます を含むブックマーク はてなブックマーク - FeedBurner「より強力なアクセス解析機能が提供されたよ、デフォルトでオンにしておいたから!」 - 金利0無利息キャッシング – キャッシングできます

FeedBurnerGoogle Analyticsと連携する機能が追加されて、FeedBurnerのアクセス解析機能を使っていた人が、自動的にこのオプションを利用するように設定変更されています。

http://blog.fkoji.com/2009/11141538.html

で、特になにも影響がないなら勝手に便利な機能は勝手に有効にしてくれれば良いと思うのですが、影響があります。

http://feedproxy.google.com/~r/*****/*******/hogehoge.html

というアクセス解析用のリダイレクトURLの飛び先が

http://example.com/blog/hogehoge.html?utm_source=feedburner&utm_medium=feed&utm_campaign=...&utm_content=...

みたいに変更されています。このため

  • 余計なクエリが付いた状態で検索エンジンにインデクスされる確率が高くなる。
  • 余計なクエリが付いた状態でソーシャルブックマーク等のURL保存/共有するサービスに登録される確率が高くなる。
    • 同一の記事が別のURLになってしまうので、意見を共有しづらくなる
  • 同じ記事を表示するのにブラウザのキャッシュが効かない、既読判別が効かない、etc
  • クエリを含むURLは動的な可能性が高いのでプロキシやアプリケーション側でキャッシュを作らないように設定されている可能性がある
    • サーバー負荷や転送量が増加する
    • 動的に生成しているようなサイトだと問題になる
  • キャッシュするならするで、別コンテンツとして保存されるのでディスク領域を消費する

という現象が起こります。

こんな具合です。

http://www.google.co.jp/search?q=inurl%3Autm_source%3Dfeedburner

Googleのアナウンス

http://adsenseforfeeds.blogspot.com/2009/11/afternoon-frank-hey-howdy-george.html

コメント欄のやりとり

Chris said...

Question: does this explain why anyone who clicks through to my site via my feed hits a URL with the suffix utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A%20

etc?

Seems like a sloppy way to implement this. Any way I can get rid of that extra baggage?

November 16, 2009 8:50 PM

担当者っぽい人の返事

Steve Olechowski said...

... 前略 ...

@Chris - this post shows you how to turn it off

everyone - else - yes we hear you and are working on it!

November 17, 2009 7:52 AM

「この記事で無効化する方法を説明しているよ」と一蹴されている。多分この担当者は意味が分かってないか、あるいは意図的に問題を無視してる。

何でわざわざユーザーが操作して無効にしないといけないんだ?勝手に有効にしておいて。utm_***というのは、少なくともGoogle Analyticsを使ってない人には全く無意味なクエリで、そのような人にとってはURLが汚くなるだけで完全に無駄な機能だ。デフォルトはオフであるべきで、この機能を有効にしたい人が「デメリットを承知した上で」有効にすべきだろう。なぜこのような大きな副作用の伴う変更をユーザーの同意を得ずに勝手に行うのか疑問に思う。恐らくデフォルトオンになっているのは、Analyticsの管理画面を見ている人に新機能が登場したことに気付かせるためだろう。デフォルトオンでも全然かまわないよ、URLが汚れないなら。

クエリにトラッキング用のパラメータを含めるというのは、簡単だが、悪く言えば「手抜き」の方法だ。URLに余計なクエリを付けずに同等の機能を提供できるかと言えば、出来ると思う。なぜならFeedBurnerもAnalyticsもGoogleが運営するサービスだからだ。FeedBurnerのアクセス解析結果をGoogle Analyticsに反映させるならアカウントを紐づけてアクセス解析結果を交換するようにすればいい。精度は下がるかも知れない、が、クエリ付きの状態でコピーアンドペーストされて様々なサービスに伝搬してしまう方が、よっぽど精度を下げることになると思う。