#class boatRaceclass
#ボートレース公式サイトから情報を取得するスクレイピング
#2021/07/07
#環境:OS:Mac, 言語python, anaconda, 作業をするときはanaconda jupterのエディタよりMS Codeのほうがやりやすい。
#boatRaceCompuYosou.ipynbで使用
'''
・仕様
ボートレース公式https://www.boatrace.jp/
から「本日の払戻金一覧」のところを
https://www.boatrace.jp/owpc/pc/race/pay?hd=20210707
クリックして、本日開催の場の開催時間をクリックする
出走表から結果までの情報を掲載したページが表示される
コンピュータ予想 https://www.boatrace.jp/owpc/pc/race/racelist?
その中のコンピュータ予想タブをクリック
「予想フォーカス」という項目に予想が表示される。
例
蒲郡 jcd=07 hd=20210707
1R rno=1
https://www.boatrace.jp/owpc/pc/race/racelist?rno=1&jcd=07&hd=20210707
・目的:予想するレースの当選番号が出現するかもしれないレースを探す。
例えば6-全-全が出現するレースを探したい。その場合コンピュータ予想の予想フォーカスで予想されている番号に
6-全-全に該当しそうな予想が表示されていたら、そのレースを出力したい。
なお、コンピュータ予想は当然ながら、当たるとは限らず確率は低い
すべてのレースの予想を手動で探すのは面倒なので、自動で探したい。
・機能:該当のページから必要な情報を取得して、該当の情報(ここでは予想番号)を取得して
CSV出力する。
作業内容
1.「本日の払戻金一覧」
https://www.boatrace.jp/owpc/pc/race/pay?hd=20210707
から開催される全レースのURLを取得
2.取得したURLを場ごとに取得。ループ
3.レースごとにループする
41。「予想フォーカス」という項目のところの予想番号を取得する
5.予想する番号、例えば6,1の数字があればリストに追加する
6.CSV出力する。該当する予想がなければ出力しない。
※使用上の注意点
一度のスクレイピングとCSV出力だけに対応していて、前回作成したCSVを読み込んで処理を追記する仕様はない。
一度作成したファイルに、レース結果を追記変更することはしない。
ようするに、朝実行して、夜に実行しても朝作成したファイルを読み込んで処理をするものではない。
朝実行すると、朝のファイルを作成し、その後実行すればそのWebサイトの情報をCSVとして出力する。
>>なぜこのように書くかというと、レースが開催されるとレース結果が払戻金一覧ページに表示される。
レースが開催されていない場合は時刻が記載されていて、出力されるCSVの記載が異なるので、後日CSVの内容を
確認すると、「レース結果が後で追記されるのか?」と勘違いするかもしれない。
現時点では、払戻金一覧のレース前の状態で、コンピュータ予想を知りたいだけの機能である。
よってコンピュータ予想を取得してCSV出力して、その後の処理は何もしない。
'''
import os
import re
import sys
import time
import datetime
import glob#ファイル一覧
import shutil#move
from selenium import webdriver
import pandas as pd
import requests#スクレイピング
from bs4 import BeautifulSoup#スクレイピング
#場管理クラスを作成する
#まだこれから
class jouKanriClass():
def __init__(self):
#dict3 = {'JP':'Tokyo','US':'Washington'} #要素に文字列を入れて初期化
self.jouCode=0
self.jouName=""
#連想配列 print(dict[1])#大村と出力される びわこだけひらがな
self.dict={1:'桐生',2:'戸田',3:'江戸川',4:'平和島',5:'多摩川',6:'浜名湖',7:'蒲郡',8:'常滑',\
9:'津',10:'三国',11:'びわこ',12:'住之江',13:'尼崎',14:'鳴門',15:'丸亀',16:'児島',17:'宮島',\
18:'徳山',19:'下関',20:'若松',21:'芦屋',22:'福岡',23:'唐津',24:'大村'}
def getJouCode(self,jouName):#場名から数字コードを出力する
for key, value in self.dict.items():
if jouName == value:
return key
return "There is no such Key"
#個別レースクラス 1レースのコンピュータ予想情報を管理する。選手情報を管理するためのものではない
class raceCompuKanriClass():
def __init__(self,jouCode,raceNum,starttime,cdate,neraime,url):
self.jouCode=jouCode
self.raceNum=raceNum
self.time=starttime#時刻
self.date=cdate#日付
self.neraime=neraime#6を狙い目としている
self.kekka=""#レース結果,結果が表示されていれば。時間帯によって結果が表示されている
#以降のデータは随時取得してから挿入する
self.url=url
self.star=""#コンピュータ予想の自信度の星、1-5
self.nirentanyosou=[]#コンピュータ予想2連単予想
self.sanrentanyosou=[]#コンピュータ予想3連単1-6
self.playerNumAndClass=[]#6人分の入れる
#個別場クラス、個別レースクラスをオブジェクト配列として管理する
class jouClass():
def __init__(self,jouCode):
self.jouArray=[]#初期化 raceCompuKanriClassオブジェクトを追加する
self.jouCode=jouCode
def getJouCodeHikisuNashi(self):
return self.jouCode
#レース情報クラス、個別の場のレース情報をまとめて管理する
class boatRaceClass():
def __init__(self):
self.tableCount=0#テーブルのカウンター
self.totalJouCount=0
self.zenjouArrayPositionCount=0#全部の場の配列の位置把握のためのカウンター
self.zenjouArray=[]#全部の場の配列
self.neraime=6#狙い目
self.baseurl='https://www.boatrace.jp'
#self.jouCode=jouCode
def seleniumReadPage(self,furl):#selenium版、requestでうまく取得できないClassがあった
# 仮想ブラウザ起動、URL先のサイトにアクセス
driver = webdriver.Chrome('/usr/local/bin/chromedriver')#ここでエラーになったらパスが違うかchromedriverをインストールする
#他のエラーで、「unexpectedly exited. Status code was:-9」だったら、Macの場合はシステム環境設定 → セキュリティとプライバシー で許可すればよい
#
driver.get(furl)
soup=driver
return soup
def readPage(self,furl):
#URL読み込み 2021/07/07
r = requests.get(furl)#requestsを使って、webから取得
soup = BeautifulSoup(r.text, 'lxml') #要素を抽出
return soup
def checkFileExist(self,filename,path):
#他にもimport os os.listdir(path) というのもある
if path[-1]=='/':#文字列の最後の1文字がスラッシュならなにもしない。
pass
else:
path+='/'
files=glob.glob(path+'*.csv')
count=0
for file in files:
#print(file)
if filename in file:#ファイル名を含んでいればTRUE
print('ファイルは存在します')
print(file)
count+=1
return True#見つかれがすぐに抜ける
#ループで探し終わってから判断もいれる
if count>0:
return True
else:
print('ファイルはありません')
return False
#end def
def outputCSV(self,columArray,filename,path):#def checkFileExist()を事前にチェックすると良い
#CSV出力用
#TableColumクラス専用のCSV出力、通常の配列は使用できない。
#path='/Users/toshiromaseda/Documents/2021年/2021年株/yahoofinance_data/'
if filename.find('.csv')==-1:#拡張子.csvがないときは付記する
filename+='.csv'
os.chdir(path)#ディレクトリ変更
print(os.getcwd())#ディレクトリ確認
try:
ofile=open(filename,'tw')
except FileNotFoundError as e: # FileNotFoundErrorは例外クラス名
print("ファイルが見つかりません", e)
sys._exit()#ファイルがなければ終了#tryのときは_exit()が良いらしい
except Exception as e: # Exceptionは、それ以外の例外が発生した場合
print(e)
for i in columArray:
ofile.write(i+'\n')
ofile.close()
def tupleBunkai(self,tuple):#タプルtupleを処理するためのものtupleはタプルで配列ではない
tmp=[]
stockStr=""
for inside in tuple:
for m in inside:
stockStr+=str(m)+','
tmp.append(stockStr)
stockStr=""
output=tmp
return output
def neraimeHantei(self,obj):
#6かどうかを判定する。イコール=があった場合も処理する
#今後複数もしくは別の狙い目についてもどうするか考える。
#class raceCompuKanriClass()
#self.star=""#コンピュータ予想の自信度の星、1-5
#self.nirentanyosou=[]#コンピュータ予想2連単予想
#self.sanrentanyosou=[]#コンピュータ予想3連単1-6
tmp=[]
tuple=()#こっちはタプルでタプルをtmp配列に入れる
#見出しを与える 時刻は締め切り時刻、「結果」はレース終了となったときWebに更新されるので実行する時刻によっては空欄になる
tuple=('日付','時刻','場コード','場名','レース番号','結果','予想')
tmp.append(tuple)
if self.neraime=="":
print("self.neraimeに値がありません。設定していないと探せません")
exit()
for i in range(0,len(obj)):
print("狙い目作業中------------")
print("場コード:"+str(obj[i].jouArray[0].jouCode))
for m in range(0,len(obj[i].jouArray)):
#class jouKanriClass():これに場名があるprint(dict[1])#大村と出力される
jouNameObj=jouKanriClass()
jouName=jouNameObj.dict[obj[i].jouArray[m].jouCode]
#print(jouName,str(obj[i].jouArray[m].raceNum),str(obj[i].jouArray[m].kekka),str(obj[i].jouArray[m].url))
#配列 obj[i].jouArray[m].nirentanyosou
stockStrNirentan=""
stockStrSanrentan=""
for f in range(0,len(obj[i].jouArray[m].nirentanyosou)):
stockStrNirentan=obj[i].jouArray[m].nirentanyosou[f]
if stockStrNirentan=="":
pass
else:
#print("stockStrNirentan2連単:"+stockStrNirentan)
if (str(self.neraime) in stockStrNirentan):
#タプルで追加する jouCodeを場名に変える
tuple=(obj[i].jouArray[m].date,obj[i].jouArray[m].time,obj[i].jouArray[m].jouCode,jouName,'R'+str(obj[i].jouArray[m].raceNum),obj[i].jouArray[m].kekka, stockStrNirentan)
tmp.append(tuple)
# #該当のオブジェクトをtmpに入れる。もしかすると該当するレースだけを入れてもよい。
#もし、1-12全部に狙い目があると全部同じ場が入ることになるのでそれを避ける必要がある。
#やっぱりレースごとにするか考える。
tuple=()
#配列 obj[i].jouArray[m].sanrentanyosou
for f in range(0,len(obj[i].jouArray[m].sanrentanyosou)):
stockStrSanrentan=obj[i].jouArray[m].sanrentanyosou[f]
if stockStrSanrentan=="":
pass
else:
#print("stockStrSanrentan3連単:"+stockStrSanrentan)
if(str(self.neraime) in stockStrSanrentan):
tuple=(obj[i].jouArray[m].date,obj[i].jouArray[m].time,obj[i].jouArray[m].jouCode,jouName,'R'+str(obj[i].jouArray[m].raceNum),obj[i].jouArray[m].kekka, stockStrSanrentan)
tmp.append(tuple)
#tmp=obj
if len(tmp)==1:#見出し行のみなら狙い目はない
print("狙い目はありませんでした。")
output_tuple=tmp
return output_tuple
def getIchiran(self,ftoday):
#「本日の払戻金一覧」から各場のレースのURLを取得する
#https://www.boatrace.jp/owpc/pc/race/pay?hd=20210707
jouname=""#場名
jouCount=0#場コードではなくて、左上から数えた番号
self.totalJouCount=0#総合的な場数
#場名とコードクラスオブジェクト
jouKanri=jouKanriClass()
print("getIchiran_START")
print("処理中")
if ftoday=='':
print("注意!!日付が設定されていませんのでダミーの日付で処理します")
ftoday='20210707'#ダミーデータ
target_url='https://www.boatrace.jp/owpc/pc/race/pay?hd='
mysoup=self.readPage(target_url+ftoday)#class内のメソッド
main=mysoup.find(class_='l-main')
mainInner=main.find(class_='l-mainInner')
contentsFrame1=mainInner.find(class_='contentsFrame1')
contentsFrame1_inner=contentsFrame1.find(class_='contentsFrame1_inner')
#table1クラスは複数存在する。そして列が5個ある.その1個が1場分
table1_class=contentsFrame1_inner.find_all(class_='table1')#こっちはクラスのテーブル
print(str(len(table1_class))+"テーブル分、1テーブル5場")
for i in range(0,len(table1_class)):
table1_tag=table1_class[i].find("table")#ここにタグのTableタグ以降の情報が入る
if table1_tag==None:#'該当なし、1件もない場合、中身がない'
print("table情報を取得できませんでした。")
else:
jouCount=0
self.tableCount+=1
thead=table1_tag.find("thead")
#trが2行あるけど1行目のclass指定はない
trs=thead.find_all("tr")
for m in range(0,len(trs)):
if m==0:#TR1行目だけ処理
ths=trs[m].find_all("th")
for l in range(0,len(ths)):
if l>0:
area_type1=ths[l].find('div', class_=lambda s:s.startswith("table1_area type1"))#クラスの空白の場合はこのようにする
#.find_all('div', class_=lambda s:s.startswith('foo bar '))
if area_type1==None:
print("area_type1が見つかりませんでした")
else:
areaName=area_type1.find(class_="table1_areaName")#classのときはclass_=が必要でタグのときは不要
if areaName==None:
print("areaNameが見つかりませんでした")#2021/07/07
else:
jouCount+=1
self.totalJouCount+=1
imgAlt=areaName.find("img")
jouName=imgAlt.get('alt', '')
#ここは配列にして格納する。クラスオブジェクトにカプセルにする
jouCode=jouKanri.getJouCode(jouName)#コードを出力する。びわこはひらがなで登録されている
#print(str(jouCount)+jouName+str(jouCode))
tmp=jouClass(jouCode)
self.zenjouArray.append(tmp)#次はraceCompuKanriClassオブジェクトにレース情報を与えて、zenjouArrayに入れる
tmp=""
#次へtbodyレース時刻と個別レースのURLを取得する。ただし、レース後は時刻ではなくて結果が挿入される
tbodys=table1_tag.find_all("tbody")
rCount=0
for n in range(0,len(tbodys)):
if rCount==12:#12レースでリセット
rCount=0
rCount+=1
tbody_tr=tbodys[n].find("tr")
r_th=tbody_tr.find("th")#レース番号が入る
#a.rstrip("B")末尾のBを削除
print(r_th.get_text().rstrip("R"))#tag内のテキストを取得、1とか12とかが出力される
tbody_tds=tbodys[n].find_all("td")#tdは複数存在する。なお列ごとに場が異なる。いわゆるエクセルのセルの列情報みたいな感じ
#tdの数を数えつつ、必要な場にデータを与える感じになる。
tdCount=1#ここは1から
#raceTdCount=0#セル3つごとのためのカウンター
inTableJouCount=0#1-5を数えるためのもの
for f in range(0,len(tbody_tds)):
#if tdCount==jouCount:#Max5件、jouCountはテーブルごとに場の数が変わるのでここで調整
if tdCount==4:
tdCount=1
#レース結果の場合はnumberSet1_rowが存在する。朝のレース開始前は時刻が表示される
numberSet1_row=tbody_tds[f].find(class_="numberSet1_row") #次はここから
starttime=""
url=""
if tdCount==1:#1個目だけ処理をする
inTableJouCount+=1
if inTableJouCount>5:
print("inTableJouCountが5を越えたのでインクリメントが間違っています")
#全部の場の配列の位置把握のためのカウンター、テーブルごとに場の情報を入れるので、テーブルループで位置を把握できるようにする。つまり配列の要素数
self.zenjouArrayPositionCount=((self.tableCount-1)*5+inTableJouCount)-1#配列は0からなので1を引いておく
#print("場位置配列の要素位置:"+str(self.zenjouArrayPositionCount))
if self.zenjouArrayPositionCount<0:
print("self.zenjouArrayPositionCountの値が間違っています。マイナスです")
if numberSet1_row==None:
#print("レースは開始前でまだ結果が表示されていないのでセルが違います")
#次はここから2021/07/08 午前10時12分
atag=tbody_tds[f].find("a")
if atag==None:
#print("atag がありません")
pass
else:
starttime=atag.get_text()
tmp=""
tmp=atag.get('href')
#URLは、そのままのリンクでは、コンピュータ予想のURLになっていないので置換が必要replace(before,after)
#racelistをpcexpectに置換する
url=tmp.replace('racelist','pcexpect')
#レースオブジェクト作成 raceCompuKanriClass(jouCode,raceNum,starttime,cdate,neraime,url)
#print(starttime.strip())#.strip()で前後の空白文字を削除
tmpjouCode=self.zenjouArray[self.zenjouArrayPositionCount].getJouCodeHikisuNashi()
raceObj=raceCompuKanriClass(tmpjouCode,rCount,starttime.strip(),ftoday,self.neraime,self.baseurl+url)
self.zenjouArray[self.zenjouArrayPositionCount].jouArray.append(raceObj)
else:
#レース結果raceresult
td_url=tbody_tds[f].get('data-href')
if td_url=="":
pass
else:
url=td_url.replace('raceresult','pcexpect')
spans=numberSet1_row.find_all("span")
kekkaStr=""
for x in range(0,len(spans)):
kekkaStr+=spans[x].get_text()
#print(str(tdCount)+"結果"+kekkaStr)
tmpjouCode=self.zenjouArray[self.zenjouArrayPositionCount].getJouCodeHikisuNashi()
if url=="":
raceObj=raceCompuKanriClass(tmpjouCode,rCount,"",ftoday,self.neraime,"")
else:
raceObj=raceCompuKanriClass(tmpjouCode,rCount,"",ftoday,self.neraime,self.baseurl+url)
raceObj.kekka=kekkaStr
self.zenjouArray[self.zenjouArrayPositionCount].jouArray.append(raceObj)
print("結果"+kekkaStr)
tdCount+=1
#theadを使うか、それともTRタグでいいのかどうか
print(str(self.totalJouCount)+"場数")
output=self.zenjouArray
return output
def getCompuYosou(self,arrayObj):#arrayObjはgetIchiran()の出力配列オブジェクトのURLを使用する
#レースのコンピュータ予想を取得する
tmp=arrayObj
raceYosou=[]#レース予想の配列
for i in range(0,len(tmp)):
print("残り:"+str(len(tmp)-i))
print("場コード:"+str(tmp[i].jouArray[0].jouCode))
for m in range(0,len(tmp[i].jouArray)):
#print(str(output[i].jouArray[m].raceNum))
#print(str(output[i].jouArray[m].raceNum),str(output[i].jouArray[m].kekka),str(output[i].jouArray[m].url))
tmpurl=tmp[i].jouArray[m].url
if tmpurl=='':
print("結果を参照してください。")
continue
else:
#self.getCompuYosouSeleniumSub(tmpurl)
raceYosou=self.getCompuYosouSub(tmpurl)#1レース分のコンピュータ予想を登録する。この中で秒待ち設定あり
time.sleep(2)
if len(raceYosou)>0:
#コンピュータ予想を取得して、tmpオブジェクトに入れる
#star
#tmp[i].jouArray[m].star=mstar
for f in range(0,len(raceYosou)):
#次はここから、2021/07/09
if len(raceYosou[f])==3:
#2連単でクラスメソッドに分ける
tmp[i].jouArray[m].nirentanyosou.append(raceYosou[f])
else:#3連
tmp[i].jouArray[m].sanrentanyosou.append(raceYosou[f])
else:
print("コンピュータ予想が取得できませんでした。")
if len(tmp)==0:
print("情報がありません。どこかでエラーが発生したと思われますのでチェックしてください")
output=tmp#全場オブジェクト配列を出力する。次はCSV出力
return output
#requestで正常にHTMLが取得できないClassがあったのでSeleniumにしてみた. しかしうまくいかず。結論は、URLが違ってたのと、findの指定が間違ってた
def getCompuYosouSeleniumSub(self,url):
#selenium版
mysoup=self.seleniumReadPage(url)#selenium
#https://www.boatrace.jp/owpc/pc/race/racelist?rno=10&jcd=05&hd=20210708
print("作業中")
time.sleep(2)
main=mysoup.find_element_by_class_name('l-main')
mainInner=main.find_element_by_class_name('l-mainInner')
contentsFrame1=mainInner.find_element_by_class_name('contentsFrame1')
contentsFrame1_inner=contentsFrame1.find_element_by_class_name('contentsFrame1_inner')
if contentsFrame1_inner==None:
print("contentsFrame1_innerが見つかりません")
else:
grid_is_type3_h_clear=contentsFrame1_inner.find_elements_by_css_selector(".grid.is-type3.h-clear")
#grid_is_type3_h_clear=contentsFrame1_inner.find('div', class_=lambda c:"grid" in c and "is-type3" in c and "h-clear" in c)
#test=contentsFrame1_inner.find_all('div', class_=lambda c:"grid" in c and "is-type3" in c and "h-clear" in c)
#print(test)
#grid is-type3 h-clear grid is-type3 h-clear
if grid_is_type3_h_clear==None:
print("grid_is_type3_h_clearが見つかりませんでした code112")
else:
contentsFrame1_inner.text
grid_units=contentsFrame1_inner.find_elements_by_class_name('grid_unit')
print("grid_units:"+str(len(grid_units)))#ここが0だ
for f in range(0,len(grid_units)):
if f==0:#1番目だけあればOK
numberSet2_hclear=grid_units[f].find_elements_by_css_selector('.numberSet2.h-clear')
#numberSet2_hclear=grid_units[f].find('div', class_=lambda s:s.startswith("numberSet2 h-clear"))
if numberSet2_hclear==None:
print("numberSet2 h-clearが見つかりませんでした")
else:
numberSet2_units=numberSet2_hclear.find_elements_by_class_name('numberSet2_unit')#複数numberSet2_unit
for i in range(0,len(numberSet2_units)):
numberSet2_rows=numberSet2_units[i].find_elements_by_class_name('numberSet2_row')
strSpan=numberSet2_rows.text #spanにはされた文字
print(strSpan)
for m in range(0,len(numberSet2_rows)):#2単も3単も同じクラス名
spans=numberSet2_rows[m].find_elements_by_tag_name('span')
yosouNumStr=""
for x in range(0,len(spans)):
yosouNumStr+=spans[x].text
print(strSpan,yosouNumStr)
state2=grid_units[f].find_element_by_class_name('state2')
spans=state2.find_elements_by_tag_name('span')
for k in range(0,len(spans)):
if k==1:
print(spans[k])
print("end")
output=""
return output
def getCompuYosouSub(self,url):
mysoup=self.readPage(url)#class内のメソッド
#https://www.boatrace.jp/owpc/pc/race/racelist?rno=10&jcd=05&hd=20210708
#上記を取得するので
#racelistをpcexpectに置換する。このメソッドでは置換しない予定
#https://www.boatrace.jp/owpc/pc/race/pcexpect?rno=10&jcd=05&hd=20210708 こっちだ
#自信度の星数は、クラス名の総当りでNONEでなければありと判断する
#print(url)
#print("コンピュータ予想ページ読み込み、解析中")
raceYosou=[]#初期化 1レース分のコンピュータ予想を格納する配列
#print("秒待ち設定少々待ってください。連続取り込みしません。")
time.sleep(3)
main=mysoup.find(class_='l-main')
mainInner=main.find(class_='l-mainInner')
contentsFrame1=mainInner.find(class_='contentsFrame1')
contentsFrame1_inner=contentsFrame1.find(class_='contentsFrame1_inner')
if contentsFrame1_inner==None:
print("contentsFrame1_innerが見つかりません")
else:
#Classに空白が含まれていた場合の対応
grid_is_type3_h_clear=contentsFrame1_inner.find('div', class_=lambda c:"grid" in c and "is-type3" in c and "h-clear" in c)
#test=contentsFrame1_inner.find_all('div', class_=lambda c:"grid" in c and "is-type3" in c and "h-clear" in c)
#print(test)
#grid is-type3 h-clear grid is-type3 h-clear
if grid_is_type3_h_clear==None:
print("grid_is_type3_h_clearが見つかりませんでした code112")
else:
grid_units=contentsFrame1_inner.find_all(class_='grid_unit')
#print("grid_unitの数:"+str(len(grid_units)))#ここが0だとだめ
for f in range(0,len(grid_units)):
if f==0:#1番目だけあればOK
numberSet2_hclear=grid_units[f].find('div', class_=lambda s:s.startswith("numberSet2 h-clear"))
#tmpCount=grid_units[f].find_all(class_='numberSet2_unit')
#print('numberSet2_unitのCount'+str(len(tmpCount)))
if numberSet2_hclear==None:
print("numberSet2 h-clearが見つかりませんでした")
else:
numberSet2_units=numberSet2_hclear.find_all(class_='numberSet2_unit')#複数
#print("2連単をチェック"+str(len(numberSet2_units)))
for i in range(0,len(numberSet2_units)):
numberSet2_rows=numberSet2_units[i].find_all(class_='numberSet2_row')
#yosouNumStr=""
for m in range(0,len(numberSet2_rows)):#2単も3単も同じクラス名
spans=numberSet2_rows[m].find_all('span')
divStr=numberSet2_rows[m].get_text()#spanにはされた文字を取得
tmpstr=divStr.strip().replace('\t','')
pStr=tmpstr.replace('\r\n','')#文字化けしてた理由がわかったCRLFの改行になっているから\nだけを消すと文字化けする
#print("spanの間の文字"+pStr)
raceYosou.append(pStr)
#配列に入れてoutputすればOK
#星についてはタプルで出力できるか、まだ
#選手番号と級別の取得
print("end")
output=raceYosou#コンピュータ予想配列を出力する、級別のオブジェクトをタプルで出力する
return output
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#---ここは継承クラスboatRacePlayerClass() 選手クラスを追加するためのバージョン
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#--------------------------------------------------------------------
#継承クラスを作る利点は、すでに動いているものは壊さないために別クラスを作成する。手間を省いて、追加したい機能だけを効率よく作りたい
#使用するときはクラス名を変えるだけでメソッドは極力そのままとする。(機能追加や大幅変更は、メソッド名を変更したほうがわかりやすい)
class boatRacePlayerClass(boatRaceClass):
#基底クラスのboatRaceClassと異なる点は、級別(選手番号)を追加したこと。級別取得スクレイピングとCSVに出力する部分を書き直す
#オーバーライドの記載は一部変数が追加のときにsuper()を使うと基底クラスのメソッドの一部をそのまま使えて、その分を記載する必要がない
#今回はsuperを使う必要はないかも
def allA1Reject(self,tupleObj):
#1-6コース全員がA1は除外する。タプルが配列に入っているオブジェクト
print("1-6コース全員がA1は除外するallA1Reject()")
tmpObj=[]#タプルの入った配列オブジェクトを再編する
for getstr in tupleObj:
#今の所、A1という文字が存在するのは選手クラスだけなのでタプル全部のセルを検索してA1があるかをチェックする。
count=0
for m in range(0,len(getstr)):
if ('A1' in str(getstr[m])):
count+=1
if count>=6:
pass
#print("みんなA1だったのでリストに入れない:"+getstr)
else:
tmpObj.append(getstr)
output_tuple=tmpObj
return output_tuple
def playerClassGet(self,soup):
#ここは継承クラス boatRacePlayerClass
#選手番号と級別の取得 getCompuYosouSub()の中で使用する
playerClassObj=[]
classTables=soup.find_all(class_='table1')#class名で[tabel1]と[table1 aaa]が存在するとtable1は複数あるとみなされる
#print(len(classTables))
for x in range(0,len(classTables)):
table=classTables[x].find("table")
tbodys=table.find_all("tbody")
#print("playerClassGet処理中")
for i in range(0,len(tbodys)):
trs=tbodys[i].find_all("tr")
#print("tr取得")
for f in range(0,len(trs)):
if f==0:#f=0のときだけ
tds=trs[f].find_all("td")
for m in range(0,len(tds)):
#print("td処理中")OK
is_fs11=tds[m].find_all(class_='is-fs11')
#ここで正常にis-fs11が取得できていない。2021/07/11、12:13、なんで? table1クラスが実は同名を含む複数が存在したから。
#print(str(m)+"番目"+str(len(is_fs11)))
for l in range(0,len(is_fs11)):
#print("is_fs_112処理中")
if l==0:#選手クラスは0要素の1番目
#単体テスト用の確認用メソッドを作る必要があるな。統合して実行すると15分以上かかるから。2021/07/11
#print(is_fs11[l].get_text().strip().replace('\t','').replace('\r\n',''))
#左から順に処理が進む、stripしてreplaceしてまたreplaceして、、、
playerClassObj.append(is_fs11[l].get_text().strip().replace('\t','').replace('\r\n',''))
if len(playerClassObj)==0:
print("playerClassObjの数が0です。")
output=playerClassObj
return output
#オーバーライド
def neraimeHantei(self,obj):
#6かどうかを判定する。イコール=があった場合も処理する
#今後複数もしくは別の狙い目についてもどうするか考える。
#class raceCompuKanriClass()
#self.star=""#コンピュータ予想の自信度の星、1-5
#self.nirentanyosou=[]#コンピュータ予想2連単予想
#self.sanrentanyosou=[]#コンピュータ予想3連単1-6
#選手クラスを追加した。
tmp=[]
tuple=()#こっちはタプルでタプルをtmp配列に入れる
#見出しを与える 時刻は締め切り時刻、「結果」はレース終了となったときWebに更新されるので実行する時刻によっては空欄になる
tuple=('日付','時刻','場コード','場名','レース番号','結果','予想','枠1','枠2','枠3','枠4','枠5','枠6')
tmp.append(tuple)
if self.neraime=="":
print("self.neraimeに値がありません。設定していないと探せません")
exit()
for i in range(0,len(obj)):
print("狙い目作業中------------")
print("場コード:"+str(obj[i].jouArray[0].jouCode))
for m in range(0,len(obj[i].jouArray)):
#class jouKanriClass():これに場名があるprint(dict[1])#大村と出力される
jouNameObj=jouKanriClass()
jouName=jouNameObj.dict[obj[i].jouArray[m].jouCode]
#print(jouName,str(obj[i].jouArray[m].raceNum),str(obj[i].jouArray[m].kekka),str(obj[i].jouArray[m].url))
#配列 obj[i].jouArray[m].nirentanyosou
stockStrNirentan=""
stockStrSanrentan=""
for f in range(0,len(obj[i].jouArray[m].nirentanyosou)):
stockStrNirentan=obj[i].jouArray[m].nirentanyosou[f]
if stockStrNirentan=="":
pass
else:
#print("stockStrNirentan2連単:"+stockStrNirentan)
if (str(self.neraime) in stockStrNirentan):
tuple=(obj[i].jouArray[m].date,obj[i].jouArray[m].time,obj[i].jouArray[m].jouCode,jouName,\
'R'+str(obj[i].jouArray[m].raceNum),obj[i].jouArray[m].kekka, stockStrNirentan,\
obj[i].jouArray[m].playerNumAndClass[0],obj[i].jouArray[m].playerNumAndClass[1],\
obj[i].jouArray[m].playerNumAndClass[2],obj[i].jouArray[m].playerNumAndClass[3],\
obj[i].jouArray[m].playerNumAndClass[4],obj[i].jouArray[m].playerNumAndClass[5])
tmp.append(tuple)
tuple=()
#配列 obj[i].jouArray[m].sanrentanyosou
for f in range(0,len(obj[i].jouArray[m].sanrentanyosou)):
stockStrSanrentan=obj[i].jouArray[m].sanrentanyosou[f]
if stockStrSanrentan=="":
pass
else:
#print("stockStrSanrentan3連単:"+stockStrSanrentan)
if(str(self.neraime) in stockStrSanrentan):
tuple=(obj[i].jouArray[m].date,obj[i].jouArray[m].time,obj[i].jouArray[m].jouCode,jouName,\
'R'+str(obj[i].jouArray[m].raceNum),obj[i].jouArray[m].kekka, stockStrSanrentan,\
obj[i].jouArray[m].playerNumAndClass[0],obj[i].jouArray[m].playerNumAndClass[1],\
obj[i].jouArray[m].playerNumAndClass[2],obj[i].jouArray[m].playerNumAndClass[3],\
obj[i].jouArray[m].playerNumAndClass[4],obj[i].jouArray[m].playerNumAndClass[5])
tmp.append(tuple)
#tmp=obj
if len(tmp)==1:#見出し行のみなら狙い目はない
print("狙い目はありませんでした。")
output_tuple=tmp
return output_tuple
#getCompuYosouはオーバーライド
def getCompuYosou(self,arrayObj):#arrayObjを入れる。getIchiran()の出力配列オブジェクトのURLを使用する
#ここは継承クラス boatRacePlayerClass
#レースのコンピュータ予想を取得する
#追加機能、選手クラス追加
tmp=arrayObj
raceYosou=[]#レース予想の配列
for i in range(0,len(tmp)):
print("場コード:"+str(tmp[i].jouArray[0].jouCode))
for m in range(0,len(tmp[i].jouArray)):
#print(str(output[i].jouArray[m].raceNum),str(output[i].jouArray[m].kekka),str(output[i].jouArray[m].url))
tmpurl=tmp[i].jouArray[m].url
if tmpurl=='':
print("結果を参照してください。")
continue
else:
#ここは継承クラス boatRacePlayerClass
#タプルの配列オブジェクトを受け取る
raceYosou=self.getCompuYosouSub(tmpurl)#1レース分のコンピュータ予想、選手クラスを登録する。この中で秒待ち設定あり
#0番目タプルと1番目タプルで処理を行う。2021/07/11
time.sleep(2)
if len(raceYosou)>0:
#コンピュータ予想を取得して、tmpオブジェクトに入れる
#star
#tmp[i].jouArray[m].star=mstar
#0番目タプル、予想
for f in range(0,len(raceYosou[0])):
#次はここから、2021/07/09
if len(raceYosou[0][f])==3:
#2連単でクラスメソッドに分ける
tmp[i].jouArray[m].nirentanyosou.append(raceYosou[0][f])
else:#3連
tmp[i].jouArray[m].sanrentanyosou.append(raceYosou[0][f])
#1番目タプル、選手クラス
for x in range(0,len(raceYosou[1])):#6人分が入っている。「選手番号/クラス」となっている。
tmp[i].jouArray[m].playerNumAndClass.append(raceYosou[1][x].split('/')[1])#1234/B1のときにsplitして、2番めのB1を取得する
else:
print("コンピュータ予想が取得できませんでした。")
if len(tmp)==0:
print("情報がありません。どこかでエラーが発生したと思われますのでチェックしてください")
output=tmp#全場オブジェクト配列を出力する。次はCSV出力
return output
#オーバーライド
def getCompuYosouSub(self,url):#スクレイピング部分
#ここは継承クラス boatRacePlayerClass、基底クラスはboatRaceClass()
#基底クラスとの違いは選手クラスを追加したこと
mysoup=self.readPage(url)#class内のメソッド
#https://www.boatrace.jp/owpc/pc/race/racelist?rno=10&jcd=05&hd=20210708
#上記を取得するので
#racelistをpcexpectに置換する。このメソッドでは置換しない予定
#https://www.boatrace.jp/owpc/pc/race/pcexpect?rno=10&jcd=05&hd=20210708 こっちだ
#自信度の星数は、クラス名の総当りでNONEでなければありと判断する
#print(url)
#print("コンピュータ予想ページ読み込み、解析中")
raceYosou=[]#初期化 1レース分のコンピュータ予想を格納する配列
playerObj=[]
#print("秒待ち設定少々待ってください。連続取り込みしません。")
time.sleep(3)
main=mysoup.find(class_='l-main')
mainInner=main.find(class_='l-mainInner')
contentsFrame1=mainInner.find(class_='contentsFrame1')
contentsFrame1_inner=contentsFrame1.find(class_='contentsFrame1_inner')
if contentsFrame1_inner==None:
print("contentsFrame1_innerが見つかりません")
else:
#Classに空白が含まれていた場合の対応
grid_is_type3_h_clear=contentsFrame1_inner.find('div', class_=lambda c:"grid" in c and "is-type3" in c and "h-clear" in c)
#grid is-type3 h-clear grid is-type3 h-clear
if grid_is_type3_h_clear==None:
print("grid_is_type3_h_clearが見つかりませんでした code112")
else:
grid_units=contentsFrame1_inner.find_all(class_='grid_unit')
#print("grid_unitの数:"+str(len(grid_units)))#ここが0だとだめ
for f in range(0,len(grid_units)):
if f==0:#1番目だけあればOK
numberSet2_hclear=grid_units[f].find('div', class_=lambda s:s.startswith("numberSet2 h-clear"))
#tmpCount=grid_units[f].find_all(class_='numberSet2_unit')
#print('numberSet2_unitのCount'+str(len(tmpCount)))
if numberSet2_hclear==None:
print("numberSet2 h-clearが見つかりませんでした")
else:
numberSet2_units=numberSet2_hclear.find_all(class_='numberSet2_unit')#複数
#print("2連単をチェック"+str(len(numberSet2_units)))
for i in range(0,len(numberSet2_units)):
numberSet2_rows=numberSet2_units[i].find_all(class_='numberSet2_row')
#yosouNumStr=""
for m in range(0,len(numberSet2_rows)):#2単も3単も同じクラス名
spans=numberSet2_rows[m].find_all('span')
divStr=numberSet2_rows[m].get_text()#spanにはされた文字を取得
tmpstr=divStr.strip().replace('\t','')
pStr=tmpstr.replace('\r\n','')#文字化けしてた理由がわかったCRLFの改行になっているから\nだけを消すと文字化けする
#print("spanの間の文字"+pStr)
raceYosou.append(pStr)
#配列に入れてoutputすればOK
#星についてはタプルで出力できるか、まだ
#選手番号と級別の取得
#ここは継承クラス boatRacePlayerClass
#これから2021/07/11
#playerClassGet()メソッドを作成してコードの視認性を上げる
#table1はクラス名として複数存在している[table1 aaa]みたいなだからこっちを見てるなので,
playerObj=self.playerClassGet(contentsFrame1_inner)#レースごとの出場選手のクラス情報を追加する。1レース6名となる
#print("end")
output=(raceYosou,playerObj)#コンピュータ予想配列を出力する、級別のオブジェクトをタプルで出力する
return output