Obtain favicon.ico from any website

使用wordpress构建的博客在首页的展示链接是可以在链接前加上一个图标,这个图标不能太大,否则看上去很不雅观,其实最好的选择就是通常访问一个网站时在地址栏会出现代表该网站的一个图标,通常我们称之为”favicon”.

今天也发现Google Reader可以在订阅前面直接显示网站的favicon

其实要获取某个网站的favicon并不困难,这个文件或者是在网站根目录下,或者是在<head>中的<link>标签所指的href的路径下,总之是一个.ico的文件。例如http://www.ibm.com/developerworks/cn/java/的favicon url地址为http://www.ibm.com/favicon.ico,而http://www.liferay.com/web/shuyang.zhou/profile的favicon url地址通过查看源文件可以看到是http://cdn.www.liferay.com/osb-theme/images/liferay.ico

为了过把瘾,写了一个类来把任意一个网站的favicon下载到本地,下面简单简单介绍:

为了将来下载的通用性,DownloadTookit类并不局限于下载favicon.ico,你可以使用它来从网络下载任意一个使用http协议的文件,该类的构造方法如下:

public DownloadTookit(Proxy proxy, String fileName) {
this.proxy = proxy;
this.fileName = fileName;
}

考虑到URL类里面有一个方法
URLConnection java.net.URL.openConnection(Proxy proxy) throws IOException
所以不如将DownloadTookit设置成可以使用代理的,那当有一些文件需要代理才能下载时,仍然可以使用这个类来下载,这就方便多了,不过大多数情况下你不需要使用代理,可以在上述方法中传入Proxy.NO_PROXY来指明不使用代理方式,就如我在测试类中的情况一样。

另外DownloadTookit中还包含了两个额外的方法:
public URL getRootDirectory(URL webURL) throws MalformedURLException
和一个针对获取favicon的特殊方法
public URL getFaviconURL(URL webURL) throws MalformedURLException,这个方法首先测试在站点根目录下是否存在一个名为favicon.ico的文件,如果不存在,则通过调用ReadHttpDocument类的
public List<String> getUnestedTags(String match) throws IOException
方法获得源文件中所有的<link>标签元素,逐个查看里面是否有一个href指向一个.ico的文件,如果存在,则这个地址即是favicon的存在路径!

public void obtainResource(URL resourceURL) throws IOException
是主要进行资源获取的方法,传入url就可以将资源下载到构造方法所传入的本地地址。

其实Apache的已经有一个HttpClient可以做到ReadHttpDocument的功能,不过想到只不过要取得一个link标签,还是尽量不要惊动太上老君,自己动手写了一个很简陋,但仍然满足要求的类来做到这件事。
根据html  document中的各种元素是否可以嵌套,这个类总共可以被调用的方法有两个,一个是上面已经讲到的,用来获取不可嵌套的元素,参数为一个属于不可嵌套的标签名作为匹配字符串。另一个用于获取可以嵌套的元素,我的这个方法并没有实现,想必需要用到堆栈,与本文所讨论的问题并没有很大关联,有时间再来完成。
其中最主要的方法如下:

/**
	 * 获得文档中所有类似的不可嵌套的标签对
	 *
	 * @param match
	 *            匹配模式
	 * @return 与匹配模式一致的标签对组
	 * @throws IOException
	 */
	public List getUnestedTags(String match) throws IOException {
		List list = new ArrayList();
		int beginIndex = -1, endIndex = -1;
		String obtainStr = null;

		while ((obtainStr = br.readLine()) != null || sb.length() &gt; 0) {
			if (obtainStr != null) {
				sb = sb.append(obtainStr);
			}
			if ((beginIndex = sb.indexOf(getStartTag(match))) != -1) {
				while ((endIndex = getUnestedEndTagIndex(sb, match)) == -1) {
					if ((obtainStr = br.readLine()) != null) {
						sb = sb.append(obtainStr);
					} else {// 到达文件尾
						return list;
					}
				}
				list.add(sb.substring(beginIndex, endIndex + 1));
				sb = sb.delete(0, endIndex + 1);
			} else {
				sb.setLength(0);
				continue;
			}
		}
		return list;
	}

测试:
我在测试类中的使用方法如下:

	try {
			DownloadTookit downloadTookit = new DownloadTookit(Proxy.NO_PROXY,
					"D:/favicon.ico");
			downloadTookit
					.obtainResource(downloadTookit
							.getFaviconURL(new URL(
									"http://www.sina.com.cn")));
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

有兴趣的同学可以下载src来看下。

现在博客的链接上就有了一点儿的装饰了

You know还在建设期,我在通过不断的改进,希望在这里能给大家带来一些更有用的文章。