Twitter Streaming APIを使う 1 †
恥ずかしながら、私はツイッターをやっていません。しかしTwitter API Wikiを見ていると「Streaming API」というものが目に入り、読んでみるとなかなか面白そうなので、早速試してみることにしました。今回はTwitter Streaming APIについて書かせていただきます。
私はツイッターの知識がほとんどないにもかかわらずこのような記事を書いていますので、ツイッター機能や用語に関してかなり間抜けなことを書いているかもしれないことをお許しください。
なおTwitter APIを使用する場合は、必ずご自分でドキュメントに目を通し、利用規約等をご確認ください。
Twitter Streaming APIとは? †
ツイッターに投稿された発言(ステータス)をいち早くチェックしたい場合、通常のWebサービスを使用すると、定期的にサーバーに接続し、データを送受信する必要があります。しかしStreaming APIを使用した場合は、一度サーバーに接続すれば、必要なときに勝手にサーバーがデータを送ってきてくれます。そのため、Streaming APIを使用すれば、通常のWebサービスを使用する時よりも、よりリアルタイムに近い素早さで、さらにより少ないデータのやり取りで、情報を得ることが可能になります。
とりあえずやってみる †
Twitter Streaming APIの使い方は非常に簡単で、HTTPで通常通りに接続するだけです。Twitterのサーバーに一度接続すると、基本的にはサーバーから切断することはなく*1、繋ぎっぱなしになります。そして、適当なタイミングでデータが送られてきます。
よって、「ファイルをダウンロードし表示する」や「WebRequest、WebResponseクラスを使ってファイルをダウンロードし表示する」で紹介している方法が使えます。ただしこれらの例ではReadToEndメソッドを使って読み込んでいますが、サーバーはデータを送り続けていますので、このままではダメです。少しずつデータを読み込む必要があります。
まずは、これ以上ないという位簡単な例を示します。このコンソールアプリケーションでは、"http://stream.twitter.com/1/statuses/sample.json"に接続し、送られてくるデータをStreamReader.ReadLineメソッドで読み込み、コンソールに出力しています。よって表示されるデータは、ほぼ生のデータです。ここではとりあえず10秒間だけデータを受信し、その後接続を閉じています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
| | Imports System.IO
Imports System.Net
Module TwitterStreamingApiTest
Sub Main()
Dim req As HttpWebRequest = DirectCast( _
WebRequest.Create("http://stream.twitter.com/1/statuses/sample.json"), _
HttpWebRequest)
Dim nc As New NetworkCredential("username", "password")
req.Credentials = nc
Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
Dim st As Stream = res.GetResponseStream()
Dim sr As New StreamReader(st)
Dim startTime As DateTime = DateTime.Now
While (DateTime.Now - startTime).TotalSeconds < 10
Console.WriteLine(sr.ReadLine())
End While
req.Abort()
res.Close()
sr.Close()
Console.WriteLine("----- 終了しました! -----")
Console.ReadLine()
End Sub
End Module
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| | using System;
using System.IO;
using System.Net;
class TwitterStreamingApiTest
{
static void Main()
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
"http://stream.twitter.com/1/statuses/sample.json");
NetworkCredential nc = new NetworkCredential("username", "password");
req.Credentials = nc;
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Stream st = res.GetResponseStream();
StreamReader sr = new StreamReader(st);
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalSeconds < 10)
{
Console.WriteLine(sr.ReadLine());
}
req.Abort();
res.Close();
sr.Close();
Console.WriteLine("----- 終了しました! -----");
Console.ReadLine();
}
}
|
このコードを実行すると、止めどなく、ものすごい勢いで文字が流れていきます。ただし、ASCII以外の文字はエンコードされていますので、何が書いてあるかはほとんど分かりません。
5つのメソッド †
上記で使用した"http://stream.twitter.com/1/statuses/sample.json"は、statuses/sampleメソッドを使用するためのURLです。Streaming API Documentationによると、用意されているメソッドは5つあります。しかしそのうち3つ(statuses/firehose、statuses/links、statuses/retweet)は申請が必要なため、普通の人はstatuses/sampleとstatuses/filterしか使えないようです。
statuses/sampleは、すべてのパブリックなステータスの中から幾つかをランダムで返します。statuses/filterは、パブリックなステータスの内、指定されたtrack(キーワード)、follow(投稿者)、locations(位置)にマッチしたものを返します。statuses/filterの使い方は、後述します。
データを解析する †
Twitter Streaming APIは、JSONとXMLの2つのフォーマットをサポートしています。Streaming APIのドキュメントによると、よりコンパクトであるJSONの使用を強く勧めると書かれています。しかし.NETではXMLの方が扱いが楽なので、ここではXMLを使うことにします。
Streaming APIは、1つのステータスを表すオブジェクトをJSONまたはXML形式でエンコードした文字列を連続して送信します。1つ1つのステータスオブジェクトは、キャリッジリターン(\r)で区切られています。JSONの場合は、ステータスオブジェクト内にキャリッジリターンが入ることがないため、キャリッジリターンで分割すれば1つのステータスを簡単に切り出すことができます*2。しかしXMLではステータスオブジェクト内にキャリッジリターンもラインフィードも入る可能性があるため、1つのステータスオブジェクトを切り出すのはより難しくなります。
ここでは、受信した文字列をとりあえずバッファに保存しておき、正規表現を使用して1つのステータスオブジェクトを受信できたかをチェックする方法で、切り出すことにします。
1つのステータスオブジェクトとして切り出した文字列は、XmlDocumentクラスのLoadXmlメソッドで読み込み、XMLの解析はXmlDocumentに任せてしまいます。
なお、送られてくるオブジェクトはステータスだけではありません。deleteやlimitといったオブジェクトも送られてきます。deleteは、前に送られてきた(あるいはこれから送られてくる)ステータスを削除をする必要がある時に送られてきます。limitは、Trackストリーム(filterメソッドのtrackのこと?)で送られることがあり、送られなかったステータスの数を示すようです。将来、これら以外のオブジェクトが加わる可能性があるため、柔軟に対応しなければいけないとされています。
また、オブジェクトとオブジェクトの間には空白の改行が送られてくることがありますが、これは接続を維持するためのものなので、無視します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
| | Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Xml
Module TwitterStreamingApiTest
Sub Main()
Dim xmlReg As New Regex( _
"<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>", _
RegexOptions.Singleline Or RegexOptions.Compiled)
Dim req As HttpWebRequest = DirectCast( _
WebRequest.Create("http://stream.twitter.com/1/statuses/sample.xml"), _
HttpWebRequest)
Dim nc As New NetworkCredential("username", "password")
req.Credentials = nc
Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
Dim st As Stream = res.GetResponseStream()
Dim sr As New StreamReader(st)
Dim buffer As New StringBuilder()
Dim startTime As DateTime = DateTime.Now
While (DateTime.Now - startTime).TotalSeconds < 10
Dim line As String = sr.ReadLine()
If line Is Nothing Then
Exit While
End If
If line.Length = 0 Then
Continue While
End If
buffer.AppendLine(line)
Dim m As Match = xmlReg.Match(buffer.ToString())
If m.Success Then
Dim xmlDoc As New XmlDocument()
xmlDoc.LoadXml(m.Value)
Dim rootElm As XmlElement = xmlDoc.DocumentElement
If rootElm.Name = "status" Then
Console.WriteLine("[{1}] {0}", _
rootElm("text").InnerText, rootElm("user")("name").InnerText)
ElseIf rootElm.Name = "delete" Then
Console.WriteLine("[DELETE] {0}", _
rootElm("status")("id").InnerText)
Else
Console.WriteLine("[?] {0}", rootElm.Name)
End If
buffer.Length = 0
End If
End While
req.Abort()
res.Close()
sr.Close()
Console.WriteLine("----- 終了しました! -----")
Console.ReadLine()
End Sub
End Module
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
| | using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
class TwitterStreamingApiTest
{
static void Main()
{
Regex xmlReg = new Regex(
@"<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>",
RegexOptions.Singleline | RegexOptions.Compiled);
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
"http://stream.twitter.com/1/statuses/sample.xml");
NetworkCredential nc = new NetworkCredential("username", "password");
req.Credentials = nc;
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Stream st = res.GetResponseStream();
StreamReader sr = new StreamReader(st);
StringBuilder buffer = new StringBuilder();
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalSeconds < 10)
{
string line = sr.ReadLine();
if (line == null)
break;
if (line.Length == 0)
continue;
buffer.AppendLine(line);
Match m = xmlReg.Match(buffer.ToString());
if (m.Success)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(m.Value);
XmlElement rootElm = xmlDoc.DocumentElement;
if (rootElm.Name == "status")
{
Console.WriteLine("[{1}] {0}",
rootElm["text"].InnerText, rootElm["user"]["name"].InnerText);
}
else if (rootElm.Name == "delete")
{
Console.WriteLine("[DELETE] {0}", rootElm["status"]["id"].InnerText);
}
else
{
Console.WriteLine("[?] {0}", rootElm.Name);
}
buffer.Length = 0;
}
}
req.Abort();
res.Close();
sr.Close();
Console.WriteLine("----- 終了しました! -----");
Console.ReadLine();
}
}
|
delimitedパラメータを使う †
上の例では正規表現を使ってオブジェクトを切り出しましたが、delimitedパラメータを使うという方法もあります。例えばURLを"http://stream.twitter.com/1/statuses/sample.xml?delimited=length"にすると、まずはじめに数字が一行送られてきて、その後数字のバイト数だけオブジェクトが送られてきます。よって、まず数字を受信して、その後その数字分データを受信すれば、きっちり1つ分のオブジェクトを受信できます。
以下にdelimitedパラメータを使った例を示します。ここでは、返される文字列がすべてASCIIであり、バイト数と文字数が同じであると決めつけることで、コードを簡単にしています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| | Imports System.IO
Imports System.Net
Imports System.Xml
Module TwitterStreamingApiTest
Sub Main()
Dim req As HttpWebRequest = DirectCast(WebRequest.Create( _
"http://stream.twitter.com/1/statuses/sample.xml?delimited=length"), _
HttpWebRequest)
Dim nc As New NetworkCredential("username", "password")
req.Credentials = nc
Dim res As HttpWebResponse = DirectCast(req.GetResponse(), HttpWebResponse)
Dim st As Stream = res.GetResponseStream()
Dim sr As New StreamReader(st)
Dim startTime As DateTime = DateTime.Now
While (DateTime.Now - startTime).TotalSeconds < 10
Dim line As String = sr.ReadLine()
If line Is Nothing Then
Exit While
End If
If line.Length = 0 Then
Continue While
End If
Dim xmlLen As Integer
If Integer.TryParse(line, xmlLen) Then
Dim xmlChars As Char() = New Char(xmlLen - 1) {}
Dim len As Integer = sr.ReadBlock(xmlChars, 0, xmlChars.Length)
Dim xmlDoc As New XmlDocument()
xmlDoc.LoadXml(New String(xmlChars))
Dim rootElm As XmlElement = xmlDoc.DocumentElement
If rootElm.Name = "status" Then
Console.WriteLine("[{1}] {0}", _
rootElm("text").InnerText, rootElm("user")("name").InnerText)
ElseIf rootElm.Name = "delete" Then
Console.WriteLine("[DELETE] {0}", _
rootElm("status")("id").InnerText)
End If
End If
End While
req.Abort()
res.Close()
sr.Close()
Console.WriteLine("----- 終了しました! -----")
Console.ReadLine()
End Sub
End Module
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
| | using System;
using System.IO;
using System.Net;
using System.Xml;
class TwitterStreamingApiTest
{
static void Main()
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(
"http://stream.twitter.com/1/statuses/sample.xml?delimited=length");
NetworkCredential nc = new NetworkCredential("username", "password");
req.Credentials = nc;
HttpWebResponse res = (HttpWebResponse)req.GetResponse();
Stream st = res.GetResponseStream();
StreamReader sr = new StreamReader(st);
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalSeconds < 10)
{
string line = sr.ReadLine();
if (line == null)
break;
if (line.Length == 0)
continue;
int xmlLen;
if (int.TryParse(line, out xmlLen))
{
char[] xmlChars = new char[xmlLen];
int len = sr.ReadBlock(xmlChars, 0, xmlChars.Length);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(new string(xmlChars));
XmlElement rootElm = xmlDoc.DocumentElement;
if (rootElm.Name == "status")
{
Console.WriteLine("[{1}] {0}",
rootElm["text"].InnerText, rootElm["user"]["name"].InnerText);
}
else if (rootElm.Name == "delete")
{
Console.WriteLine("[DELETE] {0}", rootElm["status"]["id"].InnerText);
}
}
}
req.Abort();
res.Close();
sr.Close();
Console.WriteLine("----- 終了しました! -----");
Console.ReadLine();
}
}
|
ステータスオブジェクトのフィールドと意味 †
上の例ではとりあえずtext(ステータスの本文)とユーザー名のみを表示しましたが、これ以外にもステータスオブジェクトには多くの情報が含まれています。
ステータスオブジェクトのフィールドとしてどのような名前のものが返される可能性があるのかについて、正直なところ、Twitterのドキュメントのどこに記述があるのか分かりませんでした。ただその意味については、「Twitter API Wiki / Return Values」に説明があります。しかしここで紹介されていないフィールドも返されます。
これらについて、「Twitter API Wiki / Return Values」の説明と、私が試してみて多分こうではと想像した結果を、以下にまとめます。
statusオブジェクト †
名前 | 説明 | 例*3 | 型*4 | created_at | ステータスが作成された日時(UTC) | Sat Jan 24 22:14:29 +0000 2009 | DateTime | id | ステータスのID | 1145445329 | Int64 または String | text | ステータスの本文 | I am eating oatmeal, The first tag is always <html> | String | source | ステータスを送ったアプリケーション | web (Default), <a href="http://www.tweetdeck.com/">TweetDeck</a> | String | truncated | ステータスが省略されたか | true, false | Boolean | in_reply_to_status_id | 返信元のステータスのID | empty, 1047468972 | Nullable<Int64> または String | in_reply_to_user_id | 返信元のステータスを書いたユーザーのID | empty, 14597523 | Nullable<Int32> または String | favorited | 認証ユーザーがステータスをお気に入りとしているか | true, false | Boolean | in_reply_to_screen_name | 返信元のステータスを書いたユーザーの表示名 | empty, tweetybird | String | retweeted_status | リツイート元のステータス | | status | user | 投稿したユーザーの情報 | | user | geo | 投稿された場所 | | geo | coordinates | ? | | | place | ? | | | contributors | ステータスの投稿に関わったユーザーのIDの配列 | 14198354 | List<String> |
userオブジェクト †
名前 | 説明 | 例*5 | 型*6 | id | ユーザーのID | 14198354 | Int32 または String | name | ユーザーの名前 | empty (Default), Twitter API Chatter | String | screen_name | ユーザーの表示名 | tweetybird, dougw | String | location | ユーザーの現在地 | empty (Default), California OR New York, NY, In The Woods, 27.893621,-82.243706 | String | description | ユーザーの自己紹介文 | empty (Default), I like new shiny things. | String | profile_image_url | ユーザーのアイコンのURL | http://static.twitter.com/images/default_profile_normal.png(Default),  | String | url | ユーザーのWeb(ホームページ) | empty, http://downforeveryoneorjustme.com | String | protected | ツイートを非公開にしているか | true, false | Boolean | followers_count | ユーザーをフォローしている人の数 | 0, 4034 | Int32 | profile_background_color | ユーザーの背景色(RGB) | 9ae4e8 (Default) | String | profile_text_color | ユーザーの前景色(RGB) | 000000 (Default) | String | profile_link_color | ユーザーのリンクの色(RGB) | 0000ff (Default) | String | profile_sidebar_fill_color | ユーザーのサイドバーの色(RGB) | e0ff92 (Default) | String | profile_sidebar_border_color | ユーザーのサイドバーの輪郭の色(RGB) | 87bc44 (Default) | String | friends_count | ユーザーがフォローしている人の数 | 0, 221 | Int32 | created_at | 作成日時(UTC) | Sat Jan 24 22:14:29 +0000 2009 | DateTime | favourites_count | お気に入りの数 | 0, 451 | Int32 | utc_offset | ユーザーのタイムゾーンとUTCの秒数差 | -21600 (Default), 32400, 36000 | String | time_zone | ユーザーのタイムゾーン | Central Time (US & Canada) (Default), Tokyo, Osaka, Sydney | String | profile_background_image_url | ユーザーの背景画像のURL | empty, (Default),  | String | profile_background_tile | ユーザーの背景画像がタイルされているか | true, false | Boolean | notifications | ユーザーのNotificationsが有効か | true, false | Boolean | geo_enabled | ユーザーがツイート位置情報を有効にしているか | true, false | Boolean | verified | 認証済みアカウントか | true, false | Boolean | following | ユーザーがログインしているユーザーをフォローしているか | true, false | Boolean | statuses_count | ユーザーが投稿したステータスの数 | 0, 9423 | Int32 | lang | ユーザーの言語 | ja, en, fr | String | contributors_enabled | contributors accessが可能か | true, false | Boolean |
非同期でデータを受信する †
「WebRequest、WebResponseクラスを使ってファイルをダウンロードし表示する」で紹介しているのと同じように、非同期でデータを受信してみます。
下の例では、エンターキーを押すまで受信をし続けます。ここでも、受信したデータはすべてASCII文字であると決めつけています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
| | Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Xml
Module TwitterStreamingApiTest
Dim bufferData As Byte()
Dim bufferString As StringBuilder
Dim xmlReg As New Regex( _
"<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>", _
RegexOptions.Singleline Or RegexOptions.Compiled)
Sub Main()
Dim req As HttpWebRequest = DirectCast(System.Net.WebRequest.Create( _
"http://stream.twitter.com/1/statuses/sample.xml"), HttpWebRequest)
Dim nc As New NetworkCredential("username", "password")
req.Credentials = nc
Dim r As IAsyncResult = DirectCast(req.BeginGetResponse( _
New AsyncCallback(AddressOf ResponseCallback), req), IAsyncResult)
Console.ReadLine()
req.Abort()
Console.WriteLine("----- 終了しました! -----")
Console.ReadLine()
End Sub
Sub ResponseCallback(ByVal ar As IAsyncResult)
Dim req As HttpWebRequest = DirectCast(ar.AsyncState, HttpWebRequest)
Dim res As HttpWebResponse = Nothing
Try
res = DirectCast(req.EndGetResponse(ar), HttpWebResponse)
Catch ex As WebException
req.Abort()
Return
End Try
Dim st As Stream = res.GetResponseStream()
bufferData = New Byte(5119) {}
bufferString = New StringBuilder()
Dim r As IAsyncResult = DirectCast( _
st.BeginRead(bufferData, _
0, _
bufferData.Length, _
New AsyncCallback(AddressOf ReadCallback), st), _
IAsyncResult)
End Sub
Sub ReadCallback(ByVal ar As IAsyncResult)
Dim st As Stream = DirectCast(ar.AsyncState, Stream)
Dim readSize As Integer = st.EndRead(ar)
If readSize > 0 Then
Dim str As String = Encoding.ASCII.GetString(bufferData, 0, readSize)
bufferString.Append(str)
Dim mc As MatchCollection = xmlReg.Matches(bufferString.ToString())
For Each m As Match In mc
Dim xmlDoc As New XmlDocument()
xmlDoc.LoadXml(m.Value)
Dim rootElm As XmlElement = xmlDoc.DocumentElement
If rootElm.Name = "status" Then
Console.WriteLine("[{1}] {0}", _
rootElm("text").InnerText, rootElm("user")("name").InnerText)
End If
Next
If mc.Count > 0 Then
Dim m As Match = mc(mc.Count - 1)
bufferString.Remove(0, m.Index + m.Length)
End If
Try
Dim r As IAsyncResult = DirectCast( _
st.BeginRead(bufferData, _
0, _
bufferData.Length, _
New AsyncCallback(AddressOf ReadCallback), st), _
IAsyncResult)
Catch ex As WebException
st.Close()
Return
End Try
Else
st.Close()
Console.WriteLine("----- 切断されました -----")
End If
End Sub
End Module
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
| | using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
class TwitterStreamingApiTest
{
private static byte[] bufferData;
private static StringBuilder bufferString;
private static Regex xmlReg = new Regex(
@"<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>",
RegexOptions.Singleline | RegexOptions.Compiled);
static void Main()
{
HttpWebRequest req = (HttpWebRequest)System.Net.WebRequest.Create(
"http://stream.twitter.com/1/statuses/sample.xml");
NetworkCredential nc = new NetworkCredential("username", "password");
req.Credentials = nc;
IAsyncResult r = (IAsyncResult)req.BeginGetResponse(
new AsyncCallback(ResponseCallback), req);
Console.ReadLine();
req.Abort();
Console.WriteLine("----- 終了しました! -----");
Console.ReadLine();
}
private static void ResponseCallback(IAsyncResult ar)
{
HttpWebRequest req = (HttpWebRequest)ar.AsyncState;
HttpWebResponse res = null;
try
{
res = (HttpWebResponse)req.EndGetResponse(ar);
}
catch (WebException)
{
req.Abort();
return;
}
Stream st = res.GetResponseStream();
bufferData = new byte[5120];
bufferString = new StringBuilder();
IAsyncResult r = (IAsyncResult)st.BeginRead(
bufferData,
0,
bufferData.Length,
new AsyncCallback(ReadCallback),
st);
}
private static void ReadCallback(IAsyncResult ar)
{
Stream st = (Stream)ar.AsyncState;
int readSize = st.EndRead(ar);
if (readSize > 0)
{
string str = Encoding.ASCII.GetString(bufferData, 0, readSize);
bufferString.Append(str);
MatchCollection mc = xmlReg.Matches(bufferString.ToString());
foreach (Match m in mc)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(m.Value);
XmlElement rootElm = xmlDoc.DocumentElement;
if (rootElm.Name == "status")
{
Console.WriteLine("[{1}] {0}",
rootElm["text"].InnerText, rootElm["user"]["name"].InnerText);
}
}
if (mc.Count > 0)
{
Match m = mc[mc.Count - 1];
bufferString.Remove(0, m.Index + m.Length);
}
try
{
IAsyncResult r = (IAsyncResult)st.BeginRead(
bufferData,
0,
bufferData.Length,
new AsyncCallback(ReadCallback),
st);
}
catch (WebException)
{
st.Close();
return;
}
}
else
{
st.Close();
Console.WriteLine("----- 切断されました -----");
}
}
}
|
次回予告 †
長くなってしまいましたので、statuses/filterメソッドについては、次回紹介します。
履歴 †
- 2010/6/18 : 10秒の測り方の間違いを修正。
コメント †
|