arc の日記

はてなダイアリーから引っ越してきました。さらに新しい記事は https://junkato.jp/ja/blog/ で書いています。

論文のタイトルから発行年を取得するJavaプログラム

論文のタイトルが分かっているとき、その発行年をGoogle検索に問い合わせ、結果を標準出力に返すJavaプログラムを書きました。初めはGoogle Scholarから取得しようかと思ったんですが、返ってくるHTMLのコードを読んだところ、パッと見で普通のGoogle検索の結果から引っ張ってくるほうが簡単そうだったので、そのようにしました。

20101106-pysearch.zip

使い方は、次のようにコマンドラインで引数に論文のタイトルを与えるだけです。出版年を取得できなかった場合は -1 が出力されます。

arc@tokinezu ~/20101106-pysearch
$ java PublicationYearSearch "Multi-touch Interface for Controlling Multiple Mobile Robots"
2009
$ java PublicationYearSearch "Hogehoge"
-1

中の処理としては、Google検索の結果として返ってくるHTMLのコードから、出版年らしい部分を切り出しているだけです。当然、Google検索の仕様が変わったら使えなくなります。

なお、この方法はスクレイピングの一種と見なすことができ、Google Apps API Termsの3番目にNo Google Scrapingと書いてある通り、大っぴらには禁じ手です。とはいえ、スクレイピングがどうしていけないのか、どこからどこまでをスクレイピングと呼ぶのかについては議論百出で広大なグレーゾーンが広がっています。また、2008年くらいからGoogle Scholar APIを用意してほしいという要望がたくさん出ているにもかかわらず、Google先生が全く対応してくれない現状ですので、お目こぼしのほどよろしくお願いしますね! > そのあたりの方々

短時間にたくさんリクエストを出すとそれだけでGoogle先生bot認定されることもあるようなので、このプログラムを使用する場合にもシェルスクリプトを書いてぶん回したりするのはお勧めできません。あくまでコマンドラインで手動でタイトルを渡すレベルに留めるのが賢明と思われます。

言い訳のほうが長くなってしまいましたが、以下ソースコードが続きます。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PublicationYearSearch {

	public static void main(String[] args) {
		int year = getPublicationYear(args[0]);
		System.out.print(year);
		if (year < 0) {
			System.exit(1);
		}
	}

	public static int getPublicationYear(String title) {
		int year = -1;
		try {
			// Build query URL.
			StringBuilder sb = new StringBuilder();
			sb.append("http://www.google.co.jp/search?hl=en&q=%22");
			sb.append(URLEncoder.encode(title, "UTF-8"));
			sb.append("%22");
			String urlString = sb.toString();

			// Open HTTP connection.
			URL url = new URL(urlString);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7");
			conn.setRequestProperty("Referer", urlString);
			conn.connect();
			BufferedReader reader = new BufferedReader(
					new InputStreamReader((conn.getInputStream())));

			// Compile regex pattern to look for.
			Pattern pattern = Pattern.compile("<span class=f>(.+?)</span>");
			int openLength = "<span class=f>".length();
			int closeLength = "</span>".length();

			// Load content.
			String line;
			while ((line = reader.readLine()) != null) {
				Matcher matcher = pattern.matcher(line);
				while (matcher.find()) {
					String match = matcher.group();
					String yearString = match.substring(openLength,
							match.length() - closeLength);

					// Get publication year if possible.
					try {
						year = Integer.parseInt(yearString);
					} catch (NumberFormatException nfe) {
						continue;
					}
					break;
				}
				if (year >= 0) break;
			}
			conn.disconnect();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return year;
	}
}