« SHOWROOM - イベント貢献ランキング(貢献ポイント)を取得する関数(ソース) | トップページ | SHOWROOM - 自動星集め/種集め - 配信ルームリストの取得(ソース) »

2019年8月 7日 (水)

SHOWROOM - 自動星集め/種集め - 配信ジャンルの取得(ソース)

SHOWROOMで星あるいは種を集めようとするとオンライブ画面から公式枠なりアマチュア枠なりのジャンルに移動して、現在配信されている配信画面を次々に開いていく必要があるのですが、ジャンル構成がたびたび変更されるのでその都度対応=プログラムの修正=を行うということになってました(Showroom - 自動星集め・星投げ・カウントツールなど)

公式枠はジャンルの3番目から7番目、アマチュア枠は10番目というようなハードコーディングをやってるからそうなるわけで、今回はオンライブ画面から公式枠とアマチュア枠のジャンルがどこにあるか調べてジャンル位置のリストを作る関数を作ってみました。

ジャンルの追加、削除、位置の変更に対し、何もしなくてもあるいはデータファイルを変更するだけで対応できるようになります。

記事の最後にソースをおいておきます。利用法を含めかなり細かい説明を入れたつもりなので、記事での説明は省略します。

それからいつジャンルのリストを作業をするかが問題です。星集めのプログラムの中で一日に一回やっておけばじゅうぶんみたいに思えるのですが、これまでの仕様変更(ジャンル枠の変更)は16時とかずいぶん思い切った(?)時間に行われたことがありましたから星集めの都度やっておいた方が無難かも....

なお、こういうをやってると規約上どうなんだろう、という問題が発生するわけですが、それはご自身で考えてください。
すべてアウト(にできるぞ!)、とも受け取れる規約ですが、私の場合は、ここまではセーフ、ここから先はグレー、という線を自分なりに想定してやってます。

ところでこういうのは、ほどほどにしておけばだいじょうぶ、と安心してもいられないことを最近知りました。

  岡崎市立中央図書館事件(Wikipedia)
  「Librahack


SHOWROOMのエンジニアさんは優秀そうだから、こんなことはないと思いますが、お互い気をつけましょう。

---------------------

SHOWROOMのための関数(GO言語、ソース)

  SHOWROOMのイベント参加者のリストを取得する(GO, スクレイピング)
  SHOWROOM - イベントでの配信者の獲得ポイント数を取得する(改良版)
  SHOWROOM - イベント貢献ランキング(貢献ポイント)を取得する関数(ソース)

 
package main
 
import (
	"fmt"
	"os"
	"time"
 
	"github.com/sclevine/agouti"
)
 
