Yahoo! オークション検索

Yahoo! オークションの検索結果を RSS 2.0 で得て、そのまま表示するプログラムです。 Feed をそのまま表示する FeedViewer.java をベースにしています。

FeedViewer.java をベースにしているので、 getElementsByTagName や XPath は利用していません。

URL エンコードの扱い

URL に全角文字や一部の半角記号を使う場合には、 そのままではなく URL エンコードという処理をして、 半角の文字列に変換する必要があります。

Yahoo! オークション検索では、 文字コードが Shift_JIS の全角文字を URL エンコードすることになっています。

            // URLエンコードをしてASCII文字に
            String url = "http://search.auctions.yahoo.co.jp/search_rss?p=" +
                                       URLEncoder.encode(args[0], "Shift_JIS");

ソース

import java.io.*;
import java.net.*;
import java.util.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.w3c.dom.*;

// Yahoo! Auctions を検索 (RSS 2.0)
// 使い方: java YahooAuctionsSearcher word

class Feed {
    private URL url;
    private String encoding;
    private Document document;
    private ArrayList<Item> itemList;
    public Feed() {
        url = null;
        encoding = "utf-8";
        document = null;
        itemList = new ArrayList<Item>();
    }
    public void setURL(String url) {
        try {
            this.url = new URL(url);
        }
        catch(IOException e) {
            System.err.println("間違ったURL: " + url);
        }
    }
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }
    /** URLで指示されたフィードを取得し DOM tree を構築 */
    public void connect() {
        BufferedReader in = null;
        try {
            URLConnection connection = url.openConnection();
            connection.connect();
            InputStream inputStream = connection.getInputStream();
            InputStreamReader reader = new InputStreamReader(inputStream,
                                                             encoding);
            in = new BufferedReader(reader);
        }
        catch (IOException e) {
            System.err.println("接続エラー: " + e);
            System.exit(1);
        }

        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.parse(new InputSource(in));
        }
        catch (ParserConfigurationException e) {
            System.err.println("DocumentBuilderFactory生成エラー:" + e);
        }
        catch (SAXException e) {
            System.err.println("構文エラー:" + e);
        }
        catch (IOException e) {
            System.err.println("入出力エラー:" + e);
        }

        try {
            in.close();
        }
        catch (IOException e) {
            System.err.println("接続終了エラー: " + e);
        }
    }
    /** Feed の内容を Item オブジェクトのリストとして返す */
    public ArrayList<Item> getItemList() {
        try {
            // root要素 (rdf:RDF or rss) を得る
            Element rootElement = document.getDocumentElement();
            String rootElementName = rootElement.getNodeName();
            System.out.println("root element name: " + rootElementName);
            findItems(rootElement);
        }
        catch (DOMException e) {
            System.err.println("DOMエラー:" + e);
        }
        return itemList;
    }
    /** node の下位(子孫)から item 要素を探し Item オブジェクトを得る */
    private void findItems(Node node) {
        for(Node current = node.getFirstChild();
                 current != null;
                 current = current.getNextSibling()) {
            if(current.getNodeType() == Node.ELEMENT_NODE) { // そもそも要素か
                String nodeName = current.getNodeName();
                if(nodeName.equals("item")) {
                    System.out.println("item 要素を発見!");
                    itemList.add(getItem(current));
                          // item 要素から Item オブジェクトを得てリストに追加
                }
                else {
                    System.out.println(nodeName + " 要素をスキップ");
                                        // channel, items などがスキップされる
                    findItems(current); // さらに子ノードを見る (再帰)
                }
            }
        }
    }
    /** item 要素から Item オブジェクトを生成 */
    private Item getItem(Node node) {
        String title = null;
        String link = null;
        String description = null;
        String date = null;
        String creator = null;
        for(Node current = node.getFirstChild();
                 current != null;
                 current = current.getNextSibling()) { // 子ノードを先頭から
            if(current.getNodeType() == Node.ELEMENT_NODE) { // 要素だったら
                String nodeName = current.getNodeName();
                if(nodeName.equals("title"))
                    title = getContent(current);
                else if(nodeName.equals("link"))
                    link = getContent(current);
                else if(nodeName.equals("description"))
                    description = getContent(current);
                else if(nodeName.equals("dc:date") || nodeName.equals("pubDate"))
                    date = getContent(current);
                else if(nodeName.equals("dc:creator"))
                    creator = getContent(current);
                else if(nodeName.equals("guid") ||
                        nodeName.equals("category") ||
                        nodeName.startsWith("dc:") ||
                        nodeName.startsWith("rdf:") ||
                        nodeName.startsWith("dcq:")){
                    ; // 処理しない
                }
                else {
                    ; // 処理しない
                }
            }
            // 要素 (Node.ELEMENT_NODE) でなかったら何もしない (改行など)
        }
        return new Item(title, link, description, date, creator);
    }
    /** title, link, description などの要素から内容を取り出す */
    private String getContent(Node node) {
        String content = "";
        node = node.getFirstChild(); // 子ノードがテキストのはず
        if(node.getNodeType() == Node.TEXT_NODE
             && node.getNodeValue().trim().length() != 0)
            content = node.getNodeValue();
        else if(node.getNodeType() == Node.CDATA_SECTION_NODE)
            content = node.getNodeValue();  // HTMLタグなどを含む
        return content;
    }
}

/** 1つの item 要素に対応するオブジェクトを表すクラス */
class Item {
    private String title;
    private String link;
    private String description;
    private String date;
    private String creator;
    public Item (String title, String link, String description,
                 String date, String creator) {
        this.title = title;
        this.link = link;
        this.description = description;
        this.date = date;
        this.creator = creator;
    }
    /** 各属性を文字列に変換(表示に利用) */
    public String toString() {
        String string = "";
        if(title != null)
            string += "title: " + title + "\n";
        if(link != null)
            string += "link: " + link + "\n";
        if(description != null)
            string += "description: " + description + "\n";
        if(date != null)
            string += "date: " + date + "\n";
        if(creator != null)
            string += "creator: " + creator + "\n";
        return string;
    }
}

class YahooAuctionsSearcher {
    public static void main(String args[]) {
        try {
            // URLエンコードをしてASCII文字に
            String url = "http://search.auctions.yahoo.co.jp/search_rss?p=" +
                                       URLEncoder.encode(args[0], "Shift_JIS");
            System.out.println(url);
            Feed feed = new Feed();
            feed.setURL(url);
            feed.setEncoding("utf-8");
            feed.connect();
            ArrayList<Item> itemList = feed.getItemList();
            // 表示
            Iterator<Item> iterator = itemList.iterator();
            while(iterator.hasNext()) {
                System.out.print(iterator.next().toString());
                System.out.println();
            }
        }
        catch(UnsupportedEncodingException e) {
            System.err.println("Unsupported Encoding.");
        }
    }
}