返回文章列表

通过Java实现你的twitter应用

关于Twitter这里就不多介绍了, 基本上能看到这篇文章的都用过的, 当然不知道也没关系, Google一下.由于Twitter的API 开放性, 所以其应用现在可以用多如牛毛也描述了, 各种各样的, 本文基于Java, 通过twitter API来获取twitter消息, 并以PDF的形式进...

8 分钟阅读

关于Twitter这里就不多介绍了, 基本上能看到这篇文章的都用过的, 当然不知道也没关系, Google一下.由于Twitter的API 开放性, 所以其应用现在可以用多如牛毛也描述了, 各种各样的, 本文基于Java, 通过twitter API来获取twitter消息, 并以PDF的形式进行归档.准备工具:

1. HttpClient 3.x 库文件, 通过此库来从Twitter API获取消息, 同时需要 Comomons Logging 和 Codec 两个库文件. 请自行下载.

2. dom4j 库文件, 通过此库来解析Twitter的消息XML和获取中间的数据.

3. iText 库文件, 用来创建PDF文档.

每个Twitter应用都是基于twitter API来实现的, 有关Twitter API的使用可以看这里: Twitter API wiki(官方的,需要翻墙), 这里我们假设你只对获取数据感兴趣, 同时想以某种方式显示他们, 至于其他功能, 相信通过本教程的介绍, 你可以自己去钻研了.从TweetConsumer开始: [code lang="java"]
import org.dom4j.Element;
public interface TweetConsumer {
public String tweet(Element element) throws TweetException;
}
[/code]

Twitter API提供XML, JSON 和RSS几种格式, 这里我们选择XML格式, 通过dom4j来解析, 上面是一个叫做TweetConsumer的借口, 作用就是来处理消息的, 其实现在后面会讲到.

自定义一个消息异常, 当出现问题时抛出:

[code lang="java"]
public class TweetException extends Exception {
private static final long serialVersionUID = 7577136074623618615L;
public TweetException(Exception e) {
super(e);
}
}
[/code]

接下来是本文的重点, 通过Twitter API去获取消息:

[code lang="java"]
public class TweetProducer {
protected HttpClient client;
protected TweetConsumer consumer;

public void createClient(String username, String password) {
client = new HttpClient();
client.getState().setCredentials(
//由于GFW的原因, 直连twitter肯定是不行了, 建议使用第三方API,比如我的GAE应用:xiao-quan.appspot.com
new AuthScope("twitter.com", 80, AuthScope.ANY_REALM),
new UsernamePasswordCredentials(username, password));
client.getParams().setAuthenticationPreemptive(true);
}

public void setConsumer(TweetConsumer consumer) {
this.consumer = consumer;
}

public String execute(String since_id) throws TweetException {
String id = null;
String tmp;
//由于GFW的原因, 直连twitter肯定是不行了, 建议使用第三方API,比如我的GAE应用:http://xiao-quan.appspot.com/api/statuses/friends_timeline.xml
GetMethod get = new GetMethod("http://twitter.com/statuses/friends_timeline.xml");
if (since_id != null && since_id.length() > 0) {
get.setQueryString("?count=200&since_id=" + since_id);
}
else {
get.setQueryString("?count=200");
}
get.setDoAuthentication(true);
try {
client.executeMethod(get);
SAXReader reader = new SAXReader();
Document document = reader.read(get.getResponseBodyAsStream());
Element root = document.getRootElement();
for (Iterator i = root.elementIterator(); i.hasNext(); ) {
tmp = consumer.tweet((Element)i.next());
if (id == null) id = tmp;
}
} catch (HttpException e) {
throw new TweetException(e);
} catch (IOException e) {
throw new TweetException(e);
} catch (DocumentException e) {
throw new TweetException(e);
} finally {
get.releaseConnection();
}
return id;
}
}
[/code]

TweetProducer 里面有三个方法:
createClient(), 创建HttpClient对象, 通过HttpClient来连接Twitter API, 由于Twitter使用了HTTP Basic Authentication,
所以你需要提供你的twitter用户名和密码来登录, 获取你的朋友们的消息.
setConsumer(), 传一个TweetConsumer接口给TweetProducer, 我们有多种输出方案, 当然这里要通用点.
excute(), 这是才是做事情的地方, 获取哪个消息ID以后的消息, 返回一个消息ID.

获取消息:

[code lang="java"]
GetMethod get = new GetMethod("http://twitter.com/statuses/friends_timeline.xml");
[/code]

Twitter API默认获取的消息条数是20, 最大200, 你可以自己定义获取的数量, 同时也可以通过一些参数来指定获取第几页的消息等, 具体使用参考Twitter API WIKI上的定义.

例如, 获取消息ID为since_id以后的200个消息:

[code lang="java"]
get.setQueryString("?count=200&since_id=" + since_id);
[/code]

设置完查询条件后, 执行:

[code lang="java"]
client.executeMethod(get);
[/code]

将返回的信息传给SAXReader,以便获取消息root:

