読者です 読者をやめる 読者になる 読者になる

彼女からは、おいちゃんと呼ばれています

ウェブ技術や日々考えたことなどを綴っていきます

Jakarta Commons HttpClient によるSBI証券での自動売買(3) - 正規表現でデータ解析編


以前,このブログで,次のように書きました。

今回のコードでは,campanyContentsというBefferdReader型のフィールドに「企業概要」ページの情報を格納しています。後は,正規表現なりで必要なところを抜き出して,煮るなり焼くなりしてください。


が,この後,

正規表現でデータを解析するところまで書いてよ

という要望がございまして。Java正規表現ってあんまり得意じゃないのですが,まぁ,やるだけやってみます。

サンプルコード

import java.io.*;
import java.util.regex.*;

public class ParseCampanyContentsSample {

    /**
     * 企業コード
     */
    private String campanyCd;

    /**
     * 企業概要ページの内容
     */
    private BufferedReader campanyContents;

    /**
     * 企業名
     */
    private String campany;

    /**
     * 優先市場
     */
    private String market;

    /**
     * 会社四季報の記事作成日
     */
    private String memoDay;

    /**
     * 企業サイトのURL
     */
    private String campanyUrl;

    /**
     * 会社四季報に掲載されている業種
     */
    private String gyosyu;

    /**
     * 決算月 数字のみ
     */
    private int kessanMonth;

    /**
     * 特色 【特色】は除く
     */
    private String tokusyoku;

    /**
     * 連結事業または単独事業の売上構成
     * 【連結事業】または【単独事業】という文言は除く
     */
    private String jigyo;

    /**
     * 会社四季報のコメント
     */
    private String memo;

    /**
     * 会社四季報のコメント2
     */
    private String memo2;

    /**
     * 会社四季報の企業概要ページの内容から企業データを取得する
     * 
     * @throws SBI_Exception
     */
    public void perseCampanyContents() {

        Pattern pattern;
        Matcher matcher;

        int i = 0;
        boolean gotJigyo = false;
        boolean gotMemo = false;
        boolean gotMemo2 = false;

        int campanyCdRow = 0;
        int campanyRow = 0;
        int gyosyuRow = 0;
        int jigyoRow = 0;
        int memoRow = 0;

        String line;
        try {
            while ((line = this.campanyContents.readLine()) != null) {
                i++;

                // 当社優先市場
                // なぜか次の正規表現では引っかからなかった
                // pattern =
                // Pattern.compile("<div class=\"margin-1\" style=\"margin-top:2px\">&nbsp;(.+)&nbsp;<font class=\"stext-gray\">(当社優先市場)</font>");

                // 不本意ながら,次の正規表現で拾う
                pattern = Pattern
                        .compile(".*<div class=\"margin-1\" style=\"margin-top:2px\">&nbsp;(.+)&nbsp;<font class=\"stext-gray\">(当社優先市場)</font>.*");
                matcher = pattern.matcher(line);
                if (matcher.matches() == true) {
                    this.market = matcher.group(1);
                }

                // 作成日
                pattern = Pattern
                        .compile("作成日:([0-9]{4})年([0-9]{1,2})月([0-9]{1,2})日");
                matcher = pattern.matcher(line);
                if (matcher.matches() == true) {
                    this.memoDay = matcher.group(1) + "/" + matcher.group(2)
                            + "/" + matcher.group(3);
                    continue;
                }

                // 企業コードから対象行を取得
                // なぜか次のif文だとうまくいかない
        // → 原因が分かり次第,修正します
                // if(line == this.campanyCd) {

                // 不本意ながら,無駄に正規表現を使う
                pattern = Pattern.compile("^" + this.campanyCd);
                matcher = pattern.matcher(line);
                if (matcher.matches() == true) {
                    campanyCdRow = i;
                    campanyRow = campanyCdRow + 2;
                    gyosyuRow = campanyRow + 4;
                    continue;
                }

                // 企業名
                if (i == campanyRow) {
                    this.campany = line.replace("(株)", "");
                    continue;
                }

                // 業種
                if (i == gyosyuRow) {
                    this.gyosyu = line;
                    continue;
                }

                // URL
                pattern = Pattern.compile("【URL】<a href=\"(.+)");
                matcher = pattern.matcher(line);
                if (matcher.matches() == true) {
                    this.campanyUrl = matcher.group(1);
                    continue;
                }

                // 決算月
                // 3月 のパターンと,3.14 のパターンがある
                pattern = Pattern.compile("【決算】([0-9]{1,2})[月.][0-9]{0,2}");
                matcher = pattern.matcher(line);
                if (matcher.matches() == true) {
                    this.kessanMonth = Integer.parseInt(matcher.group(1));
                    continue;
                }

                // 特色
                pattern = Pattern.compile("【特色】(.+)");
                matcher = pattern.matcher(line);
                if (matcher.matches() == true) {
                    this.tokusyoku = matcher.group(1);
                    continue;
                }

                // 事業
                pattern = Pattern.compile("(?:【連結事業】|【単独事業】)(.+)");
                matcher = pattern.matcher(line);
                if (matcher.matches() == true) {
                    this.jigyo = matcher.group(1);
                    jigyoRow = i;
                    gotJigyo = true;
                    memoRow = jigyoRow + 9;
                    continue;
                }

                // メモ
                // なぜか次の正規表現では引っかからなかった
                // → 原因が分かり次第,修正します
                // pattern = Pattern.compile("^【");

                // 不本意ながら次の正規表現で
                pattern = Pattern.compile("【(.+)");
                matcher = pattern.matcher(line);
                if (i >= memoRow && matcher.matches() == true
                        && gotJigyo == true && gotMemo == false) {
                    this.memo = matcher.group(0);
                    memoRow = i;
                    gotMemo = true;
                    continue;
                }

                // メモ2
                pattern = Pattern.compile("【(.+)");
                matcher = pattern.matcher(line);
                if (i >= memoRow && matcher.matches() == true
                        && gotMemo == true && gotMemo2 == false) {
                    this.memo2 = matcher.group(0);
                    gotMemo2 = true;
                    break;
                }
            }

        } catch (IOException e) {
            e.printStackTrace();

        }
    }
}

補足

これでクラスのフィールドにデータをセットできたので,あとはフィールドの値ををデータベースなどに格納すればよろしいかと。


なお,今回のコードでは,例外処理は省略しました。下記のような場合が考え得るので,何らかの例外処理は必要かもしれません。(それも要望がございましたらコメントしてください。また別の機会にがんばってみたいと思います。)

  • セットした企業コード(campanyCd)に対して,対象銘柄がない場合
  • 対象銘柄があっても,それが投資信託などで,会社四季報の情報がない場合

参考書籍

正規表現は,一見とっつきにくそうですが,やってみたら意外と便利で,だけど使いこなそうと思ったら一筋縄ではいかない代物のように感じます。参考書を何冊か買ってみましたが,一番良かったのは次の書籍です。

入門 正規表現 ~検索・置換・テキスト処理に強くなる!

入門 正規表現 ~検索・置換・テキスト処理に強くなる!