Hatena::Groupperl

local $PERL_MEMO;

November 07, 2013

[][]Encode::Entity::NCR v0.0.2 release

3年半ぶりの更新。といっても機能的な追加はなく別のモジュールの更新のついでにドキュメントの修正とMinillizeしました。

$ cpanm https://github.com/UCormorant/p5-encode-entity-ncr/archive/0.0.2.tar.gz 

でインストールできます。cpanm についてはググしてください。

see also

March 05, 2010

[][]Encode::Entity::NCR v0.0.1 Release!

  • XML::RSSが日本語を数値実体参照で返しやがるのに適当なデコーダがHTML::Entitiesくらいしか見つからない
  • Encode.pmはエンコードできるけどデコードの仕方が分からない
  • 毎回replaceかくのだるいし出来ればEncode.pmでなんとかしてほしい

ので作った!

ダウンロード

Download Sourceってところから落とせます。

URL変わりました。

使い方

use Encode;
use Encode::Entity::NCR;
$ncr  = encode("Entity-NCR", $utf8);
$utf8 = decode("Entity-NCR", $ncr);

あとはpod見て。

Encode.pmだけでやる

use Encode qw(encode decode :fallbacks);
$dec_ncr = encode('ascii', $str, FB_HTMLCREF); # 10進数
$hex_ncr = encode('ascii', $str, FB_XMLCREF);  # 16進数
$dec_ncr =~ s/&#(\d+);/chr $1/eg;
$hex_ncr =~ s/&#x([\da-f]+);/chr hex $1/eig;
$ncr =~ s/&#(x)?([\da-f]+);/chr($1?hex $2:$2)/eig; # 両方いっぺんに

デコードがわかんないのでこれを毎回書くわけですけど、めんどい!エンコードのほうも :fallbacks とか覚えられない!

おしまい

何気にmakeするモジュール初めて作ったので楽しかった。一回うっかりして module-starter で上書きしちゃったのもいい経験。いろいろ気付いたことがあったので今度はモジュールの作り方を記事にしよう。

FrenchieFrenchie2011/06/05 15:59Walking in the presence of giants here. Cool thinking all arunod!

xfimugfxhxfimugfxh2011/06/05 18:14ZQWGoL <a href="http://wcvmdvwbmwfs.com/">wcvmdvwbmwfs</a>

DollDoll2011/06/05 21:09Good to see a tanlet at work. I can’t match that.

