【问题标题】:Twitter stream API - Erlang clientTwitter 流 API - Erlang 客户端
【发布时间】:2016-04-18 02:43:14
【问题描述】:

我是 Erlang 世界的新手,我正在尝试为 Twitter Stream API 编写一个客户端。我正在使用 httpc:request 发出 POST 请求,但我经常收到 401 错误,我显然在发送请求的方式上做错了......我所拥有的看起来像这样:

fetch_data() ->
    Method = post,
    URL = "https://stream.twitter.com/1.1/statuses/filter.json",
    Headers = "Authorization: OAuth oauth_consumer_key=\"XXX\", oauth_nonce=\"XXX\", oauth_signature=\"XXX%3D\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\"XXX\", oauth_token=\"XXX-XXXXX\", oauth_version=\"1.0\"",
    ContentType = "application/json",
    Body = "{\"track\":\"keyword\"}",
    HTTPOptions = [],
    Options = [],
    R = httpc:request(Method, {URL, Headers, ContentType, Body}, HTTPOptions, Options),
    R.

在这一点上,我确信签名没有问题,因为在尝试使用 curl 访问 API 时,相同的签名可以正常工作。我猜我提出请求的方式存在一些问题。

按照上述方式提出的请求,我得到的响应是:

{ok,{{"HTTP/1.1",401,"Unauthorized"},
 [{"cache-control","must-revalidate,no-cache,no-store"},
  {"connection","close"},
  {"www-authenticate","Basic realm=\"Firehose\""},
  {"content-length","1243"},
  {"content-type","text/html"}],
 "<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>\n<title>Error 401 Unauthorized</title>\n</head>\n<body>\n<h2>HTTP ERROR: 401</h2>\n<p>Problem accessing '/1.1/statuses/filter.json'. Reason:\n<pre>    Unauthorized</pre>\n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n                                                \n</body>\n</html>\n"}}

当我尝试使用 curl 时,我正在使用这个:

curl --request 'POST' 'https://stream.twitter.com/1.1/statuses/filter.json' --data 'track=keyword' --header 'Authorization: OAuth oauth_consumer_key="XXX", oauth_nonce="XXX", oauth_signature="XXX%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="XXX", oauth_token="XXX-XXXX", oauth_version="1.0"' --verbose

而且我得到的事件就好了。

非常感谢您对此提供的任何帮助,这是 Erlang 的新手,我已经在这个问题上苦苦挣扎了很长一段时间。

【问题讨论】:

  • 我使用 oauth:get/6 (github.com/tim/erlang-oauth) 在statuses/sample 连接到他们的流,您可以使用oauth:post 进行过滤流。

标签: twitter stream erlang client


【解决方案1】:

您的代码存在几个问题

  1. 在 Erlang 中,您将参数编码为 JSON 主体,而使用 curl,您将它们编码为表单数据 (application/x-www-form-urlencoded)。 Twitter API 期望后者。事实上,你得到一个 401 是因为 OAuth 签名不匹配,因为你在计算中包含了 track=keyword 参数,而 Twitter 的服务器在没有 JSON 正文的情况下计算它,因为它应该按照 OAuth RFC

  2. 您正在使用带有默认选项的httpc。这不适用于流 API,因为流永远不会结束。您需要在结果到达时对其进行处理。为此,您需要将{sync, false} 选项传递给httpc。另请参阅 streamreceiver 选项。

最终,虽然httpc 最初可以工作以访问 Twitter 流 API,但它对您需要围绕它开发以从 Twitter API 流式传输的代码几乎没有价值。根据您的需要,您可能希望将其替换为直接基于 ssl 构建的简单客户端,特别是考虑到它可以解码 HTTP 数据包(剩下的就是 HTTP 块编码)。

例如,如果您的关键字很少见,您可能会收到来自httpc 的超时。此外,如果没有httpc,更新关键字列表或代码可能会更容易而无需停机。

直接基于ssl的流媒体客户端可以实现为gen_server(或者一个简单的过程,如果你不遵循OTP原则)甚至更好的gen_fsm来实现重新连接策略。您可以按以下方式进行:

  • 使用ssl:connect/3,4 连接,指定您希望套接字使用{packet, http_bin} 解码HTTP 数据包,并且您希望将套接字配置为被动模式{active, false}

  • 使用ssl:send/2,3 发送HTTP 请求数据包(最好作为iolist,带有二进制文件)。它应该分布在用 CRLF (\r\n) 分隔的几行上,首先是查询行 (GET /1.1/statuses/filter.json?... HTTP/1.1),然后是包含 OAuth 标头的标头。确保你也包括Host: stream.twitter.com。以空行结束。

  • 接收 HTTP 响应。您可以使用循环来实现这一点(因为套接字处于被动模式),调用ssl:recv/2,3 直到您获得http_eoh(标头结束)。通过查看Transfer-Encoding 响应标头,记下服务器是否会向您发送分块数据。

  • 使用ssl:setopts/2 将套接字配置为活动模式,并指定您希望数据包为原始数据和二进制格式的数据。事实上,如果数据被分块,您可以继续在被动模式下使用套接字。您还可以逐行获取数据或以字符串形式获取数据。这是一个口味问题:raw 是最安全的选择,逐行要求您检查缓冲区大小以防止截断较长的 JSON 编码推文。

  • 使用receive(简单流程)或handle_info 处理程序(如果您使用gen_server 实现此功能)从Twitter 接收数据作为发送到您的流程的消息。如果数据被分块,您将首先收到块大小,然后是推文和块的结尾(参见RFC 2616)。准备好将推文分散在几个块上(即维护某种缓冲区)。这里最好的是在这个过程中做最少的解码并将推文发送到另一个进程,可能是二进制格式。

您还应该处理错误和被 Twitter 关闭的套接字。请务必关注Twitter's guidelines for reconnection

【讨论】:

  • 我已经成功使用erlang-oauth库连接到twitter的流,erlang-oauth似乎使用httpc:github.com/tim/erlang-oauth/blob/…。你能解释一下为什么你说httpc 不起作用吗?
  • 你是对的,httpc 会在异步模式下工作。我相应地更新了答案。
  • @PaulGuyot 感谢您的帮助。我现在已经成功打开了一个到 stream.twitter.com 的套接字,看起来这部分工作得很好。但是,我在以正确方式通过套接字发送请求时遇到问题。我尝试过使用ssl:send(SSLSocket, "POST /1.1/statuses/filter.json?oauth_consumer_key=XXX...ssl:send(SSLSocket, "POST %2F1.1%2Fstatuses%2Ffilter.json%26oauth_consumer_key%3DXXX 和其他格式,但似乎我做错了。期望如何通过套接字发送标头?感谢您的帮助!
  • @akonsu 感谢您参与其中。你有一个简短的例子来说明你是如何使用 erlang-oauth 完成这个任务的吗?我似乎找不到一个很好的例子,我只能看到如何通过 OAuth 部分的例子,而不是如何提出请求的例子。谢谢
  • @PaulGuyot 请您指出一个在流式客户端中使用ssl 的示例代码吗?我也在构建一个 twitter 流消费者,对我来说,httpc 似乎也没有什么价值。
猜你喜欢
  • 1970-01-01
  • 2011-03-06
  • 2013-11-07
  • 2011-01-28
  • 2015-12-05
  • 2014-06-04
  • 1970-01-01
  • 1970-01-01
  • 2011-02-28
相关资源
最近更新 更多