<?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 &#8211; まったりエンジニア</title>
	<atom:link href="https://javeo.jp/tag/netkeiba/feed/" rel="self" type="application/rss+xml" />
	<link>https://javeo.jp</link>
	<description>ほどほどレベルのプログラミング</description>
	<lastBuildDate>Thu, 20 Aug 2026 14:47:09 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1</generator>

<image>
	<url>https://javeo.jp/wp-content/uploads/2026/05/cropped-サイトアイコン2-32x32.png</url>
	<title>netkeiba &#8211; まったりエンジニア</title>
	<link>https://javeo.jp</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Claudeで競馬予想AIを自作する #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・Claude]]></category>
		<category><![CDATA[netkeiba]]></category>
		<category><![CDATA[スクレイピング]]></category>
		<category><![CDATA[競馬]]></category>
		<category><![CDATA[自動化]]></category>
		<guid isPermaLink="false">https://javeo.jp/?p=3908</guid>

					<description><![CDATA[<p><img src="https://javeo.jp/wp-content/uploads/2026/03/AIで競馬-1024x538.jpg" class="webfeedsFeaturedVisual" /></p>競馬予想AIを作るための過去データ収集編。JRA-VAN データラボとnetkeibaスクレイピングを比較し、データベース保存・小分け取得・逐次処理など実際に採用した方針をまとめました]]></description>
										<content:encoded><![CDATA[<p><img src="https://javeo.jp/wp-content/uploads/2026/03/AIで競馬-1024x538.jpg" class="webfeedsFeaturedVisual" /></p><div class="codoc-evacuations" style="display:none;" data-shortcode=""></div>
<div class="wp-block-group is-style-big_icon_memo"><div class="wp-block-group__inner-container">
<p class="wp-block-paragraph">2026年初頭AIの進歩が凄まじく、AIを使った何かをやってみたいと競馬予想AIを作ってみましたのでその取り組みをまとめてみました</p>



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



<p class="wp-block-paragraph">今回は第二回でデータ収集についてまとめています</p>



<p class="wp-block-paragraph">前回(第一回)はこちら→<a href="https://javeo.jp/keiba-ai-challenge-01/" target="_blank" rel="noopener noreferrer">汎用AIと一緒に競馬予想AI作ってnote投稿まで自動化する #1-はじめに</a></p>
</div></div>



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



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



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



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



<p class="wp-block-paragraph">JRA公式データを使うなら<strong><span class="swl-marker mark_yellow">データラボが本命</span></strong></p>



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



<p class="wp-block-paragraph"><a href="https://jra-van.jp/dlb/feature.html" target="_blank" rel="noopener noreferrer">https://jra-van.jp/dlb/feature.html</a></p>



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



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



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



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>サポート対象言語</span></div><div class="cap_box_content">
<ul class="wp-block-list -list-under-dashed">
<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>



<p class="wp-block-paragraph"><a href="https://support.jra-van.jp/jravan/detail?site=SVKNEGBV&amp;category=16&amp;id=209" target="_blank" rel="noopener noreferrer">https://support.jra-van.jp/jravan/detail?site=SVKNEGBV&amp;category=16&amp;id=209</a></p>
</div></div>



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



<div class="swell-block-capbox cap_box"><div class="cap_box_ttl"><span>卍氏の書籍紹介</span></div><div class="cap_box_content">
<p class="wp-block-paragraph">2007-2009年の3年間で、1億円以上の価値を得た卍氏の書籍はコチラ</p>



<p class="wp-block-paragraph">「馬券の勝ち方」を論理的かつ丁寧に解説されてます！</p>



<p class="wp-block-paragraph"><a href="https://amzn.to/4sy3Kd8" target="_blank" rel="nofollow noopener noreferrer">卍氏の書籍を見る(Amazon)</a></p>
</div></div>



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



<p class="wp-block-paragraph">結論から言うと<strong><span class="swl-marker mark_yellow">今回採用したのはこちら</span></strong></p>



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



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



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



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



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



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



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



<p class="wp-block-paragraph"><a href="https://race.netkeiba.com/race/result.html?race_id=201405010303" target="_blank" rel="noreferrer noopener nofollow">https://race.netkeiba.com/race/result.html?race_id=201405010303</a></p>



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



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



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



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



<p class="wp-block-paragraph">※netkeibaのスクレイピングはPythonでの実装例を別記事にもまとめています→<a href="https://javeo.jp/python-scraping-netkeiba-1/" target="_blank" rel="noopener noreferrer">【Python】netkeibaをスクレイピングする-その1</a></p>



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



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



<p class="wp-block-paragraph">参考までに私はこのブログサイトのために<a href="https://px.a8.net/svt/ejp?a8mat=2ZTU22+94NC8Y+CO4+609HU" target="_blank" rel="nofollow noopener noreferrer">エックスサーバー</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">データは小分けに取得</h3>



<p class="wp-block-paragraph">データベースを使う利点の一つとも言える、<strong><span class="swl-marker mark_yellow">取得情報をもとに次の情報を取得</span></strong>が個人的推奨方法</p>



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



<ul style="background-color:#FFFFDC80" class="wp-block-list -list-under-dashed has-background">
<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 class="wp-block-paragraph">SQLの作り方次第ではありますが、これでエラーが発生しても途中再開が簡単にできるようになります</p>



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



<p class="wp-block-paragraph">前述の通り利用規約では</p>



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



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



<p class="wp-block-paragraph">早く情報を収集したい気持ちで並列処理をするとアクセスブロックをされて結果余計に時間がかかる、最悪取得できなくなる可能性もあるので<strong><span class="swl-marker mark_yellow" style="color:#e8313b">節度を持った処理をしましょう</span></strong></p>



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



<p class="wp-block-paragraph">なかなか満足できるAIができないので次回はnoteへの自動投稿をまとめる予定です</p>


<div class="p-blogParts post_content" data-partsID="4603">
<div class="jv-books" data-shelf="ai"><div class="jv-books-head"><span class="jv-books-title"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Claude・生成AIを学べる本</span><span class="jv-books-pr">PR</span></div><div class="jv-books-grid"><div class="jv-book-card"><a class="jv-book-cover" href="https://www.amazon.co.jp/dp/4297153548?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer"><img decoding="async" src="https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/3540/9784297153540_1_2.jpg?_ex=240x240" alt="実践Claude Code入門" width="120" height="170" loading="lazy"></a><div class="jv-book-info"><p class="jv-book-title">実践Claude Code入門</p><p class="jv-book-author">西見公宏・吉田真吾・大嶋勇樹</p><div class="jv-book-btns"><a class="jv-btn jv-btn-az" href="https://www.amazon.co.jp/dp/4297153548?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="amazon" data-book="4297153548">Amazonで見る</a><a class="jv-btn jv-btn-rk" href="https://hb.afl.rakuten.co.jp/hgc/24ecde15.e492bb61.24ecde16.6c9a125b/?pc=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18439208%2F&#038;m=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18439208%2F" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="rakuten" data-book="4297153548">楽天で見る</a></div></div></div><div class="jv-book-card"><a class="jv-book-cover" href="https://www.amazon.co.jp/dp/4297152754?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer"><img decoding="async" src="https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2758/9784297152758_1_2.jpg?_ex=240x240" alt="Claude CodeによるAI駆動開発入門" width="120" height="170" loading="lazy"></a><div class="jv-book-info"><p class="jv-book-title">Claude CodeによるAI駆動開発入門</p><p class="jv-book-author">平川知秀</p><div class="jv-book-btns"><a class="jv-btn jv-btn-az" href="https://www.amazon.co.jp/dp/4297152754?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="amazon" data-book="4297152754">Amazonで見る</a><a class="jv-btn jv-btn-rk" href="https://hb.afl.rakuten.co.jp/hgc/24ecde15.e492bb61.24ecde16.6c9a125b/?pc=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18401065%2F&#038;m=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18401065%2F" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="rakuten" data-book="4297152754">楽天で見る</a></div></div></div><div class="jv-book-card"><a class="jv-book-cover" href="https://www.amazon.co.jp/dp/4839990204?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer"><img decoding="async" src="https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/0206/9784839990206.jpg?_ex=240x240" alt="開発効率をアップする! Claude Code 実用入門" width="120" height="170" loading="lazy"></a><div class="jv-book-info"><p class="jv-book-title">開発効率をアップする! Claude Code 実用入門</p><p class="jv-book-author">大澤文孝</p><div class="jv-book-btns"><a class="jv-btn jv-btn-az" href="https://www.amazon.co.jp/dp/4839990204?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="amazon" data-book="4839990204">Amazonで見る</a></div></div></div></div><p class="jv-books-note">私のおすすめからランダムで3冊を表示しています</p></div>
<style>
.jv-books{border:1px solid #e0e0e0;border-radius:8px;padding:16px;margin:1.5em 0;background:#fff}
.jv-books-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.jv-books-title{font-weight:700;font-size:1.05em}
.jv-books-pr{font-size:.7em;color:#888;border:1px solid #ccc;border-radius:3px;padding:1px 6px;flex-shrink:0}
.jv-books-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;min-height:330px}
.jv-book-card{display:flex;flex-direction:column;align-items:center;text-align:center;border:1px solid #f0f0f0;border-radius:6px;padding:12px 8px;background:#fafafa}
.jv-book-cover img{width:auto;max-width:120px;height:170px;object-fit:contain}
.jv-book-info{width:100%;flex:1;display:flex;flex-direction:column}
.jv-book-title{font-size:.82em;font-weight:600;line-height:1.4;margin:8px 0 2px;min-height:2.8em;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.jv-book-author{font-size:.72em;color:#777;margin:0 0 8px}
.jv-book-btns{display:flex;flex-direction:column;gap:6px;width:100%;margin-top:auto}
.jv-btn{display:block;text-align:center;font-size:.78em;font-weight:600;color:#fff!important;text-decoration:none!important;border-radius:4px;padding:7px 4px;line-height:1.2}
.jv-btn-az{background:#e88b00}
.jv-btn-rk{background:#bf0000}
.jv-btn:hover{opacity:.85}
.jv-books-note{font-size:.7em;color:#999;text-align:right;margin:8px 0 0}
@media(max-width:599px){
.jv-books-grid{grid-template-columns:1fr;min-height:0}
.jv-book-card{flex-direction:row;text-align:left;gap:12px}
.jv-book-cover img{height:110px;max-width:80px}
.jv-book-info{min-width:0}
.jv-book-title{min-height:0}
.jv-book-btns{flex-direction:row}
.jv-btn{flex:1}
}
</style>
<script>
(function(){
var SHELF='ai';
var all=document.querySelectorAll('.jv-books[data-shelf="'+SHELF+'"]');
var root=null;
for(var k=0;k<all.length;k++){if(!all[k].dataset.jvinit){root=all[k];break;}}
if(!root)return;
root.dataset.jvinit='1';
var POOL=[{"t": "実践Claude Code入門", "a": "西見公宏・吉田真吾・大嶋勇樹", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/3540/9784297153540_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4297153548?tag=javeo2022-22", "rk": "https://hb.afl.rakuten.co.jp/hgc/24ecde15.e492bb61.24ecde16.6c9a125b/?pc=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18439208%2F&#038;m=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18439208%2F", "id": "4297153548"}, {"t": "Claude CodeによるAI駆動開発入門", "a": "平川知秀", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2758/9784297152758_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4297152754?tag=javeo2022-22", "rk": "https://hb.afl.rakuten.co.jp/hgc/24ecde15.e492bb61.24ecde16.6c9a125b/?pc=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18401065%2F&#038;m=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18401065%2F", "id": "4297152754"}, {"t": "開発効率をアップする! Claude Code 実用入門", "a": "大澤文孝", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/0206/9784839990206.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4839990204?tag=javeo2022-22", "rk": "", "id": "4839990204"}, {"t": "Microsoft 365 Copilot踏み込み活用術", "a": "太田浩史", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/3401/9784295023401_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/429502340X?tag=javeo2022-22", "rk": "", "id": "429502340X"}, {"t": "Google NotebookLM 即効活用大全", "a": "増田智明", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/5995/9784297155995_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4297155990?tag=javeo2022-22", "rk": "", "id": "4297155990"}, {"t": "できるClaude &#038; Claude Cowork", "a": "清水理史・できるシリーズ編集部", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/4842/9784295024842_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4295024848?tag=javeo2022-22", "rk": "", "id": "4295024848"}, {"t": "Claude仕事術", "a": "佐藤傑", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/5304/9784815645304.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4815645302?tag=javeo2022-22", "rk": "", "id": "4815645302"}, {"t": "あらゆる仕事が爆速化する Claude Code徹底活用術", "a": "ひつじ", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2901/9784534062901_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4534062907?tag=javeo2022-22", "rk": "", "id": "4534062907"}];
var REL='nofollow sponsored noopener noreferrer';
function el(tag,cls){var e=document.createElement(tag);if(cls)e.className=cls;return e;}
function btn(href,cls,label,mall,id){
var a=el('a','jv-btn '+cls);a.href=href;a.target='_blank';a.rel=REL;a.textContent=label;
a.setAttribute('data-shelf',SHELF);a.setAttribute('data-mall',mall);a.setAttribute('data-book',id);
return a;}
function card(b){
var d=el('div','jv-book-card');
var cover=el('a','jv-book-cover');cover.href=b.az;cover.target='_blank';cover.rel=REL;
var img=el('img');img.src=b.img;img.alt=b.t;img.width=120;img.height=170;img.loading='lazy';
cover.appendChild(img);
var info=el('div','jv-book-info');
var ttl=el('p','jv-book-title');ttl.textContent=b.t;
var au=el('p','jv-book-author');au.textContent=b.a;
var btns=el('div','jv-book-btns');
btns.appendChild(btn(b.az,'jv-btn-az','Amazonで見る','amazon',b.id));
if(b.rk){btns.appendChild(btn(b.rk,'jv-btn-rk','楽天で見る','rakuten',b.id));}
info.appendChild(ttl);info.appendChild(au);info.appendChild(btns);
d.appendChild(cover);d.appendChild(info);
return d;}
var pool=POOL.slice();
for(var i=pool.length-1;i>0;i--){var j=Math.floor(Math.random()*(i+1));var t=pool[i];pool[i]=pool[j];pool[j]=t;}
var grid=root.querySelector('.jv-books-grid');
grid.style.opacity='0';
while(grid.firstChild)grid.removeChild(grid.firstChild);
pool.slice(0,3).forEach(function(b){grid.appendChild(card(b));});
grid.style.transition='opacity .5s ease';
void grid.offsetWidth;
grid.style.opacity='1';
})();
</script>
</div>]]></content:encoded>
					
					<wfw:commentRss>https://javeo.jp/keiba-ai-challenge-02/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Claudeで競馬予想AIを自作する #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・Claude]]></category>
		<category><![CDATA[netkeiba]]></category>
		<category><![CDATA[スクレイピング]]></category>
		<category><![CDATA[競馬]]></category>
		<category><![CDATA[自動化]]></category>
		<guid isPermaLink="false">https://javeo.jp/?p=3838</guid>

					<description><![CDATA[<p><img src="https://javeo.jp/wp-content/uploads/2026/03/AIで競馬-1024x538.jpg" class="webfeedsFeaturedVisual" /></p>競馬予想AIをAIと一緒に作ってみるシリーズの第一回(はじめに)。AIで何か作りたいという動機から、相棒に選んだClaude、スクレイピング→予想→note自動投稿までの全体構成をまとめました]]></description>
										<content:encoded><![CDATA[<p><img src="https://javeo.jp/wp-content/uploads/2026/03/AIで競馬-1024x538.jpg" class="webfeedsFeaturedVisual" /></p><div class="codoc-evacuations" style="display:none;" data-shortcode=""></div>
<div class="wp-block-group is-style-big_icon_memo"><div class="wp-block-group__inner-container">
<p class="wp-block-paragraph">2026年初頭AIの進歩が凄まじく、AIを使った何かをやってみたいと競馬予想AIを作ってみましたのでその取り組みをまとめてみました</p>



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



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



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



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



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



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



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



<p class="wp-block-paragraph">今回一緒に開発を進めるAIパートナーとして選ばれたのは「<strong><span class="swl-marker mark_yellow" style="color:#e8313b">Claude（クロード）</span></strong>」でした</p>



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



<p class="wp-block-paragraph">汎用AIの優劣はネットでも様々議論されていますが、今の情勢と一カ月後の情勢はきっと違うので<strong><span class="swl-marker mark_yellow">好みで使えばいい</span></strong>と思います</p>



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


<div class="p-blogParts post_content" data-partsID="5291">
<div class="jv-adshelf" data-shelf="ai">
<style>
.jv-adshelf{border:1px solid #d7ddea;border-radius:8px;background:#f8faff;padding:16px 18px;margin:2em 0}
.jv-adshelf__head{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.jv-adshelf__title{font-weight:700;font-size:1.02em;line-height:1.4}
.jv-adshelf__pr{flex:none;font-size:.7em;font-weight:700;color:#fff;background:#8a94a6;border-radius:3px;padding:2px 6px;letter-spacing:.04em}
.jv-adshelf__card{display:flex;gap:16px;align-items:flex-start;flex-wrap:wrap;padding:14px 0}
.jv-adshelf__card+.jv-adshelf__card{border-top:1px dashed #d7ddea}
.jv-adshelf__banner{flex:0 0 200px;max-width:100%;display:flex;align-items:center;justify-content:center}
.jv-adshelf__banner img{max-width:100%;height:auto;display:block}
.jv-adshelf__body{flex:1 1 260px;min-width:0}
.jv-adshelf__name{font-weight:700;margin:0 0 6px;font-size:.95em;line-height:1.5}
.jv-adshelf__desc{margin:0 0 12px;font-size:.9em;line-height:1.8;color:#3d4451}
.jv-adshelf__btn{display:inline-block;background:#3B5BDB;color:#fff;font-weight:700;font-size:.9em;text-decoration:none;border-radius:5px;padding:9px 18px}
.jv-adshelf__btn:hover{opacity:.88}
@media(max-width:600px){.jv-adshelf__banner{flex:0 0 100%;max-width:240px;margin:0 auto}}
</style>
<div class="jv-adshelf__head"><span class="jv-adshelf__title">AIで仕事を減らしたい方へ</span><span class="jv-adshelf__pr">PR</span></div>
<div class="jv-adshelf__card">
<div class="jv-adshelf__banner"><a href="//af.moshimo.com/af/c/click?a_id=5654017&#038;p_id=7494&#038;pc_id=21647&#038;pl_id=93995" rel="nofollow" referrerpolicy="no-referrer-when-downgrade" attributionsrc><img decoding="async" src="//image.moshimo.com/af-img/7302/000000093995.png" width="500" height="500" style="border:none;"></a><img decoding="async" src="//i.moshimo.com/af/i/impression?a_id=5654017&#038;p_id=7494&#038;pc_id=21647&#038;pl_id=93995" width="1" height="1" style="border:none;" loading="lazy"></div>
<div class="jv-adshelf__body">
<p class="jv-adshelf__name">AI鬼管理｜Claude Code活用の業務自動化トレーニング</p>
<p class="jv-adshelf__desc">Claude Code を業務の自動化に使うためのトレーニングです<br>いきなり申し込む形ではなく、まずは無料の業務効率化診断(オンライン面談)から始められます</p>
<a class="jv-adshelf__btn" href="//af.moshimo.com/af/c/click?a_id=5654017&#038;p_id=7494&#038;pc_id=21647&#038;pl_id=93995" target="_blank" rel="nofollow noopener" referrerpolicy="no-referrer-when-downgrade">無料の業務効率化診断を見てみる</a>
</div>
</div>
<div class="jv-adshelf__card">
<div class="jv-adshelf__banner"><a href="https://px.a8.net/svt/ejp?a8mat=4B41ZJ+57JLVM+5988+609HT" rel="nofollow"><img decoding="async" border="0" width="300" height="250" alt="" src="https://www23.a8.net/svt/bgt?aid=260529679315&#038;wid=003&#038;eno=01&#038;mid=s00000024524001009000&#038;mc=1"></a><img decoding="async" border="0" width="1" height="1" src="https://www14.a8.net/0.gif?a8mat=4B41ZJ+57JLVM+5988+609HT" alt=""></div>
<div class="jv-adshelf__body">
<p class="jv-adshelf__name">Notta｜AI自動文字起こし</p>
<p class="jv-adshelf__desc">会議や動画の音声を自動で文字にするサービスです<br>議事録や書き起こしの手作業を減らしたいときに</p>
<a class="jv-adshelf__btn" href="https://px.a8.net/svt/ejp?a8mat=4B41ZJ+57JLVM+5988+609HT" target="_blank" rel="nofollow noopener" referrerpolicy="no-referrer-when-downgrade">Nottaを見てみる</a>
</div>
</div>
</div>
</div>


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



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



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



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



<div class="wp-block-group is-style-emboss_box"><div class="wp-block-group__inner-container">
<p class="has-text-align-center wp-block-paragraph"><strong>スクレイピング　→　競馬AIで予想　→　予想結果をnoteに投稿</strong></p>
</div></div>



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



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



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



<p class="wp-block-paragraph">今時点では4回に分けて下記順番で作るつもり</p>



<ol style="background-color:#f3fafe80" class="wp-block-list -list-under-dashed has-background">
<li>過去データの収集
<ol class="wp-block-list">
<li><a href="https://javeo.jp/keiba-ai-challenge-02/" target="_blank" rel="noopener noreferrer">netkeibaからのスクレイピング</a></li>



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



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



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



<li><a href="https://javeo.jp/keiba-ai-challenge-03/" target="_blank" rel="noopener noreferrer">予想のアウトプットでnote記事の自動作成と販売</a></li>
</ol>



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



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



<p class="wp-block-paragraph">今時点ではAI未完成で<strong><span class="swl-marker mark_yellow">スクレイピングは完成とnote投稿8割くらい</span></strong>です</p>



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



<p class="wp-block-paragraph">あまり間延びしないように作っていくので追いかけてもらえると嬉しいです！</p>


<div class="p-blogParts post_content" data-partsID="4603">
<div class="jv-books" data-shelf="ai"><div class="jv-books-head"><span class="jv-books-title"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Claude・生成AIを学べる本</span><span class="jv-books-pr">PR</span></div><div class="jv-books-grid"><div class="jv-book-card"><a class="jv-book-cover" href="https://www.amazon.co.jp/dp/4297153548?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer"><img decoding="async" src="https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/3540/9784297153540_1_2.jpg?_ex=240x240" alt="実践Claude Code入門" width="120" height="170" loading="lazy"></a><div class="jv-book-info"><p class="jv-book-title">実践Claude Code入門</p><p class="jv-book-author">西見公宏・吉田真吾・大嶋勇樹</p><div class="jv-book-btns"><a class="jv-btn jv-btn-az" href="https://www.amazon.co.jp/dp/4297153548?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="amazon" data-book="4297153548">Amazonで見る</a><a class="jv-btn jv-btn-rk" href="https://hb.afl.rakuten.co.jp/hgc/24ecde15.e492bb61.24ecde16.6c9a125b/?pc=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18439208%2F&#038;m=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18439208%2F" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="rakuten" data-book="4297153548">楽天で見る</a></div></div></div><div class="jv-book-card"><a class="jv-book-cover" href="https://www.amazon.co.jp/dp/4297152754?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer"><img decoding="async" src="https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2758/9784297152758_1_2.jpg?_ex=240x240" alt="Claude CodeによるAI駆動開発入門" width="120" height="170" loading="lazy"></a><div class="jv-book-info"><p class="jv-book-title">Claude CodeによるAI駆動開発入門</p><p class="jv-book-author">平川知秀</p><div class="jv-book-btns"><a class="jv-btn jv-btn-az" href="https://www.amazon.co.jp/dp/4297152754?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="amazon" data-book="4297152754">Amazonで見る</a><a class="jv-btn jv-btn-rk" href="https://hb.afl.rakuten.co.jp/hgc/24ecde15.e492bb61.24ecde16.6c9a125b/?pc=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18401065%2F&#038;m=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18401065%2F" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="rakuten" data-book="4297152754">楽天で見る</a></div></div></div><div class="jv-book-card"><a class="jv-book-cover" href="https://www.amazon.co.jp/dp/4839990204?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer"><img decoding="async" src="https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/0206/9784839990206.jpg?_ex=240x240" alt="開発効率をアップする! Claude Code 実用入門" width="120" height="170" loading="lazy"></a><div class="jv-book-info"><p class="jv-book-title">開発効率をアップする! Claude Code 実用入門</p><p class="jv-book-author">大澤文孝</p><div class="jv-book-btns"><a class="jv-btn jv-btn-az" href="https://www.amazon.co.jp/dp/4839990204?tag=javeo2022-22" target="_blank" rel="nofollow sponsored noopener noreferrer" data-shelf="ai" data-mall="amazon" data-book="4839990204">Amazonで見る</a></div></div></div></div><p class="jv-books-note">私のおすすめからランダムで3冊を表示しています</p></div>
<style>
.jv-books{border:1px solid #e0e0e0;border-radius:8px;padding:16px;margin:1.5em 0;background:#fff}
.jv-books-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:12px}
.jv-books-title{font-weight:700;font-size:1.05em}
.jv-books-pr{font-size:.7em;color:#888;border:1px solid #ccc;border-radius:3px;padding:1px 6px;flex-shrink:0}
.jv-books-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;min-height:330px}
.jv-book-card{display:flex;flex-direction:column;align-items:center;text-align:center;border:1px solid #f0f0f0;border-radius:6px;padding:12px 8px;background:#fafafa}
.jv-book-cover img{width:auto;max-width:120px;height:170px;object-fit:contain}
.jv-book-info{width:100%;flex:1;display:flex;flex-direction:column}
.jv-book-title{font-size:.82em;font-weight:600;line-height:1.4;margin:8px 0 2px;min-height:2.8em;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}
.jv-book-author{font-size:.72em;color:#777;margin:0 0 8px}
.jv-book-btns{display:flex;flex-direction:column;gap:6px;width:100%;margin-top:auto}
.jv-btn{display:block;text-align:center;font-size:.78em;font-weight:600;color:#fff!important;text-decoration:none!important;border-radius:4px;padding:7px 4px;line-height:1.2}
.jv-btn-az{background:#e88b00}
.jv-btn-rk{background:#bf0000}
.jv-btn:hover{opacity:.85}
.jv-books-note{font-size:.7em;color:#999;text-align:right;margin:8px 0 0}
@media(max-width:599px){
.jv-books-grid{grid-template-columns:1fr;min-height:0}
.jv-book-card{flex-direction:row;text-align:left;gap:12px}
.jv-book-cover img{height:110px;max-width:80px}
.jv-book-info{min-width:0}
.jv-book-title{min-height:0}
.jv-book-btns{flex-direction:row}
.jv-btn{flex:1}
}
</style>
<script>
(function(){
var SHELF='ai';
var all=document.querySelectorAll('.jv-books[data-shelf="'+SHELF+'"]');
var root=null;
for(var k=0;k<all.length;k++){if(!all[k].dataset.jvinit){root=all[k];break;}}
if(!root)return;
root.dataset.jvinit='1';
var POOL=[{"t": "実践Claude Code入門", "a": "西見公宏・吉田真吾・大嶋勇樹", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/3540/9784297153540_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4297153548?tag=javeo2022-22", "rk": "https://hb.afl.rakuten.co.jp/hgc/24ecde15.e492bb61.24ecde16.6c9a125b/?pc=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18439208%2F&#038;m=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18439208%2F", "id": "4297153548"}, {"t": "Claude CodeによるAI駆動開発入門", "a": "平川知秀", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2758/9784297152758_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4297152754?tag=javeo2022-22", "rk": "https://hb.afl.rakuten.co.jp/hgc/24ecde15.e492bb61.24ecde16.6c9a125b/?pc=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18401065%2F&#038;m=https%3A%2F%2Fbooks.rakuten.co.jp%2Frb%2F18401065%2F", "id": "4297152754"}, {"t": "開発効率をアップする! Claude Code 実用入門", "a": "大澤文孝", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/0206/9784839990206.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4839990204?tag=javeo2022-22", "rk": "", "id": "4839990204"}, {"t": "Microsoft 365 Copilot踏み込み活用術", "a": "太田浩史", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/3401/9784295023401_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/429502340X?tag=javeo2022-22", "rk": "", "id": "429502340X"}, {"t": "Google NotebookLM 即効活用大全", "a": "増田智明", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/5995/9784297155995_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4297155990?tag=javeo2022-22", "rk": "", "id": "4297155990"}, {"t": "できるClaude &#038; Claude Cowork", "a": "清水理史・できるシリーズ編集部", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/4842/9784295024842_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4295024848?tag=javeo2022-22", "rk": "", "id": "4295024848"}, {"t": "Claude仕事術", "a": "佐藤傑", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/5304/9784815645304.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4815645302?tag=javeo2022-22", "rk": "", "id": "4815645302"}, {"t": "あらゆる仕事が爆速化する Claude Code徹底活用術", "a": "ひつじ", "img": "https://thumbnail.image.rakuten.co.jp/@0_mall/book/cabinet/2901/9784534062901_1_2.jpg?_ex=240x240", "az": "https://www.amazon.co.jp/dp/4534062907?tag=javeo2022-22", "rk": "", "id": "4534062907"}];
var REL='nofollow sponsored noopener noreferrer';
function el(tag,cls){var e=document.createElement(tag);if(cls)e.className=cls;return e;}
function btn(href,cls,label,mall,id){
var a=el('a','jv-btn '+cls);a.href=href;a.target='_blank';a.rel=REL;a.textContent=label;
a.setAttribute('data-shelf',SHELF);a.setAttribute('data-mall',mall);a.setAttribute('data-book',id);
return a;}
function card(b){
var d=el('div','jv-book-card');
var cover=el('a','jv-book-cover');cover.href=b.az;cover.target='_blank';cover.rel=REL;
var img=el('img');img.src=b.img;img.alt=b.t;img.width=120;img.height=170;img.loading='lazy';
cover.appendChild(img);
var info=el('div','jv-book-info');
var ttl=el('p','jv-book-title');ttl.textContent=b.t;
var au=el('p','jv-book-author');au.textContent=b.a;
var btns=el('div','jv-book-btns');
btns.appendChild(btn(b.az,'jv-btn-az','Amazonで見る','amazon',b.id));
if(b.rk){btns.appendChild(btn(b.rk,'jv-btn-rk','楽天で見る','rakuten',b.id));}
info.appendChild(ttl);info.appendChild(au);info.appendChild(btns);
d.appendChild(cover);d.appendChild(info);
return d;}
var pool=POOL.slice();
for(var i=pool.length-1;i>0;i--){var j=Math.floor(Math.random()*(i+1));var t=pool[i];pool[i]=pool[j];pool[j]=t;}
var grid=root.querySelector('.jv-books-grid');
grid.style.opacity='0';
while(grid.firstChild)grid.removeChild(grid.firstChild);
pool.slice(0,3).forEach(function(b){grid.appendChild(card(b));});
grid.style.transition='opacity .5s ease';
void grid.offsetWidth;
grid.style.opacity='1';
})();
</script>
</div>]]></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[<p><img src="https://javeo.jp/wp-content/uploads/2023/07/python-1024x538.png" class="webfeedsFeaturedVisual" /></p>スクレイピングを始める前に 今回はnetkeibaから大量のデータを取得しますし、取得したデータは分析してナンボなので取得情報はデータベース（今回はXserverのMariaDB）に保存する前提でプログラム作成しています [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><img src="https://javeo.jp/wp-content/uploads/2023/07/python-1024x538.png" class="webfeedsFeaturedVisual" /></p><div class="codoc-evacuations" style="display:none;" data-shortcode=""></div>
<div class="wp-block-cocoon-blocks-info-box block-box primary-box">
<p class="wp-block-paragraph">趣味で競馬をやっている私、ジャベ雄です</p>



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



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



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



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



<p class="wp-block-paragraph">PythonでXserverを操作する方法は↓↓を参考にどうぞ</p>



<p class="wp-block-paragraph"><a href="https://javeo.jp/python-xserver-connect">https://javeo.jp/python-xserver-connect</a></p>



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



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



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



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



<p class="wp-block-paragraph">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 class="wp-block-paragraph">確認するためにははChromeの設定メニューで切り替えるのですが、頻繁に切り替える可能性がある人はChrome拡張機能（私は<a 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" target="_blank" rel="noreferrer noopener nofollow">Quick Javascript Switcher</a>）を使うこと推奨です</p>



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



<figure class="wp-block-image size-large is-resized"><a 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 class="wp-block-paragraph">ぶっちゃけ、&#8221;<strong>Beautifulsoup</strong>&#8220;だけで完結するサイトは少ないので&#8221;<span class="bold-red">Selenium</span>&#8220;を使う前提でもOK</p>



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



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



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



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



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



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



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



<figure class="wp-block-image size-large"><a 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 class="wp-block-paragraph">このパラメータを変えれば当然その日のレース一覧に変わるわけですが開催されていない日を指定するとレースが何も表示されないので1日ずる変更すると無駄な読み込みになってしまう</p>



<figure class="wp-block-image size-large"><a 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 class="wp-block-paragraph">もう一つ、上部の&#8221;前&#8221;や&#8221;次&#8221;をクックすると1週間分の日付が前後することがわかります</p>



<figure class="wp-block-image size-large"><a 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">レース一覧を取得するための結論</h3>



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



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



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



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



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



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



<div class="hcb_wrap"><pre class="prism line-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; li[date=&quot;{race_date.get(&#39;date&#39;)}&quot;]&#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">あとがき</h2>



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



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



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