Author: anis774
MetroスタイルアプリでUser-Agentを指定しつつクッキーも使ってHTTP通信したい
※この記事はWindows8RPについて書いていますがわかりません。当然今後のことも本当のところも分かりません。
User-Agent指定してダウンロードするコード
using System.Net.Http;
using System.Threading.Tasks;
static class Connection
{
public static async Task<string> Get(string url)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("User-Agent", "Hogezilla");
return await client.GetStringAsync(url);
}
}
}
とっても簡単です。でもこれクッキー使えないんですよね。
そこで、クッキー使えそうなコード。
using System.IO;
using System.Net;
using System.Text;
using System.Threading.Tasks;
static class Connection
{
static CookieContainer cookies = new CookieContainer();
static Encoding enc = Encoding.GetEncoding("Shift-Jis");
public static async Task<string> Get(string url)
{
HttpWebRequest req = HttpWebRequest.CreateHttp(url);
req.CookieContainer = cookies;
req.UserAgent = "Hogezilla"; //←そんなプロパティねーよって怒られる
req.Headers.Add("User-Agent", "Hogezilla"); //←そんなメソッドねーよって怒られる
using (WebResponse res = await req.GetResponseAsync())
using(Stream resStream = res.GetResponseStream())
{
byte[] resBytes = new byte[res.ContentLength];
await resStream.ReadAsync(resBytes, 0, resBytes.Length);
return enc.GetString(resBytes, 0, resBytes.Length);
}
}
}
確かにここを見るとUser-AgentヘッダーはHeadersプロパティから変えられないからUserAgentプロパティから変更しろって書いてるんだけどなー。
WinRTの時だけ違う方法があるのだろうか。かれこれ数日間悩んでるんで、上手く書けたらアウトプットしますよ。
ってことで数日悩んでやっと出来ました。HttpClientHandlerクラスが鍵でしたね。
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
static class Connection
{
static CookieContainer cookies = new CookieContainer();
public static async Task<string> Get2(string url)
{
using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
handler.CookieContainer = cookies;
client.DefaultRequestHeaders.Add("User-Agent", "Hogezilla");
return await client.GetStringAsync(url);
}
}
}
VirtualBoxにWidows8 Cousumer Preview をインストールしてskkimeの設定と辞書を移行した
例に漏れずにWin8CP入れたんです。ホストOSがWin7の32bitで、ゲストにWin8CP64bit入れたら激重だったんで、ゲストは32bitにした。あとVM作成時に聞かれる仮想HDDの種類も速いって書いてあるFixedにした。他はまぁググったら出てくるような手順でインストール完了。これで何とか使えるかなってレベル。ちなみにホスト環境はAthlonX2の2.11GHzでメモリ3G。
最初に躓いたのが、”メトロのアプリが起動しねぇ…!”。Storeでアプリの更新して、WindowsUpdateしたら起動するようになった。どっちがキッカケかはわかんない。ググっても画面の解像度が云々ってのばっかり出てくるから、それ試しても駄目だった人がこの記事に辿り着きますように。
1920*1080のモニタ使ってるんだけど、Win8が画面一杯にならないし、やっぱりまだ遅い。VirtualBox Guest Additonsってのを入れるといいらしい。これに含まれてるDirect3D Supportってコンポーネントがホストマシンのグラボを直接制御してパフォーマンスが上がるらしい。しかし、ゲストのWin8がセーフモードじゃないとDirect3D Supportのインストールに失敗する。
Win8CPをセーフモードで起動しようにも、起動時にF8連打しても何も起こらない。ググったらやり方見付かった。Win8のスタート画面でそのまま”msconfig”ってタイプすると自動的に検索になる。これは便利。これでMSCONFIGを実行するとセーフモードで起動するオプションがあった。これでセーフモードにしてVirtualBox Guest Additonsのインストールが無事終了。これでやっと実用的なパフォーマンスが出るようになった。
次にskkimeの移行。僕は1.5系を使ってる。このインストールは問題なく成功。問題は設定画面の出し方。コントロールパネルを漁ってもskkimeの設定画面の場所がわからない。HDD内をskkで検索したら見付かった。”C:\Windows\IME\SKKIM15\skimconf.exe”にあった。これを実行するとskkimeの設定画面が開かれる。まぁ、結果的にはこの画面を操作することはなかったのだけれど。
まずホストOSに入ってる辞書ファイルをWin8に配置。続いて設定はレジストリに書かれてるのでregeditで”HKEY_CURRENT_USER\Software\TT\Windows\CurrentVersion\SKKIME”をエクスポート。これをテキストエディタで修正してWin8上でダブルクリックすれば設定移行は完了。修正箇所は”HKEY_CURRENT_USER\Software\TT\Windows\CurrentVersion\SKKIME\1.5\Dictionaries”にある辞書ファイルのパス。これをWin8での辞書ファイルの配置場所に合わせるだけで上手くいった。
これでめでたしめでたしとなるかと思ったら問題が見付かった。メトロアプリでskkimeが使えない!Windows+Spaceを押してIMEの選択画面出してもskkimeがグレーアウトしてて選べない。これは大問題である。解決策求む。かしこ。
IronPythonで配列を引数に取るメソッドを使う
リストを渡してもエラーになる。タプルを渡すと配列として認識される。そんなときは組み込みのtuple関数を使うといい。
>>> tuple([1,2,3]) (1, 2, 3)
.NETのコレクションでも同じ方法が使える
>>> tuple(List[Int32]([1,2,3])) (1, 2, 3)
IronPythonでインターフェースのイベントを実装する
Pythonにはイベントを定義する構文がないから困った。
そんなときはadd_イベント名(self, handler)、remove_イベント名(self, handler) を実装する。
例えば、INotifyPropertyChangedインターフェースのPropertyChangedイベントだとこう。
class NotifyObject(INotifyPropertyChanged): def add_PropertyChanged(self, handler): if not hasattr(self, "_propertyChangedHandlers"): self._propertyChangedHandlers = [handler] else: self._propertyChangedHandlers.append(handler) def remove_PropertyChanged(self, handler): if not hasattr(self, "_propertyChangedHandlers"): self._propertyChangedHandlers = [] else: try: self._propertyChangedHandlers.remove(handler) except ValueError: pass def RaisePropertyChanged(self, propertyName): if not hasattr(self, "_propertyChangedHandlers"): self._propertyChangedHandlers = [] else: for handler in self._propertyChangedHandlers: handler(self, PropertyChangedEventArgs(propertyName))
こんな感じでイベントハンドラの管理も自分でやる。
ハリオ カフェプレス・スリム
ハリオ カフェプレス・スリム ブラック CPS-2TBを買った。味がブレにくい抽出器具が欲しくて、初めはサイフォンを検討していたのだけど、安くてペーパードリップでもサイフォンでも得られない味わいを体験させてくれそうなフレンチプレスを購入。
書籍とかネットとかで見た抽出方法を色々試してみたんだけど、なんだかんだ言って外箱に書いてあるハリオ公式(?)の淹れ方、粗挽き、熱湯、2分抽出が美味しかった。
これは手軽に美味しいコーヒーが飲める良いものです。
ハリオのサーバーとドリッパーを購入
今までメーカー不詳のサーバーを使っていたのだけど、目盛が二杯目からしか入ってなくて使いにくかった。そこでハリオ V60コーヒーサーバー 700 VCS-02Bを買ってきた。早速試そうと思ったら問題発覚。カリタの101ドリッパー(1~2杯用のね)が嵌らない!(2~4杯用のは問題ない)
しかしガッカリすることは何もない。だって、これを買うときに、同じ棚にあったハリオ V60透過ドリッパー クリア VD-01Tも一緒に衝動買いしちゃってたんですもの。
早速使ってみましたよ。カリタは注ぐ湯量が多いと豆が湯に完全に漬かってしまって雑味が出てしまうのだけど、ハリオのは注いだ湯がそのまま落ちて、結果抽出時間が短くなってスッキリした味になる。その分、ハリオの方が抽出時間が短かい方向への自由度は高いと感じた。カリタは湯が落ちる速度に合わせて湯を注ぐことになるから味が安定する。そのかわり失敗すると凄く不味い。ハリオは自由度が高い分、狙った味から外れることはあっても失敗は少ない。
その味なんだけど、最初の一口目では角が丸くてスッキリした印象を持った。ただ、少し冷めてくると、甘味が良く出てるのに気付いた。”ネルドリップの味わい”って宣伝文句はこれのことなんだなと理解。渋みやエグ味は出にくい。
これ、お勧めですよ。どの程度まで味に幅を持たせられるのか、これから使い込んでいくのが楽しみです。
TwitterizerでUserStreamを利用する
TwitterのAPIライブラリ、Twitterizer Ver 2.3.1を使ってUserStreamを利用しようと以下のコードを書いたんです。
using System;
using System.Net;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string consumerKey = "hoge";
string consumerSecret = "hogehoge";
Twitterizer.OAuthTokenResponse reqToken = Twitterizer.OAuthUtility.GetRequestToken(consumerKey, consumerSecret, "oob");
Uri uri = Twitterizer.OAuthUtility.BuildAuthorizationUri(reqToken.Token);
System.Diagnostics.Process.Start(uri.ToString());
Console.WriteLine("Pinコードを入力しる");
string pinCode = Console.ReadLine();
Twitterizer.OAuthTokenResponse accToken = Twitterizer.OAuthUtility.GetAccessToken(consumerKey, consumerSecret, reqToken.Token, pinCode);
Twitterizer.OAuthTokens token = new Twitterizer.OAuthTokens
{
ConsumerKey = consumerKey,
ConsumerSecret = consumerSecret,
AccessToken = accToken.Token,
AccessTokenSecret = accToken.TokenSecret
};
using (var twitterStream = new Twitterizer.Streaming.TwitterStream(token))
{
twitterStream.OnStatusReceived += (Twitterizer.TwitterStatus status) =>
{
Console.WriteLine(string.Format("{0}/({1})\r\n\t{2}",
status.User.Name,
status.User.ScreenName,
status.Text));
};
twitterStream.StartUserStream();
while (true) { }
}
}
}
}
何度試しても404が返ってくる。実はこれ、ベータ版のアドレスにアクセスしてるんですよ。これがソースコード
がっつりとハードコーディングされちゃっててどうにもなりません。自家ビルドして使う?嫌ですよ、そんなの。面倒じゃないですか。幸い、このStartUserStreamメソッドは短いです。じゃあStartUserStreamメソッドを呼び出してる部分のコードをURLだけ置き換えた同じ内容のコードに書き換えればいいじゃない!
以下の3行までは問題ないです。
WebRequestBuilder builder = new WebRequestBuilder(new Uri("http://betastream.twitter.com/2b/user.json"), HTTPVerb.GET, this.Tokens);
HttpWebRequest request = builder.PrepareRequest();
request.KeepAlive = true;
URLを置き換えるだけです。WebRequestBuilderクラスはpublicなので問題なく利用出来ます。引数に渡しているthis.Tokensはコンストラクタに渡したインスタンスそのものなので、こちらのスコープで保持しているものに置き換えればいいです。
問題は最後の行ですね
request.BeginGetResponse(StreamCallback, request);
引数に渡しているStreamCallbackっての、これprivateなメソッドなんですよ。これはリフレクションで取ってくることにしました。
MethodInfo method = twitterStream.GetType().GetMethod("StreamCallback",
BindingFlags.Instance |
BindingFlags.NonPublic);
AsyncCallback callback = (_) =>
{
method.Invoke(twitterStream, new[] { _ });
};
リフレクションで取ってきたメソッドをInvokeするコードをAsyncCallbackデリゲートで包んであげればOKじゃんって寸法です。
で、最終的なコードがこんな感じ
using System;
using System.Net;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string consumerKey = "hoge";
string consumerSecret = "hogehoge";
Twitterizer.OAuthTokenResponse reqToken = Twitterizer.OAuthUtility.GetRequestToken(consumerKey, consumerSecret, "oob");
Uri uri = Twitterizer.OAuthUtility.BuildAuthorizationUri(reqToken.Token);
System.Diagnostics.Process.Start(uri.ToString());
Console.WriteLine("Pinコードを入力しる");
string pinCode = Console.ReadLine();
Twitterizer.OAuthTokenResponse accToken =Twitterizer.OAuthUtility.GetAccessToken(consumerKey, consumerSecret, reqToken.Token, pinCode);
Twitterizer.OAuthTokens token = new Twitterizer.OAuthTokens
{
ConsumerKey = consumerKey,
ConsumerSecret = consumerSecret,
AccessToken = accToken.Token,
AccessTokenSecret = accToken.TokenSecret
};
using (var twitterStream = new Twitterizer.Streaming.TwitterStream(token))
{
twitterStream.OnStatusReceived += (Twitterizer.TwitterStatus status) =>
{
Console.WriteLine(string.Format("{0}/({1})\r\n\t{2}",
status.User.Name,
status.User.ScreenName,
status.Text));
};
//twitterStream.StartUserStream();
//StartUserStreamメソッドがベータ版のURLに繋いでエラーになるから
//下記ソースの動作をURLだけ変えて実行
//http://pm.twitterizer.net/projects/twitterizer/repository/entry/tags/twitterizer-2.3.1/Twitterizer2.Streaming/TwitterStream.cs#L138
var builder = new Twitterizer.WebRequestBuilder(new Uri("https://userstream.twitter.com/2/user.json"), Twitterizer.HTTPVerb.GET, twitterStream.Tokens);
HttpWebRequest request = builder.PrepareRequest();
request.KeepAlive = true;
MethodInfo method = twitterStream.GetType().GetMethod("StreamCallback",
BindingFlags.Instance |
BindingFlags.NonPublic);
AsyncCallback callback = (_) =>
{
method.Invoke(twitterStream, new[] { _ });
};
request.BeginGetResponse(callback, request);
while (true) { }
}
}
}
}
これでめでたくUserStreamが使えるようになりました。ちなみにtrunkの最新のソースコードでもURLはベータ版のものがベタ書きのままでした。中の人忙しいのかな…。
IronPythonでメソッドをオーバーライドして基底クラスに投げる。
IronPythonに限ったことじゃないけど、IronPythonでWinforms触ってたら避けられない問題なので。組込み関数のsuperを使う。
import clr
clr.AddReference("system.windows.forms")
from System.Windows.Forms import *
class HogeForm(Form):
WM_LBUTTONDBLCLK = 0x0203
count = 0
def WndProc(self, m):
if m.Msg == self.WM_LBUTTONDBLCLK:
self.Text = self.count.ToString()
self.count += 1
super(HogeForm, self).WndProc(m)
if __name__ == "__main__":
Application.Run(HogeForm())
Form.WndProc(self, m)
でも動くけど推奨されていないみたい。
IronPythonで参照渡しが必要なメソッドを利用する
>>> import clr
>>> from System import *
>>> ref = clr.Reference[Int32](0)
>>> hoge = Int32.TryParse("123", ref)
>>> hoge
True
>>> ref.Value
123
>>>
>>> fuga, piyo = Int32.TryParse("123")
>>> fuga
True
>>> piyo
123