/*
	GetCategoryList()
	SHOWROOMの「オンライブ」画面で左側のジャンルのリストから、公式枠の位置と、アマチュア枠の位置を見つけ
	その位置をスライスに格納して戻します。
 
	引数
	page *agouti.Page			ブラウザ画面のポインタ
								nilであれば新たにブラウザを開きます。
	ChromeTmpFolder string		pageがnilの場合はChromeの作業用フォルダを指定します。
								あらかじめ--user-data-dirにこのフォルダを指定してChromeを
								起動し、SHOWROOMの視聴環境を整えておくことが必要です。
								(この関数に限ってであれば、SHOWROOMへのログインは不要です)
								
								Chromeの作業用フォルダの起動オプションを
								  --user-data-dir=E:\tmp\chrome01
								と指定したときは、この引数は
								  "E:\\Tmp\\Chrome11"
								と指定します。
	FilenameOfCatName string	ジャンル名とタイプ(公式枠/アマチュア枠)の対応リストのファイル名を指定します。
								ファイル名だけを指定した場合起動環境にあるファイルを読み込みます。
 
	戻り値
	IdxAmateur	[]int		オンライブ画面でのアマチュア枠ジャンルの位置
	IdxOfficial	[]int		オンライブ画面での公式枠ジャンルの位置
	statas		int			実行結果
							0	正常終了
							-1	カテゴリータイプ対応リストが開けない
							-2	オンライブ画面が開けないか、開けてもデータが取得できない
 
	(2019年8月6日時点では)ジャンル名とタイプの対応リストファイルの内容は以下のようになります。
 
Official	アイドル
Official	タレント・モデル
Official	ミュージック
Official	声優・アニメ
Official	お笑い・トーク
Amateur		アマチュア
 
	これに対してオンライブ画面でのアマチュア枠/公式枠ジャンルの位置は次のような結果が得られます。
 
 IdxAmateur =  10
 IdxOfficial =   3  4  5  6  7
 
--------------------
 
この関数で得られたデータを利用してオンライブの各ジャンルの画面を巡回するときは次のような方法をとります。
 
		//	すべての公式枠について巡回する。
		IdxAmateur, IdxOfficial, status := GetCategoryList(nil, "E:\\Tmp\\Chrome11", "CategoryName.txt")
 
		for i := 0; i < len(IdxOfficial); i++ {
 
			FmtFrame := "#js-categorymenu-list > li:nth-child(%d)"
			SelectorFrame := fmt.Sprintf(FmtFrame, IdxOfficial[i])
			page.Find(SelectorFrame).Click()
			.
			.
			.
		}
 
--------------------
 
	***
	これまで作った(公開)したプログラムにはハードコーディングしたところが多いです。
	少しずつ直してきたのですが、いちばん問題になるジャンルの変更への対応を解決するために
	用意した関数です。
	これで以下のようなケースにプログラムを変更せずに対応できるようになります。
	(もちろん星集め/種集めの関数をこの関数に合わせて変更する必要はあります)
 
	ジャンルの順番	ジャンルの並び順が変更されても影響は受けません。
	ジャンルの削除	対応リストから該当ジャンルを削除します。ただ削除しなくても正常に動作します。
	ジャンルの追加	対応リストの変更が必要です。
				対応リストを変更しない場合、追加されたジャンルがないものとして動作します。
				また、本来のジャンルとは違うもの(カラオケやメンズ枠みたいなやつ)の追加は
				何もしなくて対応できます。
	ジャンル名の変更	対応リストのジャンル名を変更します。
				対応リストを変更しない場合、名称が変更されたジャンルがないものとして動作します。
 
	情報収集にしても星集め、星投げにしてもSHOWROOMのシステムに依存しているわけで、
	プログラムを変更しないで、SHOWROOMの仕様変更すべてに対応できるわけはないのですが...
--------------------
 
	それから、この関数はデータ収集が目的だからagoutiではなくてgoqueryでいいのではないのかという指摘がありそうですが、
	オンライブ画面からのジャンル情報の取得は動的サイトとして扱う必要がありgoqueryでは必要なデータを得ることができません。
	Scrapy + Splash を使えば動的サイトとして扱わなければならない場合もgoqueryで行けるそうですが、まだ実際に
	やってみたことがありません。
	なおagoutiを使うにしてもheadlessで動かせれば負荷の軽減になるのですが、これもまだうまく行っていません。
 
*/
func GetCategoryList(page *agouti.Page, ChromeTmpFolder, FilenameOfCatName string) (
	IdxAmateur, IdxOfficial []int, status int) {
 
	//	カテゴリーリストを読み込みマップに格納します。
	CategoryMap := make(map[string]string)
	var CategName, TypeName string
	FileCategory, err := os.Open(FilenameOfCatName)
	if err != nil {
		//	カテゴリーリストをオープンできなかった場合
		status = -1
		return
	}
	for {
		nod, _ := fmt.Fscanf(FileCategory, "%s%s\n", &TypeName, &CategName)
		if nod != 2 {
			//	読み込んだデータが2個でない場合は読み込みを終了します。
			//	EOFや空白行がデータの終わりということになります。
			break
		}
		CategoryMap[CategName] = TypeName
	}
 
	//	ブラウザが開かれていない場合は、新たにブラウザを開きます。
	//	ここで開いたブラウザはこの関数が終了するときとじられます( ==> defer() 
	if page == nil {
		// Chromeを利用することを宣言
		agoutiDriver := agouti.ChromeDriver()
		agoutiDriver.Start()
		defer agoutiDriver.Stop()
 
		page, _ = agoutiDriver.NewPage(agouti.Desired(agouti.Capabilities{
			"chromeOptions": map[string][]string{
				"args": []string{
					"user-data-dir=" + ChromeTmpFolder, //	Hyperion
				},
			},
		}),
		)
 
	}
 
	//	「オンライブ」画面、左側にジャンルの一覧が表示されています。
	page.Navigate("https://www.showroom-live.com/onlive")
 
	//	画面が安定するまで待つため。これでもダメなときはデータ取得のときリトライを繰り返します。
	time.Sleep(2 * time.Second)
 
	//	表示されているジャンルの名称を上から順番に取得します。
	retry := 0
	for i := 1; ; i++ {
		Selector := fmt.Sprintf("#js-categorymenu-list > li:nth-child(%d)", i)
		NameOfCateg, _ := page.Find(Selector).Text()
		if NameOfCateg == "" {
			if retry < 0 {
				//	すでに少なくともデータを一つ以上取得できているケースです。
				//	すべてのデータが処理済みと考えます。
				break
			}
			if retry > 9 {
				//	ウェイトが10回繰り返された場合は
				//	何らかの理由で画面が開けないものとしエラーを戻します
				status = -2
				return
			}
			//	最初のデータが取得できない場合はウェイトします。
			time.Sleep(2 * time.Second)
			i--
			retry++
			fmt.Println("GetCategoryList() Retry ", retry)
		} else {
			//	データを取得できたとき
			retry = -1
		}
 
		//	カテゴリーリストを参照して公式、アマチュアに振り分けます。
		switch CategoryMap[NameOfCateg] {
		case "Official":
			IdxOfficial = append(IdxOfficial, i)
		case "Amateur":
			IdxAmateur = append(IdxAmateur, i)
		default:
			//	振り分けられないジャンルが存在する場合は
			//		本来の意味ではないジャンル(人気、メンズ、バーチャル、カラオケ、誕生日など)の場合
			//		カテゴリーリストの記述に誤りがある。
			//		カテゴリーリストが最新の状況を反映していない
			//	のいずれかでしょう。
			fmt.Printf("GetCategoryList() No such category or type <%s><%s>.\r\n",
				NameOfCateg, CategoryMap[NameOfCateg])
		}
	}
 
	status = 0
	return
}
 