[code lang="java"]
SAXReader reader = new SAXReader();
Document document = reader.read(get.getResponseBodyAsStream());
Element root = document.getRootElement();
[/code]

最后迭代每个带 标签的Element:

[code lang="java"]
for (Iterator i = root.elementIterator(); i.hasNext(); ) {
tmp = consumer.tweet((Element)i.next());
if (id == null) id = tmp;
}
[/code]

由于Twitter的消息列表是按时间降序排列, 那么第一个消息就是最新的消息了, 这个消息ID就是excute()要返回的String.

获取到消息后, 接下来我们就输出消息:

[code lang="java"]
import org.dom4j.Element;

public class SystemOutTweets implements TweetConsumer {
public String tweet(Element tweet) {
System.out.println(tweet.asXML());
return tweet.elementText("id");
}

public static void main(String[] args) throws TweetException {
TweetProducer t = new TweetProducer();
t.createClient("gavinquan", "123456");
t.setConsumer(new SystemOutTweets());
System.out.println("Start tweeting:");
String id = t.execute(null);
System.out.println("The End; last tweet: " + id);
}
}
[/code]

将上面的介绍的代码整理好, 并执行, 就会在控制台输出, XML格式的消息列表, 如下是其中的一个片段:

[code lang="xml"]
<status>
<created_at>Wed Sep 09 06:07:04 +0000 2009</created_at>
<id>3858278078</id>
<text>gmail服务器错误了?</text>
<source><a href="http://twitterfox.net/" rel="nofollow">TwitterFox</a></source>
<truncated>false</truncated>
<in_reply_to_status_id/>
<in_reply_to_user_id/>
<favorited>false</favorited>
<in_reply_to_screen_name/>
<user>
<id>10415752</id>
<name>hugege</name>
<screen_name>hugege</screen_name>
<location>Shantou Guangdong China </location>
<description>www.gegehost.com 博客主机服务</description>
<profile_image_url>http://a1.twimg.com/profile_images/329549706/hugegelogo_normal.jpg</profile_image_url>
<url>http://hugege.com/</url>
<protected>false</protected>
<followers_count>1607</followers_count>
<profile_background_color>C6E2EE</profile_background_color>
<profile_text_color>663B12</profile_text_color>
<profile_link_color>1F98C7</profile_link_color>
<profile_sidebar_fill_color>DAECF4</profile_sidebar_fill_color>
<profile_sidebar_border_color>C6E2EE</profile_sidebar_border_color>
<friends_count>1335</friends_count>
<created_at>Tue Nov 20 16:00:58 +0000 2007</created_at>
<favourites_count>3</favourites_count>
<utc_offset>28800</utc_offset>
<time_zone>Beijing</time_zone>
<profile_background_image_url>http://s.twimg.com/a/1252448032/images/themes/theme2/bg.gif</profile_background_image_url>
<profile_background_tile>false</profile_background_tile>
<statuses_count>1540</statuses_count>
<notifications>false</notifications>
<verified>false</verified>
<following>false</following>
</user>
</status>
[/code]

同时会输出, 你的最后一个消息的消息ID:

[code lang="java"]
The End; last tweet: 3858279409
[/code]

当然你也可以直接通过这个ID, 来去获取你的该ID以后的消息:

[code lang="java"]
t.execute("3858279409");
[/code]

OK, 到这里, 我们的任务已经过去一半多了, 获取twitter消息, 那么接下来的内容, 我们来看如何将消息以PDF的形式进行归档.

通过iText生成PDF, 需要5步, 请看下面的Demo :

[code lang="java"]
import java.io.FileOutputStream;
import com.lowagie.text.Document;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfWriter;

public class HelloWorld {
public static void main(String args[]) {
try {
// step 1: create a Document object
Document document = new Document();
// step 2: connect the Document with an OutputStream using a PdfWriter
PdfWriter.getInstance(document, new FileOutputStream("hello.pdf"));
// step 3: open the document

document.open();
// step 4: add content
document.add(new Paragraph("Hello World"));
// step 5: close the document
document.close();
} catch (Exception e) {
System.out.println(e);
}
}
}
[/code]

大体工作流程就如上面所说,相信都可以看懂, OK, 下面我就直接给出代码, 重点地方提一下.

PDFWriter类

