Twitter Streaming APIを使う 2 †
前回に続いて、Twitter Streaming APIです。前回も書きましたが、私はツイッターについての知識がほとんどないということをご了承ください。
statuses/filterメソッドを使う †
statuses/filterメソッドを使用すると、指定したキーワードを含むパブリックステータスだけを送ってもらうことができます。キーワードだけでなく、ユーザーや場所を指定することもできます。
「Streaming API」によると、statuses/filterメソッドはPOSTでデータを送信することになっていますが、GETでも大丈夫のようです(詳しく調べたわけではありませんが)。
trackパラメータ †
キーワードを指定するには、trackパラメータを使います。キーワードは大文字小文字を区別しません。また、キーワードは1〜60バイトである必要があります。スペースの入ったキーワードは指定できませんし、UTF-8のキーワードもうまくいかないそうです。
例えば「Twitter」をtrackパラメータに指定したときは、textフィールド内の「TWITTER」、「twitter」、「"Twitter"」、「twitter.」、「#twitter」、「@twitter」などにマッチし、「TwitterTracker」や「http://www.twitter.com」にはマッチしないということです。
trackパラメータに複数のキーワードを指定する場合は、それぞれのキーワードをコンマ(,)区切りにします。複数のキーワードが指定された場合は、どれか1つでも含まれていればマッチしたことになります。例えば、パラメータを「track=basketball,football,baseball」とすると、「basketball」、「football」、「baseball」のいずれかのキーワードを含むステータスが送られてきます。キーワードは最大で200個指定できます。
followパラメータ †
ユーザーを指定するには、followパラメータを使います。followパラメータには、ユーザーのIDを指定します。マッチするステータスは、そのユーザーが作成したか、そのユーザーがReTweetしたか、そのユーザーが作成したものを誰かがReTweetしたか、そのユーザーが作成したステータスへの返信(in-reply-to)かのどれかです。ただし、ReTweetボタンや返信(reply "swoosh")ボタンを押さないで作成されたステータスにはマッチしない(「@user」や「RT @user」などと書かれたステータスでも)ということです。
followパラメータもtrackパラメータと同様に、コンマ区切りで複数のIDを指定可能です。最大で400個指定できます。
locationsパラメータ †
指定した場所から投稿されたステータスだけを送ってもらうには、locationsパラメータを使います。これは、その場所の範囲を南西と北東の経度緯度をコンマで区切った文字列で指定します(つまり範囲は、矩形(box)っぽくなります)。例えば、サンフランシスコは「locations=-122.75,36.8,-121.75,37.8」となるそうです。
locationsパラメータもコンマ区切りで複数指定可能で、例えばサンフランシスコとニューヨークであれば、「locations=-122.75,36.8,-121.75,37.8,-74,40,-73,41」となります。locationsパラメータの範囲は、1辺が1度以下でなければならず、最大で10個まで指定できます。
trackとlocationsパラメータは、あまりに広い範囲を指定すると、すべてのステータスを返せなくなり、制限された状態になります。制限状態が終了すると、制限メッセージ(前号で紹介したlimitオブジェクトのことのようです)と、マッチしたすべてのステータスがもう一度送られます。
なおtrackとlocationsパラメータの両方を指定すると、どちらかにマッチしたステータスが送られます。
サンプルコード †
以下の例では、「2010wc」というキーワードを含むステータスを受信しています。
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
| | Imports System.IO
Imports System.Net
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Xml
Module TwitterStreamingApiTest
Sub Main()
Dim webreq As HttpWebRequest = DirectCast(System.Net.WebRequest.Create( _
"http://stream.twitter.com/1/statuses/filter.xml"), HttpWebRequest)
Dim nc As New NetworkCredential("username", "password")
webreq.Credentials = nc
webreq.Method = "POST"
webreq.ContentType = "application/x-www-form-urlencoded"
webreq.ServicePoint.Expect100Continue = False
Dim postStr As String = "track=2010wc"
Dim postData As Byte() = Encoding.UTF8.GetBytes(postStr)
webreq.ContentLength = postData.Length
Dim reqStrm As Stream = webreq.GetRequestStream()
reqStrm.Write(postData, 0, postData.Length)
reqStrm.Close()
Dim webres As HttpWebResponse = DirectCast(webreq.GetResponse(), HttpWebResponse)
Dim st As Stream = webres.GetResponseStream()
Dim sr As New StreamReader(st)
Dim xmlReg As New Regex( _
"<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>", _
RegexOptions.Singleline Or RegexOptions.Compiled)
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 = "" 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")("screen_name").InnerText)
End If
buffer.Length = 0
End If
End While
webreq.Abort()
webres.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
87
88
89
90
| | using System;
using System.IO;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
class TwitterStreamingApiTest
{
static void Main()
{
HttpWebRequest webreq = (HttpWebRequest)System.Net.WebRequest.Create(
"http://stream.twitter.com/1/statuses/filter.xml");
NetworkCredential nc = new NetworkCredential("username", "password");
webreq.Credentials = nc;
webreq.Method = "POST";
webreq.ContentType = "application/x-www-form-urlencoded";
webreq.ServicePoint.Expect100Continue = false;
string postStr = "track=2010wc";
byte[] postData = Encoding.UTF8.GetBytes(postStr);
webreq.ContentLength = postData.Length;
Stream reqStrm = webreq.GetRequestStream();
reqStrm.Write(postData, 0, postData.Length);
reqStrm.Close();
HttpWebResponse webres = (HttpWebResponse)webreq.GetResponse();
Stream st = webres.GetResponseStream();
StreamReader sr = new StreamReader(st);
Regex xmlReg = new Regex(
@"<\?xml\s[^>]+>\s*<(?<root>[^\s>]+)[^>]*>(?:.+?)</\k<root>>",
RegexOptions.Singleline | RegexOptions.Compiled);
StringBuilder buffer = new StringBuilder();
DateTime startTime = DateTime.Now;
while ((DateTime.Now - startTime).TotalSeconds < 10)
{
string line = sr.ReadLine();
if (line == null)
break;
if (line == "")
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"]["screen_name"].InnerText);
}
buffer.Length = 0;
}
}
webreq.Abort();
webres.Close();
sr.Close();
Console.WriteLine("----- 終了しました! -----");
Console.ReadLine();
}
}
|
JSONを解析する †
Twitter Streaming APIのドキュメントでは、JSONの使用をかなり強く勧めています。それにも関わらず、XMLを使うというのはちょっと申し訳ない気がします。そこで、JSONを解析する方法を考えます。
.NET FrameworkでJSONを解析するとなると、.NET Framework 3.5以降であれば、JavaScriptSerializer*1(ASP.NET 2.0 AJAX Extensionsでも可)やDataContractJsonSerializerクラスを使うことで比較的簡単にできます。そうでなければ、自分で解析するか、JSONで紹介されているようなライブラリを使用すれば良いでしょう。
JavaScriptSerializerを使う †
以下に、JavaScriptSerializerを使用した例を示します。このコードを実行するには、System.Web.Extensionsを参照設定に追加してください。ここでは手抜きでデータをDictionaryに変換していますが、専用のクラスを作った方が良いでしょう。また、statusオブジェクトなのかを判断する方法がわかりませんでしたので、とりあえずtextフィールドがあればstatusと判断するようにしました。
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
| | Imports System.IO
Imports System.Net
Imports System.Collections.Generic
Module TwitterStreamingApiTest
Sub Main()
Dim webreq As HttpWebRequest = DirectCast(System.Net.WebRequest.Create( _
"http://stream.twitter.com/1/statuses/sample.json"), HttpWebRequest)
Dim nc As New NetworkCredential("username", "password")
webreq.Credentials = nc
Dim webres As HttpWebResponse = DirectCast(webreq.GetResponse(), HttpWebResponse)
Dim st As Stream = webres.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 = "" Then
Continue While
End If
Dim serializer As New System.Web.Script.Serialization.JavaScriptSerializer()
Dim status As Dictionary(Of String, Object) = _
serializer.Deserialize(Of Dictionary(Of String, Object))(line)
If status.ContainsKey("text") Then
Dim user As Dictionary(Of String, Object) = _
DirectCast(status("user"), Dictionary(Of String, Object))
Console.WriteLine("[{1}] {0}", status("text"), user("name"))
ElseIf status.ContainsKey("delete") Then
Console.WriteLine("[DELETE]")
End If
End While
webreq.Abort()
webres.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
| | using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
class TwitterStreamingApiTest
{
static void Main()
{
HttpWebRequest webreq = (HttpWebRequest)System.Net.WebRequest.Create(
"http://stream.twitter.com/1/statuses/sample.json");
NetworkCredential nc = new NetworkCredential("username", "password");
webreq.Credentials = nc;
HttpWebResponse webres = (HttpWebResponse)webreq.GetResponse();
Stream st = webres.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 == "")
continue;
System.Web.Script.Serialization.JavaScriptSerializer serializer =
new System.Web.Script.Serialization.JavaScriptSerializer();
Dictionary<string, object> status =
serializer.Deserialize<Dictionary<string, object>>(line);
if (status.ContainsKey("text"))
{
Dictionary<string, object> user =
(Dictionary<string, object>)status["user"];
Console.WriteLine("[{1}] {0}", status["text"], user["name"]);
}
else if (status.ContainsKey("delete"))
{
Console.WriteLine("[DELETE]");
}
}
webreq.Abort();
webres.Close();
sr.Close();
Console.WriteLine("----- 終了しました! -----");
Console.ReadLine();
}
}
|
Json.NETを使う †
JSONを扱うフリーのライブラリとしては、Json.NET(MITライセンス)が有名です。Json.NETは.NET Framework 2.0以降で使用できますが、Json.NETの機能の1つである「LINQ to JSON」は.NET Framework 3.5以降でなければ使用できません。
以下にJson.NETのLINQ to JSONを使った例を紹介します。なお.NET Framework 2.0以降であれば、JsonConvert.DeserializeXmlNodeメソッドを使ってXmlDocumentに変換するといった方法があります。
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
| | Imports System.IO
Imports System.Net
Module TwitterStreamingApiTest
Sub Main()
Dim webreq As HttpWebRequest = DirectCast(System.Net.WebRequest.Create( _
"http://stream.twitter.com/1/statuses/sample.json"), HttpWebRequest)
Dim nc As New NetworkCredential("username", "password")
webreq.Credentials = nc
Dim webres As HttpWebResponse = DirectCast(webreq.GetResponse(), HttpWebResponse)
Dim st As Stream = webres.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 = "" Then
Continue While
End If
Dim json As Newtonsoft.Json.Linq.JObject = _
Newtonsoft.Json.Linq.JObject.Parse(line)
Dim token As Newtonsoft.Json.Linq.JToken = Nothing
If json.TryGetValue("text", token) Then
Console.WriteLine("[{1}] {0}", _
CType(json("text"), String), _
CType(json("user")("name"), String))
ElseIf json.TryGetValue("delete", token) Then
Console.WriteLine("[DELETE] {0}", json("delete")("status")("id"))
End If
End While
webreq.Abort()
webres.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
| | using System;
using System.IO;
using System.Net;
class TwitterStreamingApiTest
{
static void Main()
{
HttpWebRequest webreq = (HttpWebRequest)System.Net.WebRequest.Create(
"http://stream.twitter.com/1/statuses/sample.json");
NetworkCredential nc = new NetworkCredential("username", "password");
webreq.Credentials = nc;
HttpWebResponse webres = (HttpWebResponse)webreq.GetResponse();
Stream st = webres.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 == "")
continue;
Newtonsoft.Json.Linq.JObject json = Newtonsoft.Json.Linq.JObject.Parse(line);
Newtonsoft.Json.Linq.JToken token;
if (json.TryGetValue("text", out token))
{
Console.WriteLine("[{1}] {0}",
(string)json["text"], (string)json["user"]["name"]);
}
else if (json.TryGetValue("delete", out token))
{
Console.WriteLine("[DELETE] {0}", json["delete"]["status"]["id"]);
}
}
webreq.Abort();
webres.Close();
sr.Close();
Console.WriteLine("----- 終了しました! -----");
Console.ReadLine();
}
}
|
補足 : .NET Framework 1.1以前の場合 †
JSONで紹介されているC#のクラスの内、.NET Framework 1.1以前に対応しているのは、「How do I write my own parser?で紹介されているクラスだけのようです。このクラスを使用してみたところ、コードポイントをUnicode文字に変換する箇所でエラーが発生することがありました。
JSON.csの277行にある
1
| | s.Append(Char.ConvertFromUtf32((int)codePoint));
|
を
1
| | s.Append(Encoding.UTF32.GetString(BitConverter.GetBytes(codePoint)));
|
に変更することで、このエラーは出なくなりました。
コメント †