/*
GetCategoryList()のテスト用プログラム
 
	"CategoryName.txt"の内容はGetCategoryList()の説明にあります。
 
*/
 
func main() {
 
	IdxAmateur, IdxOfficial, status := GetCategoryList(nil, "E:\\Tmp\\Chrome11", "CategoryName.txt")
 
	fmt.Println(" status=", status)
 
	fmt.Print(" IdxAmateur = ")
	for i := 0; i < len(IdxAmateur); i++ {
		fmt.Printf("%3d", IdxAmateur[i])
	}
	fmt.Println()
 
	fmt.Print(" IdxOfficial = ")
	for i := 0; i < len(IdxOfficial); i++ {
		fmt.Printf("%3d", IdxOfficial[i])
	}
	fmt.Println()
 
}
 
 

« SHOWROOM - イベント貢献ランキング(貢献ポイント)を取得する関数(ソース) | トップページ | SHOWROOM - 自動星集め/種集め - 配信ルームリストの取得(ソース) »

パソコン・インターネット」カテゴリの記事

コメント

この記事へのコメントは終了しました。

フォト

サイト内検索

  • 記事を探されるんでしたらこれがいちばん早くて確実です。私も使ってます (^^;; 検索窓が表示されるのにちょっと時間がかかるのはどうにかしてほしいです。

新着記事

リンク元別アクセス数

  • (アクセス元≒リンク元、原則PCのみ・ドメイン別、サイト内等除く)

人気記事ランキング

  • (原則PCのみ、直近2週間)
無料ブログはココログ