bhgexmbhgexm2011/06/06 23:23RcdD4s , [url=http://vrwahvyypoaq.com/]vrwahvyypoaq[/url], [link=http://weomrireqfxh.com/]weomrireqfxh[/link], http://qistkodzangp.com/

tlmchwtlmchw2011/06/07 18:359cvKCK <a href="http://ympguridusyx.com/">ympguridusyx</a>

rmctzrcrmctzrc2011/06/09 19:082WmlOV , [url=http://oteehvbukvqk.com/]oteehvbukvqk[/url], [link=http://inijznmksduz.com/]inijznmksduz[/link], http://yxxtutwvudaw.com/

MadhukarMadhukar2012/11/01 22:38Me and this article, sititng in a tree, L-E-A-R-N-I-N-G!

wkdhcgsgnwkdhcgsgn2012/11/02 10:326Xf0HG <a href="http://fxjiofvcfuhu.com/">fxjiofvcfuhu</a>

hkyalkobyhkyalkoby2012/11/02 15:03Z6VnZF , [url=http://beupvryzbvcl.com/]beupvryzbvcl[/url], [link=http://zcbiwcewmxul.com/]zcbiwcewmxul[/link], http://hqtwliwehdgm.com/

czmqhjkwnavczmqhjkwnav2012/11/04 23:427z7Obi <a href="http://drhmxswddnsd.com/">drhmxswddnsd</a>

btwkgbwmsyybtwkgbwmsyy2012/11/05 12:58b05OhP , [url=http://czszfxeuziaa.com/]czszfxeuziaa[/url], [link=http://ssnthjwfowdj.com/]ssnthjwfowdj[/link], http://ekablygensfg.com/

March 03, 2010

[][][]WWW::Mechanizeの$mech->contentと$res->decoded_contentがややこしい

LiveDoorReaderのAPIをごにょごにょする用事ができたのでAPI叩いてリダイレクトされたらWWW::Mechanizeでログインするようなコードを書いていたのですが、ちょっと詰まった。

use JSON::XS;
use WWW::Mechanize;
$mech = WWW::Mechanize->new;
$res = $mech->get('http://reader.livedoor.com/api/config/load');
$content = $res->content;
$json = json_decode($content);

上記のようなことをすると、LDR(というかライブドアのいろんなところ)だとgzip圧縮されて送られてくるので、もちろんバイナリ突っ込んでるのでjsonのパースでこける。

でもちょっとだけ書き方変えてみて

$mech->('http://reader.livedoor.com/api/config/load');
$content = $mech->content;

だと$contentはなぜかgzipが解けている。なんでなんで。$mech->contentって$res->contentとは違うの?

ということでちょっと調べてみた

gzipされたコンテンツをテキストに戻すにはHTTP::Response->decoded_contentを呼び出すのが一般的だと思うので、この現象もたぶんWWW::MechanizeのどっかでHTTP::Response->decoded_contentを呼び出してるから起こってるはず。検索。あった。

    # Try to decode the content. Undef will be returned if there&#39;s nothing to decompress.
    # See docs in HTTP::Message for details. Do we need to expose the options there?
    my $content = $res->decoded_content();
    $content = $res->content if (not defined $content);

sub _update_pageにあった。sub requestから呼び出してる。つまり、$mechのオブジェクトにcontentを記録する際に、自動でデコードしているわけだ。普段WWW::Mechanize使わないから気付かなかったけど、ブラウジングが目的のモジュールだから裏で起こってることに気を使わなくていいように設計してあるのか。

とりあえず、原因が分かってよかったです。

まとめると

  • $mech->content
    • レスポンスヘッダを呼んで自動でデコードする。デコードしないとかいう選択肢はない。とにかくする。するんだ。
  • $res->content
    • レスポンスヘッダから下をそのまま返す。かってにいじらない。すべての入力をすべて自動でチェックするというのはちょっとめんどいのでいっそのこと全部手動で確認したい派の僕はこっちのほうがありがたい。
  • $mech->decoded_content
    • 存在しない。
  • $res->decoded_content
    • レスポンスヘッダから推測してよきに計らってくれる。明示的なので書き手のわかっててやってるというのが感じられてベネ。

わかりにくい

わかりにくいですね。わかりにくいです。

WWW::Mechanizeの目的を考えると間違いなく自動でやってくれたほうがいいのですが

$mech->get();
$content = $meck->content;

$content = $mech->get()->content;

で挙動が、というか混乱するからHTTP::Responseオブジェクト返すなよ。

とにかく僕は json_decode したいので勝手にperlのutfフラグ付文字列に変換してもらっては困るし、かといってgzip解凍するのはdecoded_contentが一番楽だ。ああもうもう一回utf8でエンコードすればいいんだろともうなげやりです。なやましい。ねむい。

結論

use utf8;
use strict;
use warnings;
use WWW::Mechanize;
{ no warnings; *WWW::Mechanize::decoded_content = *WWW::Mechanize::content; }

として、レスポンスからコンテンツを取得するときは全部decoded_contentを通した!これで好きなように書いてもデコードされてたりされてなかったりというふらふらした問題は起きん!解決!

LettieLettie2011/06/05 14:23Posts like this brighten up my day. Thanks for taknig the time.

toainvnpdcytoainvnpdcy2011/06/05 18:206B3Hl7 <a href="http://agwauffemhzn.com/">agwauffemhzn</a>

xcyzionpkvixcyzionpkvi2011/06/06 23:09oUloUp , [url=http://octxbchmojcx.com/]octxbchmojcx[/url], [link=http://jbzxirbzkajz.com/]jbzxirbzkajz[/link], http://bgexnmjazfie.com/

mpzzhifsmpzzhifs2011/06/07 17:27uA8xKB <a href="http://btmmykeouaqr.com/">btmmykeouaqr</a>

vliskoyovliskoyo2011/06/09 19:21WgzPtD , [url=http://wqshikjjkaxs.com/]wqshikjjkaxs[/url], [link=http://iwotqequcate.com/]iwotqequcate[/link], http://dqhuyqdnvxvq.com/

April 29, 2009

[][][][]User-Agentとencoded-wordについて (Encode.pmmime encodingする)

User Agentを変更してオリジナリティ溢れるサーバログを撒き散らしたいなーと思い、User Agent Switcherアドオンを導入したり、日本語をUAに使うにはどうしたらいいかを調べているうちに、

  • UAにはascii以外と一部の記号は使えない
  • 使いたいときはRFC2047(原文)で定められている方法、つまりencoded-wordに変換する

ということがわかりました。そういうわけで、今日はmime encodingについてのメモ。

User Agentとは

ユーザーエージェントとは、閲覧者が使っているソフトやハードのこと。ここでは特に、使用している言語、ウェブブラウザ、OS、ディスプレイなどの閲覧環境の情報を指します。

HTTPリクエストの際にブラウザがUser-Agentヘッダをサーバに送信することで、サーバあるいはコンテンツ作成者は閲覧者がどのような環境でウェブサイトを閲覧しているか知ることができ、その情報を元に、閲覧者の環境に合わせたより良いコンテンツを提供できるようになることが期待できます。

User-Agentヘッダの書式

RFC2616によれば以下のような感じ。

User-Agent      = "User-Agent" ":" 1*( product | comment )
 
product         = token ["/" product-version]
product-version = token
token           = 1*<any CHAR except CTLs or separators>
separators      = "(" | ")" | "<" | ">" | "@"
                | "," | ";" | ":" | "\" | <">
                | "/" | "[" | "]" | "?" | "="
                | "{" | "}" | SP | HT

CHAR            = <any US-ASCII character (octets 0 - 127)>
 
comment         = "(" *( ctext | quoted-pair | comment ) ")"
ctext           = <any TEXT excluding "(" and ")">
TEXT            = <any OCTET except CTLs, but including LWS>
OCTET           = <any 8-bit sequence of data>
CTL             = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
LWS             = [CRLF] 1*( SP | HT )
Hypertext Transfer Protocol -- HTTP/1.1 - User-Agent

(まとめるのがめんどくさくてコピペしてしまった…)

簡単に言えば、User-Agent: (product | comment)+ですね。productはhoge/fuga、commentは任意の文字、そして文字列はUS-ASCIIのみという感じ。なのでUser-AgentにはISO_8859_1以外の文字コードは出現出来ないことになってるんですが、Words of *TEXT MAY contain characters from character sets other than ISO-8859-1 [22] only when encoded according to the rules of RFC 2047 [14].*とあるように、RFC 2047に書かれている方法でエンコードすればコメントには他の文字コードも使えますよ、ということらしい。そして、そのほかの方法というのが、encoded-wordというわけだ。

じゃあencoded-wordって?

使用の都合で、MIME-Headerに使用できる文字(記号)が非常に少なくなってしまった。それを解消するために、使用出来ない文字列をエンコードすることでMIME-Headerに埋め込めるようにしよう、ということで出来た仕様がencoded-word...なのかな?

これは他のエンコード技術も混ざってくるので簡潔に書くと、

encoded-word = "=?" charset "?" encoding "?" encoded-text "?="

となります。例えば、=?iso-8859-1?q?this=20is=20some=20text?=など。文字コードセット名と変換方法と変換後のテキストのセットになっています。

このencoded-wordならUser-Agentヘッダのコメント部分に書いてもいいよ、と定められているみたい。User-Agentヘッダで日本語を使うには、このencoded-wordに変換しなくてはいけないのですね。

Perlでencoded-wordを作るには

やっと本題です。

このencoded-wordは、RFC2047のタイトルにもあるように、MIME(Multipurpose Internet Mail Extensions)のメッセージヘッダ拡張のための仕様なので、MIMEヘッダあたりのモジュールで何とかしなくてはいけないのかと思ったのですが、うれしいことに標準モジュールのEncode.pmMIME-Headerの変換に対応しています。

use Encode;

$encoded = encode('MIME-Header', decode('utf8', 'ほげほげ'));
# $encoded => '=?UTF-8?B?44G744GS44G744GS?='

$utf8 = decode('MIME-Header', $encoded);
# $utf8 => 'ほげほげ'

以上のように非常に簡単に変換が出来ました。弾さんすばらしい! dankogai++

v5.008_008以降にはEncode::MIME::Header::ISO_2022_JPが含まれているので、文字コードにISO_2022_JPを指定してエンコードすることも出来るようです。UTF-8を使用出来ない環境ではencode('MIME-Header-ISO_2022_JP')を使用するといいのかな。

$encoded = encode('MIME-Header-ISO_2022_JP', decode('utf8', 'ほげほげ'));
# $encoded => '=?ISO-2022-JP?B?GyRCJFskMiRbJDIbKEI=?='

$utf8 = decode('MIME-Header', $encoded);
# $utf8 => 'ほげほげ'

使用上の注意

ソースを読んだ感じではちょっと癖がありそうなので注意点をまとめてみます。

  • encode('MIME-Header')には通常のencodeと同じく内部Unicode文字列で渡してあげる。
  • decode('MIME-Header')はencoded-word以外は文字コードの変換をしない。よって通常の文字列とencoded-wordが混在するテキストは、まず最初にdecode('utf8')を行ってからdecode('MIME-Header')に渡すといい(encoded-wordASCIIのみで構成されているのでこの順序ならエラーは発生しない)。
  • encode('MIME-Header-ISO_2022_JP')は空白文字の連続を一つの空白に置き換えてしまうと思う。空白を纏められたくないときは s/\s+$//o に引っかからないようにうまく変換してから突っ込まないといけないっぽい。

おわり

mime encodingが出来るようになった!

(=?ISO-2022-JP?B?GyRCS00kLDgrJEYkayRoITwbKEI=?=)をUAに追加したのでどこかで見かけたらアレアレアレしたりするといいです。CardCaptor/1.0 (さくら怪獣じゃないもんブラウザ)のほうがよかったかな。これを機にみなさんもガンガンUAを偽装したり解析ツールのUAの項目でmime decodeするようにしたりすればいい。

最後に。User-Agentヘッダはコメントに括弧以外のASCII使えるから、標準にこだわらなければ日本語部分は文字実体参照でいいんじゃないかな。

cf.

JaylanJaylan2011/06/05 22:06Your awsenr was just what I needed. It’s made my day!

uncggzquncggzq2011/06/06 18:07VkzW2b <a href="http://bazssydhulju.com/">bazssydhulju</a>

rnotiknmernotiknme2011/06/06 22:516W6jyg , [url=http://vjlrqqsuarfu.com/]vjlrqqsuarfu[/url], [link=http://oeohkpmelmrv.com/]oeohkpmelmrv[/link], http://lalaqariytii.com/

veouwlvxzveouwlvxz2011/06/09 00:02epmGVP <a href="http://apsbpliumimg.com/">apsbpliumimg</a>

KatjaKatja2012/11/03 13:50I read your post and wsehid I'd written it

pgamkipgamki2012/11/04 05:54QFUHkU <a href="http://tirvpgeshoxg.com/">tirvpgeshoxg</a>

ncglcpncglcp2012/11/05 14:048Nqo20 , [url=http://vlordrxnqozv.com/]vlordrxnqozv[/url], [link=http://wrjynzgkehnl.com/]wrjynzgkehnl[/link], http://twifqduguylk.com/

kyjifrkyjifr2012/11/07 22:297Ok1SN <a href="http://zibsbcepneqq.com/">zibsbcepneqq</a>