質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Scala

ScalaはJava仮想マシンで動作を行うオブジェクト指向型プログラミング言語の1つです。静的型付けの関数型言語で、コンパイルエラーの検出に強みがあります。

Q&A

2回答

2902閲覧

stockedge/netkeiba-scraperの動作の終盤で、エラーが発生して、困っております。

mi-ma-

総合スコア4

Scala

ScalaはJava仮想マシンで動作を行うオブジェクト指向型プログラミング言語の1つです。静的型付けの関数型言語で、コンパイルエラーの検出に強みがあります。

0グッド

0クリップ

投稿2019/08/28 14:43

コード ```### 前提・実現したいこと scala初心者でございます。 stockedge/netkeiba-scraperで競馬情報をスクレイピングしておりますが、終盤の動作の途中で、以下のエラーメッセージが発生しました。 ### 発生している問題・エラーメッセージ [error] (run-main-0) java.lang.RuntimeException: class not found:サラ系3歳以上1勝クラス[指](定量) [error] java.lang.RuntimeException: class not found:サラ系3歳以上1勝クラス[指](定量) [error] at scala.sys.package$.error(package.scala:26) [error] at Util$.str2cls(Main.scala:1855) [error] at FeatureGenerator.$anonfun$gradeChanged$1(Main.scala:1521) [error] at scala.runtime.java8.JFunction1$mcII$sp.apply(JFunction1$mcII$sp.java:12) [error] at scala.Option.map(Option.scala:146) [error] at FeatureGenerator.<init>(Main.scala:1519) [error] at FeatureGenerator$.$anonfun$iterator$2(Main.scala:662) [error] at scala.collection.Iterator$$anon$10.next(Iterator.scala:455) [error] at scala.collection.Iterator$GroupedIterator.takeDestructively(Iterator.scala:1155) [error] at scala.collection.Iterator$GroupedIterator.go(Iterator.scala:1170) [error] at scala.collection.Iterator$GroupedIterator.fill(Iterator.scala:1207) [error] at scala.collection.Iterator$GroupedIterator.hasNext(Iterator.scala:1211) [error] at scala.collection.Iterator.foreach(Iterator.scala:937) [error] at scala.collection.Iterator.foreach$(Iterator.scala:937) [error] at scala.collection.AbstractIterator.foreach(Iterator.scala:1425) [error] at FeatureDao$.$anonfun$rr2f$1(Main.scala:1740) [error] at FeatureDao$.$anonfun$rr2f$1$adapted(Main.scala:1737) [error] at scalikejdbc.DBConnection.readOnly(DBConnection.scala:202) [error] at scalikejdbc.DBConnection.readOnly$(DBConnection.scala:200) [error] at scalikejdbc.DB.readOnly(DB.scala:60) [error] at scalikejdbc.DB$.$anonfun$readOnly$1(DB.scala:175) [error] at scalikejdbc.LoanPattern.using(LoanPattern.scala:18) [error] at scalikejdbc.LoanPattern.using$(LoanPattern.scala:16) [error] at scalikejdbc.DB$.using(DB.scala:140) [error] at scalikejdbc.DB$.readOnly(DB.scala:174) [error] at FeatureDao$.rr2f(Main.scala:1737) [error] at Main$.main(Main.scala:1995) [error] at Main.main(Main.scala) [error] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [error] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) [error] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [error] at java.lang.reflect.Method.invoke(Method.java:498) [error] Nonzero exit code: 1 [error] (Compile / run) Nonzero exit code: 1 [error] Total time: 5435 s, completed 2019/08/28 2:51:53 ### 該当のソースコード [info] Loading project definition from /Users/AAA/Desktop/netkeiba-scraper-master/project [info] Loading settings for project netkeiba-scraper-master from build.sbt ... [info] Set current project to netkeiba-scraper-master (in build file:/Users/AAA/Desktop/netkeiba-scraper-master/) [info] Running Main genfeature 01:21:20.287 [run-main-0] DEBUG scalikejdbc.ConnectionPool$ - Registered connection pool : ConnectionPool(url:jdbc:sqlite:race.db, user:null) using factory : <default> 01:21:20.291 [run-main-0] DEBUG scalikejdbc.ConnectionPool$ - Registered singleton connection pool : ConnectionPool(url:jdbc:sqlite:race.db, user:null) ### 試したこと 当たり前のことではございますが、stockedge/netkeiba-scraperの使い方の通りに最初からコマンドを実行してみました。 (以下マニュアル引用) 以下のコマンドを上から順に実行していけば最後に素性が作成される。 ①sbt "run collecturl" レース結果が載っているURLを収集して「race_list.txt」に保存する。 ②sbt "run scrapehtml" レース結果のHTMLをスクレイピングしてhtmlフォルダに保存する。HTMLをまるごとスクレイピングするので結構時間がかかる。 ③sbt "run extract" HTMLからレース結果を抜き出しSQLiteに保存する。 ④sbt "run genfeature" レース結果を元にして素性を作りSQLiteに保存する。 この④のコマンドを実行後に上記のエラーが発生いたしました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

私も先ほど、まったく同様のエラーが出ました。

scalaはちょっとソースの意味がわからない部分がありましたが、若干修正を加えたり、DBをいじることで問題を回避することができました。
他の作業の合間に2,3時間でやったことを箇条書きでまとめたことを書いておきます。
本来ならば、ソースを移植してpythonで素性データを作成するようなことをしたいのですが、ちょっと忙しく手をつけられないので、とりあえずの措置です。

まず、基本的なことからわかる範囲で説明します。以下、万一間違いがあったら、どなたかご指摘ください。

素性(そせい)とは、特徴量のことです。特徴量を使って機械学習させ、予測なりなんなりするのが目的です。このデータを得るためにwebスクレイピングします。ただし、webから取得した生データはそのまま扱えないので、数値変換などを行うことで、特徴量をまとめたテーブルを作成するということをsbt "run genfeature"で行います。そのコマンドで作成されるテーブルは、featureテーブルです。

上の方(rysh様)が書かれたエラーの原因箇所ですが、これはクラス(グレード)になります。
競馬のグレードと、Main.scalaのソース内のstr2clsMapのArray配列の中身が対応しています。
競馬のルールレースのクラス分け
を参照するとわかりますが、

オープン:0
1600万下:1
1000万下:2
500万下:3
未勝利:4
新馬:5

といったように、sbt "run genfeature"を実行すると、数値に変換されてfeatureテーブルに格納されます。

イメージ説明

エラーの出た箇所は、サラ系3歳以上1勝クラスとなっており、1勝クラスであれば500万下になりますが、500万下という文字列が含まれないためにエラーとなっています(図は、race_infoテーブルのrace_classカラムの、エラーの出たのと同様のデータですが、すでにSQL文で置換してしまったものです)。

エラーを回避するには、ソースを修正するか、テーブルを修正するかします。

まず、準備として、DB Browser for SQLiteをインストールしてください。このアプリは、プラットフォームを選ばない上、使い方が簡単なので、もし導入されていないならば、是非お使いください。
インストールや使い方の説明は長くなるので省かせていただきますが、DB操作がわからないと話にならないので、もしご存知ないならば、調べるなり勉強するなりして覚えてください。

以下、私が行ったことを書きます。

DB Browserでの作業

①DBのバックアップ
race.dbをコピーしてバックアップ用として別のフォルダへ貼り付けてください。

DB Browser for SQLiteを起動し、File>Open DataBaseを選択して、race.dbを開いてください。

②SQL文を発行し、データを修正(その1)

SQLite

1UPDATE race_info SET race_class=REPLACE(race_class,"サラ系3歳以上1勝クラス","サラ系3歳以上1勝クラス500万下");

Execute SQLタブを開き、上記のコードを貼り付けて、実行してください。
イメージ説明
▶のアイコンを押し、successと下段(結果出力)に表示されれば、無事、500万下が挿入されています。エラーになった場合は、結果出力が赤くなります。

③SQL文を発行し、データを修正(その2)
サラ系3歳以上2勝クラスでもエラーが出たので、

SQLite

1UPDATE race_info SET race_class=REPLACE(race_class,"サラ系3歳以上2勝クラス","サラ系3歳以上2勝クラス1000万下");

を実行する。

④SQL文を発行し、データを修正(その3)
サラ系2歳1勝クラスでもエラーが出たので、

SQLite

1UPDATE race_info SET race_class=REPLACE(race_class,"サラ系2歳1勝クラス","サラ系2歳1勝クラス500万下");

を行う。

⑤各テーブルをCSV出力
イメージ説明
Browse Dataでテーブル(payoff,race_info,race_resultの各テーブル)を選択し、File>ExportでCSV形式でエクスポートする。

⑥エラー箇所のrace_idの行番号を確認
イメージ説明
featureテーブルの最後のrace_idに何頭立てのデータが入っているか確認する。
ここでエラーが出た。実際の頭数を満たしていないので、最後のrace_idを持つ行をすべて手動で削除する。
featureテーブルのrace_idが31581を右クリックでDelete Recordし、すべて削除する。

⑦sqlite_sequenceテーブル
sqlite_sequenceテーブルのseqの値を、seqの値(34390)-31580=2810にする。
もしかしたら、必要ないかも。

⑧payoff,race_info,race_resultテーブルのデータの削除
payoff,race_info,race_resultの各テーブルをそれぞれ、sql文でrace_idが1~31580までのデータを削除する。
上から順番に行うこと(そうしないと、外部キー参照エラーになる)

SQLite

1DELETE FROM payoff WHERE race_id < 31581; 2DELETE FROM race_result WHERE race_id < 31581; 3DELETE FROM race_info WHERE id < 31581;

⑨sbt "run genfeature"を再実行
以上で準備ができたので、DB Browserを閉じ、閉じる際に保存するかどうか聞いてくるので、Saveを必ず行う。
再度、sbt "run genfeature"を実行する。

⑩重複行の確認

SQLite

1SELECT * FROM feature WHERE (race_id, horse_id) in ( SELECT race_id, horse_id FROM feature WHERE race_id > 31580 GROUP BY race_id, horse_id HAVING COUNT(*) > 1 );

DB Browserを起動し、念のため、race_idが31581以上の行で重複行がないか確認。何も表示されていなければOK

⑪出力したcsvのインポート
DB Browserから各テーブルのcsvをインポートして、元に戻す。
この場合は、⑧とは逆に、race_infoを一番先にインポートする。

*注意:現時点までのスクレイピングは、sbt "run scrapehtml"を実行することで完了しているので、再び最初からnetkeiba-scraperのコマンドを行う必要はなく、上記の通りに行った時点で、素性データまで最新のものが作られている。

次からのスクレイピングの方法

一定期間ごとにスクレイピングするとして、時間短縮のため、過去1年以内のURLデータ(html自体ではなく、sbt "run collecturl"で行う分)を取得し、スクレイピングを取得済みの一番番号が上のもの以上にする・・・ちょっと何言ってるかわからないw

以下の通りにしてください。

①race_url.txt、race.db、htmlを削除する
race_url.txt、race.dbは、別フォルダにバックアップをコピーしておいてください。
htmlフォルダ内のファイル名で、一番最近のものをメモしておく(私の場合、201910021212.htmlだった)。このファイル名は、②で使う。
htmlを空にする。時間がかかるようであれば、コマンドプロンプトから
イメージ説明

のようにdel *でフォルダ内のファイルをすべて削除する。

なぜrace_url.txt、race.dbを削除するかというと、単純に、netkeiba-scraperの処理を行うと、race_url.txt、race.dbがすでに存在している場合、エラーになり停止するからです。

②Main.scalaを修正する

Main.scalaを2か所修正する。以降、秀丸のようなgrepか正規表現で置換できるエディタがあると便利。
まず、143行目を下記のように書き換える

SQLite

1 //タイム指数が表示される2006年以降のデータだけ使う 2 //filter(file => FilenameUtils.getBaseName(file.getName).take(4) >= "2006"). 3 filter(file => FilenameUtils.getBaseName(file.getName) > "201910021212").

//は、コメントアウトの意味。
ここの処理は、①でメモしたファイル名を拡張子なしで書いているが、そのファイルよりも新しいもののみ、sbt "run scrapehtml"の対象とするということ。よって、それ以前のファイルがあっても、処理されないし、すでにrace.dbに最新の状態でデータが格納されているわけだから、問題はない。

次に、1993行目あたりを次のように書き換える。

SQLite

1 //過去10年分のURLを収集する 2 //RaceListScraper.scrape(period = 12 * 10) 3 RaceListScraper.scrape(period = 12 * 1)

すでに取得している過去10年分のurlを再び取得するのは馬鹿げているので、上記のように1年分にする。
これより短い期間を指定したかったが、わからなかった。

③sbt "run collecturl"を実行する
1年分のURLがrace_url.txtに格納される。
私の環境は、仮想環境内にubuntuを入れてwindows10との共有フォルダ内にnetkeiba-scraper一式を置いて実行しているが、デフォルトのCPU1個とメモリの割り当てで、3分ぐらいで終了した。

④race_url.txtから最新分だけ抜き出す
ある文字が含まれない行を削除するを参考にして、秀丸で、①でメモしたファイル名の201910021212の201910が含まれる行だけ一覧として取り出す(201910となっていると、2019年10月かと思うが、実際は、9月。それ以降のものを取得したいわけ)。
一覧を修正して、race_url.txtの書式通りに直し、race_url.txtでファイル保存する。要するに、1年分のurlを再びスクレイプするのも面倒なので、取得したファイルよりも新しいものだけをスクレイプするということ。
秀丸がなければ、"ある文字が含まれない行を削除する"で検索して、適当なエディタなり正規表現を使うなりして、race_url.txtを加工すると良い。

⑤sbt "run scrapehtml"を実行する
④で加工したにもかかわらず、480個ものurlを取得。20分ほどで処理が終了。

⑥sbt "run extract"を実行する
race.dbが作成されるが、取得済みのurlよりも新しいものがない場合は、データは空である。
DB Browserで確認のこと。

⑥バックアップしたrace.dbから、csv出力し、新しく生成されたrace.dbへインポート
⑥で新しく生成されたrace.dbにデータがなかった場合は、以降、やる必要なし。
①でバックアップを取っておいたrace.dbをDB Browserで開き、payoff,race_info,race_result各テーブルをcsv出力する。
新しく生成されたrace.dbに、バックアップしたrace.dbから出力された各テーブルのcsvを読み込む。
この際、『DB Browserでの作業』の⑪に書いた通り、race_id.csvから読み込む。

⑦重複行の確認
⑩重複行の確認で書いたのを参考に、各テーブルで重複行がないか確認する。
テーブルごとに、どのカラムがあれば一意にデータが決まるかを考えて、SQL文を発行して確認する。
ある行を特定できればよいので、なんなら、すべてのカラムをSQL文に書いてもよい。重複行があるなら、複数行結果表示されるはず。
重複行があったら、削除する。

⑧sbt "run genfeature"を実行する

最新のデータとの差分だけの処理となるので、短時間で済むはず。

以上です。

投稿2019/11/03 10:58

編集2019/11/03 11:11
sengokuK

総合スコア10

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

エラーがでているのは下の文字列が含まれていないからですが、何をしようとしているのかわからないので作者に聞いて見てはどうですか_

scala

1 val str2clsMap = 2 Array( 3 "オープン", 4 "1600万下", 5 "1000万下", 6 "500万下", 7 "未勝利", 8 "新馬").zipWithIndex

投稿2019/10/20 17:22

rysh

総合スコア874

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問