A few months ago, I started looking into the Twitter API and I have developed twitter4s, an asynchronous non-blocking Twitter client in Scala.
In this article, we will introduce twitter4s providing examples of how to download tweets from a user timeline and how to perform some simple data analysis.
The code shown in this tutorial is available on Github.
Getting Started
The Twitter API can be accessed by means of a registered app. So, first of all, we need to register our app. Login with your twitter account and have a look at the Twitter API terms and conditions.
If you are happy with them, register your app at http://apps.twitter.com.
In order to do so, you will need to provide an app name and an brief description of what it does. After the registration, a consumer key and consumer secret will be provided: save them as we will need them when setting up twitter4s.
Also, generate an access key and access secret and make sure you have the correct permissions: for this tutorial “Read Only” is enough.
Finally, please note that the Twitter API has rate limits — have a look at the Twitter’s development website for more information.
Also, have a look at the rate limits chart where the rate limit for each endpoint is summarized.
Setup
If not already there, add Maven Central as resolver in your SBT configuration:
resolvers += "Maven central" at "http://repo1.maven.org/maven2/"
Also, you need to include the library as your dependency:
libraryDependencies ++= Seq( "com.danielasfregola" %% "twitter4s" % "0.2.1" )
Usage
Add your consumer and access token to your configuration file and initialize your Twitter Client:
import com.danielasfregola.twitter4s.TwitterClient val client = new TwitterClient()
Alternatively, you can also specify your tokens directly when creating the client:
import com.danielasfregola.twitter4s.TwitterClient import com.danielasfregola.twitter4s.entities.{AccessToken, ConsumerToken} val consumerToken = ConsumerToken(key = "my-consumer-key", secret = "my-consumer-secret") val accessToken = AccessToken(key = "my-access-key", secret = "my-access-secret") val client = new TwitterClient(consumerToken, accessToken)
Now that our Twitter Client has been initialized, we are now ready to use it! π
Have a look at its documentation for a complete list of the supported functionalities.
Top Hashtags in Timeline
As a sample code, let’s collect the tweets in a user timeline and display the top 10 hashtags used. In the tutorial, we will download and analyze tweets by Martin Odersky (the creator of Scala).
First, we need to get the tweets from the user timeline:
client.getUserTimelineForUser(screen_name = "odersky", count = 200)
The method getUserTimelineForUser (see scaladoc) return type is Future[Seq[Tweet]].
Note that a Tweet is a quite rich case class that contains a lot of information (see its scaladoc): it has more than 22 fields!
The need of having huge case classes is the reason why this library doesn’t support Scala versions older than 2.11: previous versions allow up to 22 fields in a case class.
In order to retrieve the hashtags used in a Tweet, we don’t have to parse the text of the tweet, as the Twitter API has already done all the hard work for us: we just need to access the Entities field and count how many times each hashtag is used:
def getTopHashtags(tweets: Seq[Tweet], n: Int = 10): Seq[(String, Int)] = { val hashtags: Seq[Seq[HashTag]] = tweets.map { tweet => tweet.entities.map(_.hashtags).getOrElse(Seq.empty) } val hashtagTexts: Seq[String] = hashtags.flatten.map(_.text.toLowerCase) val hashtagFrequencies: Map[String, Int] = hashtagTexts.groupBy(identity).mapValues(_.size) hashtagFrequencies.toSeq.sortBy { case (entity, frequency) => -frequency }.take(n) }
Let’s put everything together and add some code to print the results with a nice layout:
import com.danielasfregola.twitter4s.TwitterClient import com.danielasfregola.twitter4s.entities.{HashTag, Tweet} import scala.concurrent.ExecutionContext.Implicits.global object UserTopHashtags extends App { def getTopHashtags(tweets: Seq[Tweet], n: Int = 10): Seq[(String, Int)] = { val hashtags: Seq[Seq[HashTag]] = tweets.map { tweet => tweet.entities.map(_.hashtags).getOrElse(Seq.empty) } val hashtagTexts: Seq[String] = hashtags.flatten.map(_.text.toLowerCase) val hashtagFrequencies: Map[String, Int] = hashtagTexts.groupBy(identity).mapValues(_.size) hashtagFrequencies.toSeq.sortBy { case (entity, frequency) => -frequency }.take(n) } val client = new TwitterClient() val user = "odersky" client.getUserTimelineForUser(screen_name = user, count = 200).map { tweets => val topHashtags: Seq[((String, Int), Int)] = getTopHashtags(tweets).zipWithIndex val rankings = topHashtags.map { case ((entity, frequency), idx) => s"[${idx + 1}] $entity (found $frequency times)"} println(s"${user.toUpperCase}'S TOP HASHTAGS:") println(rankings.mkString("\n")) } }
At the time of this writing, running the following code generates the following output:
ODERSKY'S TOP HASHTAGS: [1] scala (found 25 times) [2] scaladays (found 5 times) [3] scalajs (found 4 times) [4] progfun (found 3 times) [5] coursera (found 2 times) [6] scalax (found 1 times) [7] community (found 1 times) [8] aws (found 1 times) [9] iexpectmoreofapple (found 1 times) [10] scalamatsuri (found 1 times)
Summary
In this article we have introduced a new asynchronous non-blocking Scala Client for Twitter, called twitter4s.
We have described how to register our app, setup the Twitter Client and we have provided a sample code to download tweets from a user timeline and analyze their hashtags.
The code shown in this tutorial can be found here.
Hi, Daniela,
thank you for the client. To your knowledge, are people using it? Also, if I need to post a text and a picture, will it do it?
Thank you and further success.
LikeLike
Hi Mark, unfortunately the current version only allows you to post text without a picture. From the next release, you should also be able to post media together with some text.
Regards,
Daniela
LikeLike
Hi, Daniela,
Your library is amazingly complete, hundreds of methods in the implementations. If I can figure it out, I may suggest a patch for uploading an image as well.
Thank you,
Mark
LikeLike
Hi Mark, I have already done something as part of the 0.2-SNAPSHOT version — but I haven’t published yet as I’d like to test it a bit more and add more features. Feel free to have a look and contribute! π
LikeLike
You mean, it’s on GitHub but not pushed to maven? I may try it then.
LikeLike
Hi Mark, to help you out I have just published the version 0.2-SNAPSHOT — see https://oss.sonatype.org/content/repositories/snapshots/com/danielasfregola/twitter4s_2.11/0.2-SNAPSHOT/
Note that the documentation is still missing, it’s a work in progress! π
LikeLike
Thank you, Daniela. I am teaching a class today, hope to get to this tonight.
LikeLike
Hi, Daniela,
I tried this in my build.sbt
libraryDependencies ++= Seq(
“com.danielasfregola” %% “twitter4s” % “0.2-SNAPSHOT”
)
and I get this error below. I probably miss some understanding in the package publishing in repos.
Thank you
Error:Error while importing SBT project:…
See complete log in /home/mark/.IntelliJIdea15/system/log/sbt.last.log
LikeLike
Hi Mark,
you are probably missing the resolver for snapshot versions. I have updated the README with instructions on how to use snapshot versions. Please have a look at https://github.com/DanielaSfregola/twitter4s#snapshot-versions
LikeLike
Yes, that was the problem.
Thank you
LikeLike
So no documentation is OK π
But how do I attached an image in this case
client.tweet(status = “My new status (test) ” + new Date())
LikeLike
Hi, Daniela,
it all works for me (with your help), and here you can observe the results, https://twitter.com/TalmudTweets, daily posts automated instead of me doing it manually. You can also see why image posting would be nice: previously I was posting manually, with images, and it looks more appealing.
Regards,
Mark
LikeLike
Hi Mark, I am glad is working for you. I am finishing the image uploa — it shouldn’t take me more than a week or so — and I am going to publish version 0.2 asap
LikeLike
Thank you, I will be the first tester
LikeLike
Hi Mark, the version 0.2 with media upload support has been published. See https://github.com/DanielaSfregola/twitter4s#usage for instructions on how to tweets with media.
Cheers,
Daniela
LikeLike
Looks great, Daniela, will try as soon as I can.
Thank you,
Mark
LikeLike
Hi, Daniela,
0.1 used to work, but now neither 0.1 or 0.2 work. Here is a part of my build.sbt
resolvers += Resolver.sonatypeRepo(“snapshots”)
libraryDependencies ++= Seq(
“org.specs2” %% “specs2-core” % “3.8.2” % “test”,
“org.jsoup” % “jsoup” % “1.9.2”,
“com.danielasfregola” %% “twitter4s” % “0.1-SNAPSHOT”
)
and here is what I get
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: UNRESOLVED DEPENDENCIES ::
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn] :: com.danielasfregola#twitter4s_2.11;0.1-SNAPSHOT: not found
[warn] ::::::::::::::::::::::::::::::::::::::::::::::
[warn]
[warn] Note: Unresolved dependencies path:
[warn] com.danielasfregola:twitter4s_2.11:0.1-SNAPSHOT (/Users/mark/projects/SiteOps/build.sbt#L11-16)
[warn] +- default:siteops_2.11:1.0
sbt.ResolveException: unresolved dependency: com.danielasfregola#twitter4s_2.11;0.1-SNAPSHOT: not found
Another question about the keys. You put them into application.conf, and I currently use environmental variables. If I put the keys into application.conf, how do I make sure that I do not accidentally push them to the repository?
Thank you for your good work.
Mark
LikeLike
Hi Mark, please try to always read the README carefully and follow the instruction of the Setup section (https://github.com/DanielaSfregola/twitter4s#setup): both the resolver and version number are wrong.
About your second question, you can inject environment variable directly in your application.conf with the following syntax:
value = $MY_ENV_VARIABLE
Cheers,
Daniela
LikeLike
Hi, Daniela,
my fault, I was confused between the release and the snapshot. All is well now.
Mark
LikeLike
Image upload works, thank you
LikeLike
Hi, Daniela, things are working great now. I may be being obtuse, but my process never ends, running in a separate thread. I expected it to terminate, but it does the work, then hangs.
Thank you
LikeLike
Finished working product, https://github.com/markkerzner/SiteOps
LikeLike
Hi Daniela. Thank you for such a useful library.
How do I get to set proxy details for a TwitterClient instance. I am failing to fetch data using the API on a network behind a corporate proxy.
LikeLike
Hi Pepukayi,
I am afraid this is something that we do not support at the moment. If this is important for you, please create an issue with all the details of what you need or even open a PR about it — contributions are always welcome!
Cheers,
Daniela
LikeLike
Hi Daniela, Okay thank you.
Is there a way I can write the whole contents of the Future[Seq[Tweet]] that is returned by the getUserTimelineForUser() method to a textfile?
LikeLike
Hi Pepukayi, you just need to serialize the list in the format of your selection (json? text?) and then store the result in a file. See for example this article as a starting point: https://www.safaribooksonline.com/library/view/scala-cookbook/9781449340292/ch12s03.html.
Cheers,
Daniela
LikeLike
Hi Pepukayi, twitter4s v1.0 has just been released and it has proxy support!
Cheers,
Daniela
LikeLike
hi, daniela! thanks for this! any chance you’re going to release a version compiled for 2.12?
/mark k.
LikeLike
Hi Mark,
absolutely, this is my highest priority right now! We do want to support Scala 2.12 (see https://github.com/DanielaSfregola/twitter4s/issues/54).
Unfortunately, it is not as simple as it looks as the project is currently based on Spray, that does not support Scala 2.12. I am migrating the project to Akka Http (see https://github.com/DanielaSfregola/twitter4s/issues/55 for updates on its progress). Once the project has been migrated to Akka Http, supporting Scala 2.12 should not be a problem.
LikeLike
Hi Mark, twitter4s 4.0 is now out! This new version has support for Scala 2.12 π
LikeLike
thanks!
LikeLike
and it is working. thanks again!
LikeLike
Thank you Daniela for this amazing client π
LikeLike
Hi Daniela, this is a great library. Thanks for all your hard work! I do have a quick question about it though. I am using it to write an app that calculates metrics about tweets from the sample stream and I noticed that when I look at the text of the tweets the emojis that were present were replaced with a ‘?’. Is there something else I need to do to ensure that the emojis are properly converted to their unicode characters? Thanks!
LikeLike
Hi Ryan, emojii should be already supported — if you do a simple `println(tweet.text)` you should see them in the terminal. Maybe double check that the encoding you are using when saving the results is correct? If you want more assistance, please create an issue on github at https://github.com/DanielaSfregola/twitter4s/issues/new
Thanks,
Daniela
LikeLike
Thanks, Daniela. I think I found what was wrong. I was actually developing on Windows and there was some issue where the Windows command line tool wasn’t using the correct encoding. I couldn’t get it to work even when changing my encoding, so I just switched to Ubuntu and the emojis show up just fine in the terminal. Thanks!
LikeLike