<?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>Men of Letters（メン・オブ・レターズ） &#8211; 論理的思考/業務改善/プログラミング</title>
	<atom:link href="https://laboratory.kazuuu.net/feed/" rel="self" type="application/rss+xml" />
	<link>https://laboratory.kazuuu.net</link>
	<description></description>
	<lastBuildDate>Thu, 17 Sep 2026 23:49:09 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.1</generator>

<image>
	<url>https://laboratory.kazuuu.net/wp-content/uploads/2021/02/cropped-L-32x32.png</url>
	<title>Men of Letters（メン・オブ・レターズ） &#8211; 論理的思考/業務改善/プログラミング</title>
	<link>https://laboratory.kazuuu.net</link>
	<width>32</width>
	<height>32</height>
</image> 
<atom:link rel="hub" href="https://pubsubhubbub.appspot.com"/>
<atom:link rel="hub" href="https://pubsubhubbub.superfeedr.com"/>
<atom:link rel="hub" href="https://websubhub.com/hub"/>
<atom:link rel="self" href="https://laboratory.kazuuu.net/feed/"/>
	<item>
		<title>Googleドライブの標準設定は利用者が鍵を独占するエンドツーエンド暗号化（E2EE）ではない。</title>
		<link>https://laboratory.kazuuu.net/default-setting-for-googledrive-is-not-end-to-end-encryption-where-the-user-has-exclusive-control-over-keys/</link>
					<comments>https://laboratory.kazuuu.net/default-setting-for-googledrive-is-not-end-to-end-encryption-where-the-user-has-exclusive-control-over-keys/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Thu, 17 Sep 2026 23:45:10 +0000</pubDate>
				<category><![CDATA[その他]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17908</guid>

					<description><![CDATA[非公開は「他人に見せない」だけ Googleドライブの標準設定は、利用者が鍵を独占するエンドツーエンド暗号化（E2EE）ではない。通信中も保管中も暗号化されてはいるが、鍵を持つのは事業者側だ。鍵を持つ側は、必要なら中身を [&#8230;]]]></description>
										<content:encoded><![CDATA[<h1>非公開は「他人に見せない」だけ</h1>
<div>
<div>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">Googleドライブの標準設定は、利用者が鍵を独占するエンドツーエンド暗号化（E2EE）ではない。通信中も保管中も暗号化されてはいるが、鍵を持つのは事業者側だ。鍵を持つ側は、必要なら中身を検査できる。</span></span></span><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1x3r274 r-p1pxzi"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">暗号化には大別して3種類ある。</span></span></span></p>
<ol dir="ltr" start="1">
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">通信路だけ暗号化する方式 </span></span></li>
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">保管中も暗号化するが、鍵は事業者が持つ方式 </span></span></li>
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">利用者だけが鍵を持つE2EE</span></span></li>
</ol>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">Googleドライブの標準は2番だ。DropboxやOneDriveも同じ系統で、OneDriveの「個人用Vault」も鍵はMicrosoftが持つ。したがって「非公開」は共有リンクを切る意味であり、Googleのシステムから見えなくする意味ではない。</span></span></span></p>
</div>
<div></div>
<div><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">逆に、Google Workspaceのクライアントサイド暗号化（CSE）では、鍵を組織側が管理する。公式ヘルプは、CSEされたファイルはフィッシングやマルウェアのスキャン対象にならない、と明記している。サーバーが中身を読めないからだ。標準の個人向けドライブには、この前提がそのまま当てはまらない。</span></span></span></div>
</div>
<div>
<div>検査の中身は、人が全ファイルを開く作業ではない。画像の指紋照合（知覚ハッシュ）と、機械学習による分類だ。既知の問題画像に近い特徴、あるいはポリシーに抵触すると判断された描写を、システムが機械的に拾う。</div>
</div>
<div>
<h1>公式数字が示す「ほぼ自動」</h1>
</div>
<div>
<div>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">人が見ていない、という説明は感覚論ではない。Google自身の透明性レポートが規模を出している。</span></span></span><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1x3r274 r-p1pxzi"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">対象期間は2025年7月〜12月。対象はGoogleとYouTube。報告されたCSAM関連コンテンツは570万7562件。内訳は次の通りだ。</span></span></span></p>
<ul dir="ltr">
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">自動検出：569万1747件（99.7%） </span></span></li>
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">手動検出：1万5815件（0.3%）</span></span></li>
</ul>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">自動検出の中身として、レポートはハッシュ照合と機械学習分類器を挙げている。手動はユーザー報告、政府・法執行機関の報告、優先報告者などだ。</span></span></span><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">つまりこの半年間、報告の99.7%は機械が先に見つけている。人が全件を開いているわけではない。一方で、機械が先に見つけるからこそ、非公開フォルダでも引っかかる。</span></span></span></p>
</div>
</div>
<div><a href="https://laboratory.kazuuu.net/wp-content/uploads/2026/09/スクリーンショット-2026-09-18-084033.png"><img fetchpriority="high" decoding="async" class="alignnone size-medium wp-image-17909" src="https://laboratory.kazuuu.net/wp-content/uploads/2026/09/スクリーンショット-2026-09-18-084033-274x300.png" alt="" width="274" height="300" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2026/09/スクリーンショット-2026-09-18-084033-274x300.png 274w, https://laboratory.kazuuu.net/wp-content/uploads/2026/09/スクリーンショット-2026-09-18-084033.png 652w" sizes="(max-width: 274px) 100vw, 274px" /></a></div>
<div>
<div>読み方は単純だ。円のほとんどが自動検出。中央の570万件は「人が700万枚を目視した」数字ではない。同一内容が複数アカウントや複数回で見つかると重複して数えられる、とレポート自身も注記している。それでも方向は明確で、検出の主体は人ではなくシステムだ。</div>
</div>
<div>
<h1>コンテンツ件数だけ見ると誤解する</h1>
</div>
<div>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1x3r274 r-p1pxzi"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">同じ半年間には、別の公式指標もある。</span></span></span></p>
<ul dir="ltr">
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">NCMECへのCyberTipline報告：86万2284件 </span></span></li>
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">CSAM違反で措置が講じられたアカウント：36万5597件</span></span></li>
</ul>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">コンテンツ件数、報告件数、アカウント件数は同じものではない。1件の報告に複数コンテンツが含まれることも、1人のアカウントから複数ファイルが見つかることもある。アカウント措置には、停止だけでなくサービス制限も含まれる。</span></span></span></p>
</div>
<div><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">重要なのは比率だ。検出はほぼ自動。その先で報告とアカウント措置が走る。漫画家の原稿BANでGmailまで止まったのは、この「ファイル単位の警告」が「アカウント単位の処分」に跳ねる構造があるからだ。</span></span></span></div>
<div><a href="https://laboratory.kazuuu.net/wp-content/uploads/2026/09/image-9.png"><img decoding="async" class="alignnone size-medium wp-image-17910" src="https://laboratory.kazuuu.net/wp-content/uploads/2026/09/image-9-300x178.png" alt="" width="300" height="178" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2026/09/image-9-300x178.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2026/09/image-9-1024x609.png 1024w, https://laboratory.kazuuu.net/wp-content/uploads/2026/09/image-9-768x457.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2026/09/image-9-1536x913.png 1536w, https://laboratory.kazuuu.net/wp-content/uploads/2026/09/image-9.png 1961w" sizes="(max-width: 300px) 100vw, 300px" /></a></div>
<div>
<div><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">透明性レポートはこう書いている。ユーザーのGoogleアカウントでCSAMが見つかった場合、NCMECへの報告などの措置を取る。加えて、サービスの停止やアクセス制限もあり得る。影響を受けたユーザーには通知し、不服申立ての機会を与える、と。</span></span></span></div>
<div></div>
<div><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">再審査が通らなければ、ドライブの1ファイルの問題が、Gmailを含むアカウント全体の停止になる。非公開だから軽い、という段階はない。</span></span></span></div>
</div>
<div>
<h1>では「中を見ている」のか</h1>
</div>
<div>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">正確に言うと、次の整理になる。</span></span></span></p>
<ul dir="ltr">
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">社員が日常的に個人フォルダを閲覧している、という意味では、通常は見ていない </span></span></li>
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">ただし標準ドライブでは、システムがファイルの特徴を照合できる </span></span></li>
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">照合の99.7%は自動で、人が見るのはごく一部 </span></span></li>
<li><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">非公開設定は、その自動検査を止めるスイッチではない</span></span></li>
</ul>
<p><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">「誰にも見せていないのにBANされた」は、利用者の感覚としては正しい。技術と規約の側から見ると、見せていない相手は他人であって、預かり先の検査システムではなかった。</span></span></span></p>
</div>
<div><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1g7jtus r-1x3r274"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">鍵を利用者が独占するサービスなら、サーバー側の同じ検査は成立しにくい。講演でMEGA、Proton Drive、Tresorit、iCloudの「高度なデータ保護」が挙がったのは、その違いだ。ただしE2EEでも、ファイル名、サイズ、更新時刻などのメタデータは事業者側に残る。完全な不可視ではない。</span></span></span></div>
<div><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1adg3ll r-1x3r274 r-p1pxzi"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3"><span class="css-1jxf684 r-bcqeeo r-1ttztb7 r-qvutc0 r-poiln3 r-1x3r274">非公開の安心感は、共有範囲の話だ。検査されるかどうかは、鍵を誰が持つかの話だ。この2つを混ぜたままクラウドに原稿を置くと、今回のように「自分しか見ないファイル」が、アカウント全体を止める材料になる。</span></span></span></div>
<div>■関連記事</div>
<div><a href="https://www.itmedia.co.jp/news/article/2609/16/2000001527/">マンガ原稿をクラウドに上げただけでGoogleアカウントBAN　Gmailも道連れ……日本では合法なのになぜ？</a></div>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/default-setting-for-googledrive-is-not-end-to-end-encryption-where-the-user-has-exclusive-control-over-keys/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Building an Automated B2B Lead Generator &#038; Spatial Heatmap with SearchApi and Python</title>
		<link>https://laboratory.kazuuu.net/building-an-automated-b2b-lead-generator-spatial-heatmap-with-searchapi-and-python/</link>
					<comments>https://laboratory.kazuuu.net/building-an-automated-b2b-lead-generator-spatial-heatmap-with-searchapi-and-python/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Tue, 07 Jul 2026 02:09:26 +0000</pubDate>
				<category><![CDATA[Python【初歩から現場実務的なもの】]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17903</guid>

					<description><![CDATA[Public economic data and official directories are often plagued by lag and rigid structures. For businesses lo [&#8230;]]]></description>
										<content:encoded><![CDATA[<p data-path-to-node="4">Public economic data and official directories are often plagued by lag and rigid structures. For businesses looking for high-intent B2B sales leads, or data analysts seeking real-time local market indicators, having a &#8220;live&#8221; data pipeline is a massive competitive advantage.</p>
<p data-path-to-node="5">In this tutorial, we will build an end-to-end Python pipeline using <b data-path-to-node="5" data-index-in-node="68">SearchApi</b> (specifically the Google Maps and Google Place engines) to achieve two high-value outcomes simultaneously:</p>
<ol start="1" data-path-to-node="6">
<li>
<p data-path-to-node="6,0,0"><b data-path-to-node="6,0,0" data-index-in-node="0">Commercial Use Case:</b> Automatically generate a premium, corporate-styled Excel spreadsheet (<code data-path-to-node="6,0,0" data-index-in-node="91">.xlsx</code>) containing deeply enriched B2B sales leads (Names, Addresses, Phone Numbers, Websites, and Pricing Tiers).</p>
</li>
<li>
<p data-path-to-node="6,1,0"><b data-path-to-node="6,1,0" data-index-in-node="0">Analytical Use Case:</b> Automatically render a fully interactive HTML spatial map (<code data-path-to-node="6,1,0" data-index-in-node="80">folium</code>), dynamic and color-coded based on local price levels ($, $$, $$$).</p>
</li>
</ol>
<h2>Why SearchApi? Bypassing Infrastructure Hurdles</h2>
<p>Extracting high-quality, high-frequency location data directly from mapping platforms is notoriously difficult. Sophisticated anti-bot protections, aggressive IP rate-limiting, and sudden CAPTCHAs can easily break custom-built web scrapers, leading to high maintenance costs and incomplete datasets.</p>
<p>This is where <span class="hljs-strong">SearchApi</span> serves as the critical infrastructure for our project. By handling real-time proxy rotation, CAPTCHA solving, and data retrieval under the hood, it returns clean, structured JSON instantly. Instead of wasting dozens of hours battling infrastructure hurdles and managing headless browsers, this API-first approach allows us to focus 100% of our energy on the core logic of data interpretation, commercial structuring, and visual mapping.</p>
<h2 data-path-to-node="8">1. Architecture Overview: The Power of Multi-Engine Data Enrichment</h2>
<p data-path-to-node="9">Standard web scraping tools easily break due to Google&#8217;s robust anti-bot infrastructure. By treating <b data-path-to-node="9" data-index-in-node="101">SearchApi</b> as our critical infrastructure, we bypass proxy management and headless browser configurations entirely, allowing us to focus 100% on data structure and analysis.</p>
<p data-path-to-node="10">Our script utilizes a two-tier data enrichment pipeline:</p>
<ul data-path-to-node="11">
<li>
<p data-path-to-node="11,0,0"><b data-path-to-node="11,0,0" data-index-in-node="0">Tier 1 (<code data-path-to-node="11,0,0" data-index-in-node="8">google_maps</code>)</b>: Sweeps a targeted geographic coordinate and radius based on your industry query (e.g., &#8220;steakhouse&#8221;, &#8220;dentist&#8221;, &#8220;coffee shop&#8221;) to compile a comprehensive local business registry.</p>
</li>
<li>
<p data-path-to-node="11,1,0"><b data-path-to-node="11,1,0" data-index-in-node="0">Tier 2 (<code data-path-to-node="11,1,0" data-index-in-node="8">google_place</code>)</b>: Sequentially takes the unique identifiers (<code data-path-to-node="11,1,0" data-index-in-node="66">kgmid</code> or <code data-path-to-node="11,1,0" data-index-in-node="75">place_id</code>) from Tier 1 to fetch deep-layer operational metadata, such as exact pricing details and missing contact points.</p>
</li>
</ul>
<h2 data-path-to-node="13">2. Complete Python Script (<code data-path-to-node="13" data-index-in-node="27">sales_and_map_pipeline.py</code>)</h2>
<p data-path-to-node="14">This production-grade script features defensive type-checking and exponential backoff retry logic to handle unexpected API structures flawlessly without crashing.</p>
<pre>import requests
import time
import logging
import os
import math
from datetime import datetime
from collections import Counter
from typing import List, Dict, Tuple

import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment
from openpyxl.utils.dataframe import dataframe_to_rows
from openpyxl.utils import get_column_letter

try:
    import folium
    from folium import FeatureGroup, LayerControl
    FOLIUM_AVAILABLE = True
except ImportError:
    FOLIUM_AVAILABLE = False

logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s", datefmt="%H:%M:%S")
logger = logging.getLogger(__name__)

# ============================================================
# ====================== CONFIGURATION =======================
# ============================================================

API_KEY = os.getenv("SEARCHAPI_KEY") or "YOUR_SEARCHAPI_KEY_HERE"

# === Search Settings ===
SEARCH_QUERY = "steakhouse"                    # Target industry (e.g. "coffee shop", "dentist", "sushi")
LOCATION = "@40.7128,-74.0060,14z"             # Coordinates + zoom level (e.g., New York City)
LANGUAGE = "en"                                # "en", "ja", "es", etc.
COUNTRY = "us"                                 # Country code

MAX_PAGES = 3
SEARCH_SLEEP = 1.8

# === Detail Enrichment (Price + Contact Info) ===
ENRICH_DETAILS = False                         # Set True to fetch deep price/contact details via google_place
DETAIL_TOP_N = 0                               # 0 = all results, or limit number to control costs

# === Output Files ===
TIMESTAMP = datetime.now().strftime('%Y%m%d_%H%M')
EXCEL_FILENAME = f"business_analysis_{TIMESTAMP}.xlsx"
CSV_FILENAME = f"business_analysis_{TIMESTAMP}.csv"
MAP_FILENAME = f"business_map_{TIMESTAMP}.html"

# ============================================================
# ====================== HELPER FUNCTIONS ====================
# ============================================================

def normalize_price_band(price_str: str) -&gt; str:
    if not price_str:
        return "Unknown"
    price_str = str(price_str).strip().lower()
    if "¥¥¥¥" in price_str or "$$$$" in price_str:
        return "Very Expensive"
    elif "¥¥¥" in price_str or "$$$" in price_str:
        return "Expensive"
    elif "¥¥" in price_str or "$$" in price_str:
        return "Moderate"
    elif "¥" in price_str or "$" in price_str:
        return "Inexpensive"
    return "Unknown"

def get_price_color(price_level: str) -&gt; str:
    colors = {
        "Inexpensive": "green",
        "Moderate": "blue",
        "Expensive": "orange",
        "Very Expensive": "red",
        "Unknown": "gray"
    }
    return colors.get(price_level, "gray")

def parse_gps(place: dict) -&gt; Tuple[float, float]:
    gps = place.get("gps_coordinates", {})
    if isinstance(gps, dict):
        try:
            return float(gps.get("latitude", 0)), float(gps.get("longitude", 0))
        except:
            pass
    return 0.0, 0.0

def calculate_popularity_score(rating: float, reviews: int) -&gt; float:
    if not rating or not reviews:
        return 0.0
    return round(rating * math.log(reviews + 1), 2)

def extract_base_info(place: dict) -&gt; dict:
    lat, lng = parse_gps(place)
    rating = float(place.get("rating", 0) or 0)
    reviews = int(place.get("reviews", 0) or 0)
    raw_price = place.get("price", "") or place.get("price_description", "")

    return {
        "Name": place.get("title", ""),
        "Address": place.get("address", ""),
        "Phone": place.get("phone", ""),
        "Website": place.get("website", ""),
        "Latitude": lat,
        "Longitude": lng,
        "Rating": rating,
        "Review Count": reviews,
        "Category": ", ".join(place.get("types", [])) if isinstance(place.get("types"), list) else place.get("type", ""),
        "Price Level": normalize_price_band(raw_price),
        "Raw Price": raw_price,
        "Business Status": place.get("open_state", ""),
        "Popularity Score": calculate_popularity_score(rating, reviews),
        "kgmid": place.get("kgmid", ""),
        "Place ID": place.get("place_id", ""),
        "Price Min": None,
        "Price Max": None,
        "Price Range Text": "",
        "Typical Spend": "",
        "Scraped At": datetime.now().isoformat()
    }

def get_place_details(kgmid: str = None, place_id: str = None) -&gt; dict:
    if not (kgmid or place_id):
        return {}

    identifier = kgmid or place_id
    param_name = "kgmid" if kgmid else "place_id"

    try:
        params = {
            "engine": "google_place",
            param_name: identifier,
            "hl": LANGUAGE,
            "gl": COUNTRY
        }
        data = requests.get(
            "https://www.searchapi.io/api/v1/search",
            params={"api_key": API_KEY, **params},
            timeout=25
        ).json()

        place = data.get("place_result") or data.get("place_results", {})
        if not isinstance(place, dict):
            return {}

        price_info = place.get("price", {})
        if isinstance(price_info, dict):
            price_min = price_info.get("min")
            price_max = price_info.get("max")
            price_text = price_info.get("text", "")
        else:
            price_min = price_max = None
            price_text = str(price_info) if price_info else ""

        return {
            "Phone": place.get("phone", ""),
            "Website": place.get("website", ""),
            "Price Min": price_min,
            "Price Max": price_max,
            "Price Range Text": price_text,
            "Typical Spend": place.get("people_typically_spend", "")
        }
    except Exception as e:
        logger.warning(f"Detail fetch error: {e}")
        return {}

def extract_and_enrich_stores(data: dict) -&gt; List[Dict]:
    stores = [extract_base_info(place) for place in data.get("local_results", []) if isinstance(place, dict)]

    if ENRICH_DETAILS and stores:
        sorted_stores = sorted(stores, key=lambda x: x.get("Popularity Score", 0), reverse=True)
        target_stores = sorted_stores[:DETAIL_TOP_N] if DETAIL_TOP_N &gt; 0 else stores

        for store in target_stores:
            if store.get("kgmid") or store.get("Place ID"):
                detail = get_place_details(store.get("kgmid"), store.get("Place ID"))
                if detail:
                    for key in ["Phone", "Website", "Price Min", "Price Max", "Price Range Text", "Typical Spend"]:
                        if detail.get(key):
                            store[key] = detail[key]
                time.sleep(DETAIL_SLEEP)

    return stores

# ================== ANALYSIS ==================
def analyze_businesses(stores: List[Dict]):
    if not stores:
        return

    print("\n" + "="*80)
    print("📊 Local Business Analysis Report")
    print("="*80)

    total = len(stores)
    price_counts = Counter(s.get("Price Level", "Unknown") for s in stores)

    print(f"Total Businesses Found: {total}")
    print("\n【Price Level Distribution】")
    for level in ["Inexpensive", "Moderate", "Expensive", "Very Expensive", "Unknown"]:
        count = price_counts.get(level, 0)
        if count &gt; 0:
            print(f"  {level:15} : {count:3} businesses ({count/total*100:5.1f}%)")

    has_phone = sum(1 for s in stores if s.get("Phone"))
    print(f"\nBusinesses with Phone Number: {has_phone}/{total}")
    print("="*80 + "\n")

# ================== EXPORT ==================
def save_to_excel(stores: List[Dict], filename: str):
    if not stores:
        return

    df = pd.DataFrame(stores)
    columns_order = [
        "Name", "Address", "Phone", "Website", "Rating", "Review Count",
        "Price Level", "Price Min", "Price Max", "Popularity Score",
        "Latitude", "Longitude", "Category", "Business Status", "Scraped At"
    ]
    df = df[[col for col in columns_order if col in df.columns]]

    wb = Workbook()
    ws = wb.active
    ws.title = "Business Data"

    for r_idx, row in enumerate(dataframe_to_rows(df, index=False, header=True), 1):
        for c_idx, value in enumerate(row, 1):
            ws.cell(row=r_idx, column=c_idx, value=value)

    # Corporate Header Styling (Navy / White)
    header_fill = PatternFill(start_color="1F4E79", end_color="1F4E79", fill_type="solid")
    header_font = Font(bold=True, color="FFFFFF", size=11)
    for cell in ws[1]:
        cell.fill = header_fill
        cell.font = header_font
        cell.alignment = Alignment(horizontal='center', vertical='center')

    # Auto-fit Column Widths
    for col_idx in range(1, ws.max_column + 1):
        ws.column_dimensions[get_column_letter(col_idx)].width = 18

    ws.freeze_panes = 'A2'
    wb.save(filename)
    logger.info(f"✅ Excel report saved: {filename}")

def save_to_csv(stores: List[Dict], filename: str):
    if stores:
        pd.DataFrame(stores).to_csv(filename, index=False, encoding="utf-8-sig")
        logger.info(f"✅ CSV saved: {filename}")

# ================== INTERACTIVE MAP ==================
def generate_map(stores: List[Dict], filename: str):
    if not FOLIUM_AVAILABLE or not stores:
        return

    valid_coords = [(s["Latitude"], s["Longitude"]) for s in stores if s.get("Latitude") and s.get("Longitude")]
    if valid_coords:
        center_lat = sum(lat for lat, _ in valid_coords) / len(valid_coords)
        center_lng = sum(lng for _, lng in valid_coords) / len(valid_coords)
        center = [center_lat, center_lng]
    else:
        center = [40.7128, -74.0060]

    m = folium.Map(location=center, zoom_start=13, tiles="cartodbpositron")
    price_layer = FeatureGroup(name="By Price Level", show=True)

    for store in stores:
        if not store.get("Latitude") or not store.get("Longitude"):
            continue

        color = get_price_color(store.get("Price Level", "Unknown"))
        popup_html = f"""
        <b>{store['Name']}</b>
        Phone: {store.get('Phone', 'N/A')}
        <a href="{store.get('Website', '#')}" target="_blank">Website</a>
        Price Level: <b>{store.get('Price Level')}</b>
        """

        folium.Marker(
            location=[store["Latitude"], store["Longitude"]],
            popup=folium.Popup(popup_html, max_width=300),
            tooltip=store['Name'],
            icon=folium.Icon(color=color, icon="info-sign")
        ).add_to(price_layer)

    price_layer.add_to(m)
    LayerControl(collapsed=False).add_to(m)
    m.save(filename)
    logger.info(f"🗺️ Interactive map saved: {filename}")

# ================== MAIN ==================
def main():
    target_params = {
        "engine": "google_maps",
        "q": SEARCH_QUERY,
        "ll": LOCATION,
        "hl": LANGUAGE,
        "gl": COUNTRY
    }

    logger.info(f"🚀 Starting analysis → Query: '{SEARCH_QUERY}' | Location: {LOCATION}")

    all_stores = []
    for page in range(1, MAX_PAGES + 1):
        target_params["page"] = page
        logger.info(f"📄 Fetching page {page}...")
        try:
            response = requests.get(
                "https://www.searchapi.io/api/v1/search",
                params={"api_key": API_KEY, **target_params},
                timeout=30
            )
            data = response.json()
            all_stores.extend(extract_and_enrich_stores(data))

            if page &lt; MAX_PAGES:
                time.sleep(SEARCH_SLEEP)
        except Exception as e:
            logger.error(f"Error on page {page}: {e}")
            break

    analyze_businesses(all_stores)
    save_to_excel(all_stores, EXCEL_FILENAME)
    save_to_csv(all_stores, CSV_FILENAME)

    if FOLIUM_AVAILABLE:
        generate_map(all_stores, MAP_FILENAME)

    logger.info(f"🎉 Analysis complete! Processed {len(all_stores)} businesses.")

if __name__ == "__main__":
    main()
</pre>
<h2 data-path-to-node="17">3. Core Highlights of the Script</h2>
<h3 data-path-to-node="18">3.1. Instant Premium B2B Lead List (Commercial Value)</h3>
<p data-path-to-node="19">Instead of outputting an unstyled, raw text file, the script integrates <code data-path-to-node="19" data-index-in-node="72">openpyxl</code> to build an execution-ready corporate directory.</p>
<ul data-path-to-node="20">
<li>
<p data-path-to-node="20,0,0"><b data-path-to-node="20,0,0" data-index-in-node="0">Freeze Panes:</b> Keeps the header visible while scrolling through hundreds of entries.</p>
</li>
<li>
<p data-path-to-node="20,1,0"><b data-path-to-node="20,1,0" data-index-in-node="0">Auto-Fit Layout:</b> Calculates optimal column widths dynamically so strings like addresses and domain names don&#8217;t get clipped.</p>
</li>
<li>
<p data-path-to-node="20,2,0"><b data-path-to-node="20,2,0" data-index-in-node="0">Rich Fields:</b> Extracts telephone contacts and websites directly, providing an instant directory for sales teams or outreach automation tools.</p>
</li>
</ul>
<h3 data-path-to-node="21">3.2. Macroeconomic Spatial Profiling (Analytical Value)</h3>
<p data-path-to-node="22">By taking advantage of <code data-path-to-node="22" data-index-in-node="23">folium</code> (a Python wrapper for the powerful Leaflet.js library), the data pipeline parses incoming JSON GPS coordinates on the fly.</p>
<ul data-path-to-node="23">
<li>
<p data-path-to-node="23,0,0">Marker icons are evaluated dynamically based on parsed market values.</p>
</li>
<li>
<p data-path-to-node="23,1,0">It generates an standalone interactive HTML file containing a map overlay, grouping targets into distinct pricing sub-layers (Inexpensive, Moderate, Expensive, Very Expensive). This visually highlights local inflation density, high-end commercial hubs, or market gaps in target metropolitan areas.</p>
</li>
</ul>
<h2 data-path-to-node="25">4. Conclusion &amp; Next Steps</h2>
<p data-path-to-node="26">By combining SearchApi&#8217;s robust mapping extraction capabilities with smart Python styling and geographic rendering, we transformed raw location records into highly structured commercial intelligence.</p>
<p data-path-to-node="27">Whether you are seeking to automate cold outreach lead generation or map out the socio-economic topography of a city, this script serves as your robust foundation. In a future post, we will explore turning this static mapping pipeline into a dynamic, real-time web application dashboard.</p>
<p data-path-to-node="28"><i data-path-to-node="28" data-index-in-node="0">What industry or region are you planning to map out first? Let us know in the comments below!</i></p>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/building-an-automated-b2b-lead-generator-spatial-heatmap-with-searchapi-and-python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Building a Real-time Economic Indicator (REI) Tracker with SearchApi and Python</title>
		<link>https://laboratory.kazuuu.net/building-a-real-time-economic-indicator-rei-tracker-with-searchapi-and-python/</link>
					<comments>https://laboratory.kazuuu.net/building-a-real-time-economic-indicator-rei-tracker-with-searchapi-and-python/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Thu, 21 May 2026 02:26:37 +0000</pubDate>
				<category><![CDATA[Python【初歩から現場実務的なもの】]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17898</guid>

					<description><![CDATA[1. Introduction: The Problem of Latency in Official Statistics Official economic metrics, such as the Consumer [&#8230;]]]></description>
										<content:encoded><![CDATA[<h4 data-path-to-node="23">1. Introduction: The Problem of Latency in Official Statistics</h4>
<p id="p-rc_ef4287599fe58577-35" data-path-to-node="24"><span data-path-to-node="24,1"><span class="citation-23">Official economic metrics, such as the Consumer Price Index (CPI), are foundational to understanding macro trends</span></span><span data-path-to-node="24,3">. </span><span data-path-to-node="24,5"><span class="citation-22">However, they suffer from a structural defect: </span><b data-path-to-node="24,5" data-index-in-node="47"><span class="citation-22">latency</span></b></span><span data-path-to-node="24,7">. </span><span data-path-to-node="24,9"><span class="citation-21">By the time government statistics are officially released, the market has already moved on</span></span><span data-path-to-node="24,11">.</span></p>
<p id="p-rc_ef4287599fe58577-36" data-path-to-node="25"><span data-path-to-node="25,1"><span class="citation-20">For developers and analysts, bridging this informational gap requires a proactive engineering approach to data collection</span></span><span data-path-to-node="25,3">. </span><span data-path-to-node="25,5"><span class="citation-19">This project aims to build a </span><b data-path-to-node="25,5" data-index-in-node="29"><span class="citation-19">Real-time Economic Indicator (REI) Tracker</span></b></span><span data-path-to-node="25,7">. </span><span data-path-to-node="25,9"><span class="citation-18">By monitoring the digital shelf prices of essential goods in a specific metropolitan area (Osaka, Japan), we can identify inflationary or deflationary trends weeks before they appear in official reports</span></span><span data-path-to-node="25,11">.</span></p>
<h4 data-path-to-node="26">2. Bypassing Infrastructure Hurdles with SearchApi</h4>
<p id="p-rc_ef4287599fe58577-37" data-path-to-node="27"><span data-path-to-node="27,1"><span class="citation-17">Extracting high-quality, high-frequency data from search engines and shopping platforms is notoriously difficult due to sophisticated anti-bot protections, IP blocks, and CAPTCHAs</span></span><span data-path-to-node="27,3">.</span></p>
<p id="p-rc_ef4287599fe58577-38" data-path-to-node="29">SearchApi serves as the critical infrastructure for this project. By handling real-time data retrieval and returning it as clean JSON, it allows us to bypass issues we would otherwise face if we were to try to scrape data ourselves and focus entirely on the logic of data interpretation.</p>
<h4 data-path-to-node="30">3. Complete Source Code</h4>
<p data-path-to-node="31">Below is the robust Python implementation that covers everything from data extraction via SearchApi to data persistence in CSV format using Pandas, featuring duplication prevention for same-day runs.</p>
<pre>import requests
import statistics
import pandas as pd
from datetime import datetime, date
import os
import re
import logging
from pathlib import Path
from dataclasses import dataclass
from typing import List, Dict, Optional
from dotenv import load_dotenv

# ====================== setting ======================
load_dotenv()

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s | %(levelname)s | %(message)s',
    datefmt='%H:%M:%S'
)
logger = logging.getLogger(__name__)

API_KEY = os.getenv("SEARCHAPI_API_KEY")
if not API_KEY:
    raise ValueError("SEARCHAPI_API_KEY is not set in the .env file!")

@dataclass
class Config:
    target_items: List[str] = None
    location: str = "Osaka, Osaka, Japan"
    gl: str = "jp"
    hl: str = "ja"
    num_results: int = 20
    min_samples: int = 3

# Tracked products
config = Config(
    target_items=[
        "Egg 10-pack",
        "Rice 5kg",
        "Tissue paper 5-pack",
        "Gasoline price",
        "iPhone 15 128GB",
    ]
)

class EconomicIndicatorTracker:
    def __init__(self, api_key: str, config: Config):
        self.api_key = api_key
        self.config = config
        self.endpoint = "https://www.searchapi.io/api/v1/search"
        self.session = requests.Session()

    @staticmethod
    def parse_price(price_str: str) -&gt; Optional[int]:
        """Securely convert price strings to numbers"""
        if not price_str:
            return None
        cleaned = str(price_str).replace('円', '').replace('¥', '').replace(' ', '')
        cleaned = re.sub(r'[^\d.,]', '', cleaned)
        cleaned = cleaned.replace(',', '')
        try:
            return int(float(cleaned))
        except ValueError:
            return None

    def get_market_price(self, query: str) -&gt; Optional[Dict]:
        """Price data is retrieved from Google Shopping"""
        params = {
            "engine": "google_shopping",
            "q": query,
            "location": self.config.location,
            "api_key": self.api_key,
            "gl": self.config.gl,
            "hl": self.config.hl,
            "num": self.config.num_results,
        }

        try:
            response = self.session.get(self.endpoint, params=params, timeout=20)
            response.raise_for_status()
            data = response.json()

            prices = []
            for item in data.get("shopping_results", []):
                price_str = item.get("price") or item.get("extracted_price")
                if price_str:
                    parsed = self.parse_price(price_str)
                    if parsed and parsed &gt; 0:
                        prices.append(parsed)

            if len(prices) &lt; self.config.min_samples:
                logger.warning(f"There are too few valid samples for {query} ({len(prices)} items).")
                return None

            return {
                "item": query,
                "date": date.today().isoformat(),
                "timestamp": datetime.now().isoformat(),
                "median_price": round(statistics.median(prices)),
                "sample_count": len(prices),
                "min_price": min(prices),
                "max_price": max(prices)
            }
        except Exception as e:
            logger.error(f"Data acquisition error {query}: {e}")
        return None

def main():
    tracker = EconomicIndicatorTracker(API_KEY, config)
    results = []

    print(f"--- Real-world economic survey begins in the Osaka area ({date.today().isoformat()}) ---")
    
    for item in config.target_items:
        logger.info(f"Fetching: {item}...")
        stats = tracker.get_market_price(item)
        if stats:
            results.append(stats)
            logger.info(f"   → median: ¥{stats['median_price']:,} ({stats['sample_count']} 件)")

    if results:
        df_new = pd.DataFrame(results)
        csv_file = f"economic_indicator_{datetime.now().strftime('%Y%m')}.csv"
        
        # CSV persistence logic (preventing duplicate entries for the same day)
        if Path(csv_file).exists():
            df_existing = pd.read_csv(csv_file)
            df_existing = df_existing[df_existing['date'] != date.today().isoformat()]
            df_combined = pd.concat([df_existing, df_new], ignore_index=True)
        else:
            df_combined = df_new

        df_combined.to_csv(csv_file, index=False)
        logger.info(f"💾 Saved to CSV: {csv_file}")
        
        print("\n" + "="*50)
        print("### Market Price Summary ###")
        print(df_new[['item', 'median_price', 'sample_count']].to_string(index=False))
        print("="*50)
    else:
        logger.error("No data was collected.")

if __name__ == "__main__":
    main()</pre>
<h4 data-path-to-node="33">4. Analytical Methodology</h4>
<ul data-path-to-node="34">
<li>
<p id="p-rc_ef4287599fe58577-39" data-path-to-node="34,0,1"><span data-path-to-node="34,0,1,0"><b data-path-to-node="34,0,1,0" data-index-in-node="0"><span class="citation-14">Median Pricing:</span></b><span class="citation-14"> By prioritizing the Median over the Mean, we effectively eliminate the noise of extreme outliers (such as steep discounts, used items, or shipping entry errors) to find the true &#8220;market middle&#8221;</span></span><span data-path-to-node="34,0,1,2">.</span></p>
</li>
<li>
<p id="p-rc_ef4287599fe58577-40" data-path-to-node="34,1,1"><span data-path-to-node="34,1,1,0"><b data-path-to-node="34,1,1,0" data-index-in-node="0"><span class="citation-13">Localized Context:</span></b><span class="citation-13"> Setting the geo-location specifically to Osaka allows the script to capture regional supply chain shifts and local logistics impacts</span></span><span data-path-to-node="34,1,1,2">.</span></p>
</li>
<li>
<p id="p-rc_ef4287599fe58577-41" data-path-to-node="34,2,1"><span data-path-to-node="34,2,1,0"><b data-path-to-node="34,2,1,0" data-index-in-node="0"><span class="citation-12">Data Persistence:</span></b><span class="citation-12"> Automated monthly CSV updates generate a clean, reliable historical baseline for robust Month-over-Month (MoM) and Year-over-Year (YoY) economic analysis</span></span><span data-path-to-node="34,2,1,2">.</span></p>
</li>
</ul>
<h4 data-path-to-node="36">5. Open Source Repository &amp; Quick Start</h4>
<p data-path-to-node="37">The complete project files and deployment guide are available on GitHub.</p>
<ol start="1" data-path-to-node="38">
<li>
<p data-path-to-node="38,0,0"><b data-path-to-node="38,0,0" data-index-in-node="0">Clone &amp; Install</b>: Clone the repository and run <code data-path-to-node="38,0,0" data-index-in-node="46">pip install -r requirements.txt</code>.</p>
</li>
<li>
<p data-path-to-node="38,1,0"><b data-path-to-node="38,1,0" data-index-in-node="0">Get SearchApi Key</b>: Sign up at <a rel="noopener" class="ng-star-inserted" href="https://www.searchapi.io/" target="_blank" data-hveid="0" data-ved="0CAAQ_4QMahgKEwj2_snVnMmUAxUAAAAAHQAAAAAQ-wI">SearchApi.io</a> and copy your API key from the developer dashboard.</p>
</li>
<li>
<p data-path-to-node="38,2,0"><b data-path-to-node="38,2,0" data-index-in-node="0">Environment Setup</b>: Create a <code data-path-to-node="38,2,0" data-index-in-node="28">.env</code> file in the root folder and add your key: <code data-path-to-node="38,2,0" data-index-in-node="75">SEARCHAPI_API_KEY=your_api_key_here</code>.</p>
</li>
<li>
<p data-path-to-node="38,3,0"><b data-path-to-node="38,3,0" data-index-in-node="0">Run</b>: Execute <code data-path-to-node="38,3,0" data-index-in-node="13">python rei_tracker.py</code> to start collecting real-time market insights and generating your localized CSV datasets.</p>
</li>
</ol>
<p data-path-to-node="36">👉 <a href="https://github.com/kobayashikazu/rei-tracker-osaka">https://github.com/kobayashikazu/rei-tracker-osaka</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/building-a-real-time-economic-indicator-rei-tracker-with-searchapi-and-python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>自由落下とpythonプログラム</title>
		<link>https://laboratory.kazuuu.net/free-fall-and-python-programs/</link>
					<comments>https://laboratory.kazuuu.net/free-fall-and-python-programs/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Sun, 18 Aug 2024 19:58:40 +0000</pubDate>
				<category><![CDATA[Python（遊び）]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[物理学]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17679</guid>

					<description><![CDATA[自由落下に関するpythonプログラムを作ってみます。 なお、初速度 v0 が 0 m/s で、地上から自由落下する物体が 10 秒後にどのくらいの速度に達するかを計算し、結果を表示するPythonプログラムを作ります。 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>自由落下に関するpythonプログラムを作ってみます。</p>
<p>なお、初速度 v0 が 0 m/s で、地上から自由落下する物体が 10 秒後にどのくらいの速度に達するかを計算し、結果を表示するPythonプログラムを作ります。</p>
<h2>■今回の環境（Python）</h2>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png"><img decoding="async" class="alignnone size-medium wp-image-17673" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png" alt="" width="300" height="131" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-768x336.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png 865w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>今回のPythonは、バージョン3.10.12を用いる。（なお、Google Colaboratory(Google Colab)を使用。）</p>
<h2>■結果を表示するPythonプログラム</h2>
<h3>■コード</h3>
<pre># 重力加速度 (m/s²)
g = 9.81

# 初速度 (m/s)
v0 = 0

# 時間 (秒)
t = 10

# 速度を計算
v = v0 + g * t

# 結果を表示
print("10 秒後の速度は {:.2f} m/s です。".format(v))</pre>
<h2>■検証、確認</h2>
<p>作成したコードは、Google Colaboratoryの新しいノートブック上のセルにコードを記述します。その後「セルを実行」ボタンをクリックします。</p>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-02-132014.png"><img loading="lazy" decoding="async" class="alignnone wp-image-17680 size-medium" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-02-132014-300x144.png" alt="" width="300" height="144" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-02-132014-300x144.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-02-132014-1024x493.png 1024w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-02-132014-768x370.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-02-132014.png 1222w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>先に計算した結果を書くと「98.10 m/s」となります。クリックすると、Google Colaboratory上で処理された結果を確認すると「10 秒後の速度は 98.10 m/s です。」と出力させることができました。</p>
<h2>■補足</h2>
<h3>※速度の時間変化をアニメーションとして描画するためのコード</h3>
<p>import matplotlib.pyplot as plt</p>
<pre>import numpy as np
from matplotlib.animation import FuncAnimation

# 重力加速度 (m/s²)
g = 9.81

# 初速度 (m/s)
v0 = 0

# 時間 (秒)
t = 10

# 時間の配列を作成（0からtまで0.1秒刻み）
time = np.arange(0, t, 0.1)

# 速度の配列を計算
velocity = v0 + g * time

# グラフの初期設定
fig, ax = plt.subplots()
ax.set_xlim(0, t)
ax.set_ylim(0, max(velocity) + 1)
line, = ax.plot([], [], lw=2)

# アニメーションの初期化
def init():
    line.set_data([], [])
    return line,

# アニメーションのフレーム更新
def update(frame):
    x_data = time[:frame]
    y_data = velocity[:frame]
    line.set_data(x_data, y_data)
    return line,

# アニメーションの作成
ani = FuncAnimation(fig, update, frames=len(time), init_func=init, blit=True)

# アニメーションの表示
plt.xlabel('Time (s)')
plt.ylabel('Velocity (m/s)')
plt.title('Velocity vs. Time')
plt.show()</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/free-fall-and-python-programs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>matplotlibを用いて数学を使って図形を描く</title>
		<link>https://laboratory.kazuuu.net/drawing-shapes-using-mathematics-with-matplotlib/</link>
					<comments>https://laboratory.kazuuu.net/drawing-shapes-using-mathematics-with-matplotlib/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Tue, 13 Aug 2024 23:03:22 +0000</pubDate>
				<category><![CDATA[Python【初歩から現場実務的なもの】]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17705</guid>

					<description><![CDATA[matplotlibを用いて数学を使って図形を描いてみます。 ■今回の環境（Python） 今回のPythonは、バージョン3.10.12を用いる。（なお、Google Colaboratory(Google Colab [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>matplotlibを用いて数学を使って図形を描いてみます。</p>
<h2>■今回の環境（Python）</h2>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-17673" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png" alt="" width="300" height="131" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-768x336.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png 865w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>今回のPythonは、バージョン3.10.12を用いる。（なお、Google Colaboratory(Google Colab)を使用。）</p>
<h2>■結果を表示するPythonプログラム</h2>
<h3>■コード</h3>
<pre>import matplotlib.pyplot as plt
import numpy as np

# 円の中心座標
center = (0, 0)

# 円の半径
radius = 2

# 角度の範囲を指定（0から360度まで）
theta = np.linspace(0, 2*np.pi, 100)

# 円の座標を計算
x = center[0] + radius * np.cos(theta)
y = center[1] + radius * np.sin(theta)

# プロット
plt.plot(x, y)
plt.title('円の描画')
plt.xlabel('X軸')
plt.ylabel('Y軸')
plt.grid(True)
plt.show()
</pre>
<p>円の中心座標と半径を指定し、それを用いて円の座標を計算します。最後に、matplotlibのplot関数を使用して円をプロットし、show関数で表示します。</p>
<h2>■検証、確認</h2>
<p>作成したコードは、Google Colaboratoryの新しいノートブック上のセルにコードを記述します。その後「セルを実行」ボタンをクリックします。</p>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-09-144238.png"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-17706" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-09-144238-300x194.png" alt="" width="300" height="194" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-09-144238-300x194.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-09-144238-1024x664.png 1024w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-09-144238-768x498.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-09-144238.png 1159w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>クリックすると、matplotlibを用いて数学を使った図形を出力させることができました。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/drawing-shapes-using-mathematics-with-matplotlib/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>斜めに投げられた物体とpythonプログラム</title>
		<link>https://laboratory.kazuuu.net/object-thrown-at-an-angle-and-python-program/</link>
					<comments>https://laboratory.kazuuu.net/object-thrown-at-an-angle-and-python-program/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Fri, 09 Aug 2024 19:13:18 +0000</pubDate>
				<category><![CDATA[Python（遊び）]]></category>
		<category><![CDATA[数学]]></category>
		<category><![CDATA[物理学]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17683</guid>

					<description><![CDATA[斜めに投げられた物体に関するpythonプログラムを作ってみます。 物体が斜めに投げられる場合、水平方向と鉛直方向の速度が影響します。水平方向の速度を Vx ​ 、鉛直方向の速度を Vy ​ とし、初速度 V0 ​ の物 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>斜めに投げられた物体に関するpythonプログラムを作ってみます。</p>
<p>物体が斜めに投げられる場合、水平方向と鉛直方向の速度が影響します。水平方向の速度を Vx ​ 、鉛直方向の速度を Vy ​ とし、初速度 V0 ​ の物体が角度 θ で斜めに投げられるとします。以下の式を使用して、物体の位置を時間と共に計算します。</p>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-141738.png"><img loading="lazy" decoding="async" class="alignnone size-full wp-image-17684" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-141738.png" alt="" width="394" height="115" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-141738.png 394w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-141738-300x88.png 300w" sizes="(max-width: 394px) 100vw, 394px" /></a></p>
<p>ここで、x は水平方向の位置（メートル）、 y は鉛直方向の位置（メートル）、 t は時間（秒）です。</p>
<h2>■今回の環境（Python）</h2>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-17673" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png" alt="" width="300" height="131" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-768x336.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png 865w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>今回のPythonは、バージョン3.10.12を用いる。（なお、Google Colaboratory(Google Colab)を使用。）</p>
<h2>■斜めに投げられた物体に関するpythonプログラム</h2>
<p>今回は初速度 <span class="math math-inline"><span class="katex"><span class="katex-html" aria-hidden="true"><span class="base"><span class="mord"><span class="mord mathnormal">v</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist"><span class="sizing reset-size6 size3 mtight"><span class="mord mtight">0</span></span></span><span class="vlist-s">​</span></span></span></span></span><span class="mrel">=</span></span><span class="base"><span class="mord">20</span></span></span></span></span> m/s で、角度 <span class="math math-inline"><span class="katex"><span class="katex-html" aria-hidden="true"><span class="base"><span class="mord mathnormal">θ</span><span class="mrel">=</span></span><span class="base"><span class="mord">3</span><span class="mord">0<span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist"><span class="sizing reset-size6 size3 mtight"><span class="mbin mtight">∘</span></span></span></span></span></span></span></span></span></span></span> で斜めに投げられた物体の高度を時間と共に計算し、0 になる時間を求めるプログラムを作ってみます。プログラムを実行後、物体が地上に戻る時間を計算します。</p>
<h3>■コード</h3>
<pre>import math

# 初速度 (m/s)
v0 = 20

# 投げる角度 (度)
theta = 30

# 重力加速度 (m/s²)
g = 9.81

# 角度をラジアンに変換
theta = math.radians(theta)

# 初速度を水平方向と鉛直方向の速度に分解
vx = v0 * math.cos(theta)
vy = v0 * math.sin(theta)

# 物体が地上に戻る時間を計算
t = (2 * vy) / g

# 結果を表示
print("物体が地上に戻る時間は {:.2f} 秒です。".format(t))</pre>
<h3>■検証、確認</h3>
<p>作成したコードは、Google Colaboratoryの新しいノートブック上のセルにコードを記述します。その後「セルを実行」ボタンをクリックします。</p>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-143003.png"><img loading="lazy" decoding="async" class="alignnone wp-image-17688 size-medium" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-143003-300x202.png" alt="" width="300" height="202" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-143003-300x202.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-143003-1024x688.png 1024w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-143003-768x516.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-06-143003.png 1027w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>先に答えを書くと「2.04 秒」となります。クリック後、Google Colaboratory上で処理された計算結果を確認すると「物体が地上に戻る時間は 2.04 秒です。」と出力させることができました。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/object-thrown-at-an-angle-and-python-program/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Pythonを使用した簡単な代数の問題を解く</title>
		<link>https://laboratory.kazuuu.net/solve-simple-algebra-problems-using-python/</link>
					<comments>https://laboratory.kazuuu.net/solve-simple-algebra-problems-using-python/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Fri, 09 Aug 2024 04:32:40 +0000</pubDate>
				<category><![CDATA[Python（遊び）]]></category>
		<category><![CDATA[数学]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17676</guid>

					<description><![CDATA[Pythonを使用した簡単な代数の問題を解くことについて解説します。 今回は代数方程式を解くためにSymPyを使用します。Google ColaboratoryではSympyがすでにインストールされた状態となっています。 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Pythonを使用した簡単な代数の問題を解くことについて解説します。</p>
<p>今回は代数方程式を解くためにSymPyを使用します。Google ColaboratoryではSympyがすでにインストールされた状態となっています。</p>
<h2>■今回の環境（Python）</h2>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-17673" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png" alt="" width="300" height="131" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-768x336.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png 865w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>今回のPythonは、バージョン3.10.12を用いる。（なお、Google Colaboratory(Google Colab)を使用。）</p>
<h2>■簡単な代数の問題を解く</h2>
<p>以下の代数方程式を解く</p>
<pre>3x−5=10</pre>
<h3>■コード</h3>
<pre>import sympy as sp

# 未知数を定義
x = sp.symbols('x')

# 代数方程式を定義
equation = 3*x - 5 - 10

# 方程式を解く
solution = sp.solve(equation, x)

print("方程式の解は x =", solution[0])</pre>
<h2>■検証、確認</h2>
<p>作成したコードは、Google Colaboratoryの新しいノートブック上のセルにコードを記述します。その後「セルを実行」ボタンをクリックします。</p>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-01-150725.png"><img loading="lazy" decoding="async" class="alignnone wp-image-17677 size-medium" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-01-150725-300x141.png" alt="" width="300" height="141" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-01-150725-300x141.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-01-150725-1024x483.png 1024w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-01-150725-768x362.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/11/スクリーンショット-2023-11-01-150725.png 1249w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>クリックすると、代数方程式の答えは「x=5」となりますので、Google Colaboratory上で処理された結果を確認すると「方程式の解は x = 5」と出力されていますので、代数の問題を解くことができました。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/solve-simple-algebra-problems-using-python/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>フィボナッチ数列を使って三角形を出力するPythonコード</title>
		<link>https://laboratory.kazuuu.net/python-code-to-output-a-triangle-using-the-fibonacci-sequence/</link>
					<comments>https://laboratory.kazuuu.net/python-code-to-output-a-triangle-using-the-fibonacci-sequence/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Wed, 07 Aug 2024 19:30:52 +0000</pubDate>
				<category><![CDATA[Python（遊び）]]></category>
		<category><![CDATA[数学]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17672</guid>

					<description><![CDATA[今回は、フィボナッチ数列を使って三角形を出力するPythonコードを作ってみます。 ■今回の環境（Python） 今回のPythonは、バージョン3.10.12を用いる。（なお、Google Colaboratory(G [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>今回は、フィボナッチ数列を使って三角形を出力するPythonコードを作ってみます。</p>
<h2>■今回の環境（Python）</h2>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-17673" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png" alt="" width="300" height="131" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-300x131.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813-768x336.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-134813.png 865w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>今回のPythonは、バージョン3.10.12を用いる。（なお、Google Colaboratory(Google Colab)を使用。）</p>
<h2>■フィボナッチ数列を使って三角形を出力する</h2>
<p>では、フィボナッチ数列を使って三角形を出力するPythonコードを書いていきます。</p>
<pre>def generate_fibonacci_triangle(rows):
    fibonacci_numbers = [1, 1]
    fibonacci_triangle = [[1], [1, 1]]

    for n in range(2, rows):
        fibonacci_n = fibonacci_numbers[n - 1] + fibonacci_numbers[n - 2]
        fibonacci_numbers.append(fibonacci_n)

        row = [1]
        for i in range(1, n):
            row.append(fibonacci_numbers[i])
        fibonacci_triangle.append(row)

    return fibonacci_triangle

def print_fibonacci_triangle(triangle):
    max_width = len(' '.join(map(str, triangle[-1])))

    for row in triangle:
        row_str = ' '.join(map(str, row))
        print(row_str.center(max_width))

if __name__ == "__main__":
    num_rows = 15  # 任意の行数を指定できます
    triangle = generate_fibonacci_triangle(num_rows)
    print_fibonacci_triangle(triangle)</pre>
<p>このコードでは、指定した行数（num_rows）のフィボナッチ数列の三角形を生成し、それを表示します。print_fibonacci_triangle関数は、各行の要素を適切に中央揃えして表示します。必要に応じて、num_rowsを調整して三角形の高さを変更できます。</p>
<h2>■検証、確認</h2>
<p>作成したコードは、Google Colaboratoryの新しいノートブック上のセルにコードを記述します。その後「セルを実行」ボタンをクリックします。</p>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-135426.png"><img loading="lazy" decoding="async" class="alignnone wp-image-17674 size-medium" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-135426-300x200.png" alt="" width="300" height="200" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-135426-300x200.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-135426-1024x683.png 1024w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-135426-768x512.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/10/スクリーンショット-2023-10-30-135426.png 1129w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>クリックすると、セル上のコードが読み込まれ処理が実行となり、出力が表示されます。出力結果を確認すると、フィボナッチ数列を使って三角形を出力することができました。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/python-code-to-output-a-triangle-using-the-fibonacci-sequence/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【Python】文字列編集の距離と類似性を計算する「python-Levenshtein」のインストール</title>
		<link>https://laboratory.kazuuu.net/python-installation-of-python-levenshtein-to-calculate-distance-and-similarity-of-string-edits/</link>
					<comments>https://laboratory.kazuuu.net/python-installation-of-python-levenshtein-to-calculate-distance-and-similarity-of-string-edits/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Wed, 07 Aug 2024 04:45:51 +0000</pubDate>
				<category><![CDATA[Python【初歩から現場実務的なもの】]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17584</guid>

					<description><![CDATA[文字列編集の距離と類似性を計算する「python-Levenshtein」のインストールについて解説しています。 「python-Levenshtein（https://github.com/maxbachmann/py [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>文字列編集の距離と類似性を計算する「python-Levenshtein」のインストールについて解説しています。</p>
<p>「python-Levenshtein（<a href="https://github.com/maxbachmann/python-Levenshtein">https://github.com/maxbachmann/python-Levenshtein</a>）」は、レーベンシュタイン距離と文字列の類似性を高速に計算する関数が含まれたPythonライブラリです。</p>
<h2>■Python</h2>
<p><a href="https://laboratory.kazuuu.net/wp-content/uploads/2023/05/スクリーンショット-2023-05-29-120334.png"><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-16692" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/05/スクリーンショット-2023-05-29-120334-300x139.png" alt="" width="300" height="139" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/05/スクリーンショット-2023-05-29-120334-300x139.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/05/スクリーンショット-2023-05-29-120334-1024x475.png 1024w, https://laboratory.kazuuu.net/wp-content/uploads/2023/05/スクリーンショット-2023-05-29-120334-768x356.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/05/スクリーンショット-2023-05-29-120334.png 1250w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<p>今回のPythonのバージョンは、「3.10.9」を使用しています。（Windows11）（pythonランチャーでの確認）</p>
<h2>■python-Levenshteinをインストールする</h2>
<p>python-Levenshteinをインストールを行いますが、今回はpipを経由してインストールを行うので、まずWindowsのコマンドプロンプトを起動します。</p>
<pre>pip install python-Levenshtein</pre>
<p>起動後、上記のコマンドを入力し、Enterキーを押します。</p>
<p>なお、今回は、pythonランチャーを使用しており、Python Version 3.10.9にインストールを行うために、バージョンの切り替えを行います。</p>
<pre>py -3.10 -m pip install python-Levenshtein</pre>
<p>切り替えるために、上記のコマンドを入力し、Enterキーを押します。</p>
<pre>Defaulting to user installation because normal site-packages is not writeable
Collecting python-Levenshtein
  Using cached python_Levenshtein-0.22.0-py3-none-any.whl (9.4 kB)
Collecting Levenshtein==0.22.0
  Using cached Levenshtein-0.22.0-cp310-cp310-win_amd64.whl (100 kB)
Collecting rapidfuzz&lt;4.0.0,&gt;=2.3.0
  Using cached rapidfuzz-3.3.1-cp310-cp310-win_amd64.whl (1.8 MB)
Installing collected packages: rapidfuzz, Levenshtein, python-Levenshtein
Successfully installed Levenshtein-0.22.0 python-Levenshtein-0.22.0 rapidfuzz-3.3.1
</pre>
<p>Enterキーを押すと、インストールが開始され、上記のように「Successfully installed」と表示されます。これが表示されれば、python-Levenshteinが正常にインストールされたことになります。</p>
<p>なお、今回はpython-Levenshteinのバージョン0.22.0をインストールしました。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/python-installation-of-python-levenshtein-to-calculate-distance-and-similarity-of-string-edits/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>https://medium.com からデータを抽出するPythonラッパー「medium-api」のインストール</title>
		<link>https://laboratory.kazuuu.net/installation-of-medium-api-python-wrapper-to-extract-data-from-https-medium-com/</link>
					<comments>https://laboratory.kazuuu.net/installation-of-medium-api-python-wrapper-to-extract-data-from-https-medium-com/#respond</comments>
		
		<dc:creator><![CDATA[小林]]></dc:creator>
		<pubDate>Mon, 05 Aug 2024 19:50:35 +0000</pubDate>
				<category><![CDATA[Python【初歩から現場実務的なもの】]]></category>
		<guid isPermaLink="false">https://laboratory.kazuuu.net/?p=17576</guid>

					<description><![CDATA[https://medium.com からデータを抽出するPythonラッパー「medium-api」のインストールについて解説しています。 「medium-api（https://github.com/weeping- [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>https://medium.com からデータを抽出するPythonラッパー「medium-api」のインストールについて解説しています。</p>
<p>「medium-api（<a href="https://github.com/weeping-angel/medium-api">https://github.com/weeping-angel/medium-api</a>）」は、Medium API上の Python ラッパーにより、MediumのWeb サイト（<a href="https://medium.com">https://medium.com</a>）からデータを抽出できる。なお、これはMediumが公式で配布しているものではなく、非公式であることを理解しておく必要があります。</p>
<h2>■Python</h2>
<p><img loading="lazy" decoding="async" class="alignnone size-medium wp-image-16915" src="https://laboratory.kazuuu.net/wp-content/uploads/2023/06/スクリーンショット-2023-06-11-082715-300x71.png" alt="" width="300" height="71" srcset="https://laboratory.kazuuu.net/wp-content/uploads/2023/06/スクリーンショット-2023-06-11-082715-300x71.png 300w, https://laboratory.kazuuu.net/wp-content/uploads/2023/06/スクリーンショット-2023-06-11-082715-1024x244.png 1024w, https://laboratory.kazuuu.net/wp-content/uploads/2023/06/スクリーンショット-2023-06-11-082715-768x183.png 768w, https://laboratory.kazuuu.net/wp-content/uploads/2023/06/スクリーンショット-2023-06-11-082715.png 1365w" sizes="(max-width: 300px) 100vw, 300px" /></p>
<p>今回のPythonのバージョンは、「3.9.9」を使用しています。（Windows11）（pythonランチャーでの確認）</p>
<h2>■medium-apiをインストールする</h2>
<p>medium-apiをインストールを行いますが、今回はpipを経由してインストールを行うので、まずWindowsのコマンドプロンプトを起動します。</p>
<pre>pip install medium-api</pre>
<p>起動後、上記のコマンドを入力し、Enterキーを押します。</p>
<p>なお、今回は、pythonランチャーを使用しており、Python Version 3.9.9にインストールを行うために、バージョンの切り替えを行います。</p>
<pre>py -3.9 -m pip install medium-api</pre>
<p>切り替えるために、上記のコマンドを入力し、Enterキーを押します。</p>
<pre>Defaulting to user installation because normal site-packages is not writeable
Collecting medium-api
  Downloading medium_api-0.5.4-py3-none-any.whl (23 kB)
Installing collected packages: medium-api
Successfully installed medium-api-0.5.4
WARNING: You are using pip version 21.2.4; however, version 23.2.1 is available.
You should consider upgrading via the 'C:\Program Files\Python39\python.exe -m pip install --upgrade pip' command.
</pre>
<p>Enterキーを押すと、インストールが開始され、上記のように「Successfully installed」と表示されます。これが表示されれば、medium-apiが正常にインストールされたことになりますが、「WARNING」と表示されました。「WARNING」はエラーではなく警告で、インストールは行われているので、今回は一旦無視しています。</p>
<p>なお、今回はmedium-apiのバージョン0.5.4をインストールしました。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://laboratory.kazuuu.net/installation-of-medium-api-python-wrapper-to-extract-data-from-https-medium-com/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