[code lang="java"]
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.Font;
import com.lowagie.text.PageSize;
import com.lowagie.text.Phrase;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfWriter;
import com.quanlei.exception.TweetException;
import com.quanlei.twitter.TweetConsumer;
import com.quanlei.twitter.TweetProducer;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class PdfTwitter implements TweetConsumer {

private int counter = 0;
private Document document;
private Font small_white = new Font(Font.HELVETICA, 8, Font.BOLD);
private Font bold = new Font(Font.HELVETICA, 11, Font.BOLD);
private BaseFont bfChinese = BaseFont.createFont("STSong-Light",
"UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
private Font FontChinese = new Font(bfChinese, 12, Font.NORMAL);
private SimpleDateFormat simpleDateTimeFormat =
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

public PdfTwitter() throws DocumentException, IOException {
document = new Document(PageSize.A4, 72, 36, 36, 36);

PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(
"world.pdf"));
writer.setPageEvent(new TwitterPage());
document.open();
}

public void close() {
document.close();
}

public String tweet(org.dom4j.Element tweet) throws TweetException {
try {
org.dom4j.Element user = tweet.element("user");
PdfPTable table = new PdfPTable(new float[]{1, 10, 12});
table.setTableEvent(new TableBackground());

table.setWidthPercentage(100);
table.setSpacingBefore(8);
table.getDefaultCell().setPadding(5);
table.getDefaultCell().setBorder(Rectangle.NO_BORDER);
table.addCell("");
table.addCell(new Phrase(user.elementText("screen_name"),
small_white));
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_RIGHT);

table.addCell(new Phrase(simpleDateTimeFormat.format(
new Date(tweet.elementText("created_at"))),
small_white));
table.getDefaultCell().setHorizontalAlignment(Element.ALIGN_LEFT);
table.getDefaultCell().setFixedHeight(38);
table.addCell(new Phrase(String.valueOf(++counter), small_white));
table.getDefaultCell().setColspan(2);
Phrase p = new Phrase();
p.add(new Phrase(user.elementText("name"), FontChinese));
p.add(new Phrase(": ", bold));
p.add(new Phrase(tweet.elementText("text"), FontChinese));
table.addCell(p);
document.add(table);
return tweet.elementText("id");
} catch (DocumentException e) {
e.printStackTrace();
throw new TweetException(e);
} catch (IOException e) {
e.printStackTrace();
throw new TweetException(e);
}
}

public static void main(String[] args)
throws DocumentException, org.dom4j.DocumentException,
FileNotFoundException, IOException, TweetException {
PdfTwitter pdf = new PdfTwitter();
TweetProducer t = new TweetProducer();
t.createClient("gavinquan", "123456");
t.setConsumer(pdf);
t.execute(null);
pdf.close();
}
}
[/code]

上面的类已经可以输出PDF了, 但是不是很美观, 加点修饰, 每个Twitter消息一个框, 这是背景:

[code lang="java"]
import java.io.IOException;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPTable;
import com.lowagie.text.pdf.PdfPTableEvent;

public class TableBackground implements PdfPTableEvent {

protected BaseFont bf;

public TableBackground() throws DocumentException, IOException {
bf = BaseFont.createFont(BaseFont.TIMES_BOLDITALIC, BaseFont.WINANSI,
BaseFont.NOT_EMBEDDED);
}

public void tableLayout(PdfPTable table, float[][] width, float[] height,
int headerRows, int rowStart, PdfContentByte[] canvas) {
PdfContentByte cb = canvas[PdfPTable.BACKGROUNDCANVAS];
cb.saveState();
cb.setRGBColorFill(0x9a, 0xe4, 0xe8);
cb.roundRectangle(width[0][0], height[2], width[0][3] - width[0][0],
height[0] - height[2], 4);
cb.roundRectangle(width[0][1], height[2] + 3,
width[0][3] - width[0][1] - 3, height[1] - height[2] - 6, 4);
cb.eoFill();
cb.beginText();
cb.setRGBColorStroke(0x9a, 0xe4, 0xe8);
cb.setFontAndSize(bf, 28);
cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
cb.showTextAligned(Element.ALIGN_LEFT, "\"", width[0][1],
height[1] - 10, 0);
cb.endText();
cb.restoreState();
}
}
[/code]

下面再画蛇添足, 加点外围修饰, 每个PDF页面左侧加上文字, 算是起点宣传作用:

[code lang="java"]
import java.io.IOException;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Element;
import com.lowagie.text.pdf.BaseFont;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfPageEventHelper;
import com.lowagie.text.pdf.PdfWriter;

public class TwitterPage extends PdfPageEventHelper {

protected BaseFont bf;

public TwitterPage() throws DocumentException, IOException {
bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
BaseFont.NOT_EMBEDDED);
}

@Override
public void onEndPage(PdfWriter writer, Document document) {
PdfContentByte cb = writer.getDirectContent();
cb.saveState();
cb.beginText();
cb.setRGBColorStroke(0x9a, 0xe4, 0xe8);
cb.setTextRenderingMode(PdfContentByte.TEXT_RENDER_MODE_STROKE);
cb.setFontAndSize(bf, 18);
cb.showTextAligned(Element.ALIGN_RIGHT, "www.quanlei.com", 68, 806, 90);
cb.endText();
cb.restoreState();
}
}
[/code]

最后配个截图, 为本文画上句号, 注, 由于图缩略了, 效果有点虚了:

英文原版出处: http://www.javaworld.com/javaworld/jw-03-2009/jw-03-write-your-own-twitter-app.html , 本文抽取了原英文的重点, 并配以文字介绍,加工而成.

评论交流

暂无评论,欢迎留下你的想法