本篇將從實際例子出發,展示如何使用api爬取twitter的數據。
1. 創建APP
進入https://apps.twitter.com/,創建自己的app。只有有了app才可以訪問twitter的api並抓取數據。只需創建最簡單的app即可,各種信息隨意填寫,並不需要進一步的認證,我們要的只是app的Consumer Key (API Key), Consumer Secret (API Secret), Access Token 和 Access Token Secret。鑒於單app的爬取次數限制,可以申請很多app來提高總次數。
2. 確定要使用的API
twitter提供多種類型的api,其中常用的有REST API和Streaming API。前者是常見的api類型,后者則可以跟蹤監視一個用戶或者一個話題。
REST API下面有很多的api,有價值爬取的有以下幾個:
- GET statuses/user_timeline:返回一個用戶發的推文。注意twitter里回復也相當於發推文。
- GET friends/ids:返回一個用戶的followees。
- GET followers/ids:返回一個用戶的followers。
- GET users/show:返回一個用戶的信息。
3. 官方類庫
下載twitter的類庫。說實話,api爬蟲好不好寫,全看類庫提供的功能強不強。twitter提供了多種語言的類庫,本文選擇java類庫。
4. 驗證授權
凡是訪問api,都需要驗證授權,也即:OAuth。一般流程為:以app的id和key,用戶的用戶名和密碼為參數訪問授權api,返回一個token(一個字符串),即算是授權完成,之后只需訪問其他api時帶上這個參數就行了。
當然,不同的網站授權過程各有不同。較為繁瑣的比如人人網需要先跳轉至回調網頁,用戶登陸后再返回token。twitter的授權過程也不簡單(需要多次http請求),但是幸運的是類庫中已經幫我們實現了此過程。
例,twitter的Auth1.1授權,其中需要設置的四個參數在app管理界面就能看到:
ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setOAuthAccessToken(accessToken); cb.setOAuthAccessTokenSecret(accessTokenSecret); cb.setOAuthConsumerKey(consumerKey); cb.setOAuthConsumerSecret(consumerSecret); OAuthAuthorization auth = new OAuthAuthorization(cb.build()); Twitter twitter = new TwitterFactory().getInstance(auth);
twitter還提供一種無需用戶授權(需app授權)的選擇,訪問某些api時可用次數比Auth1.1授權的要多:
ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setApplicationOnlyAuthEnabled(true); Twitter twitter = new TwitterFactory(cb.build()).getInstance(); twitter.setOAuthConsumer(consumerKey, consumerSecret); try { twitter.getOAuth2Token(); } catch (TwitterException e) { e.printStackTrace(); }
5. 調用API
授權之后,我們就可以真正地開始爬數據了。
- REST API
爬取用戶follower,getFollowersIDs方法每次返回最多5000個follower,cursor用戶標記從哪開始:
IDs iDs = twitter.getFollowersIDs(uid, cursor);
爬取用戶推文:
ResponseList<Status> status = twitter.getUserTimeline(uid, page);
- Streaming API
監視一個用戶的所有行為,其中UserStreamListener太長了只截取了一部分:
TwitterStream twitterStream;
twitterStream = new TwitterStreamFactory(cb.build()).getInstance(); twitterStream.addListener(listener); twitterStream.user(); private static final UserStreamListener listener = new UserStreamListener() { @Override public void onStatus(Status status) { System.out.println("onStatus @" + status.getUser().getScreenName() + " - " + status.getText() + status.getCreatedAt()); } @Override public void onDeletionNotice(StatusDeletionNotice statusDeletionNotice) { System.out.println("Got a status deletion notice id:" + statusDeletionNotice.getStatusId()); } @Override public void onDeletionNotice(long directMessageId, long userId) { System.out.println("Got a direct message deletion notice id:" + directMessageId); } @Override public void onTrackLimitationNotice(int numberOfLimitedStatuses) { System.out.println("Got a track limitation notice:" + numberOfLimitedStatuses); } @Override public void onScrubGeo(long userId, long upToStatusId) { System.out.println("Got scrub_geo event userId:" + userId + " upToStatusId:" + upToStatusId); } ...
6. 如何提速
api都是有訪問次數限制的,twitter中不少都是以15分鍾為單位的。為了爬取能達到一定的速度,我申請了50個app,只要pc給力,那么我就有50倍於單app的速度了。
那么我是單線程輪流用50個app還是50個線程一起呢?顯然50個線程不可行,通常20個線程以上的時候花費在線程同步上的時間就很可觀了,並且我們寫的是爬蟲,50個線程同時寫數據庫會嚴重拖慢速度。那么單線程呢?考慮到每個app用完其訪問次數是需要一定時間的,特別要是網絡狀況不好的話次數用完可能會花費數分鍾,那么15分鍾顯然無法讓每個app都能訪問api,造成了浪費。
所以我選擇了線程池。IO密集型任務,一般將線程數設置為cpu的核數的兩倍。同時設置兩個隊列,分別供各個線程讀取數據和寫數據。n個線程同時跑爬蟲,再分一個線程出來維護那兩個隊列。框架如下:
好了,到這里應該能寫twitter的api爬蟲了。剩下的就是閱讀各個api繁瑣的文檔,以及和各種bug搏斗的時間了╥﹏╥