<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>netkeiba</title>
	<atom:link href="https://javeo.jp/tag/netkeiba/feed/" rel="self" type="application/rss+xml" />
	<link>https://javeo.jp</link>
	<description>ほどほどレベルのプログラミング</description>
	<lastBuildDate>Sun, 29 Mar 2026 15:11:00 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://javeo.jp/wp-content/uploads/2025/08/cropped-ExcelPython_future-32x32.jpg</url>
	<title>netkeiba</title>
	<link>https://javeo.jp</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>汎用AIと一緒に競馬予想AI作ってnote投稿まで自動化する #2-スクレイピング</title>
		<link>https://javeo.jp/keiba-ai-challenge-02/</link>
					<comments>https://javeo.jp/keiba-ai-challenge-02/#respond</comments>
		
		<dc:creator><![CDATA[ジャベ雄]]></dc:creator>
		<pubDate>Sun, 29 Mar 2026 10:21:36 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[netkeiba]]></category>
		<category><![CDATA[note]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[スクレイピング]]></category>
		<category><![CDATA[競馬]]></category>
		<category><![CDATA[自動化]]></category>
		<guid isPermaLink="false">https://javeo.jp/?p=3908</guid>

					<description><![CDATA[目次 まずはデータがないと分析できないJRA-VAN データラボからのデータ取得netkeibaからのスクレイピングちょっとした注意点スクレイピングについて取得したデータはデータベースへ保存データは小分けに取得並列処理は [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="codoc-evacuations" style="display:none;" data-shortcode=""></div>
<div class="wp-block-cocoon-blocks-info-box block-box info-box">
<p>2026年初頭AIの進歩が凄まじく、AIを使った何かをやってみたいと競馬予想AIを作ってみましたのでその取り組みをまとめてみました</p>



<p>ついでにその過程で作ったスクレイピングとnoteへの自動投稿も一緒にまとめますので興味のある方は覗いてみてください</p>



<p>今回は第二回でデータ収集についてまとめています</p>
</div>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-2" checked><label class="toc-title" for="toc-checkbox-2">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">まずはデータがないと分析できない</a><ol><li><a href="#toc2" tabindex="0">JRA-VAN データラボからのデータ取得</a></li><li><a href="#toc3" tabindex="0">netkeibaからのスクレイピング</a><ol><li><a href="#toc4" tabindex="0">ちょっとした注意点</a></li></ol></li></ol></li><li><a href="#toc5" tabindex="0">スクレイピングについて</a><ol><li><a href="#toc6" tabindex="0">取得したデータはデータベースへ保存</a></li><li><a href="#toc7" tabindex="0">データは小分けに取得</a></li><li><a href="#toc8" tabindex="0">並列処理はしない</a></li></ol></li><li><a href="#toc9" tabindex="0">次回について</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">まずはデータがないと分析できない</span></h2>



<p>AI含め、分析するならデータを手元に持たないと話にならないので過去データの収集から始めます</p>



<p>選択肢としては有料でもいいから正攻法ならJRA-VAN データラボからのデータ取得、無料だけど節度を持ってゆっくりデータを取得するならnetkeibaからのスクレイピングの2つを検討しました</p>



<h3 class="wp-block-heading"><span id="toc2">JRA-VAN データラボからのデータ取得</span></h3>



<p>JRA公式データを使うならデータラボ1択</p>



<p><strong>2,090</strong>円/月（初月無料）で利用できてJRA関連会社が運営する公式ツールです</p>




<a rel="noopener noreferrer" target="_blank" href="https://jra-van.jp/dlb/feature.html" title="JRA-VAN" class="blogcard-wrap external-blogcard-wrap a-wrap cf"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img fetchpriority="high" decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fjra-van.jp%2Fdlb%2Ffeature.html?w=320&#038;h=198" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="320" height="198" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">JRA-VAN</div><div class="blogcard-snippet external-blogcard-snippet">プロも愛用の競馬ソフトから意外な穴場的中ソフトまで、データ競...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://jra-van.jp/dlb/feature.html" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">jra-van.jp</div></div></div></div></a>



<p>調査も含めて契約してみたのですが、基本はJV-Linkを組み込んだ独自アプリ開発キットのような印象でした</p>



<p>問題は対象言語サポートが2026年3月現在残念でVB系しか対象になっておらず、ちょっと厳しい印象なのは否めません</p>



<p>ネットを見ているとサポート関係なく他の言語で作成している人もいるみたいですが、コミュニティが使えないなど有料の強みがなくなってしまうので今回は諦め</p>



<div class="wp-block-cocoon-blocks-tab-caption-box-1 tab-caption-box block-box not-nested-style cocoon-block-tab-caption-box"><div class="tab-caption-box-label block-box-label box-label fab-lightbulb"><span class="tab-caption-box-label-text block-box-label-text box-label-text">サポート対象言語</span></div><div class="tab-caption-box-content block-box-content box-content">
<ul class="wp-block-list">
<li>Microsoft Visual Basic 2015</li>



<li>Microsoft Visual C++ 2015</li>



<li>Borland Delphi 7</li>



<li>Microsoft Access 2013/2016</li>



<li>Microsoft Excel 2013/2016</li>
</ul>




<a rel="noopener noreferrer" target="_blank" href="https://support.jra-van.jp/jravan/detail?site=SVKNEGBV&#038;category=16&#038;id=209" title="FAQ&#35443;&#32048; -&#12469;&#12509;&#12540;&#12488;&#12375;&#12390;&#12356;&#12427;&#38283;&#30330;&#29872;&#22659;&#12395;&#12399;&#12393;&#12398;&#12424;&#12358;&#12394;&#12418;&#12398;&#12364;&#12354;&#12426;&#12414;&#12377;&#12363;&#65311; | JRA-VAN&#12504;&#12523;&#12503;&#12475;&#12531;&#12479;&#12540;" class="blogcard-wrap external-blogcard-wrap a-wrap cf"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Fsupport.jra-van.jp%2Fjravan%2Fdetail%3Fsite%3DSVKNEGBV%26category%3D16%26id%3D209?w=320&#038;h=198" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="320" height="198" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">FAQ&#35443;&#32048; -&#12469;&#12509;&#12540;&#12488;&#12375;&#12390;&#12356;&#12427;&#38283;&#30330;&#29872;&#22659;&#12395;&#12399;&#12393;&#12398;&#12424;&#12358;&#12394;&#12418;&#12398;&#12364;&#12354;&#12426;&#12414;&#12377;&#12363;&#65311; | JRA-VAN&#12504;&#12523;&#12503;&#12475;&#12531;&#12479;&#12540;</div><div class="blogcard-snippet external-blogcard-snippet">【JRA公式データ】JRA-VANは競馬予想に役立つ情報を提...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://support.jra-van.jp/jravan/detail?site=SVKNEGBV&#038;category=16&#038;id=209" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">support.jra-van.jp</div></div></div></div></a>
</div></div>



<p>ちなみに・・有志が作ったアプリも使えるらしく、不動の1位な<strong><span class="bold-red">TARGET</span></strong>や税金問題で界隈を賑わせた卍氏が使っていたといわれる<span class="bold-blue"><span class="bold-red">馬王Z</span></span>も追加料金なしで使えるようなのでゼロベースからの分析とかじゃないなら十分有力な選択肢になりそうです</p>



<div class="wp-block-cocoon-blocks-tab-caption-box-1 tab-caption-box block-box has-border-color not-nested-style cocoon-block-tab-caption-box" style="--cocoon-custom-border-color:#de2568"><div class="tab-caption-box-label block-box-label box-label fab-amazon"><span class="tab-caption-box-label-text block-box-label-text box-label-text">卍氏の書籍紹介</span></div><div class="tab-caption-box-content block-box-content box-content">
<div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-28f84493 wp-block-columns-is-layout-flex">
<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%">

<a rel="noopener noreferrer" target="_blank" href="https://amzn.to/4sy3Kd8" title="1&#20740;5000&#19975;&#20870;&#31292;&#12356;&#12384;&#39340;&#21048;&#35009;&#21028;&#30007;&#12364;&#26126;&#12363;&#12377; &#31478;&#39340;&#12398;&#21213;&#12385;&#26041; (&#31478;&#39340;&#29579;&#39340;&#21048;&#25915;&#30053;&#26412;&#12471;&#12522;&#12540;&#12474;) | &#21325; |&#26412; | &#36890;&#36009; | Amazon" class="blogcard-wrap external-blogcard-wrap a-wrap cf"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://s.wordpress.com/mshots/v1/https%3A%2F%2Famzn.to%2F4sy3Kd8?w=320&#038;h=198" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="320" height="198" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">1&#20740;5000&#19975;&#20870;&#31292;&#12356;&#12384;&#39340;&#21048;&#35009;&#21028;&#30007;&#12364;&#26126;&#12363;&#12377; &#31478;&#39340;&#12398;&#21213;&#12385;&#26041; (&#31478;&#39340;&#29579;&#39340;&#21048;&#25915;&#30053;&#26412;&#12471;&#12522;&#12540;&#12474;) | &#21325; |&#26412; | &#36890;&#36009; | Amazon</div><div class="blogcard-snippet external-blogcard-snippet">Amazonで卍の1億5000万円稼いだ馬券裁判男が明かす ...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://amzn.to/4sy3Kd8" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">amzn.to</div></div></div></div></a>
</div>



<div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="border-top-left-radius:1px;border-top-right-radius:1px;border-bottom-left-radius:1px;border-bottom-right-radius:1px;padding-right:3px;padding-left:3px;flex-basis:66.66%">
<p>2007-2009年の3年間で、1億円以上の価値を得た卍氏の書籍はコチラ</p>



<p>「馬券の勝ち方」を論理的かつ丁寧に解説されてます！</p>
</div>
</div>
</div></div>



<h3 class="wp-block-heading"><span id="toc3">netkeibaからのスクレイピング</span></h3>



<p>結論から言うと今回採用したのはこちら</p>



<p>スクレイピングとはインターネット上の情報をプログラムで収集する手法で、サーバーに負荷をかけてしまうなどあまり推奨される手法ではありませんが、netkeibaは禁止していないようなのと情報として十分すぎる量があり、長く使うなら正直無料はありがたいです</p>



<p>参考までにnetkeibaがスクレイピングを禁止していないと判断した材料はこちら</p>



<ul class="wp-block-list has-watery-yellow-background-color has-background is-style-icon-list-check has-list-style">
<li>robots.txtがない
<ul class="wp-block-list">
<li>禁止している場合はここに記載されていることが多いです</li>
</ul>
</li>



<li>利用規約で禁止していない
<ul class="wp-block-list">
<li>厳密には<a rel="noreferrer noopener nofollow" target="_blank" href="https://support.keiba.netkeiba.com/hc/ja/articles/39720493823129">制限をかける場合があると記載</a>されていますが、過剰アクセスの場合と判断しました</li>
</ul>
</li>
</ul>



<h4 class="wp-block-heading"><span id="toc4">ちょっとした注意点</span></h4>



<p>公式データではないからなのか、netkeibaのデータで注意点として極稀に謎のデータが存在します</p>



<p>例えば2014/2/10の東京3Rの結果ページなんですが、明らかに単勝オッズがおかしくなっています</p>




<a rel="noopener noreferrer" target="_blank" href="https://race.netkeiba.com/race/result.html?race_id=201405010303" title="３歳新馬 結果・払戻 | 2014年2月10日 東京3R レース情報(JRA) - netkeiba" class="blogcard-wrap external-blogcard-wrap a-wrap cf"><div class="blogcard external-blogcard eb-left cf"><div class="blogcard-label external-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail external-blogcard-thumbnail"><img decoding="async" src="https://javeo.jp/wp-content/uploads/cocoon-resources/blog-card-cache/9e827d934d345fd5865c02aedd117131.png" alt="" class="blogcard-thumb-image external-blogcard-thumb-image" width="320" height="198" /></figure><div class="blogcard-content external-blogcard-content"><div class="blogcard-title external-blogcard-title">３歳新馬 結果・払戻 | 2014年2月10日 東京3R レース情報(JRA) - netkeiba</div><div class="blogcard-snippet external-blogcard-snippet">2014年2月10日 東京3R ３歳新馬の結果・払戻です。J...</div></div><div class="blogcard-footer external-blogcard-footer cf"><div class="blogcard-site external-blogcard-site"><div class="blogcard-favicon external-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://race.netkeiba.com/race/result.html?race_id=201405010303" alt="" class="blogcard-favicon-image external-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain external-blogcard-domain">race.netkeiba.com</div></div></div></div></a>



<p>出馬表データの単勝オッズは正しいのでカバーはできますし、発生頻度は激低なのでご愛敬で</p>



<h2 class="wp-block-heading"><span id="toc5">スクレイピングについて</span></h2>



<p>具体的なコードは別で紹介しようと思いますが、ここでは方針的なことだけまとめます</p>



<p>※近いうちに<a rel="noreferrer noopener" target="_blank" href="https://note.com/javeo2022" data-type="link" data-id="https://note.com/javeo2022">note</a>でまとめるつもりなので完了したらこの記事でもお知らせします</p>



<h3 class="wp-block-heading"><span id="toc6">取得したデータはデータベースへ保存</span></h3>



<p>ネットを見ているとプログラムを実行する度にスクレイピングするような記事を見かけますが、再利用やちょっとした加工の可能性も考えるとデータベースに保管することを推奨です</p>



<p>参考までに私はこのブログサイトのために<a rel="nofollow noopener noreferrer" target="_blank" href="https://px.a8.net/svt/ejp?a8mat=2ZTU22+94NC8Y+CO4+609HU">エックスサーバー</a><img decoding="async" border="0" width="1" height="1" src="https://www19.a8.net/0.gif?a8mat=2ZTU22+94NC8Y+CO4+609HU" alt="">を契約しているので、専用のデータベースを作成して取得した2008年以降のレース結果を保存しています</p>



<h3 class="wp-block-heading"><span id="toc7">データは小分けに取得</span></h3>



<p>データベースを使う利点の一つとも言える、取得情報をもとに次の情報を取得が個人的推奨方法</p>



<p>スクレイピングに置いて通信問題などで収集途中で原因不明かつ対策不可能なエラーが発生することは比較的多く、全てにエラーハンドリングを仕込むことはあまり現実的ではないので順番に収集します</p>



<ul class="wp-block-list is-style-blank-box-orange has-border">
<li>まずはリスト取得
<ol class="wp-block-list">
<li>開催スケジュール取得</li>



<li>開催レース取得</li>
</ol>
</li>



<li>次にデータベースと照合しながら未取得情報を収集
<ol class="wp-block-list">
<li>レース詳細取得</li>



<li>騎手マスタ取得</li>



<li>調教師マスタ取得</li>



<li>馬主マスタ取得</li>



<li>競走馬マスタ取得</li>
</ol>
</li>



<li>最後に競走馬マスタを再起処理して血統情報収集</li>
</ul>



<p>SQLの作り方次第ではありますが、これでエラーが発生しても途中再開が簡単にできるようになります</p>



<h3 class="wp-block-heading"><span id="toc8">並列処理はしない</span></h3>



<p>前述の通り利用規約では</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>スクレイピングによって多数のリクエスト（アクセス）を確認し、弊社サービスに対して支障があると判断した場合は、予告なく通信制限をかけさせていただくことがございます。</p>
</blockquote>



<p>と記載されていますので逐次処理で適切に待機時間を入れながら実行することで&#8221;<span class="marker-under"><strong>多数のリクエスト</strong></span>&#8220;を回避することで許容してもらいます</p>



<p>早く情報を収集したい気持ちで並列処理をするとアクセスブロックをされて結果余計に時間がかかる、最悪取得できなくなる可能性もあるので節度を持った処理をしましょう</p>



<h2 class="wp-block-heading"><span id="toc9">次回について</span></h2>



<p>なかなか満足できるAIができないので次回はnoteへの自動投稿をまとめる予定です</p>
]]></content:encoded>
					
					<wfw:commentRss>https://javeo.jp/keiba-ai-challenge-02/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>汎用AIと一緒に競馬予想AI作ってnote投稿まで自動化する #1-はじめに</title>
		<link>https://javeo.jp/keiba-ai-challenge-01/</link>
					<comments>https://javeo.jp/keiba-ai-challenge-01/#respond</comments>
		
		<dc:creator><![CDATA[ジャベ雄]]></dc:creator>
		<pubDate>Tue, 17 Mar 2026 15:27:25 +0000</pubDate>
				<category><![CDATA[AI]]></category>
		<category><![CDATA[netkeiba]]></category>
		<category><![CDATA[note]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[スクレイピング]]></category>
		<category><![CDATA[競馬]]></category>
		<category><![CDATA[自動化]]></category>
		<guid isPermaLink="false">https://javeo.jp/?p=3838</guid>

					<description><![CDATA[目次 とにかくAIを使って何か作りたい！相棒となるAIは「Claude」を採用予想するだけじゃない！完全自動化のワークフローを構築このシリーズの全体構成（今後の予定）今時点の状況 とにかくAIを使って何か作りたい！ 最近 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="codoc-evacuations" style="display:none;" data-shortcode=""></div>
<div class="wp-block-cocoon-blocks-info-box block-box info-box">
<p>2026年初頭AIの進歩が凄まじく、AIを使った何かをやってみたいと競馬予想AIを作ってみましたのでその取り組みをまとめてみました</p>



<p>ついでにその過程で作ったスクレイピングとnoteへの自動投稿も一緒にまとめますので興味のある方は覗いてみてください</p>
</div>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-4" checked><label class="toc-title" for="toc-checkbox-4">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">とにかくAIを使って何か作りたい！</a></li><li><a href="#toc2" tabindex="0">相棒となるAIは「Claude」を採用</a></li><li><a href="#toc3" tabindex="0">予想するだけじゃない！完全自動化のワークフローを構築</a></li><li><a href="#toc4" tabindex="0">このシリーズの全体構成（今後の予定）</a></li><li><a href="#toc5" tabindex="0">今時点の状況</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">とにかくAIを使って何か作りたい！</span></h2>



<p>最近AI使えば何でもできるがどんどん現実味を帯びてきている中、皆さんどうやってAIと付き合っていますか？</p>



<p>SNSを見るとパッと見本物と区別のつかない動画や画像の生成、ブログやnoteの作成・投稿などの自動化、希望だけ伝えると勝手にプログラムを作ってくれるバイブコーディングなどなど</p>



<p>ネット上ではまさに日進月歩のAIなんですが、私の職場では情報漏洩の危険などからなかなかAIに触れる機会がなくこのままでは世間に置いて行かれるかもと思い、まずは触ってみようが今回のスタートです</p>



<p>タイトルの通り本命は<span class="marker-under"><span class="bold-red">競馬予想AI</span></span>なんですが、せっかくなので機械学習に必要なデータを取得するための<span class="bold"><span class="marker-under">スクレイピング</span></span>とマネタイズ用に<strong><span class="marker-under">note自動投稿</span></strong>へのチャレンジもまとめていきます</p>



<h2 class="wp-block-heading"><span id="toc2">相棒となるAIは「Claude」を採用</span></h2>



<p>今回一緒に開発を進めるAIパートナーとして選ばれたのは「<span class="bold-red"><span class="marker-under">Claude（クロード）</span></span>」でした</p>



<p>当初「Google AI Studio」を使っていたのですが、ちょうどClaudeがOpus4.6が凄い！と話題になった時期で、職場で唯一使えるCopilotでもClaudeが使えるとかMicrosoft365との連携もできるとかなんだとか今後もお付き合いする機会ができそうだったのでたまたま使い始めた時期でした</p>



<p>汎用AIの優劣はネットでも様々議論されていますが、今の情勢と一カ月後の情勢はきっと違うので好みで使えばいいと思います</p>



<p>参考までに「Google AI Studio」は無料枠で今でも使っていて、「Claude」はOpus使いたかったのでProプランの課金ユーザーです</p>



<h2 class="wp-block-heading"><span id="toc3">予想するだけじゃない！完全自動化のワークフローを構築</span></h2>



<p>始める前に結末はなんとなく想像していて、効果が出ても「回収率300%！！」とかは到底無理で現実的には110％前後で大成功なんじゃないかと思って始めています</p>



<p>その分他の分野にも手を出して成功体験にしようとしているしているのかスクレイピングとnote自動投稿ってわけです</p>



<p>最悪競馬AIの精度が満足いくところまでいかなくても毎週全自動で</p>



<p class="has-text-align-center has-watery-yellow-background-color has-background is-style-border-double has-border"><strong>スクレイピング　→　競馬AIで予想　→　予想結果をnoteに投稿</strong></p>



<p>のワークフローだけでも財産になるし、何かに流用できると思ってます</p>



<h2 class="wp-block-heading"><span id="toc4">このシリーズの全体構成（今後の予定）</span></h2>



<p>このシリーズでは数回に分けて需要ありそうな部分にフォーカスして記事作成を予定しています</p>



<p>今時点では4回に分けて下記順番で作るつもり</p>



<ol class="wp-block-list has-watery-blue-background-color has-background">
<li>過去データの収集
<ol class="wp-block-list">
<li>netkeibaからのスクレイピング</li>



<li>JRA-VAN Data Lab.からの収集　←これは断念</li>
</ol>
</li>



<li>AIを使った競馬AIの作成</li>



<li>AIを使った予想</li>



<li>予想のアウトプットでnote記事の自動作成と販売</li>
</ol>



<p>作っていきながらボリューミーなったら2回に分けるとか追加機能ができたら分けるとかあるかもしれないので構成変更はご了承を</p>



<h2 class="wp-block-heading"><span id="toc5">今時点の状況</span></h2>



<p>今時点ではAI未完成でスクレイピングは完成とnote投稿8割くらいです</p>



<p>スクレイピングは何段階かあるのと過去レース取得と毎週の自動取得が別物なので分けるかもって感じです</p>



<p>あまり間延びしないように作っていくので追いかけてもらえると嬉しいです！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://javeo.jp/keiba-ai-challenge-01/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【Python】netkeibaをスクレイピングする-その1</title>
		<link>https://javeo.jp/python-scraping-netkeiba-1/</link>
					<comments>https://javeo.jp/python-scraping-netkeiba-1/#respond</comments>
		
		<dc:creator><![CDATA[ジャベ雄]]></dc:creator>
		<pubDate>Wed, 05 Jun 2024 23:00:00 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<category><![CDATA[netkeiba]]></category>
		<category><![CDATA[Selenium]]></category>
		<category><![CDATA[スクレイピング]]></category>
		<guid isPermaLink="false">https://javeo.jp/?p=2240</guid>

					<description><![CDATA[目次 スクレイピングを始める前に一度手動で操作しながらURLやページを観察する最初に静的ページか動的ページか確認するまずはレースマスタを作る（ベースの一覧を取得する）レース一覧を取得するために動作を確認するレース一覧を取 [&#8230;]]]></description>
										<content:encoded><![CDATA[<div class="codoc-evacuations" style="display:none;" data-shortcode=""></div>
<div class="wp-block-cocoon-blocks-info-box block-box primary-box">
<p>趣味で競馬をやっている私、ジャベ雄です</p>



<p>ブラッドスポーツと呼ばれるほど血統が重要視されていたり、会場による傾向や枠の有利不利などデータがものを言う競馬界においてデータ収集先の代表格、<a rel="noreferrer noopener nofollow" target="_blank" href="https://www.netkeiba.com/" data-type="link" data-id="https://www.netkeiba.com/">netkeiba</a>をPythonでスクレイピングする方法をまとめます</p>



<p>今回は&#8221;netkeibaをスクレイピングする&#8221;ことではなく、&#8221;netkeibaを題材にスクレイピングを学ぶ&#8221;ところに重きを置いているのでその前提で見てもらえると嬉しいです</p>
</div>




  <div id="toc" class="toc tnt-number toc-center tnt-number border-element"><input type="checkbox" class="toc-checkbox" id="toc-checkbox-6" checked><label class="toc-title" for="toc-checkbox-6">目次</label>
    <div class="toc-content">
    <ol class="toc-list open"><li><a href="#toc1" tabindex="0">スクレイピングを始める前に</a></li><li><a href="#toc2" tabindex="0">一度手動で操作しながらURLやページを観察する</a></li><li><a href="#toc3" tabindex="0">最初に静的ページか動的ページか確認する</a></li><li><a href="#toc4" tabindex="0">まずはレースマスタを作る（ベースの一覧を取得する）</a><ol><li><a href="#toc5" tabindex="0">レース一覧を取得するために動作を確認する</a></li><li><a href="#toc6" tabindex="0">レース一覧を取得するための結論</a></li></ol></li><li><a href="#toc7" tabindex="0">実際のプログラム</a></li><li><a href="#toc8" tabindex="0">あとがき</a></li></ol>
    </div>
  </div>

<h2 class="wp-block-heading"><span id="toc1">スクレイピングを始める前に</span></h2>



<p>今回はnetkeibaから大量のデータを取得しますし、取得したデータは分析してナンボなので取得情報はデータベース（今回はXserverのMariaDB）に保存する前提でプログラム作成していますので必要に応じて適宜変更してください</p>



<p>PythonでXserverを操作する方法は↓↓を参考にどうぞ</p>




<a rel="noopener follow noreferrer" target="_blank" href="https://javeo.jp/python-xserver-connect" title="【Python】Xserverのデータベースを操作する" class="blogcard-wrap internal-blogcard-wrap a-wrap cf"><div class="blogcard internal-blogcard ib-left cf"><div class="blogcard-label internal-blogcard-label"><span class="fa"></span></div><figure class="blogcard-thumbnail internal-blogcard-thumbnail"><img decoding="async" width="320" height="198" src="https://javeo.jp/wp-content/uploads/2023/07/python-320x198.png" class="blogcard-thumb-image internal-blogcard-thumb-image wp-post-image" alt="" srcset="https://javeo.jp/wp-content/uploads/2023/07/python-320x198.png 320w, https://javeo.jp/wp-content/uploads/2023/07/python-240x148.png 240w, https://javeo.jp/wp-content/uploads/2023/07/python-640x396.png 640w" sizes="(max-width: 320px) 100vw, 320px" /></figure><div class="blogcard-content internal-blogcard-content"><div class="blogcard-title internal-blogcard-title">【Python】Xserverのデータベースを操作する</div><div class="blogcard-snippet internal-blogcard-snippet">PythonでXserverのデータベースであるMySQL（MariaDB）を操作するためのコネクタープログラムを作りました。データベースからの情報取得はもちろん、追加・更新もできるので外部データ連携にも使えますので参考にどうぞ。</div></div><div class="blogcard-footer internal-blogcard-footer cf"><div class="blogcard-site internal-blogcard-site"><div class="blogcard-favicon internal-blogcard-favicon"><img decoding="async" src="https://www.google.com/s2/favicons?domain=https://javeo.jp" alt="" class="blogcard-favicon-image internal-blogcard-favicon-image" width="16" height="16" /></div><div class="blogcard-domain internal-blogcard-domain">javeo.jp</div></div><div class="blogcard-date internal-blogcard-date"><div class="blogcard-post-date internal-blogcard-post-date">2024.03.12</div></div></div></div></a>



<h2 class="wp-block-heading"><span id="toc2">一度手動で操作しながらURLやページを観察する</span></h2>



<p>スクレイピングの初手はRPA的に情報取得すべきページへ遷移するところからです</p>



<p>一度手動で一連の動作を実施しながら特にURLに注目しながら観察すればパラメータを変更しながらページを読み込むのか、ボタンをクリックする必要があるのかなどがわかります</p>



<h2 class="wp-block-heading"><span id="toc3">最初に静的ページか動的ページか確認する</span></h2>



<p>Pythonでスクレイピングをする時の2台巨塔&#8221;<span class="marker-under"><strong>Selenium</strong></span>&#8220;と&#8221;<strong><span class="marker-under">Beautifulsoup</span></strong>&#8220;ですが、どちらを使うかの切り分けはそのサイト（ページ）がJavascriptを有効にする必要があるかどうか</p>



<p>確認するためにははChromeの設定メニューで切り替えるのですが、頻繁に切り替える可能性がある人はChrome拡張機能（私は<a rel="noreferrer noopener nofollow" target="_blank" href="https://chromewebstore.google.com/detail/quick-javascript-switcher/geddoclleiomckbhadiaipdggiiccfje?hl=ja" data-type="link" data-id="https://chromewebstore.google.com/detail/quick-javascript-switcher/geddoclleiomckbhadiaipdggiiccfje?hl=ja">Quick Javascript Switcher</a>）を使うこと推奨です</p>



<p>で、肝心のnetkibaはどうかと言うと開催レース一覧の画面がJavascript無効ではと永遠と馬が走っている状態になってしまうので有効にしておく必要があるとわかるので<strong>Selenium</strong>必須</p>



<figure class="wp-block-image size-large is-resized"><a rel="follow noopener noreferrer" target="_blank" href="https://javeo.jp/wp-content/uploads/2024/05/2024-05-29_23h02_31.png"><img decoding="async" width="1024" height="490" src="https://javeo.jp/wp-content/uploads/2024/05/2024-05-29_23h02_31-1024x490.png" alt="" class="wp-image-2253" style="width:840px;height:auto" srcset="https://javeo.jp/wp-content/uploads/2024/05/2024-05-29_23h02_31-1024x490.png 1024w, https://javeo.jp/wp-content/uploads/2024/05/2024-05-29_23h02_31-300x144.png 300w, https://javeo.jp/wp-content/uploads/2024/05/2024-05-29_23h02_31-150x72.png 150w, https://javeo.jp/wp-content/uploads/2024/05/2024-05-29_23h02_31-768x368.png 768w, https://javeo.jp/wp-content/uploads/2024/05/2024-05-29_23h02_31-1536x735.png 1536w, https://javeo.jp/wp-content/uploads/2024/05/2024-05-29_23h02_31.png 1920w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>ぶっちゃけ、&#8221;<strong>Beautifulsoup</strong>&#8220;だけで完結するサイトは少ないので&#8221;<span class="bold-red">Selenium</span>&#8220;を使う前提でもOK</p>



<h2 class="wp-block-heading"><span id="toc4">まずはレースマスタを作る（ベースの一覧を取得する）</span></h2>



<p>私のスクレイピング方針のようなものですが最初は一覧情報を作ってその後に詳細情報を収集します</p>



<p>なぜかと言うとスクレイピングはNW状況含めて予想外にエラーが起こる可能性がありますし、詳細データでは予想外のデータもあるのでエラーが起こる前提として処理を途中から再開できるような仕様にすることが望ましいです</p>



<p>具体的にはnetkeibaなら開催日ごとのレース一覧、ECサイトなら検索結果画面にある商品ページリストを収集するイメージです</p>



<p>ここで&#8221;<strong>一度手動で操作しながらURLやページを観察する</strong>&#8220;に倣ってプログラムの方針を考えます</p>



<h3 class="wp-block-heading"><span id="toc5">レース一覧を取得するために動作を確認する</span></h3>



<p>まずは今週のレース一覧ページを開くとURLに&#8221;kaisai_date=yyyymmdd&#8221;がパラメータになっていることがわかるのでここを変更すれば良さそう</p>



<figure class="wp-block-image size-large"><a rel="follow noopener noreferrer" target="_blank" href="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h27_07.png"><img decoding="async" width="1024" height="538" src="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h27_07-1024x538.png" alt="" class="wp-image-2283" srcset="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h27_07-1024x538.png 1024w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h27_07-300x158.png 300w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h27_07-150x79.png 150w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h27_07-768x403.png 768w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h27_07.png 1200w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>このパラメータを変えれば当然その日のレース一覧に変わるわけですが開催されていない日を指定するとレースが何も表示されないので1日ずる変更すると無駄な読み込みになってしまう</p>



<figure class="wp-block-image size-large"><a rel="follow noopener noreferrer" target="_blank" href="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h23_14.png"><img decoding="async" width="1024" height="538" src="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h23_14-1024x538.png" alt="" class="wp-image-2284" srcset="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h23_14-1024x538.png 1024w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h23_14-300x158.png 300w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h23_14-150x79.png 150w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h23_14-768x403.png 768w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h23_14.png 1200w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<p>もう一つ、上部の&#8221;前&#8221;や&#8221;次&#8221;をクックすると1週間分の日付が前後することがわかります</p>



<figure class="wp-block-image size-large"><a rel="follow noopener noreferrer" target="_blank" href="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h34_06.png"><img decoding="async" width="1024" height="538" src="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h34_06-1024x538.png" alt="" class="wp-image-2286" srcset="https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h34_06-1024x538.png 1024w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h34_06-300x158.png 300w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h34_06-150x79.png 150w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h34_06-768x403.png 768w, https://javeo.jp/wp-content/uploads/2024/06/2024-06-02_22h34_06.png 1200w" sizes="(max-width: 1024px) 100vw, 1024px" /></a></figure>



<h3 class="wp-block-heading"><span id="toc6">レース一覧を取得するための結論</span></h3>



<p>楽をするなら最初のパターンで取得対象の日まで1日読み込めばいいんですが、なるべくサイトへ負荷をかけないように無駄なページロードを避けるためやめました</p>



<p>続いて土日だけにするとイレギュラー開催（土日以外）の情報がカバーできなので止めました<br>※イレギュラー開催→1月の金杯、年末のホープフル、3連休の月曜日開催など</p>



<p>結論、取得したい起点の日を表示した状態から&#8221;次&#8221;ボタンをクリックし続けて日付が当日以上になるまでループする仕様にしました</p>



<h2 class="wp-block-heading"><span id="toc7">実際のプログラム</span></h2>



<p>私が実際に使っているプログラムですが必要な項目は違うでしょうし、データベース操作用のプログラムも違うと思うので適当に変更してください</p>



<p>コメントを大量に残しているのでプログラム自体の説明は割愛</p>



<div class="hcb_wrap"><pre class="prism undefined-numbers lang-python" data-lang="Python"><code>from datetime import datetime
import re
import urllib.parse
from time import sleep

from bs4 import BeautifulSoup
from db_connector import xserver_keiba
from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait


# ====================================================================================================
class data_class:
    def __init__(self, data_title: list) -&gt; None:
        self.race_id = &#39;&#39;
        self.race_date = &#39;&#39;
        self.race_times = data_title[0]
        self.course_place = data_title[1]
        self.race_days = data_title[2]
        self.race_no = 0
        self.race_name = &#39;&#39;
        self.race_grade = &#39;&#39;
        self.race_time = &#39;&#39;
        self.coruse_type = &#39;&#39;
        self.course_distance = &#39;&#39;
        self.not_found = 0

    def upsert_sql(self):
        sql = f&#39;&#39;&#39;
            INSERT INTO race_mst(race_id,race_date,race_times,course_place,race_days,race_no,race_name,race_grade,race_time,coruse_type,course_distance,not_found)
            values ({self.race_id},&#39;{self.race_date}&#39;,&#39;{self.race_times}&#39;,&#39;{self.course_place}&#39;,&#39;{self.race_days}&#39;,{self.race_no},&#39;{self.race_name}&#39;,&#39;{self.race_grade}&#39;,&#39;{self.race_time}&#39;,&#39;{self.coruse_type}&#39;,&#39;{self.course_distance}&#39;,{self.not_found})
            ON DUPLICATE KEY UPDATE race_date=&#39;{self.race_date}&#39;,race_times=&#39;{self.race_times}&#39;,course_place=&#39;{self.course_place}&#39;,race_days=&#39;{self.race_days}&#39;,race_no={self.race_no},race_name=&#39;{self.race_name}&#39;,race_grade=&#39;{self.race_grade}&#39;,race_time=&#39;{self.race_time}&#39;,course_distance=&#39;{self.course_distance}&#39;,not_found=&#39;{self.not_found}&#39;
        &#39;&#39;&#39;.replace(&quot;&#39;&#39;&quot;, &quot;Null&quot;)
        return sql


# ====================================================================================================
def grade_converter(grade_class) -&gt; str:
    # クラス名からレースのグレードを判定して返す
    if &#39;Icon_GradeType1&#39; in grade_class:
        return &#39;GⅠ&#39;
    elif &#39;Icon_GradeType2&#39; in grade_class:
        return &#39;GⅡ&#39;
    elif &#39;Icon_GradeType3&#39; in grade_class:
        return &#39;GⅢ&#39;
    elif &#39;Icon_GradeType4&#39; in grade_class:
        return &#39;重賞&#39;
    elif &#39;Icon_GradeType5&#39; in grade_class:
        return &#39;OP&#39;
    elif &#39;Icon_GradeType6&#39; in grade_class:
        return &#39;1600万下&#39;
    elif &#39;Icon_GradeType7&#39; in grade_class:
        return &#39;1000万下&#39;
    elif &#39;Icon_GradeType8&#39; in grade_class:
        return &#39;900万下&#39;
    elif &#39;Icon_GradeType9&#39; in grade_class:
        return &#39;500万下&#39;
    elif &#39;Icon_GradeType10&#39; in grade_class:
        return &#39;JGⅠ&#39;
    elif &#39;Icon_GradeType11&#39; in grade_class:
        return &#39;JGⅡ&#39;
    elif &#39;Icon_GradeType12&#39; in grade_class:
        return &#39;JGⅢ&#39;
    elif &#39;Icon_GradeType13&#39; in grade_class:
        return &#39;WIN5&#39;
    elif &#39;Icon_GradeType14&#39; in grade_class:
        return &#39;特選&#39;
    elif &#39;Icon_GradeType15&#39; in grade_class:
        return &#39;L&#39;
    elif &#39;Icon_GradeType16&#39; in grade_class:
        return &#39;3勝&#39;
    elif &#39;Icon_GradeType17&#39; in grade_class:
        return &#39;2勝&#39;
    elif &#39;Icon_GradeType18&#39; in grade_class:
        return &#39;1勝&#39;
    else:
        pass


# ====================================================================================================
def main():
    db = xserver_keiba()
    # データベース内の最新レース日取得する
    sql = &#39;&#39;&#39;
        SELECT
            MAX(race_date) AS last_get_date
        FROM
            race_mst
    &#39;&#39;&#39;
    ret = db.fetch(sql)
    # dbにあるレース開催日の最大値を最初の取得対象日にする
    # レースデータがない（≒初回）は&#39;2014-01-05&#39;で固定する
    target_date = datetime(2014, 1, 5) if ret[0][&#39;last_get_date&#39;] is None else ret[0][&#39;last_get_date&#39;]
    options = Options()
    # 画像を読み込まない
    options.add_argument(&#39;--blink-settings=imagesEnabled=false&#39;)
    # シークレットモードで起動
    options.add_argument(&#39;--incognito&#39;)
    # Chromeを起動する
    driver = webdriver.Chrome(options=options)
    # urlに日付を設定すれば対象日のレース画面になる
    driver.get(f&quot;https://race.netkeiba.com/top/race_list.html?&kaisai_date={datetime.strftime(target_date, &#39;%Y%m%d&#39;)}&quot;)
    sleep(0.5)
    # 対象日付が未来日になるまでループする
    while target_date &lt; datetime.today():
        next_date_tab = True
        while next_date_tab is True:
            next_date_tab = False
            # ページソースをBeautifulSoupに食わせる
            soup = BeautifulSoup(driver.page_source)
            # 上部の日付のliにを確認
            for race_date in soup.select(&#39;#date_list_sub &gt; li[role=&quot;tab&quot;]&#39;):
                # 上部の日付のliにあるdate属性から対象の日付を取得して未来日になったら終わらせる
                if int(race_date.get(&#39;date&#39;)) &gt;= int(datetime.now().strftime(&#39;%Y%m%d&#39;)):
                    break
                # 取得済みの日付ならスキップ・未取得ならクリックする
                sql = f&#39;&#39;&#39;
                    SELECT
                        *
                    FROM
                        race_mst
                    WHERE
                        race_date=&#39;{race_date.get(&#39;date&#39;)}&#39;
                &#39;&#39;&#39;
                ret = db.fetch(sql)
                if len(ret) &gt; 0:
                    continue
                else:
                    driver.find_element(By.CSS_SELECTOR, f&#39;#date_list_sub &gt; li2026/04/11&#39;).click()
                # ↑のクリックでソースが書き換わっているのでもう一度ページソースをBeautifulSoupに食わせる
                soup = BeautifulSoup(driver.page_source)
                # id=&quot;RaceTopRace&quot;が複数ある可能性があるのでループさせる
                for RaceTopRace in soup.select(&#39;#RaceTopRace&#39;):
                    # 今表示されているRaceTopRaceは上の階層の属性で判断する
                    if RaceTopRace.parent.get(&#39;aria-hidden&#39;) == &#39;true&#39;:
                        continue
                    # 会場別でdlが分かれているのでループする
                    for dl in RaceTopRace.select(&#39;dl.RaceList_DataList&#39;):
                        # レースごとにliが分かれてるからループする
                        for li in dl.select(&#39;dd.RaceList_Data &gt; ul &gt; li.RaceList_DataItem&#39;):
                            # --- ここから実際のデータ取得 ---
                            # リンクが複数あるから&#39;result.html&#39;を含んでいるかで正誤判定する
                            for a in li.select(&#39;a&#39;):
                                if &#39;result.html&#39; in a.get(&#39;href&#39;):
                                    # 会場のヘッダー部から会場情報を取得する
                                    data = data_class(dl.select_one(&#39;dt.RaceList_DataHeader &gt; div.RaceList_DataHeader_Top &gt; p.RaceList_DataTitle&#39;).get_text(separator=&#39;/&#39;, strip=True).split(&#39;/&#39;))
                                    # urlのクエリパラメータにレースIDがあるので分解する
                                    url = a.get(&#39;href&#39;)
                                    qs = urllib.parse.urlparse(url).query
                                    qs_d = urllib.parse.parse_qs(qs)
                                    data.race_id = qs_d[&#39;race_id&#39;][0]
                                    # --- a配下にレース情報があるので取得する ---
                                    # レース番号
                                    data.race_no = a.select_one(&#39;div.Race_Num.Race_Fixed &gt; span&#39;).get_text(strip=True).replace(&#39;R&#39;, &#39;&#39;)
                                    # レース名
                                    data.race_name = a.select_one(&#39;div.RaceList_ItemContent &gt; div.RaceList_ItemTitle &gt; span.ItemTitle&#39;).get_text(strip=True)
                                    # レースのグレード設定があれば取得する
                                    if len(a.select(&#39;div.RaceList_ItemContent &gt; div.RaceList_ItemTitle &gt; span.Icon_GradeType&#39;)) &gt; 0:
                                        data.race_grade = grade_converter(a.select_one(&#39;div.RaceList_ItemContent &gt; div.RaceList_ItemTitle &gt; span.Icon_GradeType&#39;).get(&#39;class&#39;))
                                    # 出走時刻
                                    data.race_time = a.select_one(&#39;div.RaceList_ItemContent &gt; div.RaceData &gt; span.RaceList_Itemtime&#39;).get_text(strip=True)
                                    # 障害レースはクラス設定がないので分岐
                                    if len(a.select(&#39;div.RaceList_ItemContent &gt; div.RaceData &gt; span.RaceList_ItemLong&#39;)) &gt; 0:
                                        # 芝・ダートはクラスが設定されているからセレクタ―で指定する
                                        data.coruse_type = a.select_one(&#39;div.RaceList_ItemContent &gt; div.RaceData &gt; span.RaceList_ItemLong&#39;).get_text(strip=True)[0]
                                        data.course_distance = re.findall(R&#39;\d+&#39;, a.select_one(&#39;div.RaceList_ItemContent &gt; div.RaceData &gt; span.RaceList_ItemLong&#39;).get_text(strip=True))[0]
                                    else:
                                        # 障害はクラスが設定されていないから苦肉でindex指定する
                                        data.coruse_type = a.select(&#39;div.RaceList_ItemContent &gt; div.RaceData &gt; span&#39;)[1].get_text(strip=True)[0]
                                        data.course_distance = re.findall(R&#39;\d+&#39;, a.select(&#39;div.RaceList_ItemContent &gt; div.RaceData &gt; span&#39;)[1].get_text(strip=True))[0]
                                    # レース日は変数から反映させる
                                    data.race_date = race_date.get(&#39;date&#39;)
                                    db.execute(data.upsert_sql())
                next_date_tab = True
                break

        # 次ボタンを押すと次開催日に移るのでクリックする
        try:
            WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, &#39;#nextBtn &gt; a&#39;)))
        except TimeoutException:
            # 次がボタンになっていない（≒次がない）時はループを抜ける（けど、過去データしか取得しないから基本的にありえない）
            break
        driver.find_element(By.CSS_SELECTOR, &#39;#nextBtn &gt; a&#39;).click()
        # 一瞬でもsleepしないと処理が早すぎてエラーになる可能性があるので0.5秒待機
        sleep(0.5)
        # クエリパラメータに開催日があるので日付型に変換する
        qs = urllib.parse.urlparse(driver.current_url).query
        qs_d = urllib.parse.parse_qs(qs)
        target_date = datetime.strptime(qs_d[&#39;kaisai_date&#39;][0], &#39;%Y%m%d&#39;)
    # 最後にChromedriverを落として終了
    driver.close()
    driver.quit()


# ====================================================================================================
if __name__ == &#39;__main__&#39;:
    main()
</code></pre></div>



<h2 class="wp-block-heading"><span id="toc8">あとがき</span></h2>



<p>プログラムはもう少しスマートに書いた方が良さそうですが問題なく動いているのでこの辺りで手打ちにしました</p>



<p>netkeibaのスクレイピングにはまだ先は長いですが第一歩のまとめでした</p>



<p>次はレース画面を収集する方法をまとめます！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://javeo.jp/python-scraping-netkeiba-1/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
