[{"data":1,"prerenderedAt":1881},["ShallowReactive",2],{"content-/amazon-price-watcher-spec":3,"all-pages-for-dir":1859,"related-/amazon-price-watcher-spec":1860,"og-image-/amazon-price-watcher-spec":1880},{"id":4,"title":5,"body":6,"category":1843,"concepts":1843,"description":1844,"extension":1845,"meta":1846,"navigation":1847,"ogImage":1843,"path":1848,"project_name":1843,"published":1849,"publishedAt":1850,"seo":1851,"source":1843,"stem":1852,"tags":1853,"todo":1843,"unpublished":1849,"updatedAt":1850,"__hash__":1858},"pages/2025-12/2025-12-06/amazon-price-watcher-spec.md","Amazon 価格監視ツール設計書（PA-API + GitHub Actions + CSV）",{"type":7,"value":8,"toc":1808},"minimark",[9,13,18,57,60,64,100,102,106,111,178,182,230,232,236,246,248,252,259,273,277,406,409,444,446,453,475,478,611,614,648,650,654,658,1022,1024,1028,1032,1055,1059,1065,1093,1097,1102,1231,1235,1261,1265,1290,1398,1402,1405,1411,1414,1451,1455,1487,1489,1493,1496,1500,1507,1511,1526,1530,1552,1554,1558,1570,1661,1664,1678,1681,1683,1687,1734,1736,1740,1774,1776,1779,1782,1801,1804],[10,11,5],"h1",{"id":12},"amazon-価格監視ツール設計書pa-api-github-actions-csv",[14,15,17],"h2",{"id":16},"_1-ゴール-概要","1. ゴール / 概要",[19,20,21,25,28,36],"ul",{},[22,23,24],"li",{},"対象：Amazon.co.jp の特定商品",[22,26,27],{},"手段：Amazon Product Advertising API（PA-API）を定期的に叩き、価格を取得",[22,29,30,31,35],{},"ストレージ：",[32,33,34],"strong",{},"すべて CSV","（Excel で編集可能な前提）",[22,37,38,39],{},"処理フロー：",[40,41,42,45,48,51,54],"ol",{},[22,43,44],{},"プロダクト一覧 CSV から「監視対象商品（ASIN + ターゲット価格）」を読み込む",[22,46,47],{},"各商品の現在価格を PA-API で取得",[22,49,50],{},"商品ごとの履歴 CSV に追記",[22,52,53],{},"現在価格がターゲット価格以下になった商品について通知を送信（メール等）",[22,55,56],{},"一覧から商品を削除した場合、その商品は以降監視されないが、履歴 CSV は残る",[58,59],"hr",{},[14,61,63],{"id":62},"_2-商品コードasinについて","2. 商品コード（ASIN）について",[19,65,66,82,89],{},[22,67,68,69,72,73],{},"Amazon には、",[32,70,71],{},"ASIN (Amazon Standard Identification Number)"," という商品IDがある。",[19,74,75],{},[22,76,77,78,81],{},"同一マーケットプレイス（今回は Amazon.co.jp）内では、",[32,79,80],{},"ASIN は一意（ユニーク）","。",[22,83,84,85,88],{},"本システムでは、この ",[32,86,87],{},"ASIN を「商品コード」かつ主キー"," として扱う。",[22,90,91,92],{},"ユーザー操作：",[19,93,94,97],{},[22,95,96],{},"商品ページ URL から ASIN を抽出して「products.csv」に入力するか、",[22,98,99],{},"生成AI側の実装で「URL → ASIN 抽出」を行っても良い（仕様としては ASIN を持っていればOK）。",[58,101],{},[14,103,105],{"id":104},"_3-全体アーキテクチャ","3. 全体アーキテクチャ",[107,108,110],"h3",{"id":109},"_31-コンポーネント構成","3.1 コンポーネント構成",[19,112,113,133,148,163],{},[22,114,115,118],{},[32,116,117],{},"CSV ストレージ",[19,119,120,127],{},[22,121,122,126],{},[123,124,125],"code",{},"data/products.csv"," … 監視対象の商品一覧（Excel で編集）",[22,128,129,132],{},[123,130,131],{},"data/history/{ASIN}.csv"," … 各商品の価格履歴ログ",[22,134,135,138],{},[32,136,137],{},"価格取得スクリプト（Python）",[19,139,140,145],{},[22,141,142],{},[123,143,144],{},"src/fetch_prices.py",[22,146,147],{},"やること：products.csv を読み込み → PA-API で価格取得 → history CSV に追記 → 通知判定",[22,149,150,153],{},[32,151,152],{},"通知スクリプト（Python）",[19,154,155,160],{},[22,156,157],{},[123,158,159],{},"src/notify.py",[22,161,162],{},"やること：fetch_prices.py から渡された「通知対象商品一覧」を元に、メール or Webhook を送信",[22,164,165,168],{},[32,166,167],{},"GitHub Actions",[19,169,170,175],{},[22,171,172],{},[123,173,174],{},".github/workflows/price_watcher.yml",[22,176,177],{},"スケジュール実行（例：1日1回）＋手動実行（dispatch）の両方",[107,179,181],{"id":180},"_32-想定テクノロジースタック","3.2 想定テクノロジースタック",[19,183,184,187,210],{},[22,185,186],{},"言語：Python 3.10+",[22,188,189,190],{},"依存ライブラリ例：",[19,191,192,198,204],{},[22,193,194,197],{},[123,195,196],{},"requests","（PA-API 呼び出し）",[22,199,200,203],{},[123,201,202],{},"python-amazon-paapi"," 等の PA-API クライアント（採用は実装側判断で可）",[22,205,206,209],{},[123,207,208],{},"pandas","（CSV 操作を楽にするため、任意）",[22,211,212,213],{},"通知手段：",[19,214,215,227],{},[22,216,217,218,221,222],{},"まずは ",[32,219,220],{},"メール送信"," を前提",[19,223,224],{},[22,225,226],{},"例：SMTP（Gmail 等） or SendGrid API など",[22,228,229],{},"実装側で抽象化しやすいよう、インターフェイスのみ仕様で定義",[58,231],{},[14,233,235],{"id":234},"_4-ディレクトリ構成例","4. ディレクトリ構成（例）",[237,238,244],"pre",{"className":239,"code":241,"language":242,"meta":243},[240],"language-text","project-root/\n  README.md\n  data/\n    products.csv\n    history/\n      # 例）B0CN28DNQH.csv など ASIN ごとのファイル\n  src/\n    fetch_prices.py\n    notify.py\n    paapi_client.py   # PA-API 呼び出しラッパ\n    config.py\n  .github/\n    workflows/\n      price_watcher.yml\n  requirements.txt\n","text","",[123,245,241],{"__ignoreMap":243},[58,247],{},[14,249,251],{"id":250},"_5-csv-仕様データモデル","5. CSV 仕様（データモデル）",[107,253,255,256,258],{"id":254},"_51-dataproductscsv監視対象リスト","5.1 ",[123,257,125],{},"（監視対象リスト）",[19,260,261,264,270],{},[22,262,263],{},"役割：監視対象の商品と、ターゲット価格などのメタ情報を保持する。",[22,265,266,267],{},"フォーマット：",[32,268,269],{},"UTF-8 / カンマ区切り",[22,271,272],{},"1 行目はヘッダー行。",[274,275,276],"h4",{"id":276},"カラム定義",[278,279,280,299],"table",{},[281,282,283],"thead",{},[284,285,286,290,293,296],"tr",{},[287,288,289],"th",{},"カラム名",[287,291,292],{},"必須",[287,294,295],{},"型",[287,297,298],{},"説明",[300,301,302,322,336,350,365,392],"tbody",{},[284,303,304,310,313,316],{},[305,306,307],"td",{},[123,308,309],{},"asin",[305,311,312],{},"Yes",[305,314,315],{},"string",[305,317,318,319],{},"Amazon ASIN（商品コード、ユニーク）例: ",[123,320,321],{},"B0CN28DNQH",[284,323,324,329,331,333],{},[305,325,326],{},[123,327,328],{},"name",[305,330,312],{},[305,332,315],{},[305,334,335],{},"商品名（任意のわかりやすい名前）",[284,337,338,343,345,347],{},[305,339,340],{},[123,341,342],{},"url",[305,344,312],{},[305,346,315],{},[305,348,349],{},"商品ページの URL（Amazon.co.jp）",[284,351,352,357,359,362],{},[305,353,354],{},[123,355,356],{},"target_price",[305,358,312],{},[305,360,361],{},"number",[305,363,364],{},"通知を発火させる「ターゲット価格」（税込、JPY を想定）",[284,366,367,372,375,378],{},[305,368,369],{},[123,370,371],{},"active",[305,373,374],{},"No",[305,376,377],{},"boolean",[305,379,380,381,384,385,388,389,391],{},"監視対象フラグ。",[123,382,383],{},"true"," または ",[123,386,387],{},"false","。未指定なら ",[123,390,383],{}," 扱い。",[284,393,394,399,401,403],{},[305,395,396],{},[123,397,398],{},"notes",[305,400,374],{},[305,402,315],{},[305,404,405],{},"メモ/備考",[274,407,408],{"id":408},"挙動仕様",[19,410,411,414,425],{},[22,412,413],{},"ユーザーが Excel でこの CSV を編集する想定。",[22,415,416,417],{},"新規商品を追跡したい場合：",[19,418,419,422],{},[22,420,421],{},"新しい行を追加（ASIN、name、url、target_price を入力）。",[22,423,424],{},"この時点で次回の定期実行から監視対象となる。",[22,426,427,428],{},"監視をやめたい場合：",[19,429,430,437],{},[22,431,432,433,436],{},"行を削除する、または ",[123,434,435],{},"active=false"," にする。",[22,438,439,440,443],{},"履歴 CSV (",[123,441,442],{},"history/{ASIN}.csv",") は残るが、以降更新されない。",[58,445],{},[107,447,449,450,452],{"id":448},"_52-datahistoryasincsv商品別価格履歴","5.2 ",[123,451,131],{},"（商品別価格履歴）",[19,454,455,458,472],{},[22,456,457],{},"役割：各 ASIN の価格履歴を保存。",[22,459,460,461,464],{},"ファイル名：",[123,462,463],{},"data/history/\u003CASIN>.csv",[19,465,466],{},[22,467,468,469],{},"例：",[123,470,471],{},"data/history/B0CN28DNQH.csv",[22,473,474],{},"1 商品につき 1 ファイル（Excel で言う「商品ごとのシート」に相当）。",[274,476,276],{"id":477},"カラム定義-1",[278,479,480,492],{},[281,481,482],{},[284,483,484,486,488,490],{},[287,485,289],{},[287,487,292],{},[287,489,295],{},[287,491,298],{},[300,493,494,512,530,543,557,575,597],{},[284,495,496,501,503,505],{},[305,497,498],{},[123,499,500],{},"timestamp",[305,502,312],{},[305,504,315],{},[305,506,507,508,511],{},"ISO8601 日時（例: ",[123,509,510],{},"2025-12-06T00:00:00+09:00","）",[284,513,514,519,521,523],{},[305,515,516],{},[123,517,518],{},"date",[305,520,312],{},[305,522,315],{},[305,524,525,526,529],{},"日付のみ（例: ",[123,527,528],{},"2025-12-06","）。集計用に冗長保持。",[284,531,532,536,538,540],{},[305,533,534],{},[123,535,309],{},[305,537,312],{},[305,539,315],{},[305,541,542],{},"対象商品の ASIN",[284,544,545,550,552,554],{},[305,546,547],{},[123,548,549],{},"price",[305,551,312],{},[305,553,361],{},[305,555,556],{},"取得した販売価格（JPY）",[284,558,559,564,566,568],{},[305,560,561],{},[123,562,563],{},"currency",[305,565,312],{},[305,567,315],{},[305,569,570,571,574],{},"通貨コード。",[123,572,573],{},"JPY"," 固定想定。",[284,576,577,582,584,586],{},[305,578,579],{},[123,580,581],{},"source",[305,583,374],{},[305,585,315],{},[305,587,588,589,592,593,596],{},"価格のソース（",[123,590,591],{},"AmazonOffer"," / ",[123,594,595],{},"LowestNewPrice"," など）。",[284,598,599,604,606,608],{},[305,600,601],{},[123,602,603],{},"raw_json",[305,605,374],{},[305,607,315],{},[305,609,610],{},"PA-API のレスポンスのうち、必要な部分を JSON 文字列として保存（任意）。",[274,612,613],{"id":613},"追記ルール",[19,615,616,626,629,637],{},[22,617,618,621,622,625],{},[123,619,620],{},"fetch_prices.py"," 実行ごとに、",[32,623,624],{},"商品の数 × 1 行"," を追加。",[22,627,628],{},"行追加は「末尾」へ追記。",[22,630,631,632],{},"ファイルが存在しない場合：",[19,633,634],{},[22,635,636],{},"新規作成し、ヘッダー行 + データ 1 行を出力。",[22,638,639,640],{},"ファイルが存在する場合：",[19,641,642],{},[22,643,644,645,647],{},"最終行の ",[123,646,518],{}," が同一日でも、そのまま追記（デイリー 1 回実行想定だが、複数回でも時系列ログとして許容）。",[58,649],{},[14,651,653],{"id":652},"_6-処理フロー詳細","6. 処理フロー詳細",[107,655,657],{"id":656},"_61-fetch_pricespy-のロジック","6.1 fetch_prices.py のロジック",[40,659,660,686,802,822,864],{},[22,661,662,664,665],{},[123,663,125],{}," を読み込み",[19,666,667,673],{},[22,668,669,672],{},[123,670,671],{},"active != false"," の行のみを対象とする。",[22,674,675,676,678,679,678,681,678,683,685],{},"各行から ",[123,677,309],{},", ",[123,680,328],{},[123,682,342],{},[123,684,356],{}," を取り出す。",[22,687,688,689],{},"各 ASIN について PA-API を呼び出し、「現在価格」を取得",[19,690,691,697],{},[22,692,693,696],{},[123,694,695],{},"paapi_client.get_current_price(asin)"," のような抽象関数でラップする。",[22,698,699,700],{},"戻り値例：",[237,701,705],{"className":702,"code":703,"language":704,"meta":243,"style":243},"language-python shiki shiki-themes vitesse-light vitesse-light","{\n  \"asin\": \"B0CN28DNQH\",\n  \"price\": 19800,\n  \"currency\": \"JPY\",\n  \"source\": \"AmazonOffer\"\n}\n","python",[123,706,707,716,742,759,778,796],{"__ignoreMap":243},[708,709,712],"span",{"class":710,"line":711},"line",1,[708,713,715],{"class":714},"shFtX","{\n",[708,717,719,723,726,729,732,735,737,739],{"class":710,"line":718},2,[708,720,722],{"class":721},"sMJiu","  \"",[708,724,309],{"class":725},"sdGka",[708,727,728],{"class":721},"\"",[708,730,731],{"class":714},":",[708,733,734],{"class":721}," \"",[708,736,321],{"class":725},[708,738,728],{"class":721},[708,740,741],{"class":714},",\n",[708,743,745,747,749,751,753,757],{"class":710,"line":744},3,[708,746,722],{"class":721},[708,748,549],{"class":725},[708,750,728],{"class":721},[708,752,731],{"class":714},[708,754,756],{"class":755},"sM54T"," 19800",[708,758,741],{"class":714},[708,760,762,764,766,768,770,772,774,776],{"class":710,"line":761},4,[708,763,722],{"class":721},[708,765,563],{"class":725},[708,767,728],{"class":721},[708,769,731],{"class":714},[708,771,734],{"class":721},[708,773,573],{"class":725},[708,775,728],{"class":721},[708,777,741],{"class":714},[708,779,781,783,785,787,789,791,793],{"class":710,"line":780},5,[708,782,722],{"class":721},[708,784,581],{"class":725},[708,786,728],{"class":721},[708,788,731],{"class":714},[708,790,734],{"class":721},[708,792,591],{"class":725},[708,794,795],{"class":721},"\"\n",[708,797,799],{"class":710,"line":798},6,[708,800,801],{"class":714},"}\n",[22,803,439,804,806,807],{},[123,805,463],{},") に追記",[19,808,809,814],{},[22,810,811,813],{},[123,812,500],{}," は現在日時（タイムゾーン +09:00）。",[22,815,816,818,819,81],{},[123,817,518],{}," は ",[123,820,821],{},"YYYY-MM-DD",[22,823,824,825],{},"通知判定",[19,826,827,845],{},[22,828,829,831,832],{},[123,830,356],{}," が数値として有効な場合、以下を行う：",[19,833,834],{},[22,835,836,837,840,841,844],{},"最新の価格 ",[123,838,839],{},"current_price"," と比較し、\n",[123,842,843],{},"current_price \u003C= target_price"," のとき「通知候補」に追加。",[22,846,847,850,851],{},[32,848,849],{},"同じ条件で何度も通知しない工夫","（推奨仕様）：",[19,852,853],{},[22,854,855,856],{},"当該 ASIN の履歴ファイルを読み込み、",[19,857,858,861],{},[22,859,860],{},"「直前の価格 > target_price」かつ「今回の価格 \u003C= target_price」のときだけ通知する。",[22,862,863],{},"つまり、「ターゲットを初めて割り込んだタイミング」でのみ通知。",[22,865,866,867,870,871],{},"通知候補一覧を ",[123,868,869],{},"notify.py"," に渡す",[19,872,873],{},[22,874,875,876],{},"形式例（Python の dict list）：",[237,877,879],{"className":702,"code":878,"language":704,"meta":243,"style":243},"to_notify = [\n  {\n    \"asin\": \"B0CN28DNQH\",\n    \"name\": \"BARWING エアロバイク\",\n    \"url\": \"https://www.amazon.co.jp/...\",\n    \"target_price\": 18000,\n    \"current_price\": 17500,\n    \"timestamp\": \"2025-12-06T00:00:00+09:00\"\n  },\n  ...\n]\n",[123,880,881,893,898,917,936,955,970,986,1003,1009,1016],{"__ignoreMap":243},[708,882,883,887,890],{"class":710,"line":711},[708,884,886],{"class":885},"sG7-3","to_notify ",[708,888,889],{"class":714},"=",[708,891,892],{"class":714}," [\n",[708,894,895],{"class":710,"line":718},[708,896,897],{"class":714},"  {\n",[708,899,900,903,905,907,909,911,913,915],{"class":710,"line":744},[708,901,902],{"class":721},"    \"",[708,904,309],{"class":725},[708,906,728],{"class":721},[708,908,731],{"class":714},[708,910,734],{"class":721},[708,912,321],{"class":725},[708,914,728],{"class":721},[708,916,741],{"class":714},[708,918,919,921,923,925,927,929,932,934],{"class":710,"line":761},[708,920,902],{"class":721},[708,922,328],{"class":725},[708,924,728],{"class":721},[708,926,731],{"class":714},[708,928,734],{"class":721},[708,930,931],{"class":725},"BARWING エアロバイク",[708,933,728],{"class":721},[708,935,741],{"class":714},[708,937,938,940,942,944,946,948,951,953],{"class":710,"line":780},[708,939,902],{"class":721},[708,941,342],{"class":725},[708,943,728],{"class":721},[708,945,731],{"class":714},[708,947,734],{"class":721},[708,949,950],{"class":725},"https://www.amazon.co.jp/...",[708,952,728],{"class":721},[708,954,741],{"class":714},[708,956,957,959,961,963,965,968],{"class":710,"line":798},[708,958,902],{"class":721},[708,960,356],{"class":725},[708,962,728],{"class":721},[708,964,731],{"class":714},[708,966,967],{"class":755}," 18000",[708,969,741],{"class":714},[708,971,973,975,977,979,981,984],{"class":710,"line":972},7,[708,974,902],{"class":721},[708,976,839],{"class":725},[708,978,728],{"class":721},[708,980,731],{"class":714},[708,982,983],{"class":755}," 17500",[708,985,741],{"class":714},[708,987,989,991,993,995,997,999,1001],{"class":710,"line":988},8,[708,990,902],{"class":721},[708,992,500],{"class":725},[708,994,728],{"class":721},[708,996,731],{"class":714},[708,998,734],{"class":721},[708,1000,510],{"class":725},[708,1002,795],{"class":721},[708,1004,1006],{"class":710,"line":1005},9,[708,1007,1008],{"class":714},"  },\n",[708,1010,1012],{"class":710,"line":1011},10,[708,1013,1015],{"class":1014},"snbK4","  ...\n",[708,1017,1019],{"class":710,"line":1018},11,[708,1020,1021],{"class":714},"]\n",[58,1023],{},[14,1025,1027],{"id":1026},"_62-notifypy-のロジックresend-でメール送信","6.2 notify.py のロジック（Resend でメール送信）",[107,1029,1031],{"id":1030},"_621-前提","6.2.1 前提",[19,1033,1034,1041,1048],{},[22,1035,1036,1037,1040],{},"メール送信は ",[32,1038,1039],{},"Resend の公式 API"," を利用する。",[22,1042,1043,1044,1047],{},"Python からは HTTP(S) で ",[123,1045,1046],{},"https://api.resend.com/emails"," に POST する。",[22,1049,1050,1051,1054],{},"認証は ",[123,1052,1053],{},"Authorization: Bearer \u003CRESEND_API_KEY>"," ヘッダで行う。",[107,1056,1058],{"id":1057},"_622-環境変数","6.2.2 環境変数",[1060,1061,1062,1064],"p",{},[123,1063,869],{}," は以下の環境変数を参照する。",[19,1066,1067,1073,1083],{},[22,1068,1069,1072],{},[123,1070,1071],{},"RESEND_API_KEY","\nResend のダッシュボードで発行した API キー。",[22,1074,1075,1078,1079,1082],{},[123,1076,1077],{},"MAIL_FROM","\n送信元メールアドレス（例：",[123,1080,1081],{},"\"alerts@example.com\"","）。\nResend 側で認証済みのドメイン／アドレスを利用すること。",[22,1084,1085,1088,1089,1092],{},[123,1086,1087],{},"MAIL_TO","\n送信先メールアドレス。カンマ区切りで複数指定可（例：",[123,1090,1091],{},"\"user1@example.com,user2@example.com\"","）。",[107,1094,1096],{"id":1095},"_623-入力to_notify","6.2.3 入力（to_notify）",[1060,1098,1099,1101],{},[123,1100,620],{}," から以下形式のリストを受け取る：",[237,1103,1105],{"className":702,"code":1104,"language":704,"meta":243,"style":243},"to_notify = [\n  {\n    \"asin\": \"B0CN28DNQH\",\n    \"name\": \"BARWING エアロバイク\",\n    \"url\": \"https://www.amazon.co.jp/...\",\n    \"target_price\": 18000,\n    \"current_price\": 17500,\n    \"timestamp\": \"2025-12-06T00:00:00+09:00\"\n  },\n  # ...\n]\n",[123,1106,1107,1115,1119,1137,1155,1173,1187,1201,1217,1221,1227],{"__ignoreMap":243},[708,1108,1109,1111,1113],{"class":710,"line":711},[708,1110,886],{"class":885},[708,1112,889],{"class":714},[708,1114,892],{"class":714},[708,1116,1117],{"class":710,"line":718},[708,1118,897],{"class":714},[708,1120,1121,1123,1125,1127,1129,1131,1133,1135],{"class":710,"line":744},[708,1122,902],{"class":721},[708,1124,309],{"class":725},[708,1126,728],{"class":721},[708,1128,731],{"class":714},[708,1130,734],{"class":721},[708,1132,321],{"class":725},[708,1134,728],{"class":721},[708,1136,741],{"class":714},[708,1138,1139,1141,1143,1145,1147,1149,1151,1153],{"class":710,"line":761},[708,1140,902],{"class":721},[708,1142,328],{"class":725},[708,1144,728],{"class":721},[708,1146,731],{"class":714},[708,1148,734],{"class":721},[708,1150,931],{"class":725},[708,1152,728],{"class":721},[708,1154,741],{"class":714},[708,1156,1157,1159,1161,1163,1165,1167,1169,1171],{"class":710,"line":780},[708,1158,902],{"class":721},[708,1160,342],{"class":725},[708,1162,728],{"class":721},[708,1164,731],{"class":714},[708,1166,734],{"class":721},[708,1168,950],{"class":725},[708,1170,728],{"class":721},[708,1172,741],{"class":714},[708,1174,1175,1177,1179,1181,1183,1185],{"class":710,"line":798},[708,1176,902],{"class":721},[708,1178,356],{"class":725},[708,1180,728],{"class":721},[708,1182,731],{"class":714},[708,1184,967],{"class":755},[708,1186,741],{"class":714},[708,1188,1189,1191,1193,1195,1197,1199],{"class":710,"line":972},[708,1190,902],{"class":721},[708,1192,839],{"class":725},[708,1194,728],{"class":721},[708,1196,731],{"class":714},[708,1198,983],{"class":755},[708,1200,741],{"class":714},[708,1202,1203,1205,1207,1209,1211,1213,1215],{"class":710,"line":988},[708,1204,902],{"class":721},[708,1206,500],{"class":725},[708,1208,728],{"class":721},[708,1210,731],{"class":714},[708,1212,734],{"class":721},[708,1214,510],{"class":725},[708,1216,795],{"class":721},[708,1218,1219],{"class":710,"line":1005},[708,1220,1008],{"class":714},[708,1222,1223],{"class":710,"line":1011},[708,1224,1226],{"class":1225},"sxvE3","  # ...\n",[708,1228,1229],{"class":710,"line":1018},[708,1230,1021],{"class":714},[107,1232,1234],{"id":1233},"_624-処理フロー","6.2.4 処理フロー",[40,1236,1237,1247,1256],{},[22,1238,1239,1242,1243,1246],{},[123,1240,1241],{},"to_notify"," が空 (",[123,1244,1245],{},"len == 0",") の場合は何もせず終了。",[22,1248,1249,678,1251,678,1253,1255],{},[123,1250,1077],{},[123,1252,1087],{},[123,1254,1071],{}," がすべて設定されているか検証。足りなければログ出力して終了（エラー終了でもよい）。",[22,1257,1258,1260],{},[123,1259,1087],{}," をカンマ区切りで分割し、受信者リストを作成。",[107,1262,1264],{"id":1263},"_625-resend-へのリクエスト仕様","6.2.5 Resend へのリクエスト仕様",[19,1266,1267,1273,1287],{},[22,1268,1269,1270],{},"エンドポイント: ",[123,1271,1272],{},"POST https://api.resend.com/emails",[22,1274,1275,1276],{},"ヘッダ:",[19,1277,1278,1282],{},[22,1279,1280],{},[123,1281,1053],{},[22,1283,1284],{},[123,1285,1286],{},"Content-Type: application/json",[22,1288,1289],{},"ボディ例（単純なテキストメール）:",[237,1291,1295],{"className":1292,"code":1293,"language":1294,"meta":243,"style":243},"language-json shiki shiki-themes vitesse-light vitesse-light","{\n  \"from\": \"Your Name \u003Calerts@example.com>\",\n  \"to\": [\"user1@example.com\", \"user2@example.com\"],\n  \"subject\": \"Amazon価格アラート: 3件の商品がターゲット価格を下回りました\",\n  \"text\": \"本文（プレーンテキスト）\"\n}\n","json",[123,1296,1297,1301,1323,1357,1377,1394],{"__ignoreMap":243},[708,1298,1299],{"class":710,"line":711},[708,1300,715],{"class":714},[708,1302,1303,1306,1310,1312,1314,1316,1319,1321],{"class":710,"line":718},[708,1304,722],{"class":1305},"sqvqQ",[708,1307,1309],{"class":1308},"sz8Xr","from",[708,1311,728],{"class":1305},[708,1313,731],{"class":714},[708,1315,734],{"class":721},[708,1317,1318],{"class":725},"Your Name \u003Calerts@example.com>",[708,1320,728],{"class":721},[708,1322,741],{"class":714},[708,1324,1325,1327,1330,1332,1334,1337,1339,1342,1344,1347,1349,1352,1354],{"class":710,"line":744},[708,1326,722],{"class":1305},[708,1328,1329],{"class":1308},"to",[708,1331,728],{"class":1305},[708,1333,731],{"class":714},[708,1335,1336],{"class":714}," [",[708,1338,728],{"class":721},[708,1340,1341],{"class":725},"user1@example.com",[708,1343,728],{"class":721},[708,1345,1346],{"class":714},",",[708,1348,734],{"class":721},[708,1350,1351],{"class":725},"user2@example.com",[708,1353,728],{"class":721},[708,1355,1356],{"class":714},"],\n",[708,1358,1359,1361,1364,1366,1368,1370,1373,1375],{"class":710,"line":761},[708,1360,722],{"class":1305},[708,1362,1363],{"class":1308},"subject",[708,1365,728],{"class":1305},[708,1367,731],{"class":714},[708,1369,734],{"class":721},[708,1371,1372],{"class":725},"Amazon価格アラート: 3件の商品がターゲット価格を下回りました",[708,1374,728],{"class":721},[708,1376,741],{"class":714},[708,1378,1379,1381,1383,1385,1387,1389,1392],{"class":710,"line":780},[708,1380,722],{"class":1305},[708,1382,242],{"class":1308},[708,1384,728],{"class":1305},[708,1386,731],{"class":714},[708,1388,734],{"class":721},[708,1390,1391],{"class":725},"本文（プレーンテキスト）",[708,1393,795],{"class":721},[708,1395,1396],{"class":710,"line":798},[708,1397,801],{"class":714},[107,1399,1401],{"id":1400},"_626-メール本文のフォーマットtext","6.2.6 メール本文のフォーマット（text）",[1060,1403,1404],{},"本文はプレーンテキストでよい。例：",[237,1406,1409],{"className":1407,"code":1408,"language":242,"meta":243},[240],"以下の商品がターゲット価格を下回りました。\n\n1) BARWING エアロバイク\n   ASIN: B0CN28DNQH\n   現在価格: 17,500 円\n   ターゲット価格: 18,000 円\n   時刻: 2025-12-06T00:00:00+09:00\n   URL: https://www.amazon.co.jp/...\n\n2) ...\n",[123,1410,1408],{"__ignoreMap":243},[1060,1412,1413],{},"生成ルール:",[19,1415,1416,1419,1422],{},[22,1417,1418],{},"先頭に簡単な説明文。",[22,1420,1421],{},"各商品を番号付きで列挙。",[22,1423,1424,1425],{},"含める項目：",[19,1426,1427,1431,1435,1439,1443,1447],{},[22,1428,1429],{},[123,1430,328],{},[22,1432,1433],{},[123,1434,309],{},[22,1436,1437],{},[123,1438,839],{},[22,1440,1441],{},[123,1442,356],{},[22,1444,1445],{},[123,1446,500],{},[22,1448,1449],{},[123,1450,342],{},[107,1452,1454],{"id":1453},"_627-エラーハンドリング","6.2.7 エラーハンドリング",[19,1456,1457,1472],{},[22,1458,1459,1460],{},"Resend API が 2xx 以外を返した場合：",[19,1461,1462,1465],{},[22,1463,1464],{},"ステータスコードとレスポンスボディをログ出力。",[22,1466,1467,1468,1471],{},"必要に応じて例外を投げて GitHub Actions を ",[123,1469,1470],{},"failed"," にしてもよい（運用ポリシー次第）。",[22,1473,1474,1475],{},"ネットワークエラーなど:",[19,1476,1477,1480],{},[22,1478,1479],{},"リトライはとりあえずなし（シンプルに失敗としてログ出力）。",[22,1481,1482,1483,1486],{},"将来的に ",[123,1484,1485],{},"tenacity"," 等のリトライライブラリで拡張可能な構成にしておく。",[58,1488],{},[14,1490,1492],{"id":1491},"_7-環境変数-シークレット定義resend-版","7. 環境変数 / シークレット定義（Resend 版）",[1060,1494,1495],{},"GitHub Secrets（あるいは Actions Variables）に少なくとも以下を設定する：",[107,1497,1499],{"id":1498},"_71-resend-関連","7.1 Resend 関連",[19,1501,1502],{},[22,1503,1504,1506],{},[123,1505,1071],{},"\nResend の API キー。",[107,1508,1510],{"id":1509},"_72-メール送信設定","7.2 メール送信設定",[19,1512,1513,1521],{},[22,1514,1515,1517,1518,1092],{},[123,1516,1077],{},"\nResend で利用可能な送信元アドレス（例: ",[123,1519,1520],{},"alerts@yourdomain.com",[22,1522,1523,1525],{},[123,1524,1087],{},"\n通知を受け取りたい自分のメールアドレス（複数可）。",[107,1527,1529],{"id":1528},"_73-pa-api-関連前仕様と同じ","7.3 PA-API 関連（前仕様と同じ）",[19,1531,1532,1537,1542,1547],{},[22,1533,1534],{},[123,1535,1536],{},"AMAZON_PAAPI_ACCESS_KEY",[22,1538,1539],{},[123,1540,1541],{},"AMAZON_PAAPI_SECRET_KEY",[22,1543,1544],{},[123,1545,1546],{},"AMAZON_PAAPI_ASSOCIATE_TAG",[22,1548,1549],{},[123,1550,1551],{},"AMAZON_PAAPI_REGION",[58,1553],{},[14,1555,1557],{"id":1556},"_8-github-actions-側での設定補足","8. GitHub Actions 側での設定補足",[1060,1559,1560,1562,1563,384,1566,1569],{},[123,1561,174],{}," 内で、",[123,1564,1565],{},"env",[123,1567,1568],{},"with"," 経由で Python に渡す想定：",[237,1571,1575],{"className":1572,"code":1573,"language":1574,"meta":243,"style":243},"language-yaml shiki shiki-themes vitesse-light vitesse-light","env:\n  RESEND_API_KEY: ${{ secrets.RESEND_API_KEY }}\n  MAIL_FROM: ${{ secrets.MAIL_FROM }}\n  MAIL_TO: ${{ secrets.MAIL_TO }}\n  AMAZON_PAAPI_ACCESS_KEY: ${{ secrets.AMAZON_PAAPI_ACCESS_KEY }}\n  AMAZON_PAAPI_SECRET_KEY: ${{ secrets.AMAZON_PAAPI_SECRET_KEY }}\n  AMAZON_PAAPI_ASSOCIATE_TAG: ${{ secrets.AMAZON_PAAPI_ASSOCIATE_TAG }}\n  AMAZON_PAAPI_REGION: \"us-east-1\"  # 例\n","yaml",[123,1576,1577,1584,1594,1604,1614,1624,1634,1644],{"__ignoreMap":243},[708,1578,1579,1581],{"class":710,"line":711},[708,1580,1565],{"class":1308},[708,1582,1583],{"class":714},":\n",[708,1585,1586,1589,1591],{"class":710,"line":718},[708,1587,1588],{"class":1308},"  RESEND_API_KEY",[708,1590,731],{"class":714},[708,1592,1593],{"class":725}," ${{ secrets.RESEND_API_KEY }}\n",[708,1595,1596,1599,1601],{"class":710,"line":744},[708,1597,1598],{"class":1308},"  MAIL_FROM",[708,1600,731],{"class":714},[708,1602,1603],{"class":725}," ${{ secrets.MAIL_FROM }}\n",[708,1605,1606,1609,1611],{"class":710,"line":761},[708,1607,1608],{"class":1308},"  MAIL_TO",[708,1610,731],{"class":714},[708,1612,1613],{"class":725}," ${{ secrets.MAIL_TO }}\n",[708,1615,1616,1619,1621],{"class":710,"line":780},[708,1617,1618],{"class":1308},"  AMAZON_PAAPI_ACCESS_KEY",[708,1620,731],{"class":714},[708,1622,1623],{"class":725}," ${{ secrets.AMAZON_PAAPI_ACCESS_KEY }}\n",[708,1625,1626,1629,1631],{"class":710,"line":798},[708,1627,1628],{"class":1308},"  AMAZON_PAAPI_SECRET_KEY",[708,1630,731],{"class":714},[708,1632,1633],{"class":725}," ${{ secrets.AMAZON_PAAPI_SECRET_KEY }}\n",[708,1635,1636,1639,1641],{"class":710,"line":972},[708,1637,1638],{"class":1308},"  AMAZON_PAAPI_ASSOCIATE_TAG",[708,1640,731],{"class":714},[708,1642,1643],{"class":725}," ${{ secrets.AMAZON_PAAPI_ASSOCIATE_TAG }}\n",[708,1645,1646,1649,1651,1653,1656,1658],{"class":710,"line":988},[708,1647,1648],{"class":1308},"  AMAZON_PAAPI_REGION",[708,1650,731],{"class":714},[708,1652,734],{"class":721},[708,1654,1655],{"class":725},"us-east-1",[708,1657,728],{"class":721},[708,1659,1660],{"class":1225},"  # 例\n",[1060,1662,1663],{},"あとは実装側で：",[19,1665,1666,1673],{},[22,1667,1668,1670,1671,870],{},[123,1669,620],{}," → 通知対象を ",[123,1672,869],{},[22,1674,1675,1677],{},[123,1676,869],{}," → 上記 Resend API 仕様でメール送信",[1060,1679,1680],{},"という流れにすれば、Resend 前提の構成になります。",[58,1682],{},[14,1684,1686],{"id":1685},"_9-エラーハンドリング-考慮事項","9. エラーハンドリング / 考慮事項",[19,1688,1689,1700,1708,1723],{},[22,1690,1691,1692],{},"PA-API のレスポンスがエラーの場合：",[19,1693,1694,1697],{},[22,1695,1696],{},"該当 ASIN についてはログにエラーを残し、他の商品処理は続行。",[22,1698,1699],{},"失敗した商品についてはその回の履歴追記・通知は行わない。",[22,1701,1702,1703],{},"レートリミット：",[19,1704,1705],{},[22,1706,1707],{},"商品数が増えた場合にレートリミットにかからないよう、ASIN 数 × 実行頻度を調整する。",[22,1709,1710,1711],{},"CSV の競合：",[19,1712,1713,1716],{},[22,1714,1715],{},"本システムでは、GitHub Actions 実行ごとに commit/push は行わない前提でもよいが、",[22,1717,1718,1719,1722],{},"履歴を GitHub 上に残したい場合は、最後に ",[123,1720,1721],{},"git commit && git push"," するワークフローも別途検討可能（ただしコンフリクト対策が必要）。",[22,1724,1725,1726],{},"通貨・税：",[19,1727,1728],{},[22,1729,1730,1731,1733],{},"本仕様では Amazon.co.jp / JPY / 税込を前提とし、PA-API から取得する価格をそのまま ",[123,1732,549],{}," として保存する。",[58,1735],{},[14,1737,1739],{"id":1738},"_10-拡張案将来的なオプション","10. 拡張案（将来的なオプション）",[19,1741,1742,1745,1771],{},[22,1743,1744],{},"Slack / Discord / LINE Notify など、メール以外の通知チャネル追加",[22,1746,1747,1750,1751],{},[123,1748,1749],{},"products.csv"," に以下の項目を追加して柔軟性アップ",[19,1752,1753,1759,1765],{},[22,1754,1755,1758],{},[123,1756,1757],{},"upper_limit_price","（高すぎる場合のアラート）",[22,1760,1761,1764],{},[123,1762,1763],{},"category","（家電、トレーニング用品など）",[22,1766,1767,1770],{},[123,1768,1769],{},"last_notified_at","（通知制御を CSV 側で行う）",[22,1772,1773],{},"価格だけでなく、在庫状況・ポイント付与率なども取得して履歴に保存（PA-API で取得できる範囲に依存）",[58,1775],{},[1060,1777,1778],{},"以上です。",[1060,1780,1781],{},"この仕様を「プロジェクトの要求仕様」としてそのまま生成AIに渡せば、",[19,1783,1784,1792,1795,1798],{},[22,1785,1786,592,1788,1791],{},[123,1787,1749],{},[123,1789,1790],{},"history/*.csv"," の読み書き",[22,1793,1794],{},"PA-API のラッパ",[22,1796,1797],{},"通知ロジック",[22,1799,1800],{},"GitHub Actions の YAML",[1060,1802,1803],{},"まで一通り実装可能なレベルになっているはずです。",[1805,1806,1807],"style",{},"html pre.shiki code .shFtX, html code.shiki .shFtX{--shiki-default:#999999;--shiki-dark:#999999}html pre.shiki code .sMJiu, html code.shiki .sMJiu{--shiki-default:#B5695977;--shiki-dark:#B5695977}html pre.shiki code .sdGka, html code.shiki .sdGka{--shiki-default:#B56959;--shiki-dark:#B56959}html pre.shiki code .sM54T, html code.shiki .sM54T{--shiki-default:#2F798A;--shiki-dark:#2F798A}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sG7-3, html code.shiki .sG7-3{--shiki-default:#393A34;--shiki-dark:#393A34}html pre.shiki code .snbK4, html code.shiki .snbK4{--shiki-default:#A65E2B;--shiki-dark:#A65E2B}html pre.shiki code .sxvE3, html code.shiki .sxvE3{--shiki-default:#A0ADA0;--shiki-dark:#A0ADA0}html pre.shiki code .sqvqQ, html code.shiki .sqvqQ{--shiki-default:#99841877;--shiki-dark:#99841877}html pre.shiki code .sz8Xr, html code.shiki .sz8Xr{--shiki-default:#998418;--shiki-dark:#998418}",{"title":243,"searchDepth":718,"depth":718,"links":1809},[1810,1811,1812,1816,1817,1823,1826,1835,1840,1841,1842],{"id":16,"depth":718,"text":17},{"id":62,"depth":718,"text":63},{"id":104,"depth":718,"text":105,"children":1813},[1814,1815],{"id":109,"depth":744,"text":110},{"id":180,"depth":744,"text":181},{"id":234,"depth":718,"text":235},{"id":250,"depth":718,"text":251,"children":1818},[1819,1821],{"id":254,"depth":744,"text":1820},"5.1 data/products.csv（監視対象リスト）",{"id":448,"depth":744,"text":1822},"5.2 data/history/{ASIN}.csv（商品別価格履歴）",{"id":652,"depth":718,"text":653,"children":1824},[1825],{"id":656,"depth":744,"text":657},{"id":1026,"depth":718,"text":1027,"children":1827},[1828,1829,1830,1831,1832,1833,1834],{"id":1030,"depth":744,"text":1031},{"id":1057,"depth":744,"text":1058},{"id":1095,"depth":744,"text":1096},{"id":1233,"depth":744,"text":1234},{"id":1263,"depth":744,"text":1264},{"id":1400,"depth":744,"text":1401},{"id":1453,"depth":744,"text":1454},{"id":1491,"depth":718,"text":1492,"children":1836},[1837,1838,1839],{"id":1498,"depth":744,"text":1499},{"id":1509,"depth":744,"text":1510},{"id":1528,"depth":744,"text":1529},{"id":1556,"depth":718,"text":1557},{"id":1685,"depth":718,"text":1686},{"id":1738,"depth":718,"text":1739},null,"Amazon PA-APIとGitHub Actionsを使った価格監視ツールの設計書。CSVベースでExcel編集可能、ターゲット価格を下回ったらメール通知","md",{},true,"/amazon-price-watcher-spec",false,"2025-12-06T00:00:00.000Z",{"title":5,"description":1844},"2025-12/2025-12-06/amazon-price-watcher-spec",[1854,1855,167,1856,1857],"Amazon","PA-API","Python","設計書","POxSGNCVJUPFLG96m8jq3Wi8-7HZ_lWd5W0xokWxK-Q",[],[1861,1865,1869,1873,1876],{"title":1862,"path":1863,"publishedAt":1864},"Amazon PA-API アフィリエイトリンク生成ガイド","/amazon_affiliate_api","2026-01-03T00:00:00.000Z",{"title":1866,"path":1867,"publishedAt":1868},"Amazon書籍メタデータを841件取得しNuxt書棚UIで一覧化した1日の試行錯誤","/amazon-book-metadata-shelf-ui","2026-04-28T00:00:00.000Z",{"title":1870,"path":1871,"publishedAt":1872},"Amazonの「設備投資は3年未満で回収」を原文で確認した｜3年未満はサーバーとネットワーク機器に限った話で、データセンターは別勘定","/amazon-q2-2026-capex-payback-factcheck","2026-07-31T00:00:00.000Z",{"title":1874,"path":1875,"publishedAt":1872},"クラウド3社の設備投資とフリーキャッシュフロー｜AWS・Azure・Google Cloudの数字を検証し、黒字に戻る年を試算した","/cloud-capex-fcf-breakeven-2026",{"title":1877,"path":1878,"publishedAt":1879},"Claude Code ステータスラインにFable 5のモデル別リミット表示を追加した","/claude-code-statusline-fable5-model-limit","2026-07-20T00:00:00.000Z","https://log.eurekapu.com/og/blog/amazon-price-watcher-spec.png?v=2025-12-06T00%3A00%3A00.000Z&title=Amazon%20%E4%BE%A1%E6%A0%BC%E7%9B%A3%E8%A6%96%E3%83%84%E3%83%BC%E3%83%AB%E8%A8%AD%E8%A8%88%E6%9B%B8%EF%BC%88PA-API%20%2B%20GitHub%20Actions%20%2B%20CSV%EF%BC%89&author=Kei%20Komatsu&sig=fb53c3f332720224",1785654672635]