<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>万邦api博客</title><link>https://www.shopapi.com.cn/</link><description>Good Luck To You!</description><item><title>Open‑Claw 实战｜无需逆向，快速搭建电商商品监控与数据分析系统</title><link>https://www.shopapi.com.cn/?id=228</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营、选品、比价，很多时候我们需要持续盯紧商品的售价、库存、规格、店铺信息。手动复制粘贴做表格效率极低，大促期间价格频繁变动，人工几乎跟不上节奏。&lt;/p&gt;&lt;p&gt;很多同学尝试直接写网页解析，却遇到各种反爬、动态渲染、JS 加密，调试几天拿不到完整的 SKU 规格、原价、发货地等信息。今天分享一套落地思路，借助 Open‑Claw 工具能力，绕开逆向难题，快速实现商品的持续监控，把采集到的数据做清洗统计，完成从数据获取到分析的完整流程。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;适合人群：电商运营、数据分析爱好者、Python 开发人员；环境要求 Python3.7+，会基础 Python 语法即可。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;一、业务目标梳理&lt;/h2&gt;&lt;p&gt;我们想要达成三件事：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;5&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E5%8D%95%E5%95%86%E5%93%81%E5%AE%8C%E6%95%B4%E4%BF%A1%E6%81%AF%E8%8E%B7%E5%8F%96%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%2228T2Bs-1788343000420%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;单商品完整信息获取&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;单商品完整信息获取&quot;&gt;单商品完整信息获取&lt;/a&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/strong&gt;：拿到标题、当前售价、划线原价、库存、多规格 SKU、店铺名称、商品主图、发货地等全量资料。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;定时持续监控&lt;/strong&gt;：循环巡检，记录每一次价格、库存快照，捕捉调价、缺货、规格变更。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;简单数据分析&lt;/strong&gt;：保存历史记录，做价格波动统计、规格盘点，发现异常自动打印告警。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;不做浏览器模拟、不处理复杂 JS 解密，直接拿到结构化 JSON 结果，减少大量排错成本。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;二、环境准备&lt;/h2&gt;&lt;p&gt;只需要安装网络请求库，用于发起网络访问，解析返回结果，同时引入时间处理、csv 存储模块用于持久化监控记录。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;三、完整实战代码&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;说明：下面代码中&lt;code&gt;your_key&lt;/code&gt;、&lt;code&gt;your_secret&lt;/code&gt;替换成你自己的凭证，&lt;code&gt;product_id&lt;/code&gt;替换成目标商品编号。 逻辑分为三块：获取商品原始数据、解析提取业务字段、定时循环监控、写入本地 CSV 留存历史，异常捕获保证程序不会单次失败直接崩溃。&lt;/p&gt;&lt;/blockquote&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20coding%3Autf-8%5Cnimport%20requests%5Cnimport%20time%5Cnimport%20csv%5Cnfrom%20datetime%20import%20datetime%5Cn%5Cnclass%20ProductMonitor%3A%5Cn%20%20%20%20def%20__init__(self%2C%20key%2C%20secret)%3A%5Cn%20%20%20%20%20%20%20%20self.key%20%3D%20key%5Cn%20%20%20%20%20%20%20%20self.secret%20%3D%20secret%5Cn%20%20%20%20%20%20%20%20self.base_url%20%3D%20%5C%22https%3A%2F%2Fapi%E2%80%91gw.onebound.cn%2Fjd%2Fitem_get_pro%2F%5C%22%5Cn%20%20%20%20%20%20%20%20self.csv_file%20%3D%20%5C%22product_monitor_record.csv%5C%22%5Cn%20%20%20%20%20%20%20%20self.init_csv()%5Cn%5Cn%20%20%20%20def%20init_csv(self)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E5%88%9D%E5%A7%8B%E5%8C%96%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8%E6%96%87%E4%BB%B6%EF%BC%8C%E4%B8%8D%E5%AD%98%E5%9C%A8%E5%88%99%E5%86%99%E5%85%A5%E8%A1%A8%E5%A4%B4%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20header%20%3D%20%5B%5C%22crawl_time%5C%22%2C%20%5C%22product_id%5C%22%2C%20%5C%22title%5C%22%2C%20%5C%22current_price%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22original_price%5C%22%2C%20%5C%22stock%5C%22%2C%20%5C%22shop_name%5C%22%2C%20%5C%22sku_count%5C%22%5D%5Cn%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20with%20open(self.csv_file%2C%20%5C%22r%5C%22%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%20as%20f%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pass%5Cn%20%20%20%20%20%20%20%20except%20FileNotFoundError%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20with%20open(self.csv_file%2C%20%5C%22w%5C%22%2C%20newline%3D%5C%22%5C%22%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%20as%20f%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20writer%20%3D%20csv.writer(f)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20writer.writerow(header)%5Cn%5Cn%20%20%20%20def%20fetch_product_raw(self%2C%20product_id)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%E5%8E%9F%E5%A7%8B%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20self.key%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20self.secret%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20product_id%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%20%20%20%23%20%E5%85%B3%E9%97%AD%E7%BC%93%E5%AD%98%EF%BC%8C%E8%8E%B7%E5%8F%96%E6%9C%80%E6%96%B0%E5%BF%AB%E7%85%A7%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20headers%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22Accept%E2%80%91Encoding%5C%22%3A%20%5C%22gzip%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22Connection%5C%22%3A%20%5C%22close%5C%22%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(self.base_url%2C%20params%3Dparams%2C%20headers%3Dheaders%2C%20timeout%3D20)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20resp.json()%5Cn%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E5%BC%82%E5%B8%B8%EF%BC%9A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20def%20parse_product_data(self%2C%20raw_json)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E4%BB%8E%E5%8E%9F%E5%A7%8B%E8%BF%94%E5%9B%9E%E7%BB%93%E6%9E%9C%E8%A7%A3%E6%9E%90%E4%B8%9A%E5%8A%A1%E9%9C%80%E8%A6%81%E5%AD%97%E6%AE%B5%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20if%20not%20raw_json%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20error_code%20%3D%20raw_json.get(%5C%22error_code%5C%22)%5Cn%20%20%20%20%20%20%20%20if%20error_code%20!%3D%20%5C%220000%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20err_msg%20%3D%20raw_json.get(%5C%22error%5C%22%2C%20%5C%22%E6%9C%AA%E7%9F%A5%E9%94%99%E8%AF%AF%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E6%95%B0%E6%8D%AE%E8%8E%B7%E5%8F%96%E5%A4%B1%E8%B4%A5%EF%BC%8C%E9%94%99%E8%AF%AF%E4%BF%A1%E6%81%AF%EF%BC%9A%7Berr_msg%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20item%20%3D%20raw_json.get(%5C%22item%5C%22%2C%20%7B%7D)%5Cn%20%20%20%20%20%20%20%20if%20not%20item%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(%5C%22%E6%9C%AA%E5%8C%B9%E9%85%8D%E5%88%B0%E5%95%86%E5%93%81%E4%B8%BB%E4%BD%93%E4%BF%A1%E6%81%AF%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20%20%20%20%20sku_list%20%3D%20item.get(%5C%22skus%5C%22%2C%20%7B%7D).get(%5C%22sku%5C%22%2C%20%5B%5D)%5Cn%20%20%20%20%20%20%20%20parse_data%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22crawl_time%5C%22%3A%20datetime.now().strftime(%5C%22%25Y%E2%80%91%25m%E2%80%91%25d%20%25H%3A%25M%3A%25S%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22product_id%5C%22%3A%20item.get(%5C%22num_iid%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20item.get(%5C%22title%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22current_price%5C%22%3A%20float(item.get(%5C%22price%5C%22%2C%200))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22original_price%5C%22%3A%20float(item.get(%5C%22orginal_price%5C%22%2C%200))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3A%20item.get(%5C%22num%5C%22%2C%200)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20item.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22sku_count%5C%22%3A%20len(sku_list)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22skus_detail%5C%22%3A%20sku_list%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20return%20parse_data%5Cn%5Cn%20%20%20%20def%20save_record(self%2C%20data)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E6%8A%8A%E5%8D%95%E6%AC%A1%E7%9B%91%E6%8E%A7%E5%BF%AB%E7%85%A7%E5%86%99%E5%85%A5CSV%E5%81%9A%E5%8E%86%E5%8F%B2%E7%95%99%E5%AD%98%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20row%20%3D%20%5B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%5B%5C%22crawl_time%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%5B%5C%22product_id%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%5B%5C%22title%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%5B%5C%22current_price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%5B%5C%22original_price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%5B%5C%22stock%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%5B%5C%22shop_name%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%5B%5C%22sku_count%5C%22%5D%5Cn%20%20%20%20%20%20%20%20%5D%5Cn%20%20%20%20%20%20%20%20with%20open(self.csv_file%2C%20%5C%22a%5C%22%2C%20newline%3D%5C%22%5C%22%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%20as%20f%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20writer%20%3D%20csv.writer(f)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20writer.writerow(row)%5Cn%5Cn%20%20%20%20def%20alert_check(self%2C%20data%2C%20warn_price_threshold)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E7%AE%80%E5%8D%95%E5%91%8A%E8%AD%A6%E9%80%BB%E8%BE%91%EF%BC%9A%E4%BB%B7%E6%A0%BC%E4%BD%8E%E4%BA%8E%E9%98%88%E5%80%BC%E3%80%81%E5%BA%93%E5%AD%98%E4%B8%BA0%E8%A7%A6%E5%8F%91%E6%8F%90%E9%86%92%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20if%20data%5B%5C%22current_price%5C%22%5D%20%3C%3D%20warn_price_threshold%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(%5C%22%E2%80%91%5C%22%20*%2060)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E2%9A%A0%EF%B8%8F%20%E4%BB%B7%E6%A0%BC%E9%A2%84%E8%AD%A6%EF%BC%81%E5%95%86%E5%93%81%EF%BC%9A%7Bdata%5B&amp;#39;title&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%BD%93%E5%89%8D%E5%94%AE%E4%BB%B7%20%7Bdata%5B&amp;#39;current_price&amp;#39;%5D%7D%EF%BC%8C%E4%BD%8E%E4%BA%8E%E8%AE%BE%E5%AE%9A%E9%98%88%E5%80%BC%20%7Bwarn_price_threshold%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(%5C%22%E2%80%91%5C%22%20*%2060)%5Cn%20%20%20%20%20%20%20%20if%20data%5B%5C%22stock%5C%22%5D%20%3C%3D%200%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(%5C%22%E2%80%91%5C%22%20*%2060)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%F0%9F%94%B4%20%E5%BA%93%E5%AD%98%E5%91%8A%E8%AD%A6%EF%BC%81%E5%95%86%E5%93%81%20%7Bdata%5B&amp;#39;title&amp;#39;%5D%7D%20%E5%BD%93%E5%89%8D%E5%B7%B2%E7%BB%8F%E6%97%A0%E8%B4%A7%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(%5C%22%E2%80%91%5C%22%20*%2060)%5Cn%5Cn%20%20%20%20def%20single_monitor_task(self%2C%20product_id%2C%20warn_price)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E6%89%A7%E8%A1%8C%E4%B8%80%E6%AC%A1%E5%AE%8C%E6%95%B4%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%EF%BC%9A%E6%8B%89%E5%8F%96%E2%80%91%E8%A7%A3%E6%9E%90%E2%80%91%E5%AD%98%E5%82%A8%E2%80%91%E5%91%8A%E8%AD%A6%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20raw%20%3D%20self.fetch_product_raw(product_id)%5Cn%20%20%20%20%20%20%20%20parsed%20%3D%20self.parse_product_data(raw)%5Cn%20%20%20%20%20%20%20%20if%20parsed%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20self.save_record(parsed)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20self.alert_check(parsed%2C%20warn_price)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5B%7Bparsed%5B&amp;#39;crawl_time&amp;#39;%5D%7D%5D%20%E7%9B%91%E6%8E%A7%E5%AE%8C%E6%88%90%EF%BD%9C%7Bparsed%5B&amp;#39;title&amp;#39;%5D%5B%3A35%5D%7D...%EF%BD%9C%E7%8E%B0%E4%BB%B7%3A%7Bparsed%5B&amp;#39;current_price&amp;#39;%5D%7D%EF%BD%9C%E5%BA%93%E5%AD%98%3A%7Bparsed%5B&amp;#39;stock&amp;#39;%5D%7D%EF%BD%9C%E8%A7%84%E6%A0%BC%E6%95%B0%3A%7Bparsed%5B&amp;#39;sku_count&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20parsed%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%20%E9%85%8D%E7%BD%AE%E5%8C%BA%E5%9F%9F%EF%BC%8C%E8%AF%B7%E4%BF%AE%E6%94%B9%E4%B8%BA%E4%BD%A0%E7%9A%84%E4%BF%A1%E6%81%AF%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5Cn%20%20%20%20MY_KEY%20%3D%20%5C%22your_key%5C%22%5Cn%20%20%20%20MY_SECRET%20%3D%20%5C%22your_secret%5C%22%5Cn%20%20%20%20MONITOR_PRODUCT_ID%20%3D%20%5C%2210335871600%5C%22%20%20%23%20%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%5Cn%20%20%20%20PRICE_WARN_THRESHOLD%20%3D%20120%20%20%23%20%E4%BD%8E%E4%BA%8E%E8%AF%A5%E4%BB%B7%E6%A0%BC%E8%A7%A6%E5%8F%91%E6%8F%90%E9%86%92%5Cn%20%20%20%20MONITOR_INTERVAL%20%3D%2060%20*%2030%20%20%23%20%E7%9B%91%E6%8E%A7%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E7%A4%BA%E4%BE%8B30%E5%88%86%E9%92%9F%E5%B7%A1%E6%A3%80%E4%B8%80%E6%AC%A1%5Cn%20%20%20%20%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5Cn%5Cn%20%20%20%20monitor%20%3D%20ProductMonitor(MY_KEY%2C%20MY_SECRET)%5Cn%20%20%20%20print(%5C%22%3D%3D%3D%3D%3D%20%E5%95%86%E5%93%81%E7%9B%91%E6%8E%A7%E7%A8%8B%E5%BA%8F%E5%B7%B2%E5%90%AF%E5%8A%A8%20%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E7%9B%91%E6%8E%A7%E5%95%86%E5%93%81ID%3A%7BMONITOR_PRODUCT_ID%7D%EF%BC%8C%E4%BB%B7%E6%A0%BC%E9%A2%84%E8%AD%A6%E9%98%88%E5%80%BC%3A%7BPRICE_WARN_THRESHOLD%7D%EF%BC%8C%E8%BD%AE%E8%AF%A2%E9%97%B4%E9%9A%94%3A%7BMONITOR_INTERVAL%7D%E7%A7%92%5C%22)%5Cn%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20monitor.single_monitor_task(MONITOR_PRODUCT_ID%2C%20PRICE_WARN_THRESHOLD)%5Cn%20%20%20%20%20%20%20%20time.sleep(MONITOR_INTERVAL)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;coding:utf-8
import&amp;nbsp;requests
import&amp;nbsp;time
import&amp;nbsp;csv
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime

class&amp;nbsp;ProductMonitor:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__init__(self,&amp;nbsp;key,&amp;nbsp;secret):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.key&amp;nbsp;=&amp;nbsp;key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.secret&amp;nbsp;=&amp;nbsp;secret
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.base_url&amp;nbsp;=&amp;nbsp;&amp;quot;https://api‑gw.onebound.cn/jd/item_get_pro/&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.csv_file&amp;nbsp;=&amp;nbsp;&amp;quot;product_monitor_record.csv&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.init_csv()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;init_csv(self):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;初始化本地存储文件，不存在则写入表头&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;header&amp;nbsp;=&amp;nbsp;[&amp;quot;crawl_time&amp;quot;,&amp;nbsp;&amp;quot;product_id&amp;quot;,&amp;nbsp;&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;current_price&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;original_price&amp;quot;,&amp;nbsp;&amp;quot;stock&amp;quot;,&amp;nbsp;&amp;quot;shop_name&amp;quot;,&amp;nbsp;&amp;quot;sku_count&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(self.csv_file,&amp;nbsp;&amp;quot;r&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;FileNotFoundError:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(self.csv_file,&amp;nbsp;&amp;quot;w&amp;quot;,&amp;nbsp;newline=&amp;quot;&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer&amp;nbsp;=&amp;nbsp;csv.writer(f)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.writerow(header)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;fetch_product_raw(self,&amp;nbsp;product_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;获取商品原始结构化数据&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;self.key,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;self.secret,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;product_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;关闭缓存，获取最新快照
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Accept‑Encoding&amp;quot;:&amp;nbsp;&amp;quot;gzip&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Connection&amp;quot;:&amp;nbsp;&amp;quot;close&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(self.base_url,&amp;nbsp;params=params,&amp;nbsp;headers=headers,&amp;nbsp;timeout=20)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;网络请求异常：{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;parse_product_data(self,&amp;nbsp;raw_json):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;从原始返回结果解析业务需要字段&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;raw_json:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;error_code&amp;nbsp;=&amp;nbsp;raw_json.get(&amp;quot;error_code&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;error_code&amp;nbsp;!=&amp;nbsp;&amp;quot;0000&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;err_msg&amp;nbsp;=&amp;nbsp;raw_json.get(&amp;quot;error&amp;quot;,&amp;nbsp;&amp;quot;未知错误&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;数据获取失败，错误信息：{err_msg}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item&amp;nbsp;=&amp;nbsp;raw_json.get(&amp;quot;item&amp;quot;,&amp;nbsp;{})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;item:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;未匹配到商品主体信息&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_list&amp;nbsp;=&amp;nbsp;item.get(&amp;quot;skus&amp;quot;,&amp;nbsp;{}).get(&amp;quot;sku&amp;quot;,&amp;nbsp;[])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parse_data&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;crawl_time&amp;quot;:&amp;nbsp;datetime.now().strftime(&amp;quot;%Y‑%m‑%d&amp;nbsp;%H:%M:%S&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;product_id&amp;quot;:&amp;nbsp;item.get(&amp;quot;num_iid&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;item.get(&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;current_price&amp;quot;:&amp;nbsp;float(item.get(&amp;quot;price&amp;quot;,&amp;nbsp;0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;original_price&amp;quot;:&amp;nbsp;float(item.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;item.get(&amp;quot;num&amp;quot;,&amp;nbsp;0),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;item.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;sku_count&amp;quot;:&amp;nbsp;len(sku_list),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;skus_detail&amp;quot;:&amp;nbsp;sku_list
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;parse_data

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;save_record(self,&amp;nbsp;data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;把单次监控快照写入CSV做历史留存&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;row&amp;nbsp;=&amp;nbsp;[
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;crawl_time&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;product_id&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;title&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;current_price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;original_price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;stock&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;shop_name&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;sku_count&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(self.csv_file,&amp;nbsp;&amp;quot;a&amp;quot;,&amp;nbsp;newline=&amp;quot;&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer&amp;nbsp;=&amp;nbsp;csv.writer(f)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;writer.writerow(row)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;alert_check(self,&amp;nbsp;data,&amp;nbsp;warn_price_threshold):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;简单告警逻辑：价格低于阈值、库存为0触发提醒&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;data[&amp;quot;current_price&amp;quot;]&amp;nbsp;&amp;lt;=&amp;nbsp;warn_price_threshold:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;‑&amp;quot;&amp;nbsp;*&amp;nbsp;60)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;⚠️&amp;nbsp;价格预警！商品：{data[&amp;#39;title&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;当前售价&amp;nbsp;{data[&amp;#39;current_price&amp;#39;]}，低于设定阈值&amp;nbsp;{warn_price_threshold}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;‑&amp;quot;&amp;nbsp;*&amp;nbsp;60)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;data[&amp;quot;stock&amp;quot;]&amp;nbsp;&amp;lt;=&amp;nbsp;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;‑&amp;quot;&amp;nbsp;*&amp;nbsp;60)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;🔴&amp;nbsp;库存告警！商品&amp;nbsp;{data[&amp;#39;title&amp;#39;]}&amp;nbsp;当前已经无货&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;‑&amp;quot;&amp;nbsp;*&amp;nbsp;60)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;single_monitor_task(self,&amp;nbsp;product_id,&amp;nbsp;warn_price):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;执行一次完整监控任务：拉取‑解析‑存储‑告警&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raw&amp;nbsp;=&amp;nbsp;self.fetch_product_raw(product_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parsed&amp;nbsp;=&amp;nbsp;self.parse_product_data(raw)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;parsed:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.save_record(parsed)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.alert_check(parsed,&amp;nbsp;warn_price)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;[{parsed[&amp;#39;crawl_time&amp;#39;]}]&amp;nbsp;监控完成｜{parsed[&amp;#39;title&amp;#39;][:35]}...｜现价:{parsed[&amp;#39;current_price&amp;#39;]}｜库存:{parsed[&amp;#39;stock&amp;#39;]}｜规格数:{parsed[&amp;#39;sku_count&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;parsed

if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;==========&amp;nbsp;配置区域，请修改为你的信息&amp;nbsp;==========
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MY_KEY&amp;nbsp;=&amp;nbsp;&amp;quot;your_key&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MY_SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;your_secret&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MONITOR_PRODUCT_ID&amp;nbsp;=&amp;nbsp;&amp;quot;10335871600&amp;quot;&amp;nbsp;&amp;nbsp;#&amp;nbsp;商品编号
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;PRICE_WARN_THRESHOLD&amp;nbsp;=&amp;nbsp;120&amp;nbsp;&amp;nbsp;#&amp;nbsp;低于该价格触发提醒
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;MONITOR_INTERVAL&amp;nbsp;=&amp;nbsp;60&amp;nbsp;*&amp;nbsp;30&amp;nbsp;&amp;nbsp;#&amp;nbsp;监控间隔，单位秒，示例30分钟巡检一次
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;=============================================

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor&amp;nbsp;=&amp;nbsp;ProductMonitor(MY_KEY,&amp;nbsp;MY_SECRET)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;=====&amp;nbsp;商品监控程序已启动&amp;nbsp;=====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;监控商品ID:{MONITOR_PRODUCT_ID}，价格预警阈值:{PRICE_WARN_THRESHOLD}，轮询间隔:{MONITOR_INTERVAL}秒&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor.single_monitor_task(MONITOR_PRODUCT_ID,&amp;nbsp;PRICE_WARN_THRESHOLD)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(MONITOR_INTERVAL)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;四、代码功能说明&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;持久化存储&lt;/strong&gt;：每次巡检把时间戳、价格、库存、店铺信息写入&lt;code&gt;product_monitor_record.csv&lt;/code&gt;，Excel 可以直接打开查看历史轨迹。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;异常容错&lt;/strong&gt;：网络波动、商品下架会打印错误信息，不会直接让整个程序崩溃。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;双重告警&lt;/strong&gt;：价格低于预设阈值、库存归零，控制台输出醒目的提醒。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;SKU 完整获取&lt;/strong&gt;：返回结果中自带全部规格列表，可以扩展做规格级别的监控。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;注意：轮询间隔不要设置过小，大促场景建议至少 10 分钟以上，避免高频访问造成限制。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;五、拿到历史数据之后，怎么做数据分析&lt;/h2&gt;&lt;p&gt;采集得到 CSV 历史记录文件之后，我们可以用 Pandas 做简单的统计分析，下面补充一小段数据分析脚本：&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22import%20pandas%20as%20pd%5Cn%5Cndef%20analysis_history()%3A%5Cn%20%20%20%20df%20%3D%20pd.read_csv(%5C%22product_monitor_record.csv%5C%22%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%20%20%20%20print(%5C%22%3D%3D%3D%3D%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E6%A6%82%E8%A7%88%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E6%80%BB%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%E6%95%B0%EF%BC%9A%7Blen(df)%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E4%BB%B7%E6%A0%BC%E6%9C%80%E5%A4%A7%E5%80%BC%EF%BC%9A%7Bdf%5B&amp;#39;current_price&amp;#39;%5D.max()%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E4%BB%B7%E6%A0%BC%E6%9C%80%E5%B0%8F%E5%80%BC%EF%BC%9A%7Bdf%5B&amp;#39;current_price&amp;#39;%5D.min()%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%B9%B3%E5%9D%87%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bround(df%5B&amp;#39;current_price&amp;#39;%5D.mean()%2C2)%7D%5C%22)%5Cn%20%20%20%20print(%5C%22%5C%5Cn%E6%9C%80%E8%BF%9110%E6%9D%A1%E5%BF%AB%E7%85%A7%EF%BC%9A%5C%22)%5Cn%20%20%20%20print(df.tail(10)%5B%5B%5C%22crawl_time%5C%22%2C%5C%22current_price%5C%22%2C%5C%22stock%5C%22%5D%5D)%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20analysis_history()%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd

def&amp;nbsp;analysis_history():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.read_csv(&amp;quot;product_monitor_record.csv&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;====历史数据概览====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;总采集记录数：{len(df)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;价格最大值：{df[&amp;#39;current_price&amp;#39;].max()}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;价格最小值：{df[&amp;#39;current_price&amp;#39;].min()}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;平均售价：{round(df[&amp;#39;current_price&amp;#39;].mean(),2)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n最近10条快照：&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(df.tail(10)[[&amp;quot;crawl_time&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;stock&amp;quot;]])

if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis_history()&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;执行后可以直接看到价格最高、最低、均值，快速判断商品的价格波动区间，辅助我们做采购、选品、竞品对标。如果需要更进一步，可以接入 Matplotlib 画出价格时序走势图。&lt;/p&gt;&lt;h2&gt;六、常见踩坑点与优化方向&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;缓存干扰&lt;/strong&gt;：代码中设置&lt;code&gt;cache=no&lt;/code&gt;，保证拿到实时最新快照；不关闭缓存会返回历史旧数据，监控就失去意义。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;商品下架情况&lt;/strong&gt;：当商品已经删除、下架，返回结果会提示找不到商品，业务上可以增加逻辑，一旦连续多次返回不存在，记录到日志，停止监控。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;多商品批量监控&lt;/strong&gt;：可以维护一个商品 ID 列表，循环遍历列表，实现批量多款商品同时巡检。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;告警渠道扩展&lt;/strong&gt;：目前只是控制台打印，可扩展对接企业微信、邮件推送，做到无人值守通知。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;规格粒度监控&lt;/strong&gt;：当前示例取的商品主体价格，业务需要可以遍历&lt;code&gt;skus_detail&lt;/code&gt;列表，针对每一个子规格单独记录价格库存。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;七、小结&lt;/h2&gt;&lt;p&gt;借助 Open‑Claw，我们不需要耗费大量精力处理网页逆向、JS 解密、验证码等麻烦工作，就能够稳定拿到完整结构化的商品信息。&lt;/p&gt;&lt;p&gt;整套流程分为：获取结构化原始数据 → 清洗解析业务字段 → 定时循环采集、落地存储快照 → 统计分析历史数据。对于商家、个人比价选品都非常实用。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Wed, 02 Sep 2026 17:58:47 +0800</pubDate></item><item><title>OpenClaw 实战｜从零搭建电商商品监控与数据分析系统（Python 完整可运行）</title><link>https://www.shopapi.com.cn/?id=227</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营、比价囤货、竞品调研的时候，我们经常有这些痛点：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;需要持续盯多款商品的售价、原价变动，大促期间手动刷页面效率极低；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;多规格款式的库存、价格分散，很难统一汇总对比；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;想要留存历史数据，分析价格波动规律，找出合适入手时机；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;普通网页抓取容易遇到反访问、页面结构改版，脚本频繁失效。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;最近体验了 OpenClaw 这套工具套件，可以稳定获取商品完整资料，包含标题、品牌、售价、吊牌原价、多规格 SKU、库存、图片、店铺信息等全套结构化内容。不用折腾浏览器模拟、不用写复杂正则解析页面，把精力聚焦在&lt;strong&gt;监控逻辑、数据存储、数据分析&lt;/strong&gt;业务逻辑上。&lt;/p&gt;&lt;p&gt;本文手把手带大家实现一套完整方案：商品定时采集、变动告警、本地持久化存储、历史价格趋势分析，全部代码可以直接复制运行。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;环境依赖：Python3.8+，&lt;code&gt;requests&lt;/code&gt;、&lt;code&gt;pandas&lt;/code&gt;、&lt;code&gt;matplotlib&lt;/code&gt;、&lt;code&gt;sqlite3&lt;/code&gt;(内置无需安装)&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;整体方案设计&lt;/h2&gt;&lt;p&gt;整套程序分为 4 个模块：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;4&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E6%95%B0%E6%8D%AE%E8%8E%B7%E5%8F%96%E6%A8%A1%E5%9D%97%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%22Yam4Wc-1788169473858%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;数据获取模块&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;数据获取模块&quot;&gt;数据获取模块&lt;/a&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/strong&gt;：借助工具拿到标准化商品原始资料；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;持久化存储模块&lt;/strong&gt;：使用轻量 SQLite 保存每一次采集记录，留存历史快照；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;监控告警模块&lt;/strong&gt;：定时循环采集，对比上一轮数据，价格 / 库存发生变化输出提醒；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据分析模块&lt;/strong&gt;：读取本地历史记录，输出统计指标，绘制价格变化走势图。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;支持能力： ✅ 获取商品标题、品牌、当前售价、原价、库存、多规格 SKU 明细 ✅ 定时循环监控，识别价格下调、库存变动 ✅ 本地数据库保存全部历史采集快照 ✅ 统计最高价、最低价、平均价格 ✅ 生成可视化价格趋势图表 ✅ 支持批量监控多款商品&lt;/p&gt;&lt;h2&gt;完整代码实现&lt;/h2&gt;&lt;h3&gt;1. 基础工具类：数据获取与数据库初始化&lt;/h3&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22import%20requests%5Cnimport%20json%5Cnimport%20sqlite3%5Cnimport%20time%5Cnimport%20pandas%20as%20pd%5Cnimport%20matplotlib.pyplot%20as%20plt%5Cnfrom%20datetime%20import%20datetime%5Cn%5Cnplt.rcParams%5B%5C%22font.sans-serif%5C%22%5D%20%3D%20%5B%5C%22SimHei%5C%22%5D%5Cnplt.rcParams%5B%5C%22axes.unicode_minus%5C%22%5D%20%3D%20False%5Cn%5Cn%23%20--------------------------%E9%85%8D%E7%BD%AE%E5%8C%BA--------------------------%5Cn%23%20%E4%B8%AA%E4%BA%BA%E5%87%AD%E8%AF%81%EF%BC%8C%E5%BC%80%E9%80%9A%E5%B7%A5%E5%85%B7%E6%9C%8D%E5%8A%A1%E5%90%8E%E8%8E%B7%E5%8F%96%5CnACCESS_KEY%20%3D%20%5C%22%E6%9B%BF%E6%8D%A2%E6%88%90%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81key%5C%22%5CnACCESS_SECRET%20%3D%20%5C%22%E6%9B%BF%E6%8D%A2%E6%88%90%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81secret%5C%22%5Cn%23%20%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81ID%E5%88%97%E8%A1%A8%5CnMONITOR_ITEM_IDS%20%3D%20%5B%5C%2210335871600%5C%22%2C%20%5C%2210057467958584%5C%22%5D%5Cn%23%20%E9%87%87%E9%9B%86%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E7%A4%BA%E4%BE%8B5%E5%88%86%E9%92%9F%E9%87%87%E9%9B%86%E4%B8%80%E6%AC%A1%5CnMONITOR_INTERVAL%20%3D%20300%5Cn%23%20%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%BA%93%E6%96%87%E4%BB%B6%E5%90%8D%5CnDB_FILE%20%3D%20%5C%22goods_monitor.db%5C%22%5Cn%23%20-----------------------------------------------------------%5Cn%5Cn%5Cndef%20init_db()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%88%9D%E5%A7%8B%E5%8C%96%E6%95%B0%E6%8D%AE%E5%BA%93%EF%BC%8C%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E8%A1%A8%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_FILE)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20%23%20%E5%95%86%E5%93%81%E5%9F%BA%E7%A1%80%E4%BF%A1%E6%81%AF%E8%A1%A8%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20CREATE%20TABLE%20IF%20NOT%20EXISTS%20goods_record%20(%5Cn%20%20%20%20%20%20%20%20id%20INTEGER%20PRIMARY%20KEY%20AUTOINCREMENT%2C%5Cn%20%20%20%20%20%20%20%20num_iid%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20title%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20brand%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20orginal_price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20stock%20INTEGER%2C%5Cn%20%20%20%20%20%20%20%20shop_name%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20collect_time%20TIMESTAMP%5Cn%20%20%20%20)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;)%5Cn%20%20%20%20%23%20sku%E8%A7%84%E6%A0%BC%E6%98%8E%E7%BB%86%E8%A1%A8%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20CREATE%20TABLE%20IF%20NOT%20EXISTS%20sku_record%20(%5Cn%20%20%20%20%20%20%20%20id%20INTEGER%20PRIMARY%20KEY%20AUTOINCREMENT%2C%5Cn%20%20%20%20%20%20%20%20num_iid%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20sku_id%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20sku_name%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20orginal_price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20quantity%20INTEGER%2C%5Cn%20%20%20%20%20%20%20%20collect_time%20TIMESTAMP%5Cn%20%20%20%20)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;)%5Cn%20%20%20%20conn.commit()%5Cn%20%20%20%20conn.close()%5Cn%5Cn%5Cndef%20fetch_goods_detail(item_id)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%E8%AF%A6%E6%83%85%E5%8E%9F%E5%A7%8B%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE%5Cn%20%20%20%20%3Aparam%20item_id%3A%20%E5%95%86%E5%93%81ID%5Cn%20%20%20%20%3Areturn%3A%20dict%E5%95%86%E5%93%81%E6%95%B0%E6%8D%AE%EF%BC%8C%E5%A4%B1%E8%B4%A5%E8%BF%94%E5%9B%9ENone%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20base_url%20%3D%20%5C%22https%3A%2F%2Fapi-gw.onebound.cn%2Fjd%2Fitem_get_pro%2F%5C%22%5Cn%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20ACCESS_KEY%2C%5Cn%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20ACCESS_SECRET%2C%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20item_id%2C%5Cn%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20headers%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22Accept-Encoding%5C%22%3A%20%5C%22gzip%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22Connection%5C%22%3A%20%5C%22close%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(base_url%2C%20params%3Dparams%2C%20headers%3Dheaders%2C%20timeout%3D15)%5Cn%20%20%20%20%20%20%20%20res_json%20%3D%20resp.json()%5Cn%20%20%20%20%20%20%20%20if%20res_json.get(%5C%22error_code%5C%22)%20!%3D%20%5C%220000%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E8%8E%B7%E5%8F%96%E5%A4%B1%E8%B4%A5%E3%80%91%E5%95%86%E5%93%81%3A%7Bitem_id%7D%2C%20%E5%8E%9F%E5%9B%A0%3A%7Bres_json.get(&amp;#39;reason&amp;#39;%2C&amp;#39;%E6%9C%AA%E7%9F%A5%E9%94%99%E8%AF%AF&amp;#39;)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20item_data%20%3D%20res_json.get(%5C%22item%5C%22)%5Cn%20%20%20%20%20%20%20%20if%20not%20item_data%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E8%AD%A6%E5%91%8A%E3%80%91%E5%95%86%E5%93%81%7Bitem_id%7D%E8%BF%94%E5%9B%9E%E6%95%B0%E6%8D%AE%E4%B8%BA%E7%A9%BA%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20return%20item_data%5Cn%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E7%BD%91%E7%BB%9C%E5%BC%82%E5%B8%B8%E3%80%91%7Bitem_id%7D%20%2C%20%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%5Cndef%20save_data_to_db(item_data)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%B0%86%E9%87%87%E9%9B%86%E5%88%B0%E7%9A%84%E5%95%86%E5%93%81%E4%BB%A5%E5%8F%8Asku%E6%95%B0%E6%8D%AE%E5%AD%98%E5%85%A5sqlite%E6%95%B0%E6%8D%AE%E5%BA%93%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_FILE)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20now%20%3D%20datetime.now().strftime(%5C%22%25Y-%25m-%25d%20%25H%3A%25M%3A%25S%5C%22)%5Cn%5Cn%20%20%20%20num_iid%20%3D%20item_data.get(%5C%22num_iid%5C%22)%5Cn%20%20%20%20title%20%3D%20item_data.get(%5C%22title%5C%22%2C%20%5C%22%5C%22)%5Cn%20%20%20%20brand%20%3D%20item_data.get(%5C%22brand%5C%22%2C%20%5C%22%5C%22)%5Cn%20%20%20%20price%20%3D%20float(item_data.get(%5C%22price%5C%22%2C%200))%20if%20item_data.get(%5C%22price%5C%22)%20else%200%5Cn%20%20%20%20orginal_price%20%3D%20float(item_data.get(%5C%22orginal_price%5C%22%2C%200))%20if%20item_data.get(%5C%22orginal_price%5C%22)%20else%200%5Cn%20%20%20%20stock%20%3D%20item_data.get(%5C%22num%5C%22%2C%200)%5Cn%20%20%20%20shop_name%20%3D%20item_data.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22%2C%20%5C%22%5C%22)%5Cn%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20INSERT%20INTO%20goods_record(num_iid%2Ctitle%2Cbrand%2Cprice%2Corginal_price%2Cstock%2Cshop_name%2Ccollect_time)%5Cn%20%20%20%20VALUES%20(%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C%20(num_iid%2C%20title%2C%20brand%2C%20price%2C%20orginal_price%2C%20stock%2C%20shop_name%2C%20now))%5Cn%5Cn%20%20%20%20%23%20%E4%BF%9D%E5%AD%98SKU%E5%A4%9A%E8%A7%84%E6%A0%BC%E6%95%B0%E6%8D%AE%5Cn%20%20%20%20sku_list%20%3D%20item_data.get(%5C%22skus%5C%22%2C%20%7B%7D).get(%5C%22sku%5C%22%2C%20%5B%5D)%5Cn%20%20%20%20for%20sku%20in%20sku_list%3A%5Cn%20%20%20%20%20%20%20%20sku_id%20%3D%20sku.get(%5C%22sku_id%5C%22%2C%20%5C%22%5C%22)%5Cn%20%20%20%20%20%20%20%20sku_name%20%3D%20sku.get(%5C%22properties_name%5C%22%2C%20%5C%22%5C%22)%5Cn%20%20%20%20%20%20%20%20sku_price%20%3D%20float(sku.get(%5C%22price%5C%22%2C%200))%20if%20sku.get(%5C%22price%5C%22)%20else%200%5Cn%20%20%20%20%20%20%20%20sku_origin_price%20%3D%20float(sku.get(%5C%22orginal_price%5C%22%2C%200))%20if%20sku.get(%5C%22orginal_price%5C%22)%20else%200%5Cn%20%20%20%20%20%20%20%20sku_qty%20%3D%20sku.get(%5C%22quantity%5C%22%2C%200)%5Cn%20%20%20%20%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20%20%20%20%20INSERT%20INTO%20sku_record(num_iid%2Csku_id%2Csku_name%2Cprice%2Corginal_price%2Cquantity%2Ccollect_time)%5Cn%20%20%20%20%20%20%20%20VALUES%20(%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F)%5Cn%20%20%20%20%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C%20(num_iid%2C%20sku_id%2C%20sku_name%2C%20sku_price%2C%20sku_origin_price%2C%20sku_qty%2C%20now))%5Cn%20%20%20%20conn.commit()%5Cn%20%20%20%20conn.close()%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;import&amp;nbsp;requests
import&amp;nbsp;json
import&amp;nbsp;sqlite3
import&amp;nbsp;time
import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd
import&amp;nbsp;matplotlib.pyplot&amp;nbsp;as&amp;nbsp;plt
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime

plt.rcParams[&amp;quot;font.sans-serif&amp;quot;]&amp;nbsp;=&amp;nbsp;[&amp;quot;SimHei&amp;quot;]
plt.rcParams[&amp;quot;axes.unicode_minus&amp;quot;]&amp;nbsp;=&amp;nbsp;False

#&amp;nbsp;--------------------------配置区--------------------------
#&amp;nbsp;个人凭证，开通工具服务后获取
ACCESS_KEY&amp;nbsp;=&amp;nbsp;&amp;quot;替换成你的凭证key&amp;quot;
ACCESS_SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;替换成你的凭证secret&amp;quot;
#&amp;nbsp;需要监控的商品ID列表
MONITOR_ITEM_IDS&amp;nbsp;=&amp;nbsp;[&amp;quot;10335871600&amp;quot;,&amp;nbsp;&amp;quot;10057467958584&amp;quot;]
#&amp;nbsp;采集间隔，单位秒，示例5分钟采集一次
MONITOR_INTERVAL&amp;nbsp;=&amp;nbsp;300
#&amp;nbsp;本地数据库文件名
DB_FILE&amp;nbsp;=&amp;nbsp;&amp;quot;goods_monitor.db&amp;quot;
#&amp;nbsp;-----------------------------------------------------------


def&amp;nbsp;init_db():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;初始化数据库，创建数据表&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_FILE)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;商品基础信息表
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CREATE&amp;nbsp;TABLE&amp;nbsp;IF&amp;nbsp;NOT&amp;nbsp;EXISTS&amp;nbsp;goods_record&amp;nbsp;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;id&amp;nbsp;INTEGER&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;AUTOINCREMENT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;num_iid&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;brand&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;orginal_price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stock&amp;nbsp;INTEGER,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shop_name&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;collect_time&amp;nbsp;TIMESTAMP
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;sku规格明细表
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CREATE&amp;nbsp;TABLE&amp;nbsp;IF&amp;nbsp;NOT&amp;nbsp;EXISTS&amp;nbsp;sku_record&amp;nbsp;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;id&amp;nbsp;INTEGER&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;AUTOINCREMENT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;num_iid&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_id&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_name&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;orginal_price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;quantity&amp;nbsp;INTEGER,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;collect_time&amp;nbsp;TIMESTAMP
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.commit()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()


def&amp;nbsp;fetch_goods_detail(item_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;获取商品详情原始结构化数据
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:param&amp;nbsp;item_id:&amp;nbsp;商品ID
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:return:&amp;nbsp;dict商品数据，失败返回None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base_url&amp;nbsp;=&amp;nbsp;&amp;quot;https://api-gw.onebound.cn/jd/item_get_pro/&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;ACCESS_KEY,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;ACCESS_SECRET,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;item_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Accept-Encoding&amp;quot;:&amp;nbsp;&amp;quot;gzip&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Connection&amp;quot;:&amp;nbsp;&amp;quot;close&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(base_url,&amp;nbsp;params=params,&amp;nbsp;headers=headers,&amp;nbsp;timeout=15)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;res_json&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;res_json.get(&amp;quot;error_code&amp;quot;)&amp;nbsp;!=&amp;nbsp;&amp;quot;0000&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【获取失败】商品:{item_id},&amp;nbsp;原因:{res_json.get(&amp;#39;reason&amp;#39;,&amp;#39;未知错误&amp;#39;)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_data&amp;nbsp;=&amp;nbsp;res_json.get(&amp;quot;item&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;item_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【警告】商品{item_id}返回数据为空&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;item_data
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【网络异常】{item_id}&amp;nbsp;,&amp;nbsp;{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None


def&amp;nbsp;save_data_to_db(item_data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;将采集到的商品以及sku数据存入sqlite数据库&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_FILE)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now&amp;nbsp;=&amp;nbsp;datetime.now().strftime(&amp;quot;%Y-%m-%d&amp;nbsp;%H:%M:%S&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;num_iid&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;num_iid&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;brand&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;brand&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;price&amp;nbsp;=&amp;nbsp;float(item_data.get(&amp;quot;price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;price&amp;quot;)&amp;nbsp;else&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;orginal_price&amp;nbsp;=&amp;nbsp;float(item_data.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;orginal_price&amp;quot;)&amp;nbsp;else&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stock&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;num&amp;quot;,&amp;nbsp;0)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shop_name&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INSERT&amp;nbsp;INTO&amp;nbsp;goods_record(num_iid,title,brand,price,orginal_price,stock,shop_name,collect_time)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;VALUES&amp;nbsp;(?,?,?,?,?,?,?,?)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,&amp;nbsp;(num_iid,&amp;nbsp;title,&amp;nbsp;brand,&amp;nbsp;price,&amp;nbsp;orginal_price,&amp;nbsp;stock,&amp;nbsp;shop_name,&amp;nbsp;now))

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;保存SKU多规格数据
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_list&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;skus&amp;quot;,&amp;nbsp;{}).get(&amp;quot;sku&amp;quot;,&amp;nbsp;[])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;sku&amp;nbsp;in&amp;nbsp;sku_list:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_id&amp;nbsp;=&amp;nbsp;sku.get(&amp;quot;sku_id&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_name&amp;nbsp;=&amp;nbsp;sku.get(&amp;quot;properties_name&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_price&amp;nbsp;=&amp;nbsp;float(sku.get(&amp;quot;price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;sku.get(&amp;quot;price&amp;quot;)&amp;nbsp;else&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_origin_price&amp;nbsp;=&amp;nbsp;float(sku.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;sku.get(&amp;quot;orginal_price&amp;quot;)&amp;nbsp;else&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_qty&amp;nbsp;=&amp;nbsp;sku.get(&amp;quot;quantity&amp;quot;,&amp;nbsp;0)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INSERT&amp;nbsp;INTO&amp;nbsp;sku_record(num_iid,sku_id,sku_name,price,orginal_price,quantity,collect_time)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;VALUES&amp;nbsp;(?,?,?,?,?,?,?)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,&amp;nbsp;(num_iid,&amp;nbsp;sku_id,&amp;nbsp;sku_name,&amp;nbsp;sku_price,&amp;nbsp;sku_origin_price,&amp;nbsp;sku_qty,&amp;nbsp;now))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.commit()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;2. 监控告警逻辑：定时采集，对比变化触发提醒&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22class%20GoodsMonitor%3A%5Cn%20%20%20%20def%20__init__(self)%3A%5Cn%20%20%20%20%20%20%20%20self.last_data%20%3D%20dict()%20%20%23%20%E7%BC%93%E5%AD%98%E4%B8%8A%E4%B8%80%E8%BD%AE%E9%87%87%E9%9B%86%E7%BB%93%E6%9E%9C%20%7Bitem_id%3A%20data%7D%5Cn%5Cn%20%20%20%20def%20check_change_and_alert(self%2C%20item_data)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E5%AF%B9%E6%AF%94%E6%96%B0%E6%97%A7%E6%95%B0%E6%8D%AE%EF%BC%8C%E5%A6%82%E6%9E%9C%E4%BB%B7%E6%A0%BC%2F%E5%BA%93%E5%AD%98%E5%8F%98%E5%8A%A8%E8%BE%93%E5%87%BA%E5%91%8A%E8%AD%A6%E6%8F%90%E7%A4%BA%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20item_id%20%3D%20item_data.get(%5C%22num_iid%5C%22)%5Cn%20%20%20%20%20%20%20%20current_price%20%3D%20float(item_data.get(%5C%22price%5C%22%2C%200))%5Cn%20%20%20%20%20%20%20%20current_stock%20%3D%20int(item_data.get(%5C%22num%5C%22%2C%200))%5Cn%20%20%20%20%20%20%20%20title%20%3D%20item_data.get(%5C%22title%5C%22%2C%20%5C%22%5C%22)%5B%3A30%5D%5Cn%5Cn%20%20%20%20%20%20%20%20if%20item_id%20not%20in%20self.last_data%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E3%80%90%E9%A6%96%E6%AC%A1%E9%87%87%E9%9B%86%E3%80%91%E5%95%86%E5%93%81ID%3A%7Bitem_id%7D%20%7C%20%7Btitle%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%BD%93%E5%89%8D%E5%94%AE%E4%BB%B7%3A%7Bcurrent_price%7D%E5%85%83%20%7C%20%E5%BA%93%E5%AD%98%3A%7Bcurrent_stock%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20self.last_data%5Bitem_id%5D%20%3D%20%7B%5C%22price%5C%22%3A%20current_price%2C%20%5C%22stock%5C%22%3A%20current_stock%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%5Cn%5Cn%20%20%20%20%20%20%20%20old%20%3D%20self.last_data%5Bitem_id%5D%5Cn%20%20%20%20%20%20%20%20old_price%20%3D%20old%5B%5C%22price%5C%22%5D%5Cn%20%20%20%20%20%20%20%20old_stock%20%3D%20old%5B%5C%22stock%5C%22%5D%5Cn%5Cn%20%20%20%20%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E4%BB%B7%E6%A0%BC%E5%8F%98%E5%8A%A8%5Cn%20%20%20%20%20%20%20%20if%20abs(current_price%20-%20old_price)%20%3E%200.01%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%F0%9F%94%A5%E3%80%90%E4%BB%B7%E6%A0%BC%E5%8F%98%E5%8A%A8%E5%91%8A%E8%AD%A6%E3%80%91%7Btitle%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E6%97%A7%E4%BB%B7%E6%A0%BC%3A%7Bold_price%7D%20%E2%86%92%20%E6%96%B0%E4%BB%B7%E6%A0%BC%3A%7Bcurrent_price%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E5%BA%93%E5%AD%98%E5%8F%98%E5%8A%A8%5Cn%20%20%20%20%20%20%20%20if%20current_stock%20!%3D%20old_stock%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E2%9A%A0%EF%B8%8F%E3%80%90%E5%BA%93%E5%AD%98%E5%8F%98%E5%8A%A8%E5%91%8A%E8%AD%A6%E3%80%91%7Btitle%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E6%97%A7%E5%BA%93%E5%AD%98%3A%7Bold_stock%7D%20%E2%86%92%20%E6%96%B0%E5%BA%93%E5%AD%98%3A%7Bcurrent_stock%7D%5C%22)%5Cn%5Cn%20%20%20%20%20%20%20%20self.last_data%5Bitem_id%5D%5B%5C%22price%5C%22%5D%20%3D%20current_price%5Cn%20%20%20%20%20%20%20%20self.last_data%5Bitem_id%5D%5B%5C%22stock%5C%22%5D%20%3D%20current_stock%5Cn%5Cn%20%20%20%20def%20run_monitor_loop(self)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E5%BE%AA%E7%8E%AF%E6%89%A7%E8%A1%8C%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20print(%5C%22%3D%3D%3D%3D%3D%E5%95%86%E5%93%81%E7%9B%91%E6%8E%A7%E6%9C%8D%E5%8A%A1%E5%90%AF%E5%8A%A8%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20for%20goods_id%20in%20MONITOR_ITEM_IDS%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20item%20%3D%20fetch_goods_detail(goods_id)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20item%20is%20None%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.check_change_and_alert(item)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20save_data_to_db(item)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E7%AD%89%E5%BE%85%20%7BMONITOR_INTERVAL%7D%20%E7%A7%92%E5%90%8E%E8%BF%9B%E8%A1%8C%E4%B8%8B%E4%B8%80%E8%BD%AE%E9%87%87%E9%9B%86...%5C%5Cn%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20time.sleep(MONITOR_INTERVAL)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;class&amp;nbsp;GoodsMonitor:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__init__(self):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.last_data&amp;nbsp;=&amp;nbsp;dict()&amp;nbsp;&amp;nbsp;#&amp;nbsp;缓存上一轮采集结果&amp;nbsp;{item_id:&amp;nbsp;data}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;check_change_and_alert(self,&amp;nbsp;item_data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;对比新旧数据，如果价格/库存变动输出告警提示&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_id&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;num_iid&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;current_price&amp;nbsp;=&amp;nbsp;float(item_data.get(&amp;quot;price&amp;quot;,&amp;nbsp;0))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;current_stock&amp;nbsp;=&amp;nbsp;int(item_data.get(&amp;quot;num&amp;quot;,&amp;nbsp;0))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)[:30]

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;item_id&amp;nbsp;not&amp;nbsp;in&amp;nbsp;self.last_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n【首次采集】商品ID:{item_id}&amp;nbsp;|&amp;nbsp;{title}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;当前售价:{current_price}元&amp;nbsp;|&amp;nbsp;库存:{current_stock}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.last_data[item_id]&amp;nbsp;=&amp;nbsp;{&amp;quot;price&amp;quot;:&amp;nbsp;current_price,&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;current_stock}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old&amp;nbsp;=&amp;nbsp;self.last_data[item_id]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_price&amp;nbsp;=&amp;nbsp;old[&amp;quot;price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_stock&amp;nbsp;=&amp;nbsp;old[&amp;quot;stock&amp;quot;]

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;判断价格变动
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;abs(current_price&amp;nbsp;-&amp;nbsp;old_price)&amp;nbsp;&amp;gt;&amp;nbsp;0.01:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n🔥【价格变动告警】{title}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;旧价格:{old_price}&amp;nbsp;→&amp;nbsp;新价格:{current_price}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;判断库存变动
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;current_stock&amp;nbsp;!=&amp;nbsp;old_stock:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n⚠️【库存变动告警】{title}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;旧库存:{old_stock}&amp;nbsp;→&amp;nbsp;新库存:{current_stock}&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.last_data[item_id][&amp;quot;price&amp;quot;]&amp;nbsp;=&amp;nbsp;current_price
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.last_data[item_id][&amp;quot;stock&amp;quot;]&amp;nbsp;=&amp;nbsp;current_stock

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;run_monitor_loop(self):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;循环执行监控任务&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;=====商品监控服务启动=====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;goods_id&amp;nbsp;in&amp;nbsp;MONITOR_ITEM_IDS:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item&amp;nbsp;=&amp;nbsp;fetch_goods_detail(goods_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;item&amp;nbsp;is&amp;nbsp;None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.check_change_and_alert(item)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_data_to_db(item)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n等待&amp;nbsp;{MONITOR_INTERVAL}&amp;nbsp;秒后进行下一轮采集...\n&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(MONITOR_INTERVAL)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;3. 数据分析模块：读取历史记录，统计与绘图&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22def%20analysis_goods_history(item_id)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E8%AF%BB%E5%8F%96%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%BA%93%EF%BC%8C%E5%AF%B9%E5%8D%95%E4%B8%AA%E5%95%86%E5%93%81%E5%81%9A%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%EF%BC%8C%E8%BE%93%E5%87%BA%E7%BB%9F%E8%AE%A1%2B%E7%BB%98%E5%9B%BE%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_FILE)%5Cn%20%20%20%20df%20%3D%20pd.read_sql(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20%20%20%20%20select%20collect_time%2C%20price%2C%20orginal_price%2C%20stock%20from%20goods_record%5Cn%20%20%20%20%20%20%20%20where%20num_iid%20%3D%20%3F%20order%20by%20collect_time%20asc%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C%20conn%2C%20params%3D(item_id%2C))%5Cn%20%20%20%20conn.close()%5Cn%5Cn%20%20%20%20if%20len(df)%20%3D%3D%200%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E3%80%91%E5%95%86%E5%93%81%7Bitem_id%7D%E6%B2%A1%E6%9C%89%E5%8E%86%E5%8F%B2%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20df%5B%5C%22collect_time%5C%22%5D%20%3D%20pd.to_datetime(df%5B%5C%22collect_time%5C%22%5D)%5Cn%20%20%20%20print(f%5C%22%5C%5Cn%3D%3D%3D%3D%3D%3D%3D%3D%E5%95%86%E5%93%81%20%7Bitem_id%7D%20%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E7%BB%9F%E8%AE%A1%3D%3D%3D%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E9%87%87%E9%9B%86%E6%80%BB%E6%AC%A1%E6%95%B0%EF%BC%9A%7Blen(df)%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E6%9C%80%E4%BD%8E%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bdf%5B&amp;#39;price&amp;#39;%5D.min()%3A.2f%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E6%9C%80%E9%AB%98%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bdf%5B&amp;#39;price&amp;#39;%5D.max()%3A.2f%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%B9%B3%E5%9D%87%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bdf%5B&amp;#39;price&amp;#39;%5D.mean()%3A.2f%7D%5C%22)%5Cn%5Cn%20%20%20%20%23%20%E7%BB%98%E5%88%B6%E4%BB%B7%E6%A0%BC%E8%B6%8B%E5%8A%BF%E5%9B%BE%5Cn%20%20%20%20plt.figure(figsize%3D(12%2C%206))%5Cn%20%20%20%20plt.plot(df%5B%5C%22collect_time%5C%22%5D%2C%20df%5B%5C%22price%5C%22%5D%2C%20marker%3D%5C%22o%5C%22%2C%20color%3D%5C%22%23E53935%5C%22%2C%20label%3D%5C%22%E5%AE%9E%E6%97%B6%E5%94%AE%E4%BB%B7%5C%22)%5Cn%20%20%20%20plt.plot(df%5B%5C%22collect_time%5C%22%5D%2C%20df%5B%5C%22orginal_price%5C%22%5D%2C%20linestyle%3D%5C%22--%5C%22%2C%20color%3D%5C%22%23757575%5C%22%2C%20label%3D%5C%22%E5%95%86%E5%93%81%E5%8E%9F%E4%BB%B7%5C%22)%5Cn%20%20%20%20plt.title(f%5C%22%E5%95%86%E5%93%81ID%3A%7Bitem_id%7D%20%E4%BB%B7%E6%A0%BC%E5%8F%98%E5%8C%96%E8%B6%8B%E5%8A%BF%5C%22)%5Cn%20%20%20%20plt.xlabel(%5C%22%E9%87%87%E9%9B%86%E6%97%B6%E9%97%B4%5C%22)%5Cn%20%20%20%20plt.ylabel(%5C%22%E4%BB%B7%E6%A0%BC%EF%BC%88%E5%85%83%EF%BC%89%5C%22)%5Cn%20%20%20%20plt.legend()%5Cn%20%20%20%20plt.grid(alpha%3D0.3)%5Cn%20%20%20%20plt.tight_layout()%5Cn%20%20%20%20save_img_name%20%3D%20f%5C%22goods_%7Bitem_id%7D_trend.png%5C%22%5Cn%20%20%20%20plt.savefig(save_img_name)%5Cn%20%20%20%20plt.show()%5Cn%20%20%20%20print(f%5C%22%E8%B6%8B%E5%8A%BF%E5%9B%BE%E8%A1%A8%E5%B7%B2%E4%BF%9D%E5%AD%98%E8%87%B3%EF%BC%9A%7Bsave_img_name%7D%5C%22)%5Cn%20%20%20%20return%20df%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;def&amp;nbsp;analysis_goods_history(item_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;读取本地数据库，对单个商品做历史数据分析，输出统计+绘图&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_FILE)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.read_sql(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;select&amp;nbsp;collect_time,&amp;nbsp;price,&amp;nbsp;orginal_price,&amp;nbsp;stock&amp;nbsp;from&amp;nbsp;goods_record
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;where&amp;nbsp;num_iid&amp;nbsp;=&amp;nbsp;?&amp;nbsp;order&amp;nbsp;by&amp;nbsp;collect_time&amp;nbsp;asc
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,&amp;nbsp;conn,&amp;nbsp;params=(item_id,))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;len(df)&amp;nbsp;==&amp;nbsp;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【数据分析】商品{item_id}没有历史采集记录&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df[&amp;quot;collect_time&amp;quot;]&amp;nbsp;=&amp;nbsp;pd.to_datetime(df[&amp;quot;collect_time&amp;quot;])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n========商品&amp;nbsp;{item_id}&amp;nbsp;历史数据统计========&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;采集总次数：{len(df)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;最低售价：{df[&amp;#39;price&amp;#39;].min():.2f}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;最高售价：{df[&amp;#39;price&amp;#39;].max():.2f}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;平均售价：{df[&amp;#39;price&amp;#39;].mean():.2f}&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;绘制价格趋势图
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.figure(figsize=(12,&amp;nbsp;6))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.plot(df[&amp;quot;collect_time&amp;quot;],&amp;nbsp;df[&amp;quot;price&amp;quot;],&amp;nbsp;marker=&amp;quot;o&amp;quot;,&amp;nbsp;color=&amp;quot;#E53935&amp;quot;,&amp;nbsp;label=&amp;quot;实时售价&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.plot(df[&amp;quot;collect_time&amp;quot;],&amp;nbsp;df[&amp;quot;orginal_price&amp;quot;],&amp;nbsp;linestyle=&amp;quot;--&amp;quot;,&amp;nbsp;color=&amp;quot;#757575&amp;quot;,&amp;nbsp;label=&amp;quot;商品原价&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.title(f&amp;quot;商品ID:{item_id}&amp;nbsp;价格变化趋势&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.xlabel(&amp;quot;采集时间&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.ylabel(&amp;quot;价格（元）&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.legend()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.grid(alpha=0.3)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.tight_layout()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_img_name&amp;nbsp;=&amp;nbsp;f&amp;quot;goods_{item_id}_trend.png&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.savefig(save_img_name)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.show()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;趋势图表已保存至：{save_img_name}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;df&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;4. 程序入口，两种运行模式&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;3&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22if%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20init_db()%5Cn%20%20%20%20print(%5C%22%5C%22%5C%22%5Cn%E8%AF%B7%E9%80%89%E6%8B%A9%E8%BF%90%E8%A1%8C%E6%A8%A1%E5%BC%8F%EF%BC%9A%5Cn1%20%E2%86%92%20%E5%90%AF%E5%8A%A8%E6%8C%81%E7%BB%AD%E7%9B%91%E6%8E%A7%E5%BE%AA%E7%8E%AF%EF%BC%88%E5%AE%9A%E6%97%B6%E9%87%87%E9%9B%86%2B%E5%8F%98%E5%8A%A8%E6%8F%90%E9%86%92%EF%BC%89%5Cn2%20%E2%86%92%20%E8%AF%BB%E5%8F%96%E6%9C%AC%E5%9C%B0%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E5%81%9A%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E7%BB%98%E5%9B%BE%5Cn%5C%22%5C%22%5C%22)%5Cn%20%20%20%20mode%20%3D%20input(%5C%22%E8%BE%93%E5%85%A5%E6%A8%A1%E5%BC%8F%E6%95%B0%E5%AD%97%EF%BC%9A%5C%22)%5Cn%20%20%20%20monitor%20%3D%20GoodsMonitor()%5Cn%20%20%20%20if%20mode%20%3D%3D%20%5C%221%5C%22%3A%5Cn%20%20%20%20%20%20%20%20monitor.run_monitor_loop()%5Cn%20%20%20%20elif%20mode%20%3D%3D%20%5C%222%5C%22%3A%5Cn%20%20%20%20%20%20%20%20target_id%20%3D%20input(%5C%22%E8%AF%B7%E8%BE%93%E5%85%A5%E8%A6%81%E5%88%86%E6%9E%90%E7%9A%84%E5%95%86%E5%93%81ID%EF%BC%9A%5C%22)%5Cn%20%20%20%20%20%20%20%20analysis_goods_history(target_id)%5Cn%20%20%20%20else%3A%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E8%BE%93%E5%85%A5%E9%94%99%E8%AF%AF%EF%BC%8C%E7%A8%8B%E5%BA%8F%E9%80%80%E5%87%BA%5C%22)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init_db()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;&amp;quot;&amp;quot;
请选择运行模式：
1&amp;nbsp;→&amp;nbsp;启动持续监控循环（定时采集+变动提醒）
2&amp;nbsp;→&amp;nbsp;读取本地历史数据做数据分析绘图
&amp;quot;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;mode&amp;nbsp;=&amp;nbsp;input(&amp;quot;输入模式数字：&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor&amp;nbsp;=&amp;nbsp;GoodsMonitor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;mode&amp;nbsp;==&amp;nbsp;&amp;quot;1&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor.run_monitor_loop()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;mode&amp;nbsp;==&amp;nbsp;&amp;quot;2&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;target_id&amp;nbsp;=&amp;nbsp;input(&amp;quot;请输入要分析的商品ID：&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis_goods_history(target_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;输入错误，程序退出&amp;quot;)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;使用说明&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;将代码中&lt;code&gt;KEY&lt;/code&gt;、&lt;code&gt;SECRET&lt;/code&gt;替换成自己开通服务之后拿到的凭证；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;修改&lt;code&gt;MONITOR_ITEM_IDS&lt;/code&gt;填入你想要监控的商品编号；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;运行脚本：	&lt;/p&gt;&lt;/li&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;选择模式 1：程序持续运行，每设定秒数自动采集，价格库存变动控制台输出告警；所有采集记录自动存入&lt;code&gt;goods_monitor.db&lt;/code&gt;；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;选择模式 2：输入商品编号，读取本地数据库历史记录，输出价格统计，自动生成价格走势图 png 图片。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ol&gt;&lt;h2&gt;业务拓展方向&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;消息推送&lt;/strong&gt;：告警部分可以接入钉钉 / 企业微信机器人，一旦价格下降自动推送消息到工作群，不用盯着控制台；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;阈值告警&lt;/strong&gt;：增加心理价位判断，当售价低于我们预设目标价格，触发提醒，适合个人囤货比价；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;SKU 粒度监控&lt;/strong&gt;：目前保存全部规格数据，可以扩展针对某个具体规格单独监控；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;批量导出报表&lt;/strong&gt;：基于 pandas 把历史记录导出 Excel，用于周报、竞品复盘；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;部署后台&lt;/strong&gt;：放到服务器后台常驻运行，实现 7×24 小时不间断监控。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;踩坑与实践经验&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;采集间隔不要设置过小，建议至少 3 分钟以上，合理控制请求频次；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;工具支持关闭缓存模式&lt;code&gt;cache=no&lt;/code&gt;拿到最新实时数据，适合监控场景；静态信息如标题图片可以开启缓存，减少消耗；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;返回的多规格价格存在特殊场景，业务上建议以对应规格字段为准；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;当商品下架、编号失效，脚本会捕获异常，打印错误信息，不会直接崩溃；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;SQLite 适合个人小体量监控；如果后期监控几百个商品，建议迁移 MySQL。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;小结&lt;/h2&gt;&lt;p&gt;借助 OpenClaw 获取标准化商品资料，我们不用耗费大量精力处理页面反访问、页面改版维护解析规则。把重心放在业务逻辑：数据存储、监控告警、数据分析。 上面整套代码可以直接落地，不管是个人囤货比价、小商家竞品监控、简单的数据分析调研，都可以快速满足需求。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Mon, 31 Aug 2026 17:49:12 +0800</pubDate></item><item><title>Open Claw 实战｜快速搭建电商商品监控与数据分析能力</title><link>https://www.shopapi.com.cn/?id=226</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营、市场调研或者竞品分析的时候，相信很多人都遇到这样的痛点： 想要盯一批竞品商品，手动复制粘贴价格、库存、规格信息到 Excel，耗时又容易出错；大促期间价格频繁变动，等到人工发现调价，机会早就错过了；想要统计一批商品的价格区间、规格分布，手动统计效率极低。&lt;/p&gt;&lt;p&gt;自己写原生页面解析，会碰到层出不穷的反访问限制、页面结构改版、图片路径解析、规格多维度拆解等一堆麻烦问题。页面结构一改，之前写好的解析逻辑就要全部重写，维护成本很高。&lt;/p&gt;&lt;p&gt;最近在使用 Open Claw 这套工具，它封装好了商品信息解析能力，我们只需要传入商品编号，就可以直接拿到结构化的商品信息，把精力全部放在&lt;strong&gt;监控逻辑、数据存储、业务分析&lt;/strong&gt;上，不用耗费大量时间处理页面解析细节。&lt;/p&gt;&lt;p&gt;本文会完整演示：单商品信息获取、定时监控价格库存变动、数据落地存储、简单数据分析，代码可以直接拿来二次改造，适合部署在本地或者服务器做轻量级业务监控。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;提示：本教程仅用于学习研究，请遵守对应平台规则，不要高频大规模访问，避免对平台造成压力。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;环境准备&lt;/h2&gt;&lt;p&gt;运行代码依赖 requests 库，如果还没有安装，执行：&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%20pandas%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&amp;nbsp;pandas&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;一、获取商品结构化信息&lt;/h2&gt;&lt;p&gt;我们只需要准备好自己的&lt;strong&gt;&lt;strong&gt;&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;5&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E5%87%AD%E8%AF%81%E5%AF%86%E9%92%A5%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%224j3BrW-1788083584189%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;凭证密钥&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;凭证密钥&quot;&gt;凭证密钥&lt;/a&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/strong&gt;，传入商品编号，工具会返回整理完毕的数据：标题、售价、原价、库存、规格 SKU、店铺信息、图片地址、发货地等全部字段，不需要自己做 HTML 解析。&lt;/p&gt;&lt;p&gt;示例代码：&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20-*-%20coding%3Autf-8%20-*-%5Cnimport%20requests%5Cnimport%20json%5Cn%5Cnclass%20GoodsHelper%3A%5Cn%20%20%20%20def%20__init__(self%2C%20access_key%2C%20access_secret)%3A%5Cn%20%20%20%20%20%20%20%20self.access_key%20%3D%20access_key%5Cn%20%20%20%20%20%20%20%20self.access_secret%20%3D%20access_secret%5Cn%20%20%20%20%20%20%20%20self.base_url%20%3D%20%5C%22https%3A%2F%2Fapi-gw.onebound.cn%2Fjd%2Fitem_get_pro%5C%22%5Cn%5Cn%20%20%20%20def%20get_goods_detail(self%2C%20goods_id)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%E5%AE%8C%E6%95%B4%E7%BB%93%E6%9E%84%E5%8C%96%E4%BF%A1%E6%81%AF%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20self.access_key%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20self.access_secret%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%20%5C%22json%5C%22%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(self.base_url%2C%20params%3Dparams%2C%20timeout%3D20)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20resp.json()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20item_data%20%3D%20result.get(%5C%22item%5C%22%2C%20%7B%7D)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20item_data%5Cn%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%E5%BC%82%E5%B8%B8%EF%BC%9A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20%23%20%E6%9B%BF%E6%8D%A2%E6%88%90%E8%87%AA%E5%B7%B1%E7%9A%84%E5%87%AD%E8%AF%81%5Cn%20%20%20%20KEY%20%3D%20%5C%22%E4%BD%A0%E7%9A%84key%5C%22%5Cn%20%20%20%20SECRET%20%3D%20%5C%22%E4%BD%A0%E7%9A%84secret%5C%22%5Cn%20%20%20%20helper%20%3D%20GoodsHelper(KEY%2C%20SECRET)%5Cn%20%20%20%20goods_info%20%3D%20helper.get_goods_detail(%5C%2210335871600%5C%22)%5Cn%20%20%20%20if%20goods_info%3A%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E5%95%86%E5%93%81%E6%A0%87%E9%A2%98%EF%BC%9A%5C%22%2C%20goods_info.get(%5C%22title%5C%22))%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E5%BD%93%E5%89%8D%E5%94%AE%E4%BB%B7%EF%BC%9A%5C%22%2C%20goods_info.get(%5C%22price%5C%22))%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E5%8E%9F%E5%A7%8B%E6%A0%87%E4%BB%B7%EF%BC%9A%5C%22%2C%20goods_info.get(%5C%22orginal_price%5C%22))%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E5%BA%93%E5%AD%98%EF%BC%9A%5C%22%2C%20goods_info.get(%5C%22num%5C%22))%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E5%BA%97%E9%93%BA%E5%90%8D%E7%A7%B0%EF%BC%9A%5C%22%2C%20goods_info.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22))%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E5%95%86%E5%93%81%E9%93%BE%E6%8E%A5%EF%BC%9A%5C%22%2C%20goods_info.get(%5C%22detail_url%5C%22))%5Cn%20%20%20%20%20%20%20%20%23%20%E8%8E%B7%E5%8F%96%E5%A4%9A%E8%A7%84%E6%A0%BC%E4%BF%A1%E6%81%AF%5Cn%20%20%20%20%20%20%20%20sku_list%20%3D%20goods_info.get(%5C%22skus%5C%22%2C%20%7B%7D).get(%5C%22sku%5C%22%2C%20%5B%5D)%5Cn%20%20%20%20%20%20%20%20for%20sku%20in%20sku_list%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E8%A7%84%E6%A0%BC%3A%7Bsku.get(&amp;#39;properties_name&amp;#39;)%7D%20%7C%20%E4%BB%B7%E6%A0%BC%3A%7Bsku.get(&amp;#39;price&amp;#39;)%7D%20%7C%20sku%E5%BA%93%E5%AD%98%3A%7Bsku.get(&amp;#39;quantity&amp;#39;)%7D%5C%22)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;-*-&amp;nbsp;coding:utf-8&amp;nbsp;-*-
import&amp;nbsp;requests
import&amp;nbsp;json

class&amp;nbsp;GoodsHelper:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__init__(self,&amp;nbsp;access_key,&amp;nbsp;access_secret):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.access_key&amp;nbsp;=&amp;nbsp;access_key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.access_secret&amp;nbsp;=&amp;nbsp;access_secret
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.base_url&amp;nbsp;=&amp;nbsp;&amp;quot;https://api-gw.onebound.cn/jd/item_get_pro&amp;quot;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;get_goods_detail(self,&amp;nbsp;goods_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;获取商品完整结构化信息&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;self.access_key,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;self.access_secret,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;nbsp;&amp;quot;json&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(self.base_url,&amp;nbsp;params=params,&amp;nbsp;timeout=20)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_data&amp;nbsp;=&amp;nbsp;result.get(&amp;quot;item&amp;quot;,&amp;nbsp;{})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;item_data
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;获取商品异常：{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;替换成自己的凭证
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;KEY&amp;nbsp;=&amp;nbsp;&amp;quot;你的key&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;你的secret&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;helper&amp;nbsp;=&amp;nbsp;GoodsHelper(KEY,&amp;nbsp;SECRET)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_info&amp;nbsp;=&amp;nbsp;helper.get_goods_detail(&amp;quot;10335871600&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;goods_info:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;商品标题：&amp;quot;,&amp;nbsp;goods_info.get(&amp;quot;title&amp;quot;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;当前售价：&amp;quot;,&amp;nbsp;goods_info.get(&amp;quot;price&amp;quot;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;原始标价：&amp;quot;,&amp;nbsp;goods_info.get(&amp;quot;orginal_price&amp;quot;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;库存：&amp;quot;,&amp;nbsp;goods_info.get(&amp;quot;num&amp;quot;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;店铺名称：&amp;quot;,&amp;nbsp;goods_info.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;商品链接：&amp;quot;,&amp;nbsp;goods_info.get(&amp;quot;detail_url&amp;quot;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;获取多规格信息
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_list&amp;nbsp;=&amp;nbsp;goods_info.get(&amp;quot;skus&amp;quot;,&amp;nbsp;{}).get(&amp;quot;sku&amp;quot;,&amp;nbsp;[])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;sku&amp;nbsp;in&amp;nbsp;sku_list:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;规格:{sku.get(&amp;#39;properties_name&amp;#39;)}&amp;nbsp;|&amp;nbsp;价格:{sku.get(&amp;#39;price&amp;#39;)}&amp;nbsp;|&amp;nbsp;sku库存:{sku.get(&amp;#39;quantity&amp;#39;)}&amp;quot;)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;运行之后直接输出结构化结果。我们拿到的数据包含：主图集合、多规格 SKU、品牌、发货地、店铺信息等，省去大量解析工作。&lt;/p&gt;&lt;h2&gt;二、实现定时监控，捕捉价格、库存变动&lt;/h2&gt;&lt;p&gt;最核心的场景：持续监控目标商品，对比上一轮采集结果，价格下调、库存骤变的时候输出告警提示。 可以扩展：告警推送钉钉 / 企业微信、写入本地文件或者数据库持久化。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20-*-%20coding%3Autf-8%20-*-%5Cnimport%20requests%5Cnimport%20time%5Cnimport%20datetime%5Cnimport%20pandas%20as%20pd%5Cn%5Cnclass%20GoodsHelper%3A%5Cn%20%20%20%20def%20__init__(self%2C%20access_key%2C%20access_secret)%3A%5Cn%20%20%20%20%20%20%20%20self.access_key%20%3D%20access_key%5Cn%20%20%20%20%20%20%20%20self.access_secret%20%3D%20access_secret%5Cn%20%20%20%20%20%20%20%20self.base_url%20%3D%20%5C%22https%3A%2F%2Fapi-gw.onebound.cn%2Fjd%2Fitem_get_pro%5C%22%5Cn%5Cn%20%20%20%20def%20get_goods_detail(self%2C%20goods_id)%3A%5Cn%20%20%20%20%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20self.access_key%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20self.access_secret%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%20%5C%22json%5C%22%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(self.base_url%2C%20params%3Dparams%2C%20timeout%3D20)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20resp.json()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20result.get(%5C%22item%5C%22%2C%20%7B%7D)%5Cn%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E8%AF%B7%E6%B1%82%E5%87%BA%E9%94%99%3A%7Be%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%5Cnclass%20GoodsMonitor%3A%5Cn%20%20%20%20def%20__init__(self%2C%20helper)%3A%5Cn%20%20%20%20%20%20%20%20self.helper%20%3D%20helper%5Cn%20%20%20%20%20%20%20%20self.history%20%3D%20dict()%20%20%23%20%E4%BF%9D%E5%AD%98%E4%B8%8A%E4%B8%80%E6%AC%A1%E9%87%87%E9%9B%86%E7%9A%84%E6%95%B0%E6%8D%AE%20%7Bgoods_id%3Adata%7D%5Cn%5Cn%20%20%20%20def%20monitor_loop(self%2C%20goods_id_list%2C%20interval%3D300)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20%E5%BE%AA%E7%8E%AF%E7%9B%91%E6%8E%A7%5Cn%20%20%20%20%20%20%20%20%3Aparam%20goods_id_list%3A%20%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81id%E6%95%B0%E7%BB%84%5Cn%20%20%20%20%20%20%20%20%3Aparam%20interval%3A%20%E8%BD%AE%E8%AF%A2%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E7%A4%BA%E4%BE%8B300%3D5%E5%88%86%E9%92%9F%EF%BC%8C%E4%B8%8D%E8%A6%81%E8%AE%BE%E7%BD%AE%E8%BF%87%E5%B0%8F%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%90%AF%E5%8A%A8%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%EF%BC%8C%E7%9B%91%E6%8E%A7%E5%95%86%E5%93%81%E5%88%97%E8%A1%A8%EF%BC%9A%7Bgoods_id_list%7D%EF%BC%8C%E8%BD%AE%E8%AF%A2%E9%97%B4%E9%9A%94%20%7Binterval%7Ds%5C%22)%5Cn%20%20%20%20%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20now%20%3D%20datetime.datetime.now().strftime(%5C%22%25Y-%25m-%25d%20%25H%3A%25M%3A%25S%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20for%20gid%20in%20goods_id_list%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20data%20%3D%20self.helper.get_goods_detail(gid)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20data%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20title%20%3D%20data.get(%5C%22title%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20price%20%3D%20float(data.get(%5C%22price%5C%22%2C%200))%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20stock%20%3D%20int(data.get(%5C%22num%5C%22%2C%200))%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%5B%7Bnow%7D%5D%20%E5%95%86%E5%93%81%3A%7Btitle%5B%3A40%5D%7D%20%E4%BB%B7%E6%A0%BC%3A%7Bprice%7D%20%E5%BA%93%E5%AD%98%3A%7Bstock%7D%5C%22)%5Cn%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E5%8E%86%E5%8F%B2%E5%AD%98%E5%9C%A8%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20gid%20not%20in%20self.history%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.history%5Bgid%5D%20%3D%20%7B%5C%22price%5C%22%3A%20price%2C%20%5C%22stock%5C%22%3A%20stock%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%5Cn%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20old%20%3D%20self.history%5Bgid%5D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E4%BB%B7%E6%A0%BC%E5%8F%98%E5%8A%A8%E5%91%8A%E8%AD%A6%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20price%20!%3D%20old%5B%5C%22price%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E4%BB%B7%E6%A0%BC%E5%8F%98%E5%8A%A8%E5%91%8A%E8%AD%A6%E3%80%91%7Btitle%7D%20%E6%97%A7%E4%BB%B7%3A%7Bold%5B&amp;#39;price&amp;#39;%5D%7D%20%E2%86%92%20%E6%96%B0%E4%BB%B7%3A%7Bprice%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E5%BA%93%E5%AD%98%E5%A4%A7%E5%B9%85%E5%8F%98%E5%8A%A8%E5%91%8A%E8%AD%A6%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20abs(stock%20-%20old%5B%5C%22stock%5C%22%5D)%20%3E%2020%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E5%BA%93%E5%AD%98%E5%BC%82%E5%8A%A8%E5%91%8A%E8%AD%A6%E3%80%91%7Btitle%7D%20%E6%97%A7%E5%BA%93%E5%AD%98%3A%7Bold%5B&amp;#39;stock&amp;#39;%5D%7D%20%E2%86%92%20%E6%96%B0%E5%BA%93%E5%AD%98%3A%7Bstock%7D%5C%22)%5Cn%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E6%9B%B4%E6%96%B0%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.history%5Bgid%5D%5B%5C%22price%5C%22%5D%20%3D%20price%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.history%5Bgid%5D%5B%5C%22stock%5C%22%5D%20%3D%20stock%5Cn%20%20%20%20%20%20%20%20%20%20%20%20time.sleep(interval)%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20KEY%20%3D%20%5C%22%E4%BD%A0%E7%9A%84key%5C%22%5Cn%20%20%20%20SECRET%20%3D%20%5C%22%E4%BD%A0%E7%9A%84secret%5C%22%5Cn%20%20%20%20helper%20%3D%20GoodsHelper(KEY%2C%20SECRET)%5Cn%20%20%20%20monitor%20%3D%20GoodsMonitor(helper)%5Cn%20%20%20%20%23%20%E8%AE%BE%E7%BD%AE%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%E5%88%97%E8%A1%A8%5Cn%20%20%20%20watch_list%20%3D%20%5B%5C%2210335871600%5C%22%5D%5Cn%20%20%20%20%23%20%E5%90%AF%E5%8A%A8%E7%9B%91%E6%8E%A7%EF%BC%8C%E9%97%B4%E9%9A%945%E5%88%86%E9%92%9F%5Cn%20%20%20%20monitor.monitor_loop(watch_list%2C%20interval%3D300)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;-*-&amp;nbsp;coding:utf-8&amp;nbsp;-*-
import&amp;nbsp;requests
import&amp;nbsp;time
import&amp;nbsp;datetime
import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd

class&amp;nbsp;GoodsHelper:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__init__(self,&amp;nbsp;access_key,&amp;nbsp;access_secret):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.access_key&amp;nbsp;=&amp;nbsp;access_key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.access_secret&amp;nbsp;=&amp;nbsp;access_secret
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.base_url&amp;nbsp;=&amp;nbsp;&amp;quot;https://api-gw.onebound.cn/jd/item_get_pro&amp;quot;

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;get_goods_detail(self,&amp;nbsp;goods_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;self.access_key,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;self.access_secret,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;nbsp;&amp;quot;json&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(self.base_url,&amp;nbsp;params=params,&amp;nbsp;timeout=20)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;result.get(&amp;quot;item&amp;quot;,&amp;nbsp;{})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;请求出错:{e}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None


class&amp;nbsp;GoodsMonitor:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__init__(self,&amp;nbsp;helper):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.helper&amp;nbsp;=&amp;nbsp;helper
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.history&amp;nbsp;=&amp;nbsp;dict()&amp;nbsp;&amp;nbsp;#&amp;nbsp;保存上一次采集的数据&amp;nbsp;{goods_id:data}

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;monitor_loop(self,&amp;nbsp;goods_id_list,&amp;nbsp;interval=300):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;循环监控
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:param&amp;nbsp;goods_id_list:&amp;nbsp;需要监控的商品id数组
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:param&amp;nbsp;interval:&amp;nbsp;轮询间隔，单位秒，示例300=5分钟，不要设置过小
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;启动监控任务，监控商品列表：{goods_id_list}，轮询间隔&amp;nbsp;{interval}s&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now&amp;nbsp;=&amp;nbsp;datetime.datetime.now().strftime(&amp;quot;%Y-%m-%d&amp;nbsp;%H:%M:%S&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;gid&amp;nbsp;in&amp;nbsp;goods_id_list:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data&amp;nbsp;=&amp;nbsp;self.helper.get_goods_detail(gid)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;=&amp;nbsp;data.get(&amp;quot;title&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;price&amp;nbsp;=&amp;nbsp;float(data.get(&amp;quot;price&amp;quot;,&amp;nbsp;0))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stock&amp;nbsp;=&amp;nbsp;int(data.get(&amp;quot;num&amp;quot;,&amp;nbsp;0))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n[{now}]&amp;nbsp;商品:{title[:40]}&amp;nbsp;价格:{price}&amp;nbsp;库存:{stock}&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;判断是否历史存在
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;gid&amp;nbsp;not&amp;nbsp;in&amp;nbsp;self.history:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.history[gid]&amp;nbsp;=&amp;nbsp;{&amp;quot;price&amp;quot;:&amp;nbsp;price,&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;stock}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old&amp;nbsp;=&amp;nbsp;self.history[gid]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;价格变动告警
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;price&amp;nbsp;!=&amp;nbsp;old[&amp;quot;price&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【价格变动告警】{title}&amp;nbsp;旧价:{old[&amp;#39;price&amp;#39;]}&amp;nbsp;→&amp;nbsp;新价:{price}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;库存大幅变动告警
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;abs(stock&amp;nbsp;-&amp;nbsp;old[&amp;quot;stock&amp;quot;])&amp;nbsp;&amp;gt;&amp;nbsp;20:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【库存异动告警】{title}&amp;nbsp;旧库存:{old[&amp;#39;stock&amp;#39;]}&amp;nbsp;→&amp;nbsp;新库存:{stock}&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;更新历史记录
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.history[gid][&amp;quot;price&amp;quot;]&amp;nbsp;=&amp;nbsp;price
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.history[gid][&amp;quot;stock&amp;quot;]&amp;nbsp;=&amp;nbsp;stock
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(interval)


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;KEY&amp;nbsp;=&amp;nbsp;&amp;quot;你的key&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;你的secret&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;helper&amp;nbsp;=&amp;nbsp;GoodsHelper(KEY,&amp;nbsp;SECRET)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor&amp;nbsp;=&amp;nbsp;GoodsMonitor(helper)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;设置需要监控的商品编号列表
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;watch_list&amp;nbsp;=&amp;nbsp;[&amp;quot;10335871600&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;启动监控，间隔5分钟
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor.monitor_loop(watch_list,&amp;nbsp;interval=300)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;⚠️注意：轮询间隔不要设置过小，建议最少 3‑5 分钟以上，避免频繁请求。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;三、数据落地：保存历史记录到 CSV 做数据分析&lt;/h2&gt;&lt;p&gt;采集到的数据不能只打印控制台，我们把每次采集结果写入 csv 文件，后续可以用 pandas 做统计分析，分析价格波动、价格分布区间。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;3&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22def%20save_record_to_csv(goods_id%2C%20goods_data%2C%20csv_path%3D%5C%22goods_record.csv%5C%22)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E4%BF%9D%E5%AD%98%E5%8D%95%E6%9D%A1%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%5C%22%5C%22%5C%22%5Cn%20%20%20%20record%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22crawl_time%5C%22%3A%20datetime.datetime.now().strftime(%5C%22%25Y%E2%80%91%25m%E2%80%91%25d%20%25H%3A%25M%3A%25S%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22goods_id%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20goods_data.get(%5C%22title%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22price%5C%22%3A%20goods_data.get(%5C%22price%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22orginal_price%5C%22%3A%20goods_data.get(%5C%22orginal_price%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3A%20goods_data.get(%5C%22num%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20goods_data.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22location%5C%22%3A%20goods_data.get(%5C%22location%5C%22)%5Cn%20%20%20%20%7D%5Cn%20%20%20%20df_new%20%3D%20pd.DataFrame(%5Brecord%5D)%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20df_old%20%3D%20pd.read_csv(csv_path)%5Cn%20%20%20%20%20%20%20%20df_all%20%3D%20pd.concat(%5Bdf_old%2C%20df_new%5D%2C%20ignore_index%3DTrue)%5Cn%20%20%20%20except%20FileNotFoundError%3A%5Cn%20%20%20%20%20%20%20%20df_all%20%3D%20df_new%5Cn%20%20%20%20df_all.to_csv(csv_path%2C%20index%3DFalse%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%5Cn%5Cn%23%20%E7%AE%80%E5%8D%95%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E7%A4%BA%E4%BE%8B%5Cndef%20analysis_data(file_path%3D%5C%22goods_record.csv%5C%22)%3A%5Cn%20%20%20%20df%20%3D%20pd.read_csv(file_path)%5Cn%20%20%20%20print(%5C%22%3D%3D%3D%3D%E5%9F%BA%E7%A1%80%E7%BB%9F%E8%AE%A1%E5%88%86%E6%9E%90%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E6%80%BB%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%E6%9D%A1%E6%95%B0%EF%BC%9A%7Blen(df)%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E4%BB%B7%E6%A0%BC%E5%9D%87%E5%80%BC%EF%BC%9A%7Bdf%5B&amp;#39;price&amp;#39;%5D.mean()%3A.2f%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E4%BB%B7%E6%A0%BC%E6%9C%80%E9%AB%98%EF%BC%9A%7Bdf%5B&amp;#39;price&amp;#39;%5D.max()%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E4%BB%B7%E6%A0%BC%E6%9C%80%E4%BD%8E%EF%BC%9A%7Bdf%5B&amp;#39;price&amp;#39;%5D.min()%7D%5C%22)%5Cn%20%20%20%20print(%5C%22%5C%5Cn%E5%90%84%E5%95%86%E5%93%81%E5%B9%B3%E5%9D%87%E4%BB%B7%E6%A0%BC%EF%BC%9A%5C%22)%5Cn%20%20%20%20print(df.groupby(%5C%22goods_id%5C%22)%5B%5C%22price%5C%22%5D.mean())%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20%23%20%E6%B5%8B%E8%AF%95%E5%86%99%E5%85%A5%E4%B8%8E%E5%88%86%E6%9E%90%5Cn%20%20%20%20KEY%20%3D%20%5C%22%E4%BD%A0%E7%9A%84key%5C%22%5Cn%20%20%20%20SECRET%20%3D%20%5C%22%E4%BD%A0%E7%9A%84secret%5C%22%5Cn%20%20%20%20helper%20%3D%20GoodsHelper(KEY%2C%20SECRET)%5Cn%20%20%20%20info%20%3D%20helper.get_goods_detail(%5C%2210335871600%5C%22)%5Cn%20%20%20%20save_record_to_csv(%5C%2210335871600%5C%22%2C%20info)%5Cn%20%20%20%20analysis_data()%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;def&amp;nbsp;save_record_to_csv(goods_id,&amp;nbsp;goods_data,&amp;nbsp;csv_path=&amp;quot;goods_record.csv&amp;quot;):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;保存单条采集记录&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;record&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;crawl_time&amp;quot;:&amp;nbsp;datetime.datetime.now().strftime(&amp;quot;%Y‑%m‑%d&amp;nbsp;%H:%M:%S&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;goods_id&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;goods_data.get(&amp;quot;title&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;price&amp;quot;:&amp;nbsp;goods_data.get(&amp;quot;price&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;orginal_price&amp;quot;:&amp;nbsp;goods_data.get(&amp;quot;orginal_price&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;goods_data.get(&amp;quot;num&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;goods_data.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;location&amp;quot;:&amp;nbsp;goods_data.get(&amp;quot;location&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_new&amp;nbsp;=&amp;nbsp;pd.DataFrame([record])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_old&amp;nbsp;=&amp;nbsp;pd.read_csv(csv_path)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_all&amp;nbsp;=&amp;nbsp;pd.concat([df_old,&amp;nbsp;df_new],&amp;nbsp;ignore_index=True)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;FileNotFoundError:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_all&amp;nbsp;=&amp;nbsp;df_new
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_all.to_csv(csv_path,&amp;nbsp;index=False,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)


#&amp;nbsp;简单数据分析示例
def&amp;nbsp;analysis_data(file_path=&amp;quot;goods_record.csv&amp;quot;):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.read_csv(file_path)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;====基础统计分析====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;总采集记录条数：{len(df)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;价格均值：{df[&amp;#39;price&amp;#39;].mean():.2f}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;价格最高：{df[&amp;#39;price&amp;#39;].max()}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;价格最低：{df[&amp;#39;price&amp;#39;].min()}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n各商品平均价格：&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(df.groupby(&amp;quot;goods_id&amp;quot;)[&amp;quot;price&amp;quot;].mean())


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;测试写入与分析
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;KEY&amp;nbsp;=&amp;nbsp;&amp;quot;你的key&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;你的secret&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;helper&amp;nbsp;=&amp;nbsp;GoodsHelper(KEY,&amp;nbsp;SECRET)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;info&amp;nbsp;=&amp;nbsp;helper.get_goods_detail(&amp;quot;10335871600&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_record_to_csv(&amp;quot;10335871600&amp;quot;,&amp;nbsp;info)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis_data()&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;运行完成之后，本地生成&lt;code&gt;goods_record.csv&lt;/code&gt;，里面留存每一次采集快照，后续可以用 Excel 或者 Python 做：价格趋势、大促前后价格对比、竞品价格区间统计等分析工作。&lt;/p&gt;&lt;h2&gt;四、业务拓展思路&lt;/h2&gt;&lt;p&gt;拿到这套基础能力之后，可以衍生很多实用功能：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;批量竞品监控&lt;/strong&gt;：维护一份商品 id 清单，循环批量采集竞品，输出竞品周报；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;消息告警推送&lt;/strong&gt;：价格 / 库存变动时调用钉钉、企业微信机器人接口，把告警消息推送到工作群；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;多规格 SKU 分析&lt;/strong&gt;：解析返回的 SKU 数组，统计不同规格的定价差异，分析对手的规格定价策略；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;持久化存储&lt;/strong&gt;：把 CSV 替换成 MySQL、SQLite 数据库，支持长期海量历史数据存储；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;简单可视化&lt;/strong&gt;：借助 matplotlib 绘制价格波动曲线，直观看到一段时间价格走势。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;踩坑总结与注意事项&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;不要设置过于频繁的轮询间隔，高频采集不仅会触发限制，也没有实际业务意义；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;返回的价格为商品展示价格，部分场景下多规格商品每个细分规格真实标价需要读取 sku 列表字段；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;部分店铺不会返回完整店铺信息，这属于平台返回限制，代码做好空值判断；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;工具自带缓存开关 cache 参数，如果需要实时最新数据设置&lt;code&gt;cache:&amp;quot;no&amp;quot;&lt;/code&gt;；调试的时候可以打开缓存加快响应速度。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;总结&lt;/h2&gt;&lt;p&gt;Open Claw 最大价值就是帮我们屏蔽了复杂页面解析，不用去处理页面改版、反访问、图片路径、规格拆解等脏活累活。开发者把重心放在&lt;strong&gt;监控业务逻辑、数据存储、业务分析&lt;/strong&gt;，几十行代码就搭建一套可用的商品监控体系。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Sun, 30 Aug 2026 17:57:01 +0800</pubDate></item><item><title>告别人工盯品：借助 Open Claw 快速搭建商品自动监控与数据分析系统（附完整可运行源码）</title><link>https://www.shopapi.com.cn/?id=225</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营、竞品跟踪、选品分析，很多人都遇到过这样的困境：&lt;/p&gt;&lt;p&gt;想要持续跟进多款商品的价格变动、库存余量、规格变化、店铺信息，尤其是大促期间，价格和优惠瞬息万变。如果依靠人工打开网页逐个复制记录，不仅每天耗费大量时间，还极易错过降价、库存清空等关键信号。&lt;/p&gt;&lt;p&gt;如果自己从零开发页面解析脚本，坑点会非常多：页面 JS 动态渲染、频繁访问触发平台风控封禁 IP、网页改版后原有解析逻辑直接失效；多规格款式、图片、店铺信息都需要单独编写解析逻辑，维护成本居高不下。&lt;/p&gt;&lt;p&gt;最近在实际项目中体验了 Open Claw 这套工具，很好地解决了以上痛点。不用处理复杂的网页解析、反访问限制等底层琐事，仅编写少量代码，就可以拿到标准化的商品全量信息，实现定时巡检、异动捕捉、历史数据存储，完成基础数据分析工作。&lt;/p&gt;&lt;p&gt;本文为完整实操教程，所有代码可以复制修改后直接运行，适合个人调研和中小团队竞品跟踪场景。&lt;/p&gt;&lt;h2&gt;Open Claw 能解决什么问题&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;拿到完整标准化商品资料&lt;/strong&gt;：商品标题、原价、实时售价、库存、多规格 SKU 信息、品牌、店铺名称、商品图、发货地等信息直接输出，不需要自己解析网页源码。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;支持实时 / 缓存两种模式&lt;/strong&gt;：做监控时开启实时模式，拿到最新价格库存；日常批量查询开启缓存，提升整体速度，减少资源消耗。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;不受前端页面改版影响&lt;/strong&gt;：网页样式更新不会打乱我们获取数据的逻辑，大幅降低脚本维护工作量。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;方便做二次分析&lt;/strong&gt;：输出结构化数据，可以直接存入本地数据库，做价格趋势、库存波动、竞品对标分析。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;提示：使用前需要在后台获取两组身份凭证，用于身份校验。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;整体实现思路&lt;/h2&gt;&lt;p&gt;整个小系统拆分为 4 个简单模块：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;3&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E6%95%B0%E6%8D%AE%E8%8E%B7%E5%8F%96%E6%A8%A1%E5%9D%97%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%226tOjb7-1788078577907%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;数据获取模块&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;数据获取模块&quot;&gt;数据获取模块&lt;/a&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/strong&gt;：输入商品编号，获取商品结构化信息。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据清洗模块&lt;/strong&gt;：过滤无效字段，提取业务关心的核心字段。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;定时监控模块&lt;/strong&gt;：循环定时巡检商品，对比前后数据，捕捉价格、库存变动。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;存储与分析模块&lt;/strong&gt;：把每次采集快照存入本地数据库，后续做历史查询、简单统计分析。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;环境准备&lt;/h2&gt;&lt;p&gt;只需要基础 Python 环境，安装网络请求库即可，无需复杂框架。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;完整实战代码&lt;/h2&gt;&lt;p&gt;新建文件&lt;code&gt;goods_monitor.py&lt;/code&gt;，复制下面全部代码，修改配置区凭证、需要监控的商品编号、巡检间隔，即可直接运行。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22import%20requests%5Cnimport%20json%5Cnimport%20sqlite3%5Cnimport%20time%5Cnfrom%20datetime%20import%20datetime%5Cn%5Cn%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%20%E9%85%8D%E7%BD%AE%E5%8C%BA%E5%9F%9F%EF%BC%8C%E8%AF%B7%E8%87%AA%E8%A1%8C%E4%BF%AE%E6%94%B9%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5CnCLAW_KEY%20%3D%20%5C%22%E5%A1%AB%E5%85%A5%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81key%5C%22%5CnCLAW_SECRET%20%3D%20%5C%22%E5%A1%AB%E5%85%A5%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81secret%5C%22%5Cn%23%20%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%EF%BC%8C%E6%94%AF%E6%8C%81%E5%A4%9A%E4%B8%AA%E5%95%86%E5%93%81%E6%89%B9%E9%87%8F%E7%9B%91%E6%8E%A7%5CnMONITOR_GOODS_ID%20%3D%20%5B%5C%2210335871600%5C%22%2C%20%5C%2210057467958584%5C%22%5D%5CnPOLL_INTERVAL%20%3D%20300%20%20%23%20%E5%B7%A1%E6%A3%80%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E5%BB%BA%E8%AE%AE%E4%B8%8D%E8%A6%81%E8%AE%BE%E7%BD%AE%E8%BF%87%E5%B0%8F%5CnPRICE_CHANGE_THRESHOLD%20%3D%200.1%20%20%23%20%E4%BB%B7%E6%A0%BC%E5%8F%98%E5%8A%A8%E8%B6%85%E8%BF%8710%25%E8%BE%93%E5%87%BA%E5%91%8A%E8%AD%A6%5CnDB_NAME%20%3D%20%5C%22goods_monitor.db%5C%22%5Cn%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5Cn%5Cndef%20init_database()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%88%9D%E5%A7%8B%E5%8C%96%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%BA%93%EF%BC%8C%E5%AD%98%E5%82%A8%E5%95%86%E5%93%81%E5%8E%86%E5%8F%B2%E5%BF%AB%E7%85%A7%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_NAME)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20CREATE%20TABLE%20IF%20NOT%20EXISTS%20goods_history%20(%5Cn%20%20%20%20%20%20%20%20id%20INTEGER%20PRIMARY%20KEY%20AUTOINCREMENT%2C%5Cn%20%20%20%20%20%20%20%20goods_id%20TEXT%20NOT%20NULL%2C%5Cn%20%20%20%20%20%20%20%20title%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20brand%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20current_price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20origin_price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20stock%20INTEGER%2C%5Cn%20%20%20%20%20%20%20%20shop_name%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20capture_time%20TIMESTAMP%5Cn%20%20%20%20)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;)%5Cn%20%20%20%20conn.commit()%5Cn%20%20%20%20conn.close()%5Cn%5Cn%5Cndef%20get_goods_detail(goods_id%3A%20str%2C%20real_time%3A%20bool%20%3D%20False)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%E8%AF%A6%E6%83%85%5Cn%20%20%20%20%3Aparam%20goods_id%3A%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%5Cn%20%20%20%20%3Aparam%20real_time%3A%20True%E8%8E%B7%E5%8F%96%E5%AE%9E%E6%97%B6%E6%9C%80%E6%96%B0%E6%95%B0%E6%8D%AE%EF%BC%8CFalse%E4%BD%BF%E7%94%A8%E7%BC%93%E5%AD%98%E6%8F%90%E5%8D%87%E9%80%9F%E5%BA%A6%5Cn%20%20%20%20%3Areturn%3A%E6%B8%85%E6%B4%97%E5%90%8E%E7%9A%84%E5%95%86%E5%93%81%E5%AD%97%E5%85%B8%EF%BC%8C%E5%A4%B1%E8%B4%A5%E8%BF%94%E5%9B%9ENone%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20CLAW_KEY%2C%5Cn%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20CLAW_SECRET%2C%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%20if%20real_time%20else%20%5C%22yes%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%20%5C%22json%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22lang%5C%22%3A%20%5C%22cn%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20retry_count%20%3D%203%5Cn%20%20%20%20for%20attempt%20in%20range(retry_count)%3A%5Cn%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20session%20%3D%20requests.Session()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20resp%20%3D%20session.get(%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22https%3A%2F%2Fapi-gw.onebound.cn%2Fjd%2Fitem_get_pro%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20params%3Dparams%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20timeout%3D15%5Cn%20%20%20%20%20%20%20%20%20%20%20%20)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20resp.raise_for_status()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20res_json%20%3D%20resp.json()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20item_data%20%3D%20res_json.get(%5C%22item%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20item_data%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%20%7Bgoods_id%7D%20%E6%9C%AA%E8%8E%B7%E5%8F%96%E5%88%B0%E6%9C%89%E6%95%88%E6%95%B0%E6%8D%AE%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E5%81%9A%E7%AE%80%E5%8D%95%E5%AD%97%E6%AE%B5%E6%B8%85%E6%B4%97%5Cn%20%20%20%20%20%20%20%20%20%20%20%20clean_data%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22goods_id%5C%22%3A%20item_data.get(%5C%22num_iid%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20item_data.get(%5C%22title%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22brand%5C%22%3A%20item_data.get(%5C%22brand%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22current_price%5C%22%3A%20float(item_data.get(%5C%22price%5C%22%2C%200))%20if%20item_data.get(%5C%22price%5C%22)%20else%200%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22origin_price%5C%22%3A%20float(item_data.get(%5C%22orginal_price%5C%22%2C%200))%20if%20item_data.get(%5C%22orginal_price%5C%22)%20else%200%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3A%20int(item_data.get(%5C%22num%5C%22%2C%200))%20if%20item_data.get(%5C%22num%5C%22)%20else%200%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20item_data.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22%2C%20%5C%22%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20clean_data%5Cn%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E7%AC%AC%7Battempt%2B1%7D%E6%AC%A1%E8%AF%B7%E6%B1%82%E5%BC%82%E5%B8%B8%EF%BC%9A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20time.sleep(2)%5Cn%20%20%20%20return%20None%5Cn%5Cn%5Cndef%20save_to_db(goods_data)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%B0%86%E9%87%87%E9%9B%86%E5%88%B0%E7%9A%84%E6%95%B0%E6%8D%AE%E5%86%99%E5%85%A5%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%BA%93%5C%22%5C%22%5C%22%5Cn%20%20%20%20if%20not%20goods_data%3A%5Cn%20%20%20%20%20%20%20%20return%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_NAME)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20INSERT%20INTO%20goods_history(goods_id%2Ctitle%2Cbrand%2Ccurrent_price%2Corigin_price%2Cstock%2Cshop_name%2Ccapture_time)%5Cn%20%20%20%20VALUES%20(%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C%20(%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22goods_id%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22title%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22brand%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22current_price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22origin_price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22stock%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22shop_name%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20datetime.now().strftime(%5C%22%25Y-%25m-%25d%20%25H%3A%25M%3A%25S%5C%22)%5Cn%20%20%20%20))%5Cn%20%20%20%20conn.commit()%5Cn%20%20%20%20conn.close()%5Cn%5Cn%5Cndef%20monitor_task()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E7%9B%91%E6%8E%A7%E4%B8%BB%E9%80%BB%E8%BE%91%EF%BC%8C%E5%AF%B9%E6%AF%94%E5%8E%86%E5%8F%B2%E4%BB%B7%E6%A0%BC%EF%BC%8C%E8%BE%93%E5%87%BA%E5%8F%98%E5%8A%A8%E5%91%8A%E8%AD%A6%5C%22%5C%22%5C%22%5Cn%20%20%20%20last_status%20%3D%20%7B%7D%5Cn%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%3D%3D%3D%3D%3D%20%E5%B7%A1%E6%A3%80%E5%BC%80%E5%A7%8B%20%7Bdatetime.now().strftime(&amp;#39;%25Y-%25m-%25d%20%25H%3A%25M%3A%25S&amp;#39;)%7D%20%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20%20%20%20%20for%20gid%20in%20MONITOR_GOODS_ID%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20current_info%20%3D%20get_goods_detail(gid%2C%20real_time%3DTrue)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20not%20current_info%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%5Cn%20%20%20%20%20%20%20%20%20%20%20%20save_to_db(current_info)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%3A%7Bcurrent_info%5B&amp;#39;title&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E7%8E%B0%E4%BB%B7%3A%7Bcurrent_info%5B&amp;#39;current_price&amp;#39;%5D%7D%2C%E5%8E%9F%E4%BB%B7%3A%7Bcurrent_info%5B&amp;#39;origin_price&amp;#39;%5D%7D%2C%E5%BA%93%E5%AD%98%3A%7Bcurrent_info%5B&amp;#39;stock&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E5%AF%B9%E6%AF%94%E4%B8%8A%E4%B8%80%E6%AC%A1%E7%8A%B6%E6%80%81%EF%BC%8C%E5%88%A4%E6%96%AD%E6%98%AF%E5%90%A6%E8%A7%A6%E5%8F%91%E5%91%8A%E8%AD%A6%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20gid%20in%20last_status%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20old_price%20%3D%20last_status%5Bgid%5D%5B%5C%22current_price%5C%22%5D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20new_price%20%3D%20current_info%5B%5C%22current_price%5C%22%5D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20old_price%20%3E%200%20and%20abs(new_price%20-%20old_price)%2Fold_price%20%3E%3D%20PRICE_CHANGE_THRESHOLD%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E2%9A%A0%EF%B8%8F%E3%80%90%E4%BB%B7%E6%A0%BC%E5%BC%82%E5%8A%A8%E6%8F%90%E9%86%92%E3%80%91%E5%95%86%E5%93%81%7Bgid%7D%EF%BC%8C%E4%BB%B7%E6%A0%BC%E7%94%B1%7Bold_price%7D%E5%8F%98%E5%8A%A8%E8%87%B3%7Bnew_price%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20last_status%5Bgid%5D%20%3D%20current_info%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%3D%3D%3D%3D%3D%20%E6%9C%AC%E8%BD%AE%E5%B7%A1%E6%A3%80%E7%BB%93%E6%9D%9F%EF%BC%8C%E7%AD%89%E5%BE%85%20%7BPOLL_INTERVAL%7D%20%E7%A7%92%E5%90%8E%E4%B8%8B%E4%B8%80%E8%BD%AE%20%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20%20%20%20%20time.sleep(POLL_INTERVAL)%5Cn%5Cn%5Cndef%20analysis_demo()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E7%AE%80%E5%8D%95%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E7%A4%BA%E4%BE%8B%EF%BC%9A%E8%AF%BB%E5%8F%96%E6%95%B0%E6%8D%AE%E5%BA%93%EF%BC%8C%E6%89%93%E5%8D%B0%E6%9F%90%E5%95%86%E5%93%81%E7%9A%84%E4%BB%B7%E6%A0%BC%E5%8E%86%E5%8F%B2%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_NAME)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20target_goods%20%3D%20MONITOR_GOODS_ID%5B0%5D%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20SELECT%20capture_time%2Ccurrent_price%2Cstock%20FROM%20goods_history%20WHERE%20goods_id%3D%3F%20ORDER%20BY%20capture_time%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C%20(target_goods%2C))%5Cn%20%20%20%20rows%20%3D%20cursor.fetchall()%5Cn%20%20%20%20print(f%5C%22%5C%5Cn%3D%3D%3D%3D%3D%E5%95%86%E5%93%81%20%7Btarget_goods%7D%20%E5%8E%86%E5%8F%B2%E4%BB%B7%E6%A0%BC%E5%BA%93%E5%AD%98%E8%AE%B0%E5%BD%95%20%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20for%20row%20in%20rows%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E6%97%B6%E9%97%B4%3A%7Brow%5B0%5D%7D%20%7C%20%E4%BB%B7%E6%A0%BC%3A%7Brow%5B1%5D%7D%20%7C%20%E5%BA%93%E5%AD%98%3A%7Brow%5B2%5D%7D%5C%22)%5Cn%20%20%20%20conn.close()%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20init_database()%5Cn%20%20%20%20print(%5C%22%E5%88%9D%E5%A7%8B%E5%8C%96%E5%AE%8C%E6%88%90%EF%BC%8C%E5%90%AF%E5%8A%A8%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%EF%BC%8CCtrl%2BC%E7%BB%88%E6%AD%A2%E7%A8%8B%E5%BA%8F%5C%22)%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20monitor_task()%5Cn%20%20%20%20except%20KeyboardInterrupt%3A%5Cn%20%20%20%20%20%20%20%20print(%5C%22%5C%5Cn%E7%A8%8B%E5%BA%8F%E6%89%8B%E5%8A%A8%E7%BB%88%E6%AD%A2%EF%BC%8C%E8%BF%90%E8%A1%8Canalysis_demo()%E5%8F%AF%E4%BB%A5%E6%9F%A5%E7%9C%8B%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E7%BB%93%E6%9E%9C%5C%22)%5Cn%20%20%20%20%20%20%20%20analysis_demo()%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;import&amp;nbsp;requests
import&amp;nbsp;json
import&amp;nbsp;sqlite3
import&amp;nbsp;time
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime

#&amp;nbsp;=====================&amp;nbsp;配置区域，请自行修改&amp;nbsp;=====================
CLAW_KEY&amp;nbsp;=&amp;nbsp;&amp;quot;填入你的凭证key&amp;quot;
CLAW_SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;填入你的凭证secret&amp;quot;
#&amp;nbsp;需要监控的商品编号，支持多个商品批量监控
MONITOR_GOODS_ID&amp;nbsp;=&amp;nbsp;[&amp;quot;10335871600&amp;quot;,&amp;nbsp;&amp;quot;10057467958584&amp;quot;]
POLL_INTERVAL&amp;nbsp;=&amp;nbsp;300&amp;nbsp;&amp;nbsp;#&amp;nbsp;巡检间隔，单位秒，建议不要设置过小
PRICE_CHANGE_THRESHOLD&amp;nbsp;=&amp;nbsp;0.1&amp;nbsp;&amp;nbsp;#&amp;nbsp;价格变动超过10%输出告警
DB_NAME&amp;nbsp;=&amp;nbsp;&amp;quot;goods_monitor.db&amp;quot;
#&amp;nbsp;=================================================================

def&amp;nbsp;init_database():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;初始化本地数据库，存储商品历史快照&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_NAME)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CREATE&amp;nbsp;TABLE&amp;nbsp;IF&amp;nbsp;NOT&amp;nbsp;EXISTS&amp;nbsp;goods_history&amp;nbsp;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;id&amp;nbsp;INTEGER&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;AUTOINCREMENT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_id&amp;nbsp;TEXT&amp;nbsp;NOT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;brand&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;current_price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;origin_price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stock&amp;nbsp;INTEGER,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shop_name&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;capture_time&amp;nbsp;TIMESTAMP
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.commit()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()


def&amp;nbsp;get_goods_detail(goods_id:&amp;nbsp;str,&amp;nbsp;real_time:&amp;nbsp;bool&amp;nbsp;=&amp;nbsp;False):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;获取商品详情
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:param&amp;nbsp;goods_id:商品编号
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:param&amp;nbsp;real_time:&amp;nbsp;True获取实时最新数据，False使用缓存提升速度
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:return:清洗后的商品字典，失败返回None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;CLAW_KEY,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;CLAW_SECRET,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;&amp;nbsp;if&amp;nbsp;real_time&amp;nbsp;else&amp;nbsp;&amp;quot;yes&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;nbsp;&amp;quot;json&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;lang&amp;quot;:&amp;nbsp;&amp;quot;cn&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retry_count&amp;nbsp;=&amp;nbsp;3
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;attempt&amp;nbsp;in&amp;nbsp;range(retry_count):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;session&amp;nbsp;=&amp;nbsp;requests.Session()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;session.get(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;https://api-gw.onebound.cn/jd/item_get_pro&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params=params,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timeout=15
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp.raise_for_status()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;res_json&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_data&amp;nbsp;=&amp;nbsp;res_json.get(&amp;quot;item&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;item_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品&amp;nbsp;{goods_id}&amp;nbsp;未获取到有效数据&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;做简单字段清洗
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;clean_data&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;goods_id&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;num_iid&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;brand&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;brand&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;current_price&amp;quot;:&amp;nbsp;float(item_data.get(&amp;quot;price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;price&amp;quot;)&amp;nbsp;else&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;origin_price&amp;quot;:&amp;nbsp;float(item_data.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;orginal_price&amp;quot;)&amp;nbsp;else&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;int(item_data.get(&amp;quot;num&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;num&amp;quot;)&amp;nbsp;else&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;clean_data
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;第{attempt+1}次请求异常：{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None


def&amp;nbsp;save_to_db(goods_data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;将采集到的数据写入本地数据库&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;goods_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_NAME)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INSERT&amp;nbsp;INTO&amp;nbsp;goods_history(goods_id,title,brand,current_price,origin_price,stock,shop_name,capture_time)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;VALUES&amp;nbsp;(?,?,?,?,?,?,?,?)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,&amp;nbsp;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;goods_id&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;title&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;brand&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;current_price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;origin_price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;stock&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;shop_name&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;datetime.now().strftime(&amp;quot;%Y-%m-%d&amp;nbsp;%H:%M:%S&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.commit()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()


def&amp;nbsp;monitor_task():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;监控主逻辑，对比历史价格，输出变动告警&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_status&amp;nbsp;=&amp;nbsp;{}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n=====&amp;nbsp;巡检开始&amp;nbsp;{datetime.now().strftime(&amp;#39;%Y-%m-%d&amp;nbsp;%H:%M:%S&amp;#39;)}&amp;nbsp;=====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;gid&amp;nbsp;in&amp;nbsp;MONITOR_GOODS_ID:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;current_info&amp;nbsp;=&amp;nbsp;get_goods_detail(gid,&amp;nbsp;real_time=True)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;current_info:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_to_db(current_info)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品:{current_info[&amp;#39;title&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;现价:{current_info[&amp;#39;current_price&amp;#39;]},原价:{current_info[&amp;#39;origin_price&amp;#39;]},库存:{current_info[&amp;#39;stock&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;对比上一次状态，判断是否触发告警
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;gid&amp;nbsp;in&amp;nbsp;last_status:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_price&amp;nbsp;=&amp;nbsp;last_status[gid][&amp;quot;current_price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_price&amp;nbsp;=&amp;nbsp;current_info[&amp;quot;current_price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;old_price&amp;nbsp;&amp;gt;&amp;nbsp;0&amp;nbsp;and&amp;nbsp;abs(new_price&amp;nbsp;-&amp;nbsp;old_price)/old_price&amp;nbsp;&amp;gt;=&amp;nbsp;PRICE_CHANGE_THRESHOLD:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;⚠️【价格异动提醒】商品{gid}，价格由{old_price}变动至{new_price}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_status[gid]&amp;nbsp;=&amp;nbsp;current_info
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;=====&amp;nbsp;本轮巡检结束，等待&amp;nbsp;{POLL_INTERVAL}&amp;nbsp;秒后下一轮&amp;nbsp;=====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(POLL_INTERVAL)


def&amp;nbsp;analysis_demo():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;简单数据分析示例：读取数据库，打印某商品的价格历史&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_NAME)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;target_goods&amp;nbsp;=&amp;nbsp;MONITOR_GOODS_ID[0]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECT&amp;nbsp;capture_time,current_price,stock&amp;nbsp;FROM&amp;nbsp;goods_history&amp;nbsp;WHERE&amp;nbsp;goods_id=?&amp;nbsp;ORDER&amp;nbsp;BY&amp;nbsp;capture_time
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,&amp;nbsp;(target_goods,))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rows&amp;nbsp;=&amp;nbsp;cursor.fetchall()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n=====商品&amp;nbsp;{target_goods}&amp;nbsp;历史价格库存记录&amp;nbsp;=====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;row&amp;nbsp;in&amp;nbsp;rows:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;时间:{row[0]}&amp;nbsp;|&amp;nbsp;价格:{row[1]}&amp;nbsp;|&amp;nbsp;库存:{row[2]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init_database()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;初始化完成，启动监控任务，Ctrl+C终止程序&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor_task()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;KeyboardInterrupt:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n程序手动终止，运行analysis_demo()可以查看历史数据分析结果&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis_demo()&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;代码说明&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;初始化数据库&lt;/strong&gt;：本地 SQLite 文件数据库，无需额外安装数据库服务，每次采集会写入一条快照记录。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据获取函数&lt;/strong&gt;：内置 3 次失败重试，网络抖动的时候提高稳定性；支持切换实时 / 缓存模式。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据清洗&lt;/strong&gt;：只保留业务上最核心字段，过滤大量无关内容。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;监控主循环&lt;/strong&gt;：定时循环遍历监控列表，对比前后价格，变动幅度超过阈值直接控制台输出告警。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;简易分析示例&lt;/strong&gt;：程序终止之后，可以调用&lt;code&gt;analysis_demo()&lt;/code&gt;读取本地存储的历史记录，输出价格、库存随时间变化记录。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;如何落地扩展&lt;/h2&gt;&lt;h3&gt;1、告警能力升级&lt;/h3&gt;&lt;p&gt;现在告警只是打印控制台，实际项目可以扩展为：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;对接企业微信 / 飞书机器人，价格变动推送消息到工作群&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;邮件通知、短信提醒&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;2、多规格 SKU 监控&lt;/h3&gt;&lt;p&gt;原始返回内容中包含完整 SKU 数组，每一个子规格拥有独立编号、价格、库存。可以修改解析逻辑，单独跟踪每一个规格的价格与库存，适合服装、多型号数码产品。&lt;/p&gt;&lt;h3&gt;3、导出报表&lt;/h3&gt;&lt;p&gt;可以结合 pandas 库，直接读取 sqlite 数据库，导出 csv、Excel 报表，用于运营周报，做竞品对标分析。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22import%20pandas%20as%20pd%5Cnconn%20%3D%20sqlite3.connect(%5C%22goods_monitor.db%5C%22)%5Cndf%20%3D%20pd.read_sql(%5C%22select%20*%20from%20goods_history%5C%22%2Cconn)%5Cndf.to_excel(%5C%22%E5%95%86%E5%93%81%E7%9B%91%E6%8E%A7%E6%8A%A5%E8%A1%A8.xlsx%5C%22%2Cindex%3DFalse)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd
conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(&amp;quot;goods_monitor.db&amp;quot;)
df&amp;nbsp;=&amp;nbsp;pd.read_sql(&amp;quot;select&amp;nbsp;*&amp;nbsp;from&amp;nbsp;goods_history&amp;quot;,conn)
df.to_excel(&amp;quot;商品监控报表.xlsx&amp;quot;,index=False)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h3&gt;4、部署后台持续运行&lt;/h3&gt;&lt;p&gt;Linux 服务器可以使用 nohup、systemd 把脚本放在后台长期运行，实现 7×24 小时不间断监控。Windows 可以使用任务计划程序定时启动。&lt;/p&gt;&lt;h2&gt;注意事项&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;巡检间隔不要设置过小，过高频率不仅消耗配额，也没有实际业务意义。普通商品建议 3‑10 分钟巡检一次。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;身份凭证注意保管，不要随代码上传公开代码仓库。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;原始返回中会携带大量字段，开发时可以根据业务需求，按需取用，不用全部保存入库。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;总结&lt;/h2&gt;&lt;p&gt;很多做电商、做调研的同学，要么人工手动记录，要么花大量时间维护网页抓取脚本。Open Claw 把底层复杂的网页处理做了封装，开发者只需要聚焦业务逻辑：监控哪些商品、什么条件触发提醒、如何分析历史数据。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Sun, 30 Aug 2026 16:34:35 +0800</pubDate></item><item><title>告别人工盯品：借助 Open‑Claw 快速搭建电商商品监控与数据分析体系（附完整 Python 代码）</title><link>https://www.shopapi.com.cn/?id=224</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营、选品、竞品调研的时候，相信很多朋友都遇到过这些痛点：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;需要持续盯多款竞品，人工反复打开页面查看价格、库存、促销活动，耗费大量时间；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;大促期间竞品调价、库存变动节奏很快，人很难做到 7×24 小时实时捕捉变化；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;拿到一堆零散的商品信息，很难沉淀历史数据，做价格趋势、SKU 规格对比分析；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;直接从网页解析页面，容易遇到访问限制、页面改版导致脚本直接失效，维护成本很高。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;之前我尝试过写页面解析脚本，但是平台页面结构经常调整，隔一段时间代码就失效，调试反爬相关逻辑消耗大量精力。后来接触到 Open‑Claw，它可以直接输出结构化标准化的商品全量资料，不需要再折腾页面解析逻辑，我们只需要专注做监控逻辑、数据存储、业务分析，快速搭一套属于自己的商品监测工具。&lt;/p&gt;&lt;p&gt;本篇文章面向开发者、电商从业者，完整讲解从获取商品信息、变化监测、数据存储，再到简单数据分析的整套落地流程，代码可以直接修改配置后运行。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;说明：本教程仅做技术学习演示，使用时请遵守对应平台使用规范，合理控制访问频次。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;整体实现思路&lt;/h2&gt;&lt;p&gt;整套工具分成 4 个核心模块：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E5%95%86%E5%93%81%E4%BF%A1%E6%81%AF%E8%8E%B7%E5%8F%96%E6%A8%A1%E5%9D%97%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%222bojHB-1787797915338%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;商品信息获取模块&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;商品信息获取模块&quot;&gt;商品信息获取模块&lt;/a&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/strong&gt;：输入商品编号，返回结构化完整商品资料，包含标题、售价、原价、库存、规格、店铺信息、图片等内容；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;变化检测模块&lt;/strong&gt;：对比上一轮保存的数据，识别价格涨跌、库存变动、规格变更，输出告警提示；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;本地持久化模块&lt;/strong&gt;：把每一轮采集的数据保存到本地 JSON/CSV，沉淀历史时序数据；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;简易数据分析模块&lt;/strong&gt;：读取历史记录，做价格波动统计、SKU 规格整理，输出简单统计结果。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;环境准备&lt;/h2&gt;&lt;p&gt;依赖安装，执行下面命令：&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%20pandas%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&amp;nbsp;pandas&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;完整可运行代码实现&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20coding%3Autf-8%5Cnimport%20requests%5Cnimport%20json%5Cnimport%20time%5Cnimport%20os%5Cnimport%20pandas%20as%20pd%5Cnfrom%20datetime%20import%20datetime%5Cn%5Cn%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%20%E9%85%8D%E7%BD%AE%E5%8C%BA%EF%BC%8C%E8%AF%B7%E4%BF%AE%E6%94%B9%E4%B8%BA%E4%BD%A0%E8%87%AA%E5%B7%B1%E7%9A%84%E5%87%AD%E8%AF%81%E5%92%8C%E7%9B%91%E6%8E%A7%E5%95%86%E5%93%81%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5CnCLAW_KEY%20%3D%20%5C%22%E5%A1%AB%E5%85%A5%E4%BD%A0%E7%9A%84key%5C%22%5CnCLAW_SECRET%20%3D%20%5C%22%E5%A1%AB%E5%85%A5%E4%BD%A0%E7%9A%84secret%5C%22%5Cn%23%20%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%E5%88%97%E8%A1%A8%5CnMONITOR_GOODS%20%3D%20%5B%5C%2210335871600%5C%22%2C%20%5C%2210057467958584%5C%22%5D%5Cn%23%20%E5%BE%AA%E7%8E%AF%E6%A3%80%E6%B5%8B%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E4%B8%8D%E5%BB%BA%E8%AE%AE%E8%AE%BE%E7%BD%AE%E8%BF%87%E5%B0%8F%EF%BC%8C%E7%A4%BA%E4%BE%8B5%E5%88%86%E9%92%9F%E8%BD%AE%E8%AF%A2%E4%B8%80%E6%AC%A1%5CnMONITOR_INTERVAL%20%3D%20300%5Cn%23%20%E9%99%8D%E4%BB%B7%E5%91%8A%E8%AD%A6%E9%98%88%E5%80%BC%EF%BC%8C0.1%E4%BB%A3%E8%A1%A8%E4%BB%B7%E6%A0%BC%E4%B8%8B%E8%B7%8C%E8%B6%85%E8%BF%8710%25%E8%A7%A6%E5%8F%91%E6%8F%90%E9%86%92%5CnPRICE_DROP_ALERT%20%3D%200.1%5Cn%23%20%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E5%AD%98%E5%82%A8%E6%96%87%E4%BB%B6%5CnHISTORY_FILE%20%3D%20%5C%22goods_history.json%5C%22%5CnCSV_EXPORT%20%3D%20%5C%22goods_record.csv%5C%22%5Cn%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5Cn%5Cn%5Cndef%20load_last_cache()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%8A%A0%E8%BD%BD%E4%B8%8A%E4%B8%80%E8%BD%AE%E7%BC%93%E5%AD%98%E7%9A%84%E5%95%86%E5%93%81%E6%95%B0%E6%8D%AE%5C%22%5C%22%5C%22%5Cn%20%20%20%20if%20os.path.exists(HISTORY_FILE)%3A%5Cn%20%20%20%20%20%20%20%20with%20open(HISTORY_FILE%2C%20%5C%22r%5C%22%2C%20encoding%3D%5C%22utf%E2%80%918%5C%22)%20as%20f%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20json.load(f)%5Cn%20%20%20%20return%20%7B%7D%5Cn%5Cn%5Cndef%20save_cache(data)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E4%BF%9D%E5%AD%98%E5%BD%93%E5%89%8D%E9%87%87%E9%9B%86%E7%BB%93%E6%9E%9C%E5%88%B0%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%5C%22%5C%22%5C%22%5Cn%20%20%20%20with%20open(HISTORY_FILE%2C%20%5C%22w%5C%22%2C%20encoding%3D%5C%22utf%E2%80%918%5C%22)%20as%20f%3A%5Cn%20%20%20%20%20%20%20%20json.dump(data%2C%20f%2C%20ensure_ascii%3DFalse%2C%20indent%3D2)%5Cn%5Cn%5Cndef%20fetch_goods_data(goods_id)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20%E8%8E%B7%E5%8F%96%E5%8D%95%E4%B8%AA%E5%95%86%E5%93%81%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE%5Cn%20%20%20%20%3Aparam%20goods_id%3A%20%E5%95%86%E5%93%81%E6%95%B0%E5%AD%97%E7%BC%96%E5%8F%B7%5Cn%20%20%20%20%3Areturn%3A%20%E8%A7%A3%E6%9E%90%E5%AE%8C%E6%88%90%E7%9A%84%E5%95%86%E5%93%81%E5%AD%97%E5%85%B8%EF%BC%8C%E5%A4%B1%E8%B4%A5%E8%BF%94%E5%9B%9ENone%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20base_url%20%3D%20%5C%22https%3A%2F%2Fapi%E2%80%91gw.onebound.cn%2Fjd%2Fitem_get_pro%5C%22%5Cn%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20CLAW_KEY%2C%5Cn%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20CLAW_SECRET%2C%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%20%5C%22json%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22yes%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22lang%5C%22%3A%20%5C%22cn%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20headers%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22Accept%E2%80%91Encoding%5C%22%3A%20%5C%22gzip%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22Connection%5C%22%3A%20%5C%22close%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(base_url%2C%20params%3Dparams%2C%20headers%3Dheaders%2C%20timeout%3D20)%5Cn%20%20%20%20%20%20%20%20resp_json%20%3D%20resp.json()%5Cn%20%20%20%20%20%20%20%20%23%20%E5%88%A4%E6%96%AD%E8%BF%94%E5%9B%9E%E7%8A%B6%E6%80%81%EF%BC%8C%E9%9D%9E0000%E4%BB%A3%E8%A1%A8%E5%BC%82%E5%B8%B8%5Cn%20%20%20%20%20%20%20%20if%20resp_json.get(%5C%22error_code%5C%22)%20!%3D%20%5C%220000%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20err_msg%20%3D%20resp_json.get(%5C%22reason%5C%22%2C%20%5C%22%E6%9C%AA%E7%9F%A5%E5%BC%82%E5%B8%B8%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E8%8E%B7%E5%8F%96%E5%A4%B1%E8%B4%A5%E3%80%91%E5%95%86%E5%93%81%7Bgoods_id%7D%EF%BC%8C%E5%8E%9F%E5%9B%A0%EF%BC%9A%7Berr_msg%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20item_data%20%3D%20resp_json.get(%5C%22item%5C%22%2C%20%7B%7D)%5Cn%20%20%20%20%20%20%20%20if%20not%20item_data%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E8%8E%B7%E5%8F%96%E5%A4%B1%E8%B4%A5%E3%80%91%E5%95%86%E5%93%81%7Bgoods_id%7D%E8%BF%94%E5%9B%9E%E5%86%85%E5%AE%B9%E4%B8%BA%E7%A9%BA%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20%20%20%20%20%23%20%E6%8F%90%E5%8F%96%E6%A0%B8%E5%BF%83%E4%B8%9A%E5%8A%A1%E5%AD%97%E6%AE%B5%EF%BC%8C%E5%81%9A%E6%A0%87%E5%87%86%E5%8C%96%E6%95%B4%E7%90%86%5Cn%20%20%20%20%20%20%20%20parse_result%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22goods_id%5C%22%3A%20item_data.get(%5C%22num_iid%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20item_data.get(%5C%22title%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22price%5C%22%3A%20float(item_data.get(%5C%22price%5C%22%2C%200))%20if%20item_data.get(%5C%22price%5C%22)%20else%200.0%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22original_price%5C%22%3A%20float(item_data.get(%5C%22orginal_price%5C%22%2C%200))%20if%20item_data.get(%5C%22orginal_price%5C%22)%20else%200.0%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3A%20item_data.get(%5C%22num%5C%22%2C%200)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20item_data.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22detail_url%5C%22%3A%20item_data.get(%5C%22detail_url%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22collect_time%5C%22%3A%20datetime.now().strftime(%5C%22%25Y%E2%80%91%25m%E2%80%91%25d%20%25H%3A%25M%3A%25S%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22skus%5C%22%3A%20item_data.get(%5C%22skus%5C%22%2C%20%7B%7D).get(%5C%22sku%5C%22%2C%20%5B%5D)%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20return%20parse_result%5Cn%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E8%AF%B7%E6%B1%82%E5%BC%82%E5%B8%B8%E3%80%91%E5%95%86%E5%93%81%7Bgoods_id%7D%2C%20%E9%94%99%E8%AF%AF%EF%BC%9A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%5Cndef%20detect_change(old_data%2C%20new_data)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%AF%B9%E6%AF%94%E6%96%B0%E6%97%A7%E4%B8%A4%E4%BB%BD%E6%95%B0%E6%8D%AE%EF%BC%8C%E6%A3%80%E6%B5%8B%E4%BB%B7%E6%A0%BC%E5%BA%93%E5%AD%98%E5%8F%98%E5%8C%96%EF%BC%8C%E8%BE%93%E5%87%BA%E5%91%8A%E8%AD%A6%E4%BF%A1%E6%81%AF%5C%22%5C%22%5C%22%5Cn%20%20%20%20alert_list%20%3D%20%5B%5D%5Cn%20%20%20%20if%20not%20old_data%3A%5Cn%20%20%20%20%20%20%20%20alert_list.append(%5C%22%E9%A6%96%E6%AC%A1%E9%87%87%E9%9B%86%E8%AF%A5%E5%95%86%E5%93%81%EF%BC%8C%E6%97%A0%E5%8E%86%E5%8F%B2%E5%AF%B9%E6%AF%94%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20alert_list%5Cn%5Cn%20%20%20%20old_price%20%3D%20old_data%5B%5C%22price%5C%22%5D%5Cn%20%20%20%20new_price%20%3D%20new_data%5B%5C%22price%5C%22%5D%5Cn%20%20%20%20old_stock%20%3D%20old_data%5B%5C%22stock%5C%22%5D%5Cn%20%20%20%20new_stock%20%3D%20new_data%5B%5C%22stock%5C%22%5D%5Cn%5Cn%20%20%20%20%23%20%E4%BB%B7%E6%A0%BC%E5%8F%98%E5%8A%A8%E6%A3%80%E6%B5%8B%5Cn%20%20%20%20if%20abs(new_price%20-%20old_price)%20%3E%200.01%3A%5Cn%20%20%20%20%20%20%20%20change_rate%20%3D%20(new_price%20-%20old_price)%20%2F%20old_price%20if%20old_price%20%3E%200%20else%200%5Cn%20%20%20%20%20%20%20%20if%20new_price%20%3C%20old_price%20and%20abs(change_rate)%20%3E%3D%20PRICE_DROP_ALERT%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20alert_list.append(f%5C%22%E2%9A%A0%EF%B8%8F%E9%99%8D%E4%BB%B7%E5%91%8A%E8%AD%A6%EF%BC%81%7Bold_price%7D%20%E2%86%92%20%7Bnew_price%7D%EF%BC%8C%E9%99%8D%E5%B9%85%7Babs(change_rate)*100%3A.1f%7D%25%5C%22)%5Cn%20%20%20%20%20%20%20%20elif%20new_price%20%3E%20old_price%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20alert_list.append(f%5C%22%F0%9F%93%88%E6%B6%A8%E4%BB%B7%E6%8F%90%E9%86%92%EF%BC%81%7Bold_price%7D%20%E2%86%92%20%7Bnew_price%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20else%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20alert_list.append(f%5C%22%F0%9F%92%A1%E4%BB%B7%E6%A0%BC%E5%BE%AE%E8%B0%83%EF%BC%9A%7Bold_price%7D%20%E2%86%92%20%7Bnew_price%7D%5C%22)%5Cn%5Cn%20%20%20%20%23%20%E5%BA%93%E5%AD%98%E5%8F%98%E5%8A%A8%E6%A3%80%E6%B5%8B%5Cn%20%20%20%20if%20new_stock%20!%3D%20old_stock%3A%5Cn%20%20%20%20%20%20%20%20if%20new_stock%20%3D%3D%200%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20alert_list.append(f%5C%22%F0%9F%9A%A8%E5%BA%93%E5%AD%98%E5%91%8A%E6%80%A5%EF%BC%8C%E5%95%86%E5%93%81%E5%8F%98%E4%B8%BA%E6%97%A0%E8%B4%A7%EF%BC%81%E5%8E%9F%E5%BA%93%E5%AD%98%EF%BC%9A%7Bold_stock%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20elif%20old_stock%20%3D%3D%200%20and%20new_stock%20%3E%200%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20alert_list.append(f%5C%22%E2%9C%85%E5%95%86%E5%93%81%E8%A1%A5%E8%B4%A7%E6%88%90%E5%8A%9F%EF%BC%81%E5%BD%93%E5%89%8D%E5%BA%93%E5%AD%98%20%7Bnew_stock%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20else%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20alert_list.append(f%5C%22%F0%9F%93%A6%E5%BA%93%E5%AD%98%E5%8F%98%E5%8A%A8%20%7Bold_stock%7D%20%E2%86%92%20%7Bnew_stock%7D%5C%22)%5Cn%20%20%20%20return%20alert_list%5Cn%5Cn%5Cndef%20export_to_csv(all_records)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E6%8A%8A%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%E5%AF%BC%E5%87%BAcsv%EF%BC%8C%E6%96%B9%E4%BE%BF%E5%90%8E%E7%BB%AD%E5%81%9A%E8%A1%A8%E6%A0%BC%E5%88%86%E6%9E%90%5C%22%5C%22%5C%22%5Cn%20%20%20%20rows%20%3D%20%5B%5D%5Cn%20%20%20%20for%20goods_id%2C%20records%20in%20all_records.items()%3A%5Cn%20%20%20%20%20%20%20%20for%20rec%20in%20records%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20rows.append(%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22%E9%87%87%E9%9B%86%E6%97%B6%E9%97%B4%5C%22%3A%20rec%5B%5C%22collect_time%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22%E5%95%86%E5%93%81ID%5C%22%3A%20rec%5B%5C%22goods_id%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22%E5%95%86%E5%93%81%E6%A0%87%E9%A2%98%5C%22%3A%20rec%5B%5C%22title%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22%E7%8E%B0%E4%BB%B7%5C%22%3A%20rec%5B%5C%22price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22%E5%8E%9F%E4%BB%B7%5C%22%3A%20rec%5B%5C%22original_price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22%E5%BA%93%E5%AD%98%5C%22%3A%20rec%5B%5C%22stock%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22%E5%BA%97%E9%93%BA%E5%90%8D%E7%A7%B0%5C%22%3A%20rec%5B%5C%22shop_name%5C%22%5D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%7D)%5Cn%20%20%20%20df%20%3D%20pd.DataFrame(rows)%5Cn%20%20%20%20df.to_csv(CSV_EXPORT%2C%20index%3DFalse%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%5Cn%5Cndef%20simple_analysis()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E7%AE%80%E6%98%93%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E7%A4%BA%E4%BE%8B%EF%BC%9A%E8%AF%BB%E5%8F%96csv%EF%BC%8C%E7%BB%9F%E8%AE%A1%E4%BB%B7%E6%A0%BC%E5%8C%BA%E9%97%B4%E3%80%81%E8%BE%93%E5%87%BA%E5%9F%BA%E7%A1%80%E7%BB%9F%E8%AE%A1%E4%BF%A1%E6%81%AF%5C%22%5C%22%5C%22%5Cn%20%20%20%20if%20not%20os.path.exists(CSV_EXPORT)%3A%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E6%9A%82%E6%97%A0%E5%AF%BC%E5%87%BA%E8%AE%B0%E5%BD%95%EF%BC%8C%E8%B7%B3%E8%BF%87%E5%88%86%E6%9E%90%5C%22)%5Cn%20%20%20%20%20%20%20%20return%5Cn%20%20%20%20df%20%3D%20pd.read_csv(CSV_EXPORT%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%20%20%20%20print(%5C%22%5C%5Cn%3D%3D%3D%3D%3D%E7%AE%80%E6%98%93%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20for%20gid%20in%20df%5B%5C%22%E5%95%86%E5%93%81ID%5C%22%5D.unique()%3A%5Cn%20%20%20%20%20%20%20%20sub_df%20%3D%20df%5Bdf%5B%5C%22%E5%95%86%E5%93%81ID%5C%22%5D%20%3D%3D%20gid%5D%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E5%95%86%E5%93%81ID%20%7Bgid%7D%EF%BC%9A%7Bsub_df.iloc%5B0%5D%5B&amp;#39;%E5%95%86%E5%93%81%E6%A0%87%E9%A2%98&amp;#39;%5D%5B%3A30%5D%7D...%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%20%20%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%E6%9D%A1%E6%95%B0%EF%BC%9A%7Blen(sub_df)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%20%20%E5%8E%86%E5%8F%B2%E6%9C%80%E4%BD%8E%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Bsub_df%5B&amp;#39;%E7%8E%B0%E4%BB%B7&amp;#39;%5D.min()%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%20%20%E5%8E%86%E5%8F%B2%E6%9C%80%E9%AB%98%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Bsub_df%5B&amp;#39;%E7%8E%B0%E4%BB%B7&amp;#39;%5D.max()%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%20%20%E5%B9%B3%E5%9D%87%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Bround(sub_df%5B&amp;#39;%E7%8E%B0%E4%BB%B7&amp;#39;%5D.mean()%2C2)%7D%5C%22)%5Cn%5Cn%5Cndef%20main_loop()%3A%5Cn%20%20%20%20print(%5C%22%3D%3D%3D%3D%E5%95%86%E5%93%81%E7%9B%91%E6%8E%A7%E7%A8%8B%E5%BA%8F%E5%90%AF%E5%8A%A8%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20cache_data%20%3D%20load_last_cache()%5Cn%20%20%20%20history_records%20%3D%20%7B%7D%5Cn%20%20%20%20%23%20%E5%88%9D%E5%A7%8B%E5%8C%96%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95%E5%AE%B9%E5%99%A8%5Cn%20%20%20%20for%20gid%20in%20MONITOR_GOODS%3A%5Cn%20%20%20%20%20%20%20%20if%20gid%20not%20in%20history_records%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20history_records%5Bgid%5D%20%3D%20%5B%5D%5Cn%5Cn%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20now_time%20%3D%20datetime.now().strftime(%5C%22%25Y%E2%80%91%25m%E2%80%91%25d%20%25H%3A%25M%3A%25S%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E3%80%90%E6%9C%AC%E8%BD%AE%E9%87%87%E9%9B%86%E6%97%B6%E9%97%B4%E3%80%91%7Bnow_time%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20for%20goods_id%20in%20MONITOR_GOODS%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20new_item%20%3D%20fetch_goods_data(goods_id)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20new_item%20is%20None%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E4%BF%9D%E5%AD%98%E5%88%B0%E5%85%A8%E9%87%8F%E5%8E%86%E5%8F%B2%EF%BC%8C%E7%94%A8%E4%BA%8E%E5%AF%BC%E5%87%BA%E6%8A%A5%E8%A1%A8%5Cn%20%20%20%20%20%20%20%20%20%20%20%20history_records%5Bgoods_id%5D.append(new_item)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20old_item%20%3D%20cache_data.get(goods_id)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20alerts%20%3D%20detect_change(old_item%2C%20new_item)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%3E%3E%E5%95%86%E5%93%81%EF%BC%9A%7Bnew_item%5B&amp;#39;title&amp;#39;%5D%5B%3A40%5D%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20for%20msg%20in%20alerts%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%20%20%20%20%7Bmsg%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E6%9B%B4%E6%96%B0%E7%BC%93%E5%AD%98%E4%B8%BA%E5%BD%93%E5%89%8D%E6%9C%80%E6%96%B0%E6%95%B0%E6%8D%AE%5Cn%20%20%20%20%20%20%20%20%20%20%20%20cache_data%5Bgoods_id%5D%20%3D%20new_item%5Cn%20%20%20%20%20%20%20%20%23%20%E5%86%99%E5%85%A5%E6%9C%AC%E5%9C%B0%E7%BC%93%E5%AD%98%E6%96%87%E4%BB%B6%5Cn%20%20%20%20%20%20%20%20save_cache(cache_data)%5Cn%20%20%20%20%20%20%20%20export_to_csv(history_records)%5Cn%20%20%20%20%20%20%20%20simple_analysis()%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E6%9C%AC%E8%BD%AE%E7%BB%93%E6%9D%9F%EF%BC%8C%E7%AD%89%E5%BE%85%20%7BMONITOR_INTERVAL%7D%20%E7%A7%92%E8%BF%9B%E5%85%A5%E4%B8%8B%E4%B8%80%E8%BD%AE%5C%22)%5Cn%20%20%20%20%20%20%20%20time.sleep(MONITOR_INTERVAL)%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20main_loop()%5Cn%20%20%20%20except%20KeyboardInterrupt%3A%5Cn%20%20%20%20%20%20%20%20print(%5C%22%5C%5Cn%E7%A8%8B%E5%BA%8F%E6%89%8B%E5%8A%A8%E5%81%9C%E6%AD%A2%EF%BC%8C%E5%B7%B2%E4%BF%9D%E5%AD%98%E5%BD%93%E5%89%8D%E6%95%B0%E6%8D%AE%E6%96%87%E4%BB%B6%5C%22)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;coding:utf-8
import&amp;nbsp;requests
import&amp;nbsp;json
import&amp;nbsp;time
import&amp;nbsp;os
import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime

#&amp;nbsp;==========&amp;nbsp;配置区，请修改为你自己的凭证和监控商品&amp;nbsp;==========
CLAW_KEY&amp;nbsp;=&amp;nbsp;&amp;quot;填入你的key&amp;quot;
CLAW_SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;填入你的secret&amp;quot;
#&amp;nbsp;需要监控的商品编号列表
MONITOR_GOODS&amp;nbsp;=&amp;nbsp;[&amp;quot;10335871600&amp;quot;,&amp;nbsp;&amp;quot;10057467958584&amp;quot;]
#&amp;nbsp;循环检测间隔，单位秒，不建议设置过小，示例5分钟轮询一次
MONITOR_INTERVAL&amp;nbsp;=&amp;nbsp;300
#&amp;nbsp;降价告警阈值，0.1代表价格下跌超过10%触发提醒
PRICE_DROP_ALERT&amp;nbsp;=&amp;nbsp;0.1
#&amp;nbsp;历史数据存储文件
HISTORY_FILE&amp;nbsp;=&amp;nbsp;&amp;quot;goods_history.json&amp;quot;
CSV_EXPORT&amp;nbsp;=&amp;nbsp;&amp;quot;goods_record.csv&amp;quot;
#&amp;nbsp;==========================================================


def&amp;nbsp;load_last_cache():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;加载上一轮缓存的商品数据&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;os.path.exists(HISTORY_FILE):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(HISTORY_FILE,&amp;nbsp;&amp;quot;r&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf‑8&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;json.load(f)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;{}


def&amp;nbsp;save_cache(data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;保存当前采集结果到本地缓存文件&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;with&amp;nbsp;open(HISTORY_FILE,&amp;nbsp;&amp;quot;w&amp;quot;,&amp;nbsp;encoding=&amp;quot;utf‑8&amp;quot;)&amp;nbsp;as&amp;nbsp;f:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;json.dump(data,&amp;nbsp;f,&amp;nbsp;ensure_ascii=False,&amp;nbsp;indent=2)


def&amp;nbsp;fetch_goods_data(goods_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;获取单个商品结构化数据
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:param&amp;nbsp;goods_id:&amp;nbsp;商品数字编号
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:return:&amp;nbsp;解析完成的商品字典，失败返回None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base_url&amp;nbsp;=&amp;nbsp;&amp;quot;https://api‑gw.onebound.cn/jd/item_get_pro&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;CLAW_KEY,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;CLAW_SECRET,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;nbsp;&amp;quot;json&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;yes&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;lang&amp;quot;:&amp;nbsp;&amp;quot;cn&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Accept‑Encoding&amp;quot;:&amp;nbsp;&amp;quot;gzip&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Connection&amp;quot;:&amp;nbsp;&amp;quot;close&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(base_url,&amp;nbsp;params=params,&amp;nbsp;headers=headers,&amp;nbsp;timeout=20)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp_json&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;判断返回状态，非0000代表异常
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;resp_json.get(&amp;quot;error_code&amp;quot;)&amp;nbsp;!=&amp;nbsp;&amp;quot;0000&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;err_msg&amp;nbsp;=&amp;nbsp;resp_json.get(&amp;quot;reason&amp;quot;,&amp;nbsp;&amp;quot;未知异常&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【获取失败】商品{goods_id}，原因：{err_msg}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_data&amp;nbsp;=&amp;nbsp;resp_json.get(&amp;quot;item&amp;quot;,&amp;nbsp;{})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;item_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【获取失败】商品{goods_id}返回内容为空&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;提取核心业务字段，做标准化整理
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;parse_result&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;goods_id&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;num_iid&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;price&amp;quot;:&amp;nbsp;float(item_data.get(&amp;quot;price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;price&amp;quot;)&amp;nbsp;else&amp;nbsp;0.0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;original_price&amp;quot;:&amp;nbsp;float(item_data.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;orginal_price&amp;quot;)&amp;nbsp;else&amp;nbsp;0.0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;num&amp;quot;,&amp;nbsp;0),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;detail_url&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;detail_url&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;collect_time&amp;quot;:&amp;nbsp;datetime.now().strftime(&amp;quot;%Y‑%m‑%d&amp;nbsp;%H:%M:%S&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;skus&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;skus&amp;quot;,&amp;nbsp;{}).get(&amp;quot;sku&amp;quot;,&amp;nbsp;[])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;parse_result
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【请求异常】商品{goods_id},&amp;nbsp;错误：{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None


def&amp;nbsp;detect_change(old_data,&amp;nbsp;new_data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;对比新旧两份数据，检测价格库存变化，输出告警信息&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert_list&amp;nbsp;=&amp;nbsp;[]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;old_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert_list.append(&amp;quot;首次采集该商品，无历史对比&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;alert_list

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_price&amp;nbsp;=&amp;nbsp;old_data[&amp;quot;price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_price&amp;nbsp;=&amp;nbsp;new_data[&amp;quot;price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_stock&amp;nbsp;=&amp;nbsp;old_data[&amp;quot;stock&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_stock&amp;nbsp;=&amp;nbsp;new_data[&amp;quot;stock&amp;quot;]

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;价格变动检测
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;abs(new_price&amp;nbsp;-&amp;nbsp;old_price)&amp;nbsp;&amp;gt;&amp;nbsp;0.01:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;change_rate&amp;nbsp;=&amp;nbsp;(new_price&amp;nbsp;-&amp;nbsp;old_price)&amp;nbsp;/&amp;nbsp;old_price&amp;nbsp;if&amp;nbsp;old_price&amp;nbsp;&amp;gt;&amp;nbsp;0&amp;nbsp;else&amp;nbsp;0
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;new_price&amp;nbsp;&amp;lt;&amp;nbsp;old_price&amp;nbsp;and&amp;nbsp;abs(change_rate)&amp;nbsp;&amp;gt;=&amp;nbsp;PRICE_DROP_ALERT:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert_list.append(f&amp;quot;⚠️降价告警！{old_price}&amp;nbsp;→&amp;nbsp;{new_price}，降幅{abs(change_rate)*100:.1f}%&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;new_price&amp;nbsp;&amp;gt;&amp;nbsp;old_price:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert_list.append(f&amp;quot;📈涨价提醒！{old_price}&amp;nbsp;→&amp;nbsp;{new_price}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert_list.append(f&amp;quot;💡价格微调：{old_price}&amp;nbsp;→&amp;nbsp;{new_price}&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;库存变动检测
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;new_stock&amp;nbsp;!=&amp;nbsp;old_stock:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;new_stock&amp;nbsp;==&amp;nbsp;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert_list.append(f&amp;quot;🚨库存告急，商品变为无货！原库存：{old_stock}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;old_stock&amp;nbsp;==&amp;nbsp;0&amp;nbsp;and&amp;nbsp;new_stock&amp;nbsp;&amp;gt;&amp;nbsp;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert_list.append(f&amp;quot;✅商品补货成功！当前库存&amp;nbsp;{new_stock}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alert_list.append(f&amp;quot;📦库存变动&amp;nbsp;{old_stock}&amp;nbsp;→&amp;nbsp;{new_stock}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;alert_list


def&amp;nbsp;export_to_csv(all_records):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;把采集记录导出csv，方便后续做表格分析&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rows&amp;nbsp;=&amp;nbsp;[]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;goods_id,&amp;nbsp;records&amp;nbsp;in&amp;nbsp;all_records.items():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;rec&amp;nbsp;in&amp;nbsp;records:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rows.append({
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;采集时间&amp;quot;:&amp;nbsp;rec[&amp;quot;collect_time&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;商品ID&amp;quot;:&amp;nbsp;rec[&amp;quot;goods_id&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;商品标题&amp;quot;:&amp;nbsp;rec[&amp;quot;title&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;现价&amp;quot;:&amp;nbsp;rec[&amp;quot;price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;原价&amp;quot;:&amp;nbsp;rec[&amp;quot;original_price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;库存&amp;quot;:&amp;nbsp;rec[&amp;quot;stock&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;店铺名称&amp;quot;:&amp;nbsp;rec[&amp;quot;shop_name&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.DataFrame(rows)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df.to_csv(CSV_EXPORT,&amp;nbsp;index=False,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)


def&amp;nbsp;simple_analysis():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;简易数据分析示例：读取csv，统计价格区间、输出基础统计信息&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;os.path.exists(CSV_EXPORT):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;暂无导出记录，跳过分析&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.read_csv(CSV_EXPORT,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n=====简易数据分析报告=====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;gid&amp;nbsp;in&amp;nbsp;df[&amp;quot;商品ID&amp;quot;].unique():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sub_df&amp;nbsp;=&amp;nbsp;df[df[&amp;quot;商品ID&amp;quot;]&amp;nbsp;==&amp;nbsp;gid]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n商品ID&amp;nbsp;{gid}：{sub_df.iloc[0][&amp;#39;商品标题&amp;#39;][:30]}...&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;&amp;nbsp;&amp;nbsp;采集记录条数：{len(sub_df)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;&amp;nbsp;&amp;nbsp;历史最低价格：{sub_df[&amp;#39;现价&amp;#39;].min()}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;&amp;nbsp;&amp;nbsp;历史最高价格：{sub_df[&amp;#39;现价&amp;#39;].max()}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;&amp;nbsp;&amp;nbsp;平均价格：{round(sub_df[&amp;#39;现价&amp;#39;].mean(),2)}&amp;quot;)


def&amp;nbsp;main_loop():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;====商品监控程序启动====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cache_data&amp;nbsp;=&amp;nbsp;load_last_cache()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;history_records&amp;nbsp;=&amp;nbsp;{}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;初始化历史记录容器
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;gid&amp;nbsp;in&amp;nbsp;MONITOR_GOODS:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;gid&amp;nbsp;not&amp;nbsp;in&amp;nbsp;history_records:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;history_records[gid]&amp;nbsp;=&amp;nbsp;[]

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now_time&amp;nbsp;=&amp;nbsp;datetime.now().strftime(&amp;quot;%Y‑%m‑%d&amp;nbsp;%H:%M:%S&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n【本轮采集时间】{now_time}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;goods_id&amp;nbsp;in&amp;nbsp;MONITOR_GOODS:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_item&amp;nbsp;=&amp;nbsp;fetch_goods_data(goods_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;new_item&amp;nbsp;is&amp;nbsp;None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;保存到全量历史，用于导出报表
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;history_records[goods_id].append(new_item)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_item&amp;nbsp;=&amp;nbsp;cache_data.get(goods_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;alerts&amp;nbsp;=&amp;nbsp;detect_change(old_item,&amp;nbsp;new_item)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n&amp;gt;&amp;gt;商品：{new_item[&amp;#39;title&amp;#39;][:40]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;msg&amp;nbsp;in&amp;nbsp;alerts:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{msg}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;更新缓存为当前最新数据
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cache_data[goods_id]&amp;nbsp;=&amp;nbsp;new_item
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;写入本地缓存文件
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_cache(cache_data)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;export_to_csv(history_records)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;simple_analysis()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n本轮结束，等待&amp;nbsp;{MONITOR_INTERVAL}&amp;nbsp;秒进入下一轮&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(MONITOR_INTERVAL)


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;main_loop()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;KeyboardInterrupt:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n程序手动停止，已保存当前数据文件&amp;quot;)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;代码核心功能讲解&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据获取函数 &lt;code&gt;fetch_goods_data&lt;/code&gt;&lt;/strong&gt; 不需要解析网页 HTML，直接拿到整理好的结构化结果，提取标题、价格、原价、库存、SKU 规格、店铺、链接等关键字段。返回异常状态做捕获，商品下架、编号错误、权限问题都会打印提示。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;变化检测函数 &lt;code&gt;detect_change&lt;/code&gt;&lt;/strong&gt; 拿上一轮缓存的数据和本轮对比，自动识别涨价、降价（设置阈值，小幅度波动不告警）、缺货、补货，输出告警文本。可以后续扩展，对接企业微信、钉钉消息推送，实现真正消息提醒。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;持久化存储&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;goods_history.json&lt;/code&gt;：保存每个商品最新一轮数据，用于下一轮对比；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;goods_record.csv&lt;/code&gt;：每一轮全部历史记录写入表格，用 Excel 打开就可以直接做透视、画图，看价格随时间变化走势。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;简易数据分析 &lt;code&gt;simple_analysis&lt;/code&gt;&lt;/strong&gt; 读取 CSV 历史，对每个商品输出历史最高价、最低价、平均成交价，快速看商品的价格波动区间。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;拓展优化方向&lt;/h2&gt;&lt;h3&gt;1. 消息告警推送&lt;/h3&gt;&lt;p&gt;现在告警只打印控制台，可以增加钉钉 / 企业微信 webhook，当检测到降价、缺货的时候，直接推送消息到工作群，不用盯着控制台运行。&lt;/p&gt;&lt;h3&gt;2. 数据库存储&lt;/h3&gt;&lt;p&gt;小体量用 CSV 完全够用；如果监控几十上百款商品，建议换成 SQLite、MySQL，方便做复杂查询，比如筛选某时间段所有降价商品。&lt;/p&gt;&lt;h3&gt;3.SKU 粒度监控&lt;/h3&gt;&lt;p&gt;当前示例取商品整体信息，返回结果自带完整 SKU 数组，可以改造代码，针对每一个子规格单独记录价格库存，适合多规格复杂商品的竞品分析。&lt;/p&gt;&lt;h3&gt;4. 可视化图表&lt;/h3&gt;&lt;p&gt;结合 matplotlib、plotly，读取 CSV 历史数据，自动绘制每个商品价格趋势折线图，直观看到大促前后价格变动轨迹。&lt;/p&gt;&lt;h3&gt;5. 任务调度分离&lt;/h3&gt;&lt;p&gt;长时间运行可以把采集逻辑做成后台定时任务，不需要一直开着脚本；Linux 环境下可配置 systemd 服务或者 crontab 定时执行。&lt;/p&gt;&lt;h2&gt;踩坑记录与注意事项&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;轮询间隔不要设置过小&lt;/strong&gt;，短时间高频访问会触发限流，建议至少间隔 3‑5 分钟以上；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;部分商品多规格场景，整体展示价格和子 SKU 实际售价存在差异，做精细分析时，优先读取 SKU 数组内部对应规格的价格；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;部分自营、第三方店铺，店铺相关字段不一定全部返回，代码已经做了空值兼容；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;遇到商品下架，返回结果会提示商品未找到，脚本会捕获异常不会直接崩溃；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;凭证信息不要硬编码提交到公开代码仓库，生产环境建议放到环境变量读取。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;业务场景可以怎么用&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;竞品监控&lt;/strong&gt;：跟踪竞品多款商品，记录调价节奏、大促降价幅度，给自己的定价、促销活动做参考；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;采购备货参考&lt;/strong&gt;：持续看竞品库存变动，辅助判断市场热度，辅助备货决策；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;个人好物蹲价&lt;/strong&gt;：想要买的商品，设置降价阈值，一旦低于心理价位立刻收到提醒；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;选品分析&lt;/strong&gt;：批量收集一批候选商品历史价格、规格，筛选性价比高的目标单品。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;写在最后&lt;/h2&gt;&lt;p&gt;这套方案最大的优势，是不用投入精力维护页面解析逻辑，把重心放在业务逻辑上。Open‑Claw 帮我们处理了页面各种复杂解析工作，输出规整结构化数据，开发者可以快速把精力投入监控告警、历史统计、数据分析业务。&lt;/p&gt;&lt;p&gt;上面这份代码可以直接跑通，只需要填入自己的凭证，填写需要监控的商品编号，就可以立刻拥有一套属于自己的商品监测工具。后续可以基于这个基础，按需扩展推送、数据库、可视化等功能。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Thu, 27 Aug 2026 10:37:03 +0800</pubDate></item><item><title>告别人工盯品：借助 OpenClaw 搭建商品自动监控与数据分析系统（完整实操）</title><link>https://www.shopapi.com.cn/?id=223</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营，免不了要持续关注自家以及竞品商品状态：价格变动、库存变化、不同规格的售价波动、店铺基础信息更新。很多人最开始的做法就是打开网页，定时手动刷新页面复制数据，一旦要跟踪十几款商品，每天就要消耗大量人力。&lt;/p&gt;&lt;p&gt;如果自己写页面解析脚本，又会遇到不少麻烦：网页改版后解析规则直接失效，高频访问容易触发平台风控，多规格、图片、店铺信息的解析逻辑需要大量开发时间，遇到网络波动、商品下架，程序很容易直接报错终止运行。&lt;/p&gt;&lt;p&gt;最近在实际项目中尝试了 OpenClaw 这套工具，它可以直接输出标准化结构化商品资料，不用处理页面解析、代理池、加密校验，我们只需要聚焦业务逻辑：定时拉取信息、本地存储、异动识别、数据分析，几十行代码就可以完成一套完整的监控方案稀土掘金。&lt;/p&gt;&lt;p&gt;整套流程分为 4 个部分：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;5&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E8%B5%84%E6%96%99%E8%8E%B7%E5%8F%96%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%228GyBd1-1787795558660%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;资料获取&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;资料获取&quot;&gt;&lt;strong&gt;资料获取&lt;/strong&gt;&lt;/a&gt;&lt;/span&gt;：输入商品编号，拿到标题、原价、现价、库存、全部规格、店铺、图片等完整内容；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;定时轮询：周期性采集目标商品，对比历史记录，识别价格、库存异动；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;本地持久化：把每一次采集结果保存到本地数据库，留存时序记录；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;数据分析：读取历史记录，做价格波动统计、规格销量统计，输出简单报表。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;准备工作&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;安装依赖库&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%20apscheduler%20sqlite3%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&amp;nbsp;apscheduler&amp;nbsp;sqlite3&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;2.拿到两组身份凭证，用于 OpenClaw 的身份校验；&lt;/p&gt;&lt;p&gt;3.准备需要跟踪的商品编号，就是商品网页链接中 &lt;code&gt;item.jd.com/xxx.html&lt;/code&gt; 中间那串数字。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;提示：工具自带缓存机制，做实时监控的时候建议关闭缓存，保证拿到最新的行情；做批量数据分析场景，开启缓存可以减少消耗，提升响应速度。工具内置异常识别，商品下架、网络故障、平台维护都会返回对应的标记，程序不会直接崩溃退出。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;完整可运行代码实现&lt;/h2&gt;&lt;h3&gt;1. 基础数据获取、清洗模块&lt;/h3&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22import%20requests%5Cnimport%20json%5Cnimport%20time%5Cnimport%20sqlite3%5Cnfrom%20apscheduler.schedulers.background%20import%20BackgroundScheduler%5Cnfrom%20datetime%20import%20datetime%5Cn%5Cn%23%20----------------%E9%85%8D%E7%BD%AE%E5%8C%BA%E5%9F%9F----------------%5CnKEY%20%3D%20%5C%22%E4%BD%A0%E7%9A%84%E8%BA%AB%E4%BB%BD%E5%87%AD%E8%AF%81key%5C%22%5CnSECRET%20%3D%20%5C%22%E4%BD%A0%E7%9A%84%E8%BA%AB%E4%BB%BD%E5%87%AD%E8%AF%81secret%5C%22%5CnMONITOR_ITEM_LIST%20%3D%20%5B%5C%2210335871600%5C%22%2C%5C%2210057467958584%5C%22%5D%20%23%20%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%E5%88%97%E8%A1%A8%5CnMONITOR_INTERVAL%20%3D%203600%20%23%20%E7%9B%91%E6%8E%A7%E5%91%A8%E6%9C%9F%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E8%BF%99%E9%87%8C%E8%AE%BE%E7%BD%AE1%E5%B0%8F%E6%97%B6%E9%87%87%E9%9B%86%E4%B8%80%E6%AC%A1%5CnCACHE_SWITCH%20%3D%20%5C%22no%5C%22%20%23%20%E5%AE%9E%E6%97%B6%E7%9B%91%E6%8E%A7%E8%AE%BE%E7%BD%AEno%EF%BC%9B%E6%89%B9%E9%87%8F%E5%88%86%E6%9E%90%E5%8F%AF%E4%BB%A5%E8%AE%BE%E7%BD%AEyes%E5%BC%80%E5%90%AF%E7%BC%93%E5%AD%98%5Cn%23%20----------------------------------------%5Cn%5Cndef%20fetch_product_data(num_iid)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%E5%8E%9F%E5%A7%8B%E8%B5%84%E6%96%99%5C%22%5C%22%5C%22%5Cn%20%20%20%20base_url%20%3D%20%5C%22https%3A%2F%2Fapi%E2%80%91gw.onebound.cn%2Fjd%2Fitem_get_pro%5C%22%5Cn%20%20%20%20param%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20KEY%2C%5Cn%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20SECRET%2C%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20num_iid%2C%5Cn%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20CACHE_SWITCH%2C%5Cn%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%5C%22json%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22lang%5C%22%3A%5C%22cn%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20headers%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22Accept%E2%80%91Encoding%5C%22%3A%5C%22gzip%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22Connection%5C%22%3A%5C%22close%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(base_url%2Cparams%3Dparam%2Cheaders%3Dheaders%2Ctimeout%3D20)%5Cn%20%20%20%20%20%20%20%20res_json%20%3D%20resp.json()%5Cn%20%20%20%20%20%20%20%20return%20res_json%5Cn%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%7Bnum_iid%7D%E8%8E%B7%E5%8F%96%E5%A4%B1%E8%B4%A5%EF%BC%8C%E5%BC%82%E5%B8%B8%E4%BF%A1%E6%81%AF%EF%BC%9A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%5Cndef%20parse_raw_data(raw_json)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E6%B8%85%E6%B4%97%E5%8E%9F%E5%A7%8B%E8%BF%94%E5%9B%9E%E5%86%85%E5%AE%B9%EF%BC%8C%E6%8F%90%E5%8F%96%E4%B8%9A%E5%8A%A1%E9%9C%80%E8%A6%81%E5%AD%97%E6%AE%B5%5C%22%5C%22%5C%22%5Cn%20%20%20%20if%20not%20raw_json%3A%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20error_code%20%3D%20raw_json.get(%5C%22error_code%5C%22)%5Cn%20%20%20%20if%20error_code%20!%3D%20%5C%220000%5C%22%3A%5Cn%20%20%20%20%20%20%20%20err_msg%20%3D%20raw_json.get(%5C%22reason%5C%22%2C%5C%22%E6%9C%AA%E7%9F%A5%E5%BC%82%E5%B8%B8%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E4%B8%9A%E5%8A%A1%E5%BC%82%E5%B8%B8%EF%BC%9A%7Berr_msg%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20item%20%3D%20raw_json.get(%5C%22item%5C%22)%5Cn%20%20%20%20if%20not%20item%3A%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20%23%20%E6%8F%90%E5%8F%96%E5%A4%9A%E8%A7%84%E6%A0%BCsku%E5%88%97%E8%A1%A8%5Cn%20%20%20%20sku_list%20%3D%20%5B%5D%5Cn%20%20%20%20sku_data%20%3D%20item.get(%5C%22skus%5C%22%2C%7B%7D).get(%5C%22sku%5C%22%2C%5B%5D)%5Cn%20%20%20%20for%20sku%20in%20sku_data%3A%5Cn%20%20%20%20%20%20%20%20sku_info%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22sku_id%5C%22%3Asku.get(%5C%22sku_id%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22properties_name%5C%22%3Asku.get(%5C%22properties_name%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22price%5C%22%3Afloat(sku.get(%5C%22price%5C%22%2C0))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22original_price%5C%22%3Afloat(sku.get(%5C%22orginal_price%5C%22%2C0))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3Aint(sku.get(%5C%22quantity%5C%22%2C0))%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20sku_list.append(sku_info)%5Cn%5Cn%20%20%20%20clean_result%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3Aitem.get(%5C%22num_iid%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22title%5C%22%3Aitem.get(%5C%22title%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22brand%5C%22%3Aitem.get(%5C%22brand%5C%22%2C%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22current_price%5C%22%3Afloat(item.get(%5C%22price%5C%22%2C0))%2C%5Cn%20%20%20%20%20%20%20%20%5C%22original_price%5C%22%3Afloat(item.get(%5C%22orginal_price%5C%22%2C0))%2C%5Cn%20%20%20%20%20%20%20%20%5C%22stock_total%5C%22%3Aitem.get(%5C%22num%5C%22%2C0)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3Aitem.get(%5C%22seller_info%5C%22%2C%7B%7D).get(%5C%22shop_name%5C%22%2C%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22main_image%5C%22%3Aitem.get(%5C%22pic_url%5C%22%2C%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22location%5C%22%3Aitem.get(%5C%22location%5C%22%2C%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22skus%5C%22%3Ajson.dumps(sku_list%2Censure_ascii%3DFalse)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22crawl_time%5C%22%3Adatetime.now().strftime(%5C%22%25Y%E2%80%91%25m%E2%80%91%25d%20%25H%3A%25M%3A%25S%5C%22)%5Cn%20%20%20%20%7D%5Cn%20%20%20%20return%20clean_result%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;import&amp;nbsp;requests
import&amp;nbsp;json
import&amp;nbsp;time
import&amp;nbsp;sqlite3
from&amp;nbsp;apscheduler.schedulers.background&amp;nbsp;import&amp;nbsp;BackgroundScheduler
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime

#&amp;nbsp;----------------配置区域----------------
KEY&amp;nbsp;=&amp;nbsp;&amp;quot;你的身份凭证key&amp;quot;
SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;你的身份凭证secret&amp;quot;
MONITOR_ITEM_LIST&amp;nbsp;=&amp;nbsp;[&amp;quot;10335871600&amp;quot;,&amp;quot;10057467958584&amp;quot;]&amp;nbsp;#&amp;nbsp;需要监控的商品编号列表
MONITOR_INTERVAL&amp;nbsp;=&amp;nbsp;3600&amp;nbsp;#&amp;nbsp;监控周期，单位秒，这里设置1小时采集一次
CACHE_SWITCH&amp;nbsp;=&amp;nbsp;&amp;quot;no&amp;quot;&amp;nbsp;#&amp;nbsp;实时监控设置no；批量分析可以设置yes开启缓存
#&amp;nbsp;----------------------------------------

def&amp;nbsp;fetch_product_data(num_iid):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;获取商品原始资料&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base_url&amp;nbsp;=&amp;nbsp;&amp;quot;https://api‑gw.onebound.cn/jd/item_get_pro&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;param&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;KEY,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;SECRET,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;num_iid,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;CACHE_SWITCH,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;quot;json&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;lang&amp;quot;:&amp;quot;cn&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Accept‑Encoding&amp;quot;:&amp;quot;gzip&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Connection&amp;quot;:&amp;quot;close&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(base_url,params=param,headers=headers,timeout=20)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;res_json&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;res_json
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品{num_iid}获取失败，异常信息：{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None


def&amp;nbsp;parse_raw_data(raw_json):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;清洗原始返回内容，提取业务需要字段&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;raw_json:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;error_code&amp;nbsp;=&amp;nbsp;raw_json.get(&amp;quot;error_code&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;error_code&amp;nbsp;!=&amp;nbsp;&amp;quot;0000&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;err_msg&amp;nbsp;=&amp;nbsp;raw_json.get(&amp;quot;reason&amp;quot;,&amp;quot;未知异常&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;业务异常：{err_msg}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item&amp;nbsp;=&amp;nbsp;raw_json.get(&amp;quot;item&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;item:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;提取多规格sku列表
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_list&amp;nbsp;=&amp;nbsp;[]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_data&amp;nbsp;=&amp;nbsp;item.get(&amp;quot;skus&amp;quot;,{}).get(&amp;quot;sku&amp;quot;,[])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;sku&amp;nbsp;in&amp;nbsp;sku_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_info&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;sku_id&amp;quot;:sku.get(&amp;quot;sku_id&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;properties_name&amp;quot;:sku.get(&amp;quot;properties_name&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;price&amp;quot;:float(sku.get(&amp;quot;price&amp;quot;,0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;original_price&amp;quot;:float(sku.get(&amp;quot;orginal_price&amp;quot;,0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:int(sku.get(&amp;quot;quantity&amp;quot;,0))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_list.append(sku_info)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;clean_result&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:item.get(&amp;quot;num_iid&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:item.get(&amp;quot;title&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;brand&amp;quot;:item.get(&amp;quot;brand&amp;quot;,&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;current_price&amp;quot;:float(item.get(&amp;quot;price&amp;quot;,0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;original_price&amp;quot;:float(item.get(&amp;quot;orginal_price&amp;quot;,0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock_total&amp;quot;:item.get(&amp;quot;num&amp;quot;,0),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:item.get(&amp;quot;seller_info&amp;quot;,{}).get(&amp;quot;shop_name&amp;quot;,&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;main_image&amp;quot;:item.get(&amp;quot;pic_url&amp;quot;,&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;location&amp;quot;:item.get(&amp;quot;location&amp;quot;,&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;skus&amp;quot;:json.dumps(sku_list,ensure_ascii=False),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;crawl_time&amp;quot;:datetime.now().strftime(&amp;quot;%Y‑%m‑%d&amp;nbsp;%H:%M:%S&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;clean_result&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h3&gt;2. SQLite 本地存储模块&lt;/h3&gt;&lt;p&gt;把每次采集的数据存入本地数据库，方便后续对比和数据分析。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22def%20init_db()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%88%9D%E5%A7%8B%E5%8C%96%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E8%A1%A8%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(%5C%22.%2Fproduct_monitor.db%5C%22)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20CREATE%20TABLE%20IF%20NOT%20EXISTS%20product_history(%5Cn%20%20%20%20%20%20%20%20id%20INTEGER%20PRIMARY%20KEY%20AUTOINCREMENT%2C%5Cn%20%20%20%20%20%20%20%20num_iid%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20title%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20brand%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20current_price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20original_price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20stock_total%20INTEGER%2C%5Cn%20%20%20%20%20%20%20%20shop_name%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20skus%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20crawl_time%20TEXT%5Cn%20%20%20%20)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;)%5Cn%20%20%20%20conn.commit()%5Cn%20%20%20%20conn.close()%5Cn%5Cn%5Cndef%20save_to_db(data)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%B0%86%E6%B8%85%E6%B4%97%E5%AE%8C%E6%88%90%E7%9A%84%E6%95%B0%E6%8D%AE%E5%86%99%E5%85%A5%E6%95%B0%E6%8D%AE%E5%BA%93%5C%22%5C%22%5C%22%5Cn%20%20%20%20if%20not%20data%3A%5Cn%20%20%20%20%20%20%20%20return%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(%5C%22.%2Fproduct_monitor.db%5C%22)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20sql%20%3D%20&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20INSERT%20INTO%20product_history%5Cn%20%20%20%20(num_iid%2Ctitle%2Cbrand%2Ccurrent_price%2Coriginal_price%2Cstock_total%2Cshop_name%2Cskus%2Ccrawl_time)%5Cn%20%20%20%20VALUES%20(%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F%2C%3F)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20val%20%3D%20(%5Cn%20%20%20%20%20%20%20%20data%5B%5C%22num_iid%5C%22%5D%2Cdata%5B%5C%22title%5C%22%5D%2Cdata%5B%5C%22brand%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20data%5B%5C%22current_price%5C%22%5D%2Cdata%5B%5C%22original_price%5C%22%5D%2Cdata%5B%5C%22stock_total%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20data%5B%5C%22shop_name%5C%22%5D%2Cdata%5B%5C%22skus%5C%22%5D%2Cdata%5B%5C%22crawl_time%5C%22%5D%5Cn%20%20%20%20)%5Cn%20%20%20%20cursor.execute(sql%2Cval)%5Cn%20%20%20%20conn.commit()%5Cn%20%20%20%20conn.close()%5Cn%20%20%20%20print(f%5C%22%E2%9C%85%E6%88%90%E5%8A%9F%E4%BF%9D%E5%AD%98%E8%AE%B0%E5%BD%95%EF%BC%9A%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%7Bdata%5B&amp;#39;num_iid&amp;#39;%5D%7D%EF%BC%8C%E9%87%87%E9%9B%86%E6%97%B6%E9%97%B4%7Bdata%5B&amp;#39;crawl_time&amp;#39;%5D%7D%5C%22)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;def&amp;nbsp;init_db():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;初始化本地数据表&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(&amp;quot;./product_monitor.db&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CREATE&amp;nbsp;TABLE&amp;nbsp;IF&amp;nbsp;NOT&amp;nbsp;EXISTS&amp;nbsp;product_history(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;id&amp;nbsp;INTEGER&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;AUTOINCREMENT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;num_iid&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;brand&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;current_price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;original_price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stock_total&amp;nbsp;INTEGER,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shop_name&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;skus&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;crawl_time&amp;nbsp;TEXT
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.commit()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()


def&amp;nbsp;save_to_db(data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;将清洗完成的数据写入数据库&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(&amp;quot;./product_monitor.db&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sql&amp;nbsp;=&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INSERT&amp;nbsp;INTO&amp;nbsp;product_history
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(num_iid,title,brand,current_price,original_price,stock_total,shop_name,skus,crawl_time)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;VALUES&amp;nbsp;(?,?,?,?,?,?,?,?,?)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;val&amp;nbsp;=&amp;nbsp;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;num_iid&amp;quot;],data[&amp;quot;title&amp;quot;],data[&amp;quot;brand&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;current_price&amp;quot;],data[&amp;quot;original_price&amp;quot;],data[&amp;quot;stock_total&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data[&amp;quot;shop_name&amp;quot;],data[&amp;quot;skus&amp;quot;],data[&amp;quot;crawl_time&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(sql,val)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.commit()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;✅成功保存记录：商品编号{data[&amp;#39;num_iid&amp;#39;]}，采集时间{data[&amp;#39;crawl_time&amp;#39;]}&amp;quot;)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h3&gt;3. 异动检测 + 定时监控任务&lt;/h3&gt;&lt;p&gt;对比上一轮记录，识别价格下调上涨、库存减少，简单打印预警，实际项目可以扩展为企业微信、钉钉消息推送。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;3&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22def%20check_abnormal(num_iid%2Cnew_data)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%AF%B9%E6%AF%94%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%EF%BC%8C%E6%A3%80%E6%B5%8B%E5%BC%82%E5%8A%A8%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(%5C%22.%2Fproduct_monitor.db%5C%22)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20SELECT%20current_price%2Cstock_total%20FROM%20product_history%5Cn%20%20%20%20WHERE%20num_iid%20%3D%20%3F%20ORDER%20BY%20id%20DESC%20LIMIT%201%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C(num_iid%2C))%5Cn%20%20%20%20last_record%20%3D%20cursor.fetchone()%5Cn%20%20%20%20conn.close()%5Cn%20%20%20%20if%20not%20last_record%3A%5Cn%20%20%20%20%20%20%20%20return%5Cn%20%20%20%20last_price%2Clast_stock%20%3D%20last_record%5Cn%20%20%20%20new_price%20%3D%20new_data%5B%5C%22current_price%5C%22%5D%5Cn%20%20%20%20new_stock%20%3D%20new_data%5B%5C%22stock_total%5C%22%5D%5Cn%5Cn%20%20%20%20%23%20%E4%BB%B7%E6%A0%BC%E5%BC%82%E5%8A%A8%5Cn%20%20%20%20if%20new_price%20!%3D%20last_price%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E2%9A%A0%EF%B8%8F%E3%80%90%E4%BB%B7%E6%A0%BC%E5%BC%82%E5%8A%A8%E6%8F%90%E9%86%92%E3%80%91%E5%95%86%E5%93%81%7Bnum_iid%7D%EF%BC%8C%E6%97%A7%E4%BB%B7%7Blast_price%7D%20%E2%86%92%20%E6%96%B0%E4%BB%B7%7Bnew_price%7D%5C%22)%5Cn%20%20%20%20%23%20%E5%BA%93%E5%AD%98%E5%BC%82%E5%8A%A8%5Cn%20%20%20%20if%20new_stock%20!%3D%20last_stock%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E2%9A%A0%EF%B8%8F%E3%80%90%E5%BA%93%E5%AD%98%E5%BC%82%E5%8A%A8%E6%8F%90%E9%86%92%E3%80%91%E5%95%86%E5%93%81%7Bnum_iid%7D%EF%BC%8C%E6%97%A7%E5%BA%93%E5%AD%98%7Blast_stock%7D%20%E2%86%92%20%E6%96%B0%E5%BA%93%E5%AD%98%7Bnew_stock%7D%5C%22)%5Cn%5Cn%5Cndef%20monitor_task()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%8D%95%E6%AC%A1%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%EF%BC%8C%E5%BE%AA%E7%8E%AF%E5%A4%84%E7%90%86%E5%85%A8%E9%83%A8%E7%9B%AE%E6%A0%87%E5%95%86%E5%93%81%5C%22%5C%22%5C%22%5Cn%20%20%20%20for%20item_id%20in%20MONITOR_ITEM_LIST%3A%5Cn%20%20%20%20%20%20%20%20raw%20%3D%20fetch_product_data(item_id)%5Cn%20%20%20%20%20%20%20%20clean_data%20%3D%20parse_raw_data(raw)%5Cn%20%20%20%20%20%20%20%20if%20clean_data%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20check_abnormal(item_id%2Cclean_data)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20save_to_db(clean_data)%5Cn%20%20%20%20%20%20%20%20time.sleep(1)%20%23%20%E8%AF%B7%E6%B1%82%E4%B9%8B%E9%97%B4%E7%9F%AD%E6%9A%82%E4%BC%91%E7%9C%A0%EF%BC%8C%E9%81%BF%E5%85%8D%E8%AF%B7%E6%B1%82%E8%BF%87%E4%BA%8E%E5%AF%86%E9%9B%86%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;def&amp;nbsp;check_abnormal(num_iid,new_data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;对比历史数据，检测异动&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(&amp;quot;./product_monitor.db&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECT&amp;nbsp;current_price,stock_total&amp;nbsp;FROM&amp;nbsp;product_history
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WHERE&amp;nbsp;num_iid&amp;nbsp;=&amp;nbsp;?&amp;nbsp;ORDER&amp;nbsp;BY&amp;nbsp;id&amp;nbsp;DESC&amp;nbsp;LIMIT&amp;nbsp;1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,(num_iid,))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_record&amp;nbsp;=&amp;nbsp;cursor.fetchone()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;last_record:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_price,last_stock&amp;nbsp;=&amp;nbsp;last_record
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_price&amp;nbsp;=&amp;nbsp;new_data[&amp;quot;current_price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;new_stock&amp;nbsp;=&amp;nbsp;new_data[&amp;quot;stock_total&amp;quot;]

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;价格异动
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;new_price&amp;nbsp;!=&amp;nbsp;last_price:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;⚠️【价格异动提醒】商品{num_iid}，旧价{last_price}&amp;nbsp;→&amp;nbsp;新价{new_price}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;库存异动
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;new_stock&amp;nbsp;!=&amp;nbsp;last_stock:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;⚠️【库存异动提醒】商品{num_iid}，旧库存{last_stock}&amp;nbsp;→&amp;nbsp;新库存{new_stock}&amp;quot;)


def&amp;nbsp;monitor_task():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;单次监控任务，循环处理全部目标商品&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;item_id&amp;nbsp;in&amp;nbsp;MONITOR_ITEM_LIST:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raw&amp;nbsp;=&amp;nbsp;fetch_product_data(item_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;clean_data&amp;nbsp;=&amp;nbsp;parse_raw_data(raw)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;clean_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;check_abnormal(item_id,clean_data)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_to_db(clean_data)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(1)&amp;nbsp;#&amp;nbsp;请求之间短暂休眠，避免请求过于密集&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h3&gt;4. 简单数据分析示例&lt;/h3&gt;&lt;p&gt;读取本地历史数据，输出价格变化统计，你可以基于此扩展，做 matplotlib 绘图导出图表，或者导出 csv 报表。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;4&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22def%20analysis_product(num_iid)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E9%92%88%E5%AF%B9%E5%8D%95%E4%B8%AA%E5%95%86%E5%93%81%EF%BC%8C%E8%AF%BB%E5%8F%96%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95%E5%81%9A%E7%AE%80%E5%8D%95%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(%5C%22.%2Fproduct_monitor.db%5C%22)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20SELECT%20crawl_time%2Ccurrent_price%2Cstock_total%20FROM%20product_history%5Cn%20%20%20%20WHERE%20num_iid%20%3D%20%3F%20ORDER%20BY%20id%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C(num_iid%2C))%5Cn%20%20%20%20rows%20%3D%20cursor.fetchall()%5Cn%20%20%20%20conn.close()%5Cn%20%20%20%20if%20len(rows)%20%3D%3D%200%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%7Bnum_iid%7D%E6%9A%82%E6%97%A0%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95%5C%22)%5Cn%20%20%20%20%20%20%20%20return%5Cn%20%20%20%20price_list%20%3D%20%5Br%5B1%5D%20for%20r%20in%20rows%5D%5Cn%20%20%20%20min_p%20%3D%20min(price_list)%5Cn%20%20%20%20max_p%20%3D%20max(price_list)%5Cn%20%20%20%20avg_p%20%3D%20sum(price_list)%2Flen(price_list)%5Cn%20%20%20%20print(f%5C%22%5C%5Cn%F0%9F%93%8A%E5%95%86%E5%93%81%7Bnum_iid%7D%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E7%BB%93%E6%9E%9C%5C%22)%5Cn%20%20%20%20print(f%5C%22%E8%AE%B0%E5%BD%95%E6%80%BB%E6%9D%A1%E6%95%B0%EF%BC%9A%7Blen(rows)%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%8E%86%E5%8F%B2%E6%9C%80%E4%BD%8E%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Bmin_p%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%8E%86%E5%8F%B2%E6%9C%80%E9%AB%98%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Bmax_p%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%B9%B3%E5%9D%87%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Bround(avg_p%2C2)%7D%5C%22)%5Cn%20%20%20%20print(%5C%22%E6%97%B6%E9%97%B4%E2%80%91%E4%BB%B7%E6%A0%BC%E8%BD%A8%E8%BF%B9%EF%BC%9A%5C%22)%5Cn%20%20%20%20for%20line%20in%20rows%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%20%20%7Bline%5B0%5D%7D%20%7C%20%E4%BB%B7%E6%A0%BC%3A%7Bline%5B1%5D%7D%20%7C%E5%BA%93%E5%AD%98%3A%7Bline%5B2%5D%7D%5C%22)%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20init_db()%5Cn%20%20%20%20print(%5C%22%E5%90%AF%E5%8A%A8%E5%95%86%E5%93%81%E7%9B%91%E6%8E%A7%E7%A8%8B%E5%BA%8F...%5C%22)%5Cn%20%20%20%20scheduler%20%3D%20BackgroundScheduler()%5Cn%20%20%20%20scheduler.add_job(monitor_task%2C%5C%22interval%5C%22%2Cseconds%3DMONITOR_INTERVAL)%5Cn%20%20%20%20scheduler.start()%5Cn%20%20%20%20%23%20%E5%85%88%E6%89%A7%E8%A1%8C%E4%B8%80%E6%AC%A1%E9%87%87%E9%9B%86%5Cn%20%20%20%20monitor_task()%5Cn%20%20%20%20%23%20%E6%BC%94%E7%A4%BA%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%EF%BC%8C%E5%A1%AB%E5%85%A5%E9%9C%80%E8%A6%81%E5%88%86%E6%9E%90%E7%9A%84%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%5Cn%20%20%20%20analysis_product(MONITOR_ITEM_LIST%5B0%5D)%5Cn%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20time.sleep(60)%5Cn%20%20%20%20except%20KeyboardInterrupt%3A%5Cn%20%20%20%20%20%20%20%20print(%5C%22%5C%5Cn%E7%A8%8B%E5%BA%8F%E9%80%80%E5%87%BA%5C%22)%5Cn%20%20%20%20%20%20%20%20scheduler.shutdown()%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;def&amp;nbsp;analysis_product(num_iid):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;针对单个商品，读取历史记录做简单数据分析&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(&amp;quot;./product_monitor.db&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECT&amp;nbsp;crawl_time,current_price,stock_total&amp;nbsp;FROM&amp;nbsp;product_history
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;WHERE&amp;nbsp;num_iid&amp;nbsp;=&amp;nbsp;?&amp;nbsp;ORDER&amp;nbsp;BY&amp;nbsp;id
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,(num_iid,))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rows&amp;nbsp;=&amp;nbsp;cursor.fetchall()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;len(rows)&amp;nbsp;==&amp;nbsp;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品{num_iid}暂无历史记录&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;price_list&amp;nbsp;=&amp;nbsp;[r[1]&amp;nbsp;for&amp;nbsp;r&amp;nbsp;in&amp;nbsp;rows]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min_p&amp;nbsp;=&amp;nbsp;min(price_list)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;max_p&amp;nbsp;=&amp;nbsp;max(price_list)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;avg_p&amp;nbsp;=&amp;nbsp;sum(price_list)/len(price_list)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n📊商品{num_iid}历史数据分析结果&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;记录总条数：{len(rows)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;历史最低价格：{min_p}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;历史最高价格：{max_p}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;平均价格：{round(avg_p,2)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;时间‑价格轨迹：&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;line&amp;nbsp;in&amp;nbsp;rows:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;&amp;nbsp;&amp;nbsp;{line[0]}&amp;nbsp;|&amp;nbsp;价格:{line[1]}&amp;nbsp;|库存:{line[2]}&amp;quot;)


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init_db()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;启动商品监控程序...&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scheduler&amp;nbsp;=&amp;nbsp;BackgroundScheduler()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scheduler.add_job(monitor_task,&amp;quot;interval&amp;quot;,seconds=MONITOR_INTERVAL)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scheduler.start()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;先执行一次采集
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor_task()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;演示数据分析，填入需要分析的商品编号
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis_product(MONITOR_ITEM_LIST[0])

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(60)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;KeyboardInterrupt:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n程序退出&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scheduler.shutdown()&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;关键说明与踩坑记录&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;关于多规格处理&lt;/strong&gt; 返回的规格列表中，每一条都独立带有规格编号、售价、库存。实际业务不要只拿商品主体价格，主体价格有可能会和真实规格售价不一致，做竞品比价建议遍历全部规格分别跟踪记录。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;缓存开关怎么选&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;7*24 小时实时盯价格、盯库存：&lt;code&gt;CACHE_SWITCH=&amp;quot;no&amp;quot;&lt;/code&gt;，每次都获取最新行情；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;一次性批量拉取大量商品做选品分析：&lt;code&gt;CACHE_SWITCH=&amp;quot;yes&amp;quot;&lt;/code&gt;，复用缓存，提升速度、减少消耗。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;异常状态处理&lt;/strong&gt; 当商品下架、编号错误的时候，返回结果会带上异常标记，代码中已经做判断，不会直接崩溃。日常开发可以把异常日志写入文件，方便排查问题。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;扩展方向&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;异动消息推送：检测到价格 / 库存变动后，调用钉钉 / 企业微信推送通知；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;可视化：读取 sqlite 数据，用 matplotlib 绘制价格趋势图，导出图片；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;导出报表：把历史记录直接导出为 CSV，交给运营做复盘；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;多商品批量分析，批量导出全部监控商品统计报告。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;小结&lt;/h2&gt;&lt;p&gt;对比传统手写解析脚本，OpenClaw 最大的优势就是把繁杂的页面适配工作做了封装。开发者不用再关心网页改版、反拦截、JS 渲染这些问题，我们把精力聚焦业务：定时采集、异动告警、数据统计分析。几十行代码，就实现 7×24 小时不间断商品监控，对于中小团队和个人开发者是性价比很高的落地方案。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Thu, 27 Aug 2026 10:03:16 +0800</pubDate></item><item><title>OpenClaw 实战｜搭建电商商品监控与数据分析系统，实现价格库存自动追踪</title><link>https://www.shopapi.com.cn/?id=222</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营、选品或者竞品调研的时候，我们经常需要持续盯一批商品：价格什么时候降价、库存是否充足、不同规格的售价变化、店铺的商品动态。如果每天手动打开网页一个个复制记录，不仅耗费大量人力，还很容易漏掉关键的变动节点。&lt;/p&gt;&lt;p&gt;我之前踩过不少坑：直接解析网页源码会遇到页面动态渲染、反爬限制，频繁访问还会触发风控拦截，拿到的数据经常缺失规格、原价、店铺信息，后续分析很难开展。后来尝试 OpenClaw 工具来做数据获取，把重复的手工工作交给程序自动执行，轻松完成定时监控，再结合 Python 做清洗、统计分析，整套方案跑通之后，效率提升非常明显。&lt;/p&gt;&lt;p&gt;本篇就完整分享从获取商品原始数据、定时监控、数据清洗，到做简单竞品数据分析的完整流程，代码可以直接修改后投入使用。&lt;/p&gt;&lt;h2&gt;整体实现思路&lt;/h2&gt;&lt;p&gt;整套流程分成三个核心环节：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;4&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E5%8E%9F%E5%A7%8B%E4%BF%A1%E6%81%AF%E8%8E%B7%E5%8F%96%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%22Zl4p3b-1787645917868%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;原始信息获取&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;原始信息获取&quot;&gt;原始信息获取&lt;/a&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/strong&gt;：输入商品编号，拿到商品标题、现价、原价、库存、多规格信息、店铺、图片等全套原始内容；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;持久化存储与定时监控&lt;/strong&gt;：定时循环采集，把每一次采集结果保存，对比前后数据，捕捉价格、库存变动；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据分析处理&lt;/strong&gt;：读取历史记录，做价格波动统计、规格对比、基础数据可视化，输出可以直接参考的分析结果。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;小提示：实际使用时，做好请求间隔控制，避免短时间高频操作，兼顾稳定性。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;环境准备&lt;/h2&gt;&lt;p&gt;本地需要安装依赖库，打开终端执行：&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%20pandas%20matplotlib%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&amp;nbsp;pandas&amp;nbsp;matplotlib&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;第一部分：获取商品详情原始数据&lt;/h2&gt;&lt;p&gt;我们封装一个简易工具类，填入自己的凭证和商品编号，就可以拿到完整商品原始报文，包含多规格、店铺、原价、库存等全部字段。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20product_monitor.py%5Cnimport%20requests%5Cnimport%20json%5Cnimport%20time%5Cnfrom%20datetime%20import%20datetime%5Cnimport%20pandas%20as%20pd%5Cnimport%20matplotlib.pyplot%20as%20plt%5Cn%5Cnplt.rcParams%5B%5C%22font.sans-serif%5C%22%5D%20%3D%20%5B%5C%22SimHei%5C%22%5D%5Cnplt.rcParams%5B%5C%22axes.unicode_minus%5C%22%5D%20%3D%20False%5Cn%5Cnclass%20ProductCollector%3A%5Cn%20%20%20%20def%20__init__(self%2C%20access_key%2C%20secret_key)%3A%5Cn%20%20%20%20%20%20%20%20self.access_key%20%3D%20access_key%5Cn%20%20%20%20%20%20%20%20self.secret_key%20%3D%20secret_key%5Cn%20%20%20%20%20%20%20%20self.base_address%20%3D%20%5C%22https%3A%2F%2Fapi%E2%80%91gw.onebound.cn%2Fjd%2Fitem_get_pro%2F%5C%22%5Cn%20%20%20%20%20%20%20%20self.session%20%3D%20requests.Session()%5Cn%5Cn%20%20%20%20def%20get_product_raw(self%2C%20product_id)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E8%8E%B7%E5%8F%96%E5%8D%95%E4%B8%AA%E5%95%86%E5%93%81%E5%AE%8C%E6%95%B4%E5%8E%9F%E5%A7%8B%E6%95%B0%E6%8D%AE%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20self.access_key%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20self.secret_key%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20product_id%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%20%5C%22json%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20resp%20%3D%20self.session.get(%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.base_address%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20params%3Dparams%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20headers%3D%7B%5C%22Accept%E2%80%91Encoding%5C%22%3A%20%5C%22gzip%5C%22%2C%20%5C%22Connection%5C%22%3A%20%5C%22close%5C%22%7D%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20timeout%3D15%5Cn%20%20%20%20%20%20%20%20%20%20%20%20)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20resp.raise_for_status()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20resp.json()%5Cn%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%20%7Bproduct_id%7D%20%E8%8E%B7%E5%8F%96%E5%BC%82%E5%B8%B8%EF%BC%9A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20def%20parse_product_info(self%2C%20raw_json)%3A%5Cn%20%20%20%20%20%20%20%20%5C%22%5C%22%5C%22%E8%A7%A3%E6%9E%90%E5%8E%9F%E5%A7%8B%E6%8A%A5%E6%96%87%EF%BC%8C%E6%8F%90%E5%8F%96%E4%B8%9A%E5%8A%A1%E9%9C%80%E8%A6%81%E7%9A%84%E6%A0%B8%E5%BF%83%E5%AD%97%E6%AE%B5%5C%22%5C%22%5C%22%5Cn%20%20%20%20%20%20%20%20if%20not%20raw_json%20or%20raw_json.get(%5C%22error_code%5C%22)%20!%3D%20%5C%220000%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20item%20%3D%20raw_json.get(%5C%22item%5C%22%2C%20%7B%7D)%5Cn%20%20%20%20%20%20%20%20sku_list%20%3D%20item.get(%5C%22skus%5C%22%2C%20%7B%7D).get(%5C%22sku%5C%22%2C%20%5B%5D)%5Cn%20%20%20%20%20%20%20%20sku_result%20%3D%20%5B%5D%5Cn%20%20%20%20%20%20%20%20for%20sku%20in%20sku_list%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20sku_result.append(%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22sku_id%5C%22%3A%20sku.get(%5C%22sku_id%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22spec_name%5C%22%3A%20sku.get(%5C%22properties_name%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22current_price%5C%22%3A%20float(sku.get(%5C%22price%5C%22%2C%200))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22origin_price%5C%22%3A%20float(sku.get(%5C%22orginal_price%5C%22%2C%200))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3A%20int(sku.get(%5C%22quantity%5C%22%2C%200))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22sku_url%5C%22%3A%20sku.get(%5C%22sku_url%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%7D)%5Cn%20%20%20%20%20%20%20%20output%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22crawl_time%5C%22%3A%20datetime.now().strftime(%5C%22%25Y%E2%80%91%25m%E2%80%91%25d%20%25H%3A%25M%3A%25S%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22product_id%5C%22%3A%20item.get(%5C%22num_iid%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20item.get(%5C%22title%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22brand%5C%22%3A%20item.get(%5C%22brand%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20item.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22current_price%5C%22%3A%20float(item.get(%5C%22price%5C%22%2C%200))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22origin_price%5C%22%3A%20float(item.get(%5C%22orginal_price%5C%22%2C%200))%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22stock_total%5C%22%3A%20item.get(%5C%22num%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22detail_url%5C%22%3A%20item.get(%5C%22detail_url%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22main_image%5C%22%3A%20item.get(%5C%22pic_url%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%5C%22skus%5C%22%3A%20sku_result%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20return%20output%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20%23%20%E6%9B%BF%E6%8D%A2%E6%88%90%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81%5Cn%20%20%20%20AK%20%3D%20%5C%22%E4%BD%A0%E7%9A%84access_key%5C%22%5Cn%20%20%20%20SK%20%3D%20%5C%22%E4%BD%A0%E7%9A%84secret_key%5C%22%5Cn%20%20%20%20collector%20%3D%20ProductCollector(AK%2C%20SK)%5Cn%20%20%20%20target_product%20%3D%20%5C%2210335871600%5C%22%5Cn%20%20%20%20raw_data%20%3D%20collector.get_product_raw(target_product)%5Cn%20%20%20%20product_info%20%3D%20collector.parse_product_info(raw_data)%5Cn%20%20%20%20print(json.dumps(product_info%2C%20ensure_ascii%3DFalse%2C%20indent%3D2))%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;product_monitor.py
import&amp;nbsp;requests
import&amp;nbsp;json
import&amp;nbsp;time
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime
import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd
import&amp;nbsp;matplotlib.pyplot&amp;nbsp;as&amp;nbsp;plt

plt.rcParams[&amp;quot;font.sans-serif&amp;quot;]&amp;nbsp;=&amp;nbsp;[&amp;quot;SimHei&amp;quot;]
plt.rcParams[&amp;quot;axes.unicode_minus&amp;quot;]&amp;nbsp;=&amp;nbsp;False

class&amp;nbsp;ProductCollector:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;__init__(self,&amp;nbsp;access_key,&amp;nbsp;secret_key):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.access_key&amp;nbsp;=&amp;nbsp;access_key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.secret_key&amp;nbsp;=&amp;nbsp;secret_key
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.base_address&amp;nbsp;=&amp;nbsp;&amp;quot;https://api‑gw.onebound.cn/jd/item_get_pro/&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.session&amp;nbsp;=&amp;nbsp;requests.Session()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;get_product_raw(self,&amp;nbsp;product_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;获取单个商品完整原始数据&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;self.access_key,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;self.secret_key,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;product_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;nbsp;&amp;quot;json&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;self.session.get(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;self.base_address,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params=params,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers={&amp;quot;Accept‑Encoding&amp;quot;:&amp;nbsp;&amp;quot;gzip&amp;quot;,&amp;nbsp;&amp;quot;Connection&amp;quot;:&amp;nbsp;&amp;quot;close&amp;quot;},
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timeout=15
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp.raise_for_status()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品&amp;nbsp;{product_id}&amp;nbsp;获取异常：{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;def&amp;nbsp;parse_product_info(self,&amp;nbsp;raw_json):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;解析原始报文，提取业务需要的核心字段&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;raw_json&amp;nbsp;or&amp;nbsp;raw_json.get(&amp;quot;error_code&amp;quot;)&amp;nbsp;!=&amp;nbsp;&amp;quot;0000&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item&amp;nbsp;=&amp;nbsp;raw_json.get(&amp;quot;item&amp;quot;,&amp;nbsp;{})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_list&amp;nbsp;=&amp;nbsp;item.get(&amp;quot;skus&amp;quot;,&amp;nbsp;{}).get(&amp;quot;sku&amp;quot;,&amp;nbsp;[])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_result&amp;nbsp;=&amp;nbsp;[]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;sku&amp;nbsp;in&amp;nbsp;sku_list:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sku_result.append({
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;sku_id&amp;quot;:&amp;nbsp;sku.get(&amp;quot;sku_id&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;spec_name&amp;quot;:&amp;nbsp;sku.get(&amp;quot;properties_name&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;current_price&amp;quot;:&amp;nbsp;float(sku.get(&amp;quot;price&amp;quot;,&amp;nbsp;0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;origin_price&amp;quot;:&amp;nbsp;float(sku.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;int(sku.get(&amp;quot;quantity&amp;quot;,&amp;nbsp;0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;sku_url&amp;quot;:&amp;nbsp;sku.get(&amp;quot;sku_url&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;output&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;crawl_time&amp;quot;:&amp;nbsp;datetime.now().strftime(&amp;quot;%Y‑%m‑%d&amp;nbsp;%H:%M:%S&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;product_id&amp;quot;:&amp;nbsp;item.get(&amp;quot;num_iid&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;item.get(&amp;quot;title&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;brand&amp;quot;:&amp;nbsp;item.get(&amp;quot;brand&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;item.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;current_price&amp;quot;:&amp;nbsp;float(item.get(&amp;quot;price&amp;quot;,&amp;nbsp;0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;origin_price&amp;quot;:&amp;nbsp;float(item.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0)),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock_total&amp;quot;:&amp;nbsp;item.get(&amp;quot;num&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;detail_url&amp;quot;:&amp;nbsp;item.get(&amp;quot;detail_url&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;main_image&amp;quot;:&amp;nbsp;item.get(&amp;quot;pic_url&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;skus&amp;quot;:&amp;nbsp;sku_result
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;output

if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;替换成你的凭证
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AK&amp;nbsp;=&amp;nbsp;&amp;quot;你的access_key&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SK&amp;nbsp;=&amp;nbsp;&amp;quot;你的secret_key&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;collector&amp;nbsp;=&amp;nbsp;ProductCollector(AK,&amp;nbsp;SK)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;target_product&amp;nbsp;=&amp;nbsp;&amp;quot;10335871600&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raw_data&amp;nbsp;=&amp;nbsp;collector.get_product_raw(target_product)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;product_info&amp;nbsp;=&amp;nbsp;collector.parse_product_info(raw_data)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(json.dumps(product_info,&amp;nbsp;ensure_ascii=False,&amp;nbsp;indent=2))&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;运行代码后，就可以输出结构化后的商品信息，包含主商品基础信息，以及全部规格的价格、库存。&lt;/p&gt;&lt;h2&gt;第二部分：实现定时监控，捕捉价格库存变动&lt;/h2&gt;&lt;p&gt;很多场景下，我们需要每隔一段时间重新拉取，对比上一轮数据，如果价格下跌、库存发生变化，就记录变动事件。下面增加定时采集、变动检测、保存历史记录到 csv 文件的逻辑。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20%E6%8E%A5%E4%B8%8A%E9%9D%A2%E7%9A%84%E4%BB%A3%E7%A0%81%E7%BB%A7%E7%BB%AD%E8%BF%BD%E5%8A%A0%5Cndef%20save_to_csv(data%2C%20file_name%3D%5C%22product_history.csv%5C%22)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E4%BF%9D%E5%AD%98%E5%8D%95%E6%9D%A1%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%E5%88%B0%E6%9C%AC%E5%9C%B0csv%5C%22%5C%22%5C%22%5Cn%20%20%20%20base_row%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22crawl_time%5C%22%3A%20data%5B%5C%22crawl_time%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%5C%22product_id%5C%22%3A%20data%5B%5C%22product_id%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20data%5B%5C%22title%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20data%5B%5C%22shop_name%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%5C%22current_price%5C%22%3A%20data%5B%5C%22current_price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%5C%22origin_price%5C%22%3A%20data%5B%5C%22origin_price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20%5C%22stock_total%5C%22%3A%20data%5B%5C%22stock_total%5C%22%5D%5Cn%20%20%20%20%7D%5Cn%20%20%20%20df%20%3D%20pd.DataFrame(%5Bbase_row%5D)%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20old%20%3D%20pd.read_csv(file_name)%5Cn%20%20%20%20%20%20%20%20df%20%3D%20pd.concat(%5Bold%2C%20df%5D%2C%20ignore_index%3DTrue)%5Cn%20%20%20%20except%20FileNotFoundError%3A%5Cn%20%20%20%20%20%20%20%20pass%5Cn%20%20%20%20df.to_csv(file_name%2C%20index%3DFalse%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%5Cndef%20detect_change(old_data%2C%20new_data)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%AF%B9%E6%AF%94%E4%B8%A4%E6%AC%A1%E9%87%87%E9%9B%86%E7%BB%93%E6%9E%9C%EF%BC%8C%E8%AF%86%E5%88%AB%E5%8F%98%E5%8A%A8%5C%22%5C%22%5C%22%5Cn%20%20%20%20change_list%20%3D%20%5B%5D%5Cn%20%20%20%20if%20old_data%5B%5C%22current_price%5C%22%5D%20!%3D%20new_data%5B%5C%22current_price%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20change_list.append(f%5C%22%E4%BB%B7%E6%A0%BC%E5%8F%98%E5%8A%A8%EF%BC%9A%7Bold_data%5B&amp;#39;current_price&amp;#39;%5D%7D%20%E2%86%92%20%7Bnew_data%5B&amp;#39;current_price&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20if%20old_data%5B%5C%22stock_total%5C%22%5D%20!%3D%20new_data%5B%5C%22stock_total%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20change_list.append(f%5C%22%E5%BA%93%E5%AD%98%E5%8F%98%E5%8A%A8%EF%BC%9A%7Bold_data%5B&amp;#39;stock_total&amp;#39;%5D%7D%20%E2%86%92%20%7Bnew_data%5B&amp;#39;stock_total&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20%23%20%E5%AF%B9%E6%AF%94sku%E8%A7%84%E6%A0%BC%E4%BB%B7%E6%A0%BC%5Cn%20%20%20%20old_sku_map%20%3D%20%7Bs%5B%5C%22sku_id%5C%22%5D%3A%20s%20for%20s%20in%20old_data%5B%5C%22skus%5C%22%5D%7D%5Cn%20%20%20%20for%20new_sku%20in%20new_data%5B%5C%22skus%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20o_sku%20%3D%20old_sku_map.get(new_sku%5B%5C%22sku_id%5C%22%5D)%5Cn%20%20%20%20%20%20%20%20if%20o_sku%20and%20o_sku%5B%5C%22current_price%5C%22%5D%20!%3D%20new_sku%5B%5C%22current_price%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20change_list.append(f%5C%22%E8%A7%84%E6%A0%BC%5B%7Bnew_sku%5B&amp;#39;spec_name&amp;#39;%5D%7D%5D%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Bo_sku%5B&amp;#39;current_price&amp;#39;%5D%7D%E2%86%92%7Bnew_sku%5B&amp;#39;current_price&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20return%20change_list%5Cn%5Cndef%20monitor_loop(product_id%2C%20interval%3D60*10)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%BE%AA%E7%8E%AF%E7%9B%91%E6%8E%A7%EF%BC%8C%E9%BB%98%E8%AE%A410%E5%88%86%E9%92%9F%E9%87%87%E9%9B%86%E4%B8%80%E6%AC%A1%5C%22%5C%22%5C%22%5Cn%20%20%20%20AK%20%3D%20%5C%22%E4%BD%A0%E7%9A%84access_key%5C%22%5Cn%20%20%20%20SK%20%3D%20%5C%22%E4%BD%A0%E7%9A%84secret_key%5C%22%5Cn%20%20%20%20coll%20%3D%20ProductCollector(AK%2C%20SK)%5Cn%20%20%20%20last_record%20%3D%20None%5Cn%20%20%20%20print(%5C%22%E5%BC%80%E5%A7%8B%E7%9B%91%E6%8E%A7%EF%BC%8C%E6%8C%89Ctrl%2BC%E7%BB%88%E6%AD%A2%E7%A8%8B%E5%BA%8F%5C%22)%5Cn%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20raw%20%3D%20coll.get_product_raw(product_id)%5Cn%20%20%20%20%20%20%20%20curr%20%3D%20coll.parse_product_info(raw)%5Cn%20%20%20%20%20%20%20%20if%20curr%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20save_to_csv(curr)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E3%80%90%7Bcurr%5B&amp;#39;crawl_time&amp;#39;%5D%7D%E3%80%91%E9%87%87%E9%9B%86%E5%AE%8C%E6%88%90%EF%BD%9C%E5%95%86%E5%93%81%EF%BC%9A%7Bcurr%5B&amp;#39;title&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20last_record%20is%20not%20None%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20changes%20%3D%20detect_change(last_record%2C%20curr)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20changes%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(%5C%22%E2%9C%85%E6%A3%80%E6%B5%8B%E5%88%B0%E5%8F%98%E5%8A%A8%E4%BA%8B%E4%BB%B6%EF%BC%9A%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20item%20in%20changes%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E2%80%91%20%7Bitem%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20last_record%20%3D%20curr%5Cn%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20time.sleep(interval)%5Cn%20%20%20%20%20%20%20%20except%20KeyboardInterrupt%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(%5C%22%5C%5Cn%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%E5%B7%B2%E9%80%80%E5%87%BA%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20break%5Cn%5Cn%23%20%E5%90%AF%E5%8A%A8%E7%9B%91%E6%8E%A7%5Cn%23%20monitor_loop(%5C%2210335871600%5C%22%2C%20interval%3D600)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;接上面的代码继续追加
def&amp;nbsp;save_to_csv(data,&amp;nbsp;file_name=&amp;quot;product_history.csv&amp;quot;):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;保存单条采集记录到本地csv&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base_row&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;crawl_time&amp;quot;:&amp;nbsp;data[&amp;quot;crawl_time&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;product_id&amp;quot;:&amp;nbsp;data[&amp;quot;product_id&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;data[&amp;quot;title&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;data[&amp;quot;shop_name&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;current_price&amp;quot;:&amp;nbsp;data[&amp;quot;current_price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;origin_price&amp;quot;:&amp;nbsp;data[&amp;quot;origin_price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock_total&amp;quot;:&amp;nbsp;data[&amp;quot;stock_total&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.DataFrame([base_row])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old&amp;nbsp;=&amp;nbsp;pd.read_csv(file_name)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.concat([old,&amp;nbsp;df],&amp;nbsp;ignore_index=True)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;FileNotFoundError:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df.to_csv(file_name,&amp;nbsp;index=False,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)

def&amp;nbsp;detect_change(old_data,&amp;nbsp;new_data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;对比两次采集结果，识别变动&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;change_list&amp;nbsp;=&amp;nbsp;[]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;old_data[&amp;quot;current_price&amp;quot;]&amp;nbsp;!=&amp;nbsp;new_data[&amp;quot;current_price&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;change_list.append(f&amp;quot;价格变动：{old_data[&amp;#39;current_price&amp;#39;]}&amp;nbsp;→&amp;nbsp;{new_data[&amp;#39;current_price&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;old_data[&amp;quot;stock_total&amp;quot;]&amp;nbsp;!=&amp;nbsp;new_data[&amp;quot;stock_total&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;change_list.append(f&amp;quot;库存变动：{old_data[&amp;#39;stock_total&amp;#39;]}&amp;nbsp;→&amp;nbsp;{new_data[&amp;#39;stock_total&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;对比sku规格价格
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old_sku_map&amp;nbsp;=&amp;nbsp;{s[&amp;quot;sku_id&amp;quot;]:&amp;nbsp;s&amp;nbsp;for&amp;nbsp;s&amp;nbsp;in&amp;nbsp;old_data[&amp;quot;skus&amp;quot;]}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;new_sku&amp;nbsp;in&amp;nbsp;new_data[&amp;quot;skus&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;o_sku&amp;nbsp;=&amp;nbsp;old_sku_map.get(new_sku[&amp;quot;sku_id&amp;quot;])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;o_sku&amp;nbsp;and&amp;nbsp;o_sku[&amp;quot;current_price&amp;quot;]&amp;nbsp;!=&amp;nbsp;new_sku[&amp;quot;current_price&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;change_list.append(f&amp;quot;规格[{new_sku[&amp;#39;spec_name&amp;#39;]}]价格：{o_sku[&amp;#39;current_price&amp;#39;]}→{new_sku[&amp;#39;current_price&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;change_list

def&amp;nbsp;monitor_loop(product_id,&amp;nbsp;interval=60*10):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;循环监控，默认10分钟采集一次&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;AK&amp;nbsp;=&amp;nbsp;&amp;quot;你的access_key&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SK&amp;nbsp;=&amp;nbsp;&amp;quot;你的secret_key&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;coll&amp;nbsp;=&amp;nbsp;ProductCollector(AK,&amp;nbsp;SK)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_record&amp;nbsp;=&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;开始监控，按Ctrl+C终止程序&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raw&amp;nbsp;=&amp;nbsp;coll.get_product_raw(product_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;curr&amp;nbsp;=&amp;nbsp;coll.parse_product_info(raw)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;curr:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_to_csv(curr)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n【{curr[&amp;#39;crawl_time&amp;#39;]}】采集完成｜商品：{curr[&amp;#39;title&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;last_record&amp;nbsp;is&amp;nbsp;not&amp;nbsp;None:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;changes&amp;nbsp;=&amp;nbsp;detect_change(last_record,&amp;nbsp;curr)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;changes:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;✅检测到变动事件：&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;item&amp;nbsp;in&amp;nbsp;changes:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;‑&amp;nbsp;{item}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_record&amp;nbsp;=&amp;nbsp;curr
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(interval)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;KeyboardInterrupt:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n监控任务已退出&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break

#&amp;nbsp;启动监控
#&amp;nbsp;monitor_loop(&amp;quot;10335871600&amp;quot;,&amp;nbsp;interval=600)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;说明：interval 单位是秒，示例 10 分钟 = 600 秒，正式运行不要设置过小，防止请求频率过高。运行后所有历史记录会持续写入&lt;code&gt;product_history.csv&lt;/code&gt;，方便后续复盘。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;第三部分：基于历史记录做数据分析&lt;/h2&gt;&lt;p&gt;当我们积累一段时间的历史记录之后，就可以读取 csv 文件做价格走势、价格波动幅度分析，还可以生成简单可视化图表。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;3&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22def%20analysis_history(csv_path%3D%5C%22product_history.csv%5C%22)%3A%5Cn%20%20%20%20df%20%3D%20pd.read_csv(csv_path%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%20%20%20%20df%5B%5C%22crawl_time%5C%22%5D%20%3D%20pd.to_datetime(df%5B%5C%22crawl_time%5C%22%5D)%5Cn%20%20%20%20print(%5C%22%3D%3D%3D%3D%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E6%A6%82%E8%A7%88%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E6%80%BB%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%E6%95%B0%EF%BC%9A%7Blen(df)%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E7%9B%91%E6%8E%A7%E6%97%B6%E9%97%B4%E5%8C%BA%E9%97%B4%EF%BC%9A%7Bdf%5B&amp;#39;crawl_time&amp;#39;%5D.min()%7D%20~%20%7Bdf%5B&amp;#39;crawl_time&amp;#39;%5D.max()%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E4%BB%B7%E6%A0%BC%E7%BB%9F%E8%AE%A1%EF%BC%9A%E6%9C%80%E9%AB%98%7Bdf%5B&amp;#39;current_price&amp;#39;%5D.max()%7D%EF%BD%9C%E6%9C%80%E4%BD%8E%7Bdf%5B&amp;#39;current_price&amp;#39;%5D.min()%7D%EF%BD%9C%E5%9D%87%E5%80%BC%7Bround(df%5B&amp;#39;current_price&amp;#39;%5D.mean()%2C2)%7D%5C%22)%5Cn%5Cn%20%20%20%20%23%20%E7%BB%98%E5%88%B6%E4%BB%B7%E6%A0%BC%E8%B5%B0%E5%8A%BF%5Cn%20%20%20%20plt.figure(figsize%3D(12%2C%205))%5Cn%20%20%20%20plt.plot(df%5B%5C%22crawl_time%5C%22%5D%2C%20df%5B%5C%22current_price%5C%22%5D%2C%20marker%3D%5C%22o%5C%22%2C%20linestyle%3D%5C%22-%5C%22%2C%20color%3D%5C%22%232E86AB%5C%22)%5Cn%20%20%20%20plt.title(%5C%22%E5%95%86%E5%93%81%E4%BB%B7%E6%A0%BC%E7%9B%91%E6%8E%A7%E8%B5%B0%E5%8A%BF%5C%22)%5Cn%20%20%20%20plt.xlabel(%5C%22%E9%87%87%E9%9B%86%E6%97%B6%E9%97%B4%5C%22)%5Cn%20%20%20%20plt.ylabel(%5C%22%E5%BD%93%E5%89%8D%E5%94%AE%E4%BB%B7%5C%22)%5Cn%20%20%20%20plt.xticks(rotation%3D30)%5Cn%20%20%20%20plt.grid(alpha%3D0.3)%5Cn%20%20%20%20plt.tight_layout()%5Cn%20%20%20%20plt.savefig(%5C%22price_trend.png%5C%22%2C%20dpi%3D150)%5Cn%20%20%20%20plt.show()%5Cn%5Cn%20%20%20%20%23%20%E7%BB%9F%E8%AE%A1%E9%99%8D%E4%BB%B7%E5%B9%85%E5%BA%A6%5Cn%20%20%20%20df%5B%5C%22discount_rate%5C%22%5D%20%3D%20(df%5B%5C%22origin_price%5C%22%5D%20-%20df%5B%5C%22current_price%5C%22%5D)%20%2F%20df%5B%5C%22origin_price%5C%22%5D%5Cn%20%20%20%20print(%5C%22%5C%5Cn%3D%3D%3D%3D%E6%8A%98%E6%89%A3%E7%BB%9F%E8%AE%A1%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20print(df%5B%5B%5C%22crawl_time%5C%22%2C%5C%22current_price%5C%22%2C%5C%22origin_price%5C%22%2C%5C%22discount_rate%5C%22%5D%5D.round(3))%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20analysis_history(%5C%22product_history.csv%5C%22)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;def&amp;nbsp;analysis_history(csv_path=&amp;quot;product_history.csv&amp;quot;):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.read_csv(csv_path,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df[&amp;quot;crawl_time&amp;quot;]&amp;nbsp;=&amp;nbsp;pd.to_datetime(df[&amp;quot;crawl_time&amp;quot;])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;====历史数据概览====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;总采集记录数：{len(df)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;监控时间区间：{df[&amp;#39;crawl_time&amp;#39;].min()}&amp;nbsp;~&amp;nbsp;{df[&amp;#39;crawl_time&amp;#39;].max()}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;价格统计：最高{df[&amp;#39;current_price&amp;#39;].max()}｜最低{df[&amp;#39;current_price&amp;#39;].min()}｜均值{round(df[&amp;#39;current_price&amp;#39;].mean(),2)}&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;绘制价格走势
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.figure(figsize=(12,&amp;nbsp;5))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.plot(df[&amp;quot;crawl_time&amp;quot;],&amp;nbsp;df[&amp;quot;current_price&amp;quot;],&amp;nbsp;marker=&amp;quot;o&amp;quot;,&amp;nbsp;linestyle=&amp;quot;-&amp;quot;,&amp;nbsp;color=&amp;quot;#2E86AB&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.title(&amp;quot;商品价格监控走势&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.xlabel(&amp;quot;采集时间&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.ylabel(&amp;quot;当前售价&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.xticks(rotation=30)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.grid(alpha=0.3)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.tight_layout()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.savefig(&amp;quot;price_trend.png&amp;quot;,&amp;nbsp;dpi=150)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;plt.show()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;统计降价幅度
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df[&amp;quot;discount_rate&amp;quot;]&amp;nbsp;=&amp;nbsp;(df[&amp;quot;origin_price&amp;quot;]&amp;nbsp;-&amp;nbsp;df[&amp;quot;current_price&amp;quot;])&amp;nbsp;/&amp;nbsp;df[&amp;quot;origin_price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n====折扣统计====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(df[[&amp;quot;crawl_time&amp;quot;,&amp;quot;current_price&amp;quot;,&amp;quot;origin_price&amp;quot;,&amp;quot;discount_rate&amp;quot;]].round(3))

if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis_history(&amp;quot;product_history.csv&amp;quot;)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;运行之后，控制台输出统计信息，同时在本地生成&lt;code&gt;price_trend.png&lt;/code&gt;价格走势图，可以直接放到报告里。&lt;/p&gt;&lt;h2&gt;实际业务场景拓展&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;多商品批量监控&lt;/strong&gt;：把商品 ID 放到列表循环处理，就可以同时监控一批竞品，分别保存记录；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;消息提醒&lt;/strong&gt;：检测到变动事件后，可以对接企业微信 / 钉钉机器人，一旦降价、库存清零，立刻推送通知；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;规格维度分析&lt;/strong&gt;：解析出来的 skus 列表，可以单独保存每个规格的历史，分析哪个规格经常调价；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据导出报告&lt;/strong&gt;：Pandas 可以直接输出 Excel，方便运营同学做周报复盘。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;踩坑与注意事项&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;部分场景下返回的价格会存在偏差，这是数据来源本身特性，做分析时要结合业务校验，不要直接当作绝对唯一依据；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;不要把轮询间隔设置太短，建议至少 5‑10 分钟以上，避免短时间大量请求；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果返回找不到商品，优先核对商品编号是否正确，确认商品是否已经下架；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;做好异常捕获，网络波动时程序不会直接崩溃，保证监控任务稳定跑。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;写在最后&lt;/h2&gt;&lt;p&gt;OpenClaw 这套工具可以帮助我们绕开复杂的网页逆向、动态渲染、字体加密等难题，快速拿到完整结构化商品信息。配合 Python 做定时任务、存储、统计分析，就能低成本搭建一套属于自己的商品监控系统，不管是个人选品，还是小团队做竞品跟踪，都很实用。&lt;/p&gt;&lt;p&gt;整套代码可以直接复制运行，替换凭证、商品编号即可上手，大家可以根据自己业务继续拓展告警、多商品管理等功能。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Tue, 25 Aug 2026 16:25:04 +0800</pubDate></item><item><title>告别人工盯品！借助 Open Claw 搭建电商商品自动化监控与数据分析系统（完整可运行源码）</title><link>https://www.shopapi.com.cn/?id=221</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营或者竞品调研，相信很多人都有这样的痛点：想要持续跟踪多款商品的价格变动、库存、规格信息，每天手动打开网页复制粘贴数据到 Excel 表格。不仅重复劳动耗时间，大促节点的临时降价、库存清仓往往转瞬即逝，人工很难第一时间捕捉变化。&lt;/p&gt;&lt;p&gt;如果自己手写页面解析脚本，又会遇到页面动态渲染、网页改版导致解析失效、访问频次受限等一系列棘手问题，调试维护成本居高不下。&lt;/p&gt;&lt;p&gt;最近在项目实践中使用了 &lt;strong&gt;Open Claw&lt;/strong&gt; 工具，它可以直接输出结构化的商品信息，不用再折腾网页源码解析，我们可以把精力全部放在业务逻辑上，快速搭建一套本地商品监控与数据分析程序。&lt;/p&gt;&lt;p&gt;本篇教程会完整演示整套流程：包含本地数据持久化存储、价格库存异动提醒、历史时序数据分析、批量商品巡检，所有代码可以直接复制运行，Windows、Mac、Linux 都支持，部署到服务器就可以实现 7×24 小时不间断监控。&lt;/p&gt;&lt;h2&gt;整体实现思路&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;获取商品&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;4&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E7%BB%93%E6%9E%84%E5%8C%96%E5%8E%9F%E5%A7%8B%E4%BF%A1%E6%81%AF%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%22xpeuTh-1787564545105%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;结构化原始信息&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;结构化原始信息&quot;&gt;&lt;strong&gt;结构化原始信息&lt;/strong&gt;&lt;/a&gt;&lt;/span&gt;：商品标题、当前售价、原始标价、库存、多规格信息、店铺名称等完整内容；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;使用 SQLite 本地数据库保存每一次采集记录，沉淀历史时序数据；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;设置定时巡检任务，对比前后两次数据，识别降价、库存变动，输出告警提示；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;读取本地历史记录做数据分析，统计历史最高、最低、平均售价，分析价格波动幅度；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;支持批量加入多款商品，扩展可对接钉钉、企业微信实现消息推送。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;环境准备&lt;/h2&gt;&lt;p&gt;Python 版本建议 3.7 以上，安装需要的依赖包&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;3&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%20apscheduler%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&amp;nbsp;apscheduler&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background:rgba(220,220,220,0.5);background-image:url(https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png);display:none;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;requests：负责网络交互&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;apscheduler：实现定时巡检任务&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;sqlite3：Python 内置轻量数据库，无需额外安装，用来存储监控历史记录&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;提前准备好 Open Claw 后台生成的两组身份凭证，用于程序访问。&lt;/p&gt;&lt;h2&gt;完整代码实现&lt;/h2&gt;&lt;p&gt;新建文件 &lt;code&gt;goods_monitor.py&lt;/code&gt;，复制下面全部代码，修改配置区域的凭证、监控商品编号、告警阈值就可以直接运行。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22import%20requests%5Cnimport%20json%5Cnimport%20sqlite3%5Cnimport%20time%5Cnfrom%20datetime%20import%20datetime%5Cnfrom%20apscheduler.schedulers.background%20import%20BackgroundScheduler%5Cn%5Cn%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%20%E3%80%90%E9%85%8D%E7%BD%AE%E5%8C%BA%E5%9F%9F%EF%BC%8C%E8%AF%B7%E8%87%AA%E8%A1%8C%E4%BF%AE%E6%94%B9%E3%80%91%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5CnCLAW_KEY%20%3D%20%5C%22%E5%A1%AB%E5%85%A5%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81key%5C%22%5CnCLAW_SECRET%20%3D%20%5C%22%E5%A1%AB%E5%85%A5%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81secret%5C%22%5Cn%23%20%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%EF%BC%8C%E5%8F%AF%E4%BB%A5%E5%A1%AB%E5%85%A5%E5%A4%9A%E4%B8%AA%E5%AE%9E%E7%8E%B0%E6%89%B9%E9%87%8F%E7%9B%91%E6%8E%A7%5CnMONITOR_GOODS%20%3D%20%5B%5C%2210335871600%5C%22%2C%20%5C%2210057467958584%5C%22%5D%5CnPOLL_INTERVAL%20%3D%20300%20%20%23%20%E5%B7%A1%E6%A3%80%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E6%99%AE%E9%80%9A%E5%95%86%E5%93%81%E5%BB%BA%E8%AE%AE300%E7%A7%92%EF%BC%8C%E7%88%86%E6%AC%BE%E5%8F%AF%E7%BC%A9%E7%9F%AD%5CnPRICE_ALERT_RATIO%20%3D%200.15%20%20%23%20%E9%99%8D%E4%BB%B7%E8%B6%85%E8%BF%8715%25%E5%B0%B1%E8%A7%A6%E5%8F%91%E5%91%8A%E8%AD%A6%5CnDB_FILE%20%3D%20%5C%22goods_monitor.db%5C%22%5Cn%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5Cn%5Cndef%20init_db()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%88%9D%E5%A7%8B%E5%8C%96%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%BA%93%EF%BC%8C%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E8%A1%A8%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_FILE)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20CREATE%20TABLE%20IF%20NOT%20EXISTS%20goods_record%20(%5Cn%20%20%20%20%20%20%20%20id%20INTEGER%20PRIMARY%20KEY%20AUTOINCREMENT%2C%5Cn%20%20%20%20%20%20%20%20goods_id%20TEXT%20NOT%20NULL%2C%5Cn%20%20%20%20%20%20%20%20title%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20original_price%20REAL%2C%5Cn%20%20%20%20%20%20%20%20stock%20INTEGER%2C%5Cn%20%20%20%20%20%20%20%20shop_name%20TEXT%2C%5Cn%20%20%20%20%20%20%20%20record_time%20TIMESTAMP%5Cn%20%20%20%20)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;)%5Cn%20%20%20%20conn.commit()%5Cn%20%20%20%20conn.close()%5Cn%5Cn%5Cndef%20get_goods_info(goods_id%3A%20str)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE%5Cn%20%20%20%20%3Aparam%20goods_id%3A%20%E5%95%86%E5%93%81%E6%95%B0%E5%AD%97%E7%BC%96%E5%8F%B7%5Cn%20%20%20%20%3Areturn%3A%20%E5%AD%97%E5%85%B8%E6%A0%BC%E5%BC%8F%E5%95%86%E5%93%81%E4%BF%A1%E6%81%AF%EF%BC%8C%E5%A4%B1%E8%B4%A5%E8%BF%94%E5%9B%9ENone%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20CLAW_KEY%2C%5Cn%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20CLAW_SECRET%2C%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%20%5C%22json%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20retry%20%3D%203%5Cn%20%20%20%20while%20retry%20%3E%200%3A%5Cn%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22https%3A%2F%2Fapi-gw.onebound.cn%2Fjd%2Fitem_get_pro%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20params%3Dparams%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20timeout%3D15%5Cn%20%20%20%20%20%20%20%20%20%20%20%20)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20data%20%3D%20resp.json()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20data.get(%5C%22error_code%5C%22)%20!%3D%20%5C%220000%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%7Bgoods_id%7D%E5%BC%82%E5%B8%B8%EF%BC%9A%7Bdata.get(&amp;#39;reason&amp;#39;%2C&amp;#39;%E6%9C%AA%E7%9F%A5%E9%94%99%E8%AF%AF&amp;#39;)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20%20%20%20%20item_data%20%3D%20data.get(%5C%22item%5C%22%2C%20%7B%7D)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20result%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22goods_id%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20item_data.get(%5C%22title%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22price%5C%22%3A%20float(item_data.get(%5C%22price%5C%22%2C%200))%20if%20item_data.get(%5C%22price%5C%22)%20else%200%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22original_price%5C%22%3A%20float(item_data.get(%5C%22orginal_price%5C%22%2C%200))%20if%20item_data.get(%5C%22orginal_price%5C%22)%20else%200%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3A%20int(item_data.get(%5C%22num%5C%22%2C%200))%20if%20item_data.get(%5C%22num%5C%22)%20else%200%2C%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20item_data.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22%2C%20%5C%22%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20result%5Cn%20%20%20%20%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20retry%20-%3D%201%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E7%BD%91%E7%BB%9C%E5%BC%82%E5%B8%B8%EF%BC%8C%E5%89%A9%E4%BD%99%E9%87%8D%E8%AF%95%E6%AC%A1%E6%95%B0%20%7Bretry%7D%2C%20%E9%94%99%E8%AF%AF%EF%BC%9A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20time.sleep(2)%5Cn%20%20%20%20return%20None%5Cn%5Cn%5Cndef%20save_to_db(goods_data)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%B0%86%E9%87%87%E9%9B%86%E5%88%B0%E7%9A%84%E6%95%B0%E6%8D%AE%E5%AD%98%E5%85%A5%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%BA%93%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_FILE)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20INSERT%20INTO%20goods_record%5Cn%20%20%20%20(goods_id%2C%20title%2C%20price%2C%20original_price%2C%20stock%2C%20shop_name%2C%20record_time)%5Cn%20%20%20%20VALUES%20(%3F%2C%20%3F%2C%20%3F%2C%20%3F%2C%20%3F%2C%20%3F%2C%20%3F)%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C%20(%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22goods_id%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22title%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22original_price%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22stock%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20goods_data%5B%5C%22shop_name%5C%22%5D%2C%5Cn%20%20%20%20%20%20%20%20datetime.now().strftime(%5C%22%25Y-%25m-%25d%20%25H%3A%25M%3A%25S%5C%22)%5Cn%20%20%20%20))%5Cn%20%20%20%20conn.commit()%5Cn%20%20%20%20conn.close()%5Cn%5Cn%5Cndef%20get_last_record(goods_id)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E8%AF%BB%E5%8F%96%E8%AF%A5%E5%95%86%E5%93%81%E4%B8%8A%E4%B8%80%E6%AC%A1%E8%AE%B0%E5%BD%95%EF%BC%8C%E7%94%A8%E4%BA%8E%E5%AF%B9%E6%AF%94%E5%8F%98%E5%8A%A8%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_FILE)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20SELECT%20price%2C%20stock%20FROM%20goods_record%20WHERE%20goods_id%3D%3F%5Cn%20%20%20%20ORDER%20BY%20record_time%20DESC%20LIMIT%201%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C%20(goods_id%2C))%5Cn%20%20%20%20row%20%3D%20cursor.fetchone()%5Cn%20%20%20%20conn.close()%5Cn%20%20%20%20if%20row%3A%5Cn%20%20%20%20%20%20%20%20return%20%7B%5C%22price%5C%22%3A%20row%5B0%5D%2C%20%5C%22stock%5C%22%3A%20row%5B1%5D%7D%5Cn%20%20%20%20return%20None%5Cn%5Cn%5Cndef%20monitor_task()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%AE%9A%E6%97%B6%E6%89%A7%E8%A1%8C%E7%9A%84%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%5C%22%5C%22%5C%22%5Cn%20%20%20%20print(f%5C%22%5C%5Cn%3D%3D%3D%3D%20%E5%B7%A1%E6%A3%80%E5%BC%80%E5%A7%8B%20%7Bdatetime.now().strftime(&amp;#39;%25Y-%25m-%25d%20%25H%3A%25M%3A%25S&amp;#39;)%7D%20%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20for%20gid%20in%20MONITOR_GOODS%3A%5Cn%20%20%20%20%20%20%20%20info%20%3D%20get_goods_info(gid)%5Cn%20%20%20%20%20%20%20%20if%20not%20info%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20continue%5Cn%20%20%20%20%20%20%20%20save_to_db(info)%5Cn%20%20%20%20%20%20%20%20last%20%3D%20get_last_record(gid)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%E3%80%90%7Binfo%5B&amp;#39;title&amp;#39;%5D%7D%E3%80%91%20%E5%BD%93%E5%89%8D%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Binfo%5B&amp;#39;price&amp;#39;%5D%7D%20%E5%BA%93%E5%AD%98%EF%BC%9A%7Binfo%5B&amp;#39;stock&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20if%20last%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E9%99%8D%E4%BB%B7%E5%91%8A%E8%AD%A6%E5%88%A4%E6%96%AD%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20last%5B%5C%22price%5C%22%5D%20%3E%200%20and%20info%5B%5C%22price%5C%22%5D%20%3C%20last%5B%5C%22price%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20drop_rate%20%3D%20(last%5B%5C%22price%5C%22%5D%20-%20info%5B%5C%22price%5C%22%5D)%20%2F%20last%5B%5C%22price%5C%22%5D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20drop_rate%20%3E%3D%20PRICE_ALERT_RATIO%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E2%9A%A0%EF%B8%8F%E4%BB%B7%E6%A0%BC%E5%91%8A%E8%AD%A6%E3%80%91%E5%95%86%E5%93%81%7Bgid%7D%E5%8F%91%E7%94%9F%E5%A4%A7%E5%B9%85%E9%99%8D%E4%BB%B7%EF%BC%81%E4%B8%8A%E6%AC%A1%3A%7Blast%5B&amp;#39;price&amp;#39;%5D%7D%20%E6%9C%AC%E6%AC%A1%3A%7Binfo%5B&amp;#39;price&amp;#39;%5D%7D%20%E9%99%8D%E5%B9%85%7Bdrop_rate%3A.1%25%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%23%20%E5%BA%93%E5%AD%98%E5%8F%98%E5%8A%A8%E6%8F%90%E9%86%92%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20info%5B%5C%22stock%5C%22%5D%20!%3D%20last%5B%5C%22stock%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E3%80%90%E2%84%B9%E5%BA%93%E5%AD%98%E5%8F%98%E5%8A%A8%E3%80%91%E5%95%86%E5%93%81%7Bgid%7D%E5%BA%93%E5%AD%98%E5%8F%98%E6%9B%B4%EF%BC%8C%E4%B8%8A%E4%B8%80%E6%AC%A1%3A%7Blast%5B&amp;#39;stock&amp;#39;%5D%7D%EF%BC%8C%E5%BD%93%E5%89%8D%3A%7Binfo%5B&amp;#39;stock&amp;#39;%5D%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20time.sleep(1)%5Cn%5Cn%5Cndef%20analysis_goods(goods_id)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E9%92%88%E5%AF%B9%E5%8D%95%E4%B8%AA%E5%95%86%E5%93%81%E5%81%9A%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%5C%22%5C%22%5C%22%5Cn%20%20%20%20conn%20%3D%20sqlite3.connect(DB_FILE)%5Cn%20%20%20%20cursor%20%3D%20conn.cursor()%5Cn%20%20%20%20cursor.execute(&amp;#39;&amp;#39;&amp;#39;%5Cn%20%20%20%20SELECT%20price%2C%20record_time%20FROM%20goods_record%20WHERE%20goods_id%3D%3F%20ORDER%20BY%20record_time%5Cn%20%20%20%20&amp;#39;&amp;#39;&amp;#39;%2C%20(goods_id%2C))%5Cn%20%20%20%20rows%20%3D%20cursor.fetchall()%5Cn%20%20%20%20conn.close()%5Cn%20%20%20%20if%20not%20rows%3A%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E6%9A%82%E6%97%A0%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%EF%BC%8C%E8%AF%B7%E5%85%88%E8%BF%90%E8%A1%8C%E7%9B%91%E6%8E%A7%E9%87%87%E9%9B%86%E6%95%B0%E6%8D%AE%EF%BC%81%5C%22)%5Cn%20%20%20%20%20%20%20%20return%5Cn%20%20%20%20price_list%20%3D%20%5Br%5B0%5D%20for%20r%20in%20rows%5D%5Cn%20%20%20%20max_p%20%3D%20max(price_list)%5Cn%20%20%20%20min_p%20%3D%20min(price_list)%5Cn%20%20%20%20avg_p%20%3D%20sum(price_list)%2Flen(price_list)%5Cn%20%20%20%20print(%5C%22%5C%5Cn%3D%3D%3D%3D%3D%3D%3D%3D%E5%95%86%E5%93%81%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%3D%3D%3D%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%8E%86%E5%8F%B2%E6%9C%80%E9%AB%98%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bmax_p%3A.2f%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%8E%86%E5%8F%B2%E6%9C%80%E4%BD%8E%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bmin_p%3A.2f%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%8E%86%E5%8F%B2%E5%B9%B3%E5%9D%87%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bavg_p%3A.2f%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E6%95%B0%E6%8D%AE%E8%AE%B0%E5%BD%95%E6%80%BB%E6%9D%A1%E6%95%B0%EF%BC%9A%7Blen(price_list)%7D%5C%22)%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20init_db()%5Cn%20%20%20%20scheduler%20%3D%20BackgroundScheduler()%5Cn%20%20%20%20scheduler.add_job(monitor_task%2C%20%5C%22interval%5C%22%2C%20seconds%3DPOLL_INTERVAL)%5Cn%20%20%20%20scheduler.start()%5Cn%20%20%20%20print(%5C%22%E7%9B%91%E6%8E%A7%E7%A8%8B%E5%BA%8F%E5%B7%B2%E7%BB%8F%E5%90%AF%E5%8A%A8%EF%BC%8C%E6%8C%89%20Ctrl%2BC%20%E7%BB%88%E6%AD%A2%E7%A8%8B%E5%BA%8F%5C%22)%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20cmd%20%3D%20input(%5C%22%5C%5Cn%E8%BE%93%E5%85%A5%E6%8C%87%E4%BB%A4%EF%BC%9A%5C%5Cn1%3A%E6%89%A7%E8%A1%8C%E4%B8%80%E6%AC%A1%E5%B7%A1%E6%A3%80%20%202%3A%E5%88%86%E6%9E%90%E5%95%86%E5%93%81%E5%8E%86%E5%8F%B2%20%20q%3A%E9%80%80%E5%87%BA%5C%5Cn%3E%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20cmd%20%3D%3D%20%5C%221%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20monitor_task()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20elif%20cmd%20%3D%3D%20%5C%222%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20gid%20%3D%20input(%5C%22%E8%BE%93%E5%85%A5%E8%A6%81%E5%88%86%E6%9E%90%E7%9A%84%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%EF%BC%9A%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20analysis_goods(gid)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20elif%20cmd.lower()%20%3D%3D%20%5C%22q%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20break%5Cn%20%20%20%20except%20KeyboardInterrupt%3A%5Cn%20%20%20%20%20%20%20%20pass%5Cn%20%20%20%20finally%3A%5Cn%20%20%20%20%20%20%20%20scheduler.shutdown()%5Cn%20%20%20%20%20%20%20%20print(%5C%22%E7%A8%8B%E5%BA%8F%E5%B7%B2%E9%80%80%E5%87%BA%5C%22)%5Cn%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;import&amp;nbsp;requests
import&amp;nbsp;json
import&amp;nbsp;sqlite3
import&amp;nbsp;time
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime
from&amp;nbsp;apscheduler.schedulers.background&amp;nbsp;import&amp;nbsp;BackgroundScheduler

#&amp;nbsp;=====================&amp;nbsp;【配置区域，请自行修改】&amp;nbsp;=====================
CLAW_KEY&amp;nbsp;=&amp;nbsp;&amp;quot;填入你的凭证key&amp;quot;
CLAW_SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;填入你的凭证secret&amp;quot;
#&amp;nbsp;需要监控的商品编号，可以填入多个实现批量监控
MONITOR_GOODS&amp;nbsp;=&amp;nbsp;[&amp;quot;10335871600&amp;quot;,&amp;nbsp;&amp;quot;10057467958584&amp;quot;]
POLL_INTERVAL&amp;nbsp;=&amp;nbsp;300&amp;nbsp;&amp;nbsp;#&amp;nbsp;巡检间隔，单位秒，普通商品建议300秒，爆款可缩短
PRICE_ALERT_RATIO&amp;nbsp;=&amp;nbsp;0.15&amp;nbsp;&amp;nbsp;#&amp;nbsp;降价超过15%就触发告警
DB_FILE&amp;nbsp;=&amp;nbsp;&amp;quot;goods_monitor.db&amp;quot;
#&amp;nbsp;=================================================================

def&amp;nbsp;init_db():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;初始化本地数据库，创建数据表&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_FILE)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;CREATE&amp;nbsp;TABLE&amp;nbsp;IF&amp;nbsp;NOT&amp;nbsp;EXISTS&amp;nbsp;goods_record&amp;nbsp;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;id&amp;nbsp;INTEGER&amp;nbsp;PRIMARY&amp;nbsp;KEY&amp;nbsp;AUTOINCREMENT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_id&amp;nbsp;TEXT&amp;nbsp;NOT&amp;nbsp;NULL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;original_price&amp;nbsp;REAL,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stock&amp;nbsp;INTEGER,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shop_name&amp;nbsp;TEXT,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;record_time&amp;nbsp;TIMESTAMP
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.commit()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()


def&amp;nbsp;get_goods_info(goods_id:&amp;nbsp;str):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;获取商品结构化数据
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:param&amp;nbsp;goods_id:&amp;nbsp;商品数字编号
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:return:&amp;nbsp;字典格式商品信息，失败返回None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;CLAW_KEY,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;CLAW_SECRET,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;nbsp;&amp;quot;json&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retry&amp;nbsp;=&amp;nbsp;3
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;retry&amp;nbsp;&amp;gt;&amp;nbsp;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;https://api-gw.onebound.cn/jd/item_get_pro&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params=params,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;timeout=15
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;data&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;data.get(&amp;quot;error_code&amp;quot;)&amp;nbsp;!=&amp;nbsp;&amp;quot;0000&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;获取商品{goods_id}异常：{data.get(&amp;#39;reason&amp;#39;,&amp;#39;未知错误&amp;#39;)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_data&amp;nbsp;=&amp;nbsp;data.get(&amp;quot;item&amp;quot;,&amp;nbsp;{})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;goods_id&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;price&amp;quot;:&amp;nbsp;float(item_data.get(&amp;quot;price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;price&amp;quot;)&amp;nbsp;else&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;original_price&amp;quot;:&amp;nbsp;float(item_data.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;orginal_price&amp;quot;)&amp;nbsp;else&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;int(item_data.get(&amp;quot;num&amp;quot;,&amp;nbsp;0))&amp;nbsp;if&amp;nbsp;item_data.get(&amp;quot;num&amp;quot;)&amp;nbsp;else&amp;nbsp;0,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;result
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;retry&amp;nbsp;-=&amp;nbsp;1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;网络异常，剩余重试次数&amp;nbsp;{retry},&amp;nbsp;错误：{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None


def&amp;nbsp;save_to_db(goods_data):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;将采集到的数据存入本地数据库&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_FILE)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;INSERT&amp;nbsp;INTO&amp;nbsp;goods_record
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;(goods_id,&amp;nbsp;title,&amp;nbsp;price,&amp;nbsp;original_price,&amp;nbsp;stock,&amp;nbsp;shop_name,&amp;nbsp;record_time)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;VALUES&amp;nbsp;(?,&amp;nbsp;?,&amp;nbsp;?,&amp;nbsp;?,&amp;nbsp;?,&amp;nbsp;?,&amp;nbsp;?)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,&amp;nbsp;(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;goods_id&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;title&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;original_price&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;stock&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;goods_data[&amp;quot;shop_name&amp;quot;],
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;datetime.now().strftime(&amp;quot;%Y-%m-%d&amp;nbsp;%H:%M:%S&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.commit()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()


def&amp;nbsp;get_last_record(goods_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;读取该商品上一次记录，用于对比变动&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_FILE)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECT&amp;nbsp;price,&amp;nbsp;stock&amp;nbsp;FROM&amp;nbsp;goods_record&amp;nbsp;WHERE&amp;nbsp;goods_id=?
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;ORDER&amp;nbsp;BY&amp;nbsp;record_time&amp;nbsp;DESC&amp;nbsp;LIMIT&amp;nbsp;1
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,&amp;nbsp;(goods_id,))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;row&amp;nbsp;=&amp;nbsp;cursor.fetchone()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;row:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;{&amp;quot;price&amp;quot;:&amp;nbsp;row[0],&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;row[1]}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None


def&amp;nbsp;monitor_task():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;定时执行的监控任务&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n====&amp;nbsp;巡检开始&amp;nbsp;{datetime.now().strftime(&amp;#39;%Y-%m-%d&amp;nbsp;%H:%M:%S&amp;#39;)}&amp;nbsp;====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;gid&amp;nbsp;in&amp;nbsp;MONITOR_GOODS:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;info&amp;nbsp;=&amp;nbsp;get_goods_info(gid)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;info:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;continue
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_to_db(info)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last&amp;nbsp;=&amp;nbsp;get_last_record(gid)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品【{info[&amp;#39;title&amp;#39;]}】&amp;nbsp;当前价格：{info[&amp;#39;price&amp;#39;]}&amp;nbsp;库存：{info[&amp;#39;stock&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;last:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;降价告警判断
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;last[&amp;quot;price&amp;quot;]&amp;nbsp;&amp;gt;&amp;nbsp;0&amp;nbsp;and&amp;nbsp;info[&amp;quot;price&amp;quot;]&amp;nbsp;&amp;lt;&amp;nbsp;last[&amp;quot;price&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;drop_rate&amp;nbsp;=&amp;nbsp;(last[&amp;quot;price&amp;quot;]&amp;nbsp;-&amp;nbsp;info[&amp;quot;price&amp;quot;])&amp;nbsp;/&amp;nbsp;last[&amp;quot;price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;drop_rate&amp;nbsp;&amp;gt;=&amp;nbsp;PRICE_ALERT_RATIO:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【⚠️价格告警】商品{gid}发生大幅降价！上次:{last[&amp;#39;price&amp;#39;]}&amp;nbsp;本次:{info[&amp;#39;price&amp;#39;]}&amp;nbsp;降幅{drop_rate:.1%}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;库存变动提醒
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;info[&amp;quot;stock&amp;quot;]&amp;nbsp;!=&amp;nbsp;last[&amp;quot;stock&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;【ℹ库存变动】商品{gid}库存变更，上一次:{last[&amp;#39;stock&amp;#39;]}，当前:{info[&amp;#39;stock&amp;#39;]}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(1)


def&amp;nbsp;analysis_goods(goods_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;针对单个商品做历史数据分析&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn&amp;nbsp;=&amp;nbsp;sqlite3.connect(DB_FILE)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor&amp;nbsp;=&amp;nbsp;conn.cursor()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cursor.execute(&amp;#39;&amp;#39;&amp;#39;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;SELECT&amp;nbsp;price,&amp;nbsp;record_time&amp;nbsp;FROM&amp;nbsp;goods_record&amp;nbsp;WHERE&amp;nbsp;goods_id=?&amp;nbsp;ORDER&amp;nbsp;BY&amp;nbsp;record_time
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;#39;&amp;#39;&amp;#39;,&amp;nbsp;(goods_id,))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rows&amp;nbsp;=&amp;nbsp;cursor.fetchall()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;conn.close()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;rows:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;暂无历史数据，请先运行监控采集数据！&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;price_list&amp;nbsp;=&amp;nbsp;[r[0]&amp;nbsp;for&amp;nbsp;r&amp;nbsp;in&amp;nbsp;rows]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;max_p&amp;nbsp;=&amp;nbsp;max(price_list)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min_p&amp;nbsp;=&amp;nbsp;min(price_list)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;avg_p&amp;nbsp;=&amp;nbsp;sum(price_list)/len(price_list)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n========商品历史数据分析========&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;历史最高售价：{max_p:.2f}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;历史最低售价：{min_p:.2f}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;历史平均售价：{avg_p:.2f}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;数据记录总条数：{len(price_list)}&amp;quot;)


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;init_db()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scheduler&amp;nbsp;=&amp;nbsp;BackgroundScheduler()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scheduler.add_job(monitor_task,&amp;nbsp;&amp;quot;interval&amp;quot;,&amp;nbsp;seconds=POLL_INTERVAL)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scheduler.start()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;监控程序已经启动，按&amp;nbsp;Ctrl+C&amp;nbsp;终止程序&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;cmd&amp;nbsp;=&amp;nbsp;input(&amp;quot;\n输入指令：\n1:执行一次巡检&amp;nbsp;&amp;nbsp;2:分析商品历史&amp;nbsp;&amp;nbsp;q:退出\n&amp;gt;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;cmd&amp;nbsp;==&amp;nbsp;&amp;quot;1&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor_task()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;cmd&amp;nbsp;==&amp;nbsp;&amp;quot;2&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gid&amp;nbsp;=&amp;nbsp;input(&amp;quot;输入要分析的商品编号：&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis_goods(gid)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;elif&amp;nbsp;cmd.lower()&amp;nbsp;==&amp;nbsp;&amp;quot;q&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;break
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;KeyboardInterrupt:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pass
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;finally:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;scheduler.shutdown()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;程序已退出&amp;quot;)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background:rgba(220,220,220,0.5);background-image:url(https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png);display:none;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;程序使用说明&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;修改代码顶部配置，填入自己的两组凭证，把需要跟踪的商品编号写进&lt;code&gt;MONITOR_GOODS&lt;/code&gt;列表；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;直接运行脚本 &lt;code&gt;python goods_monitor.py&lt;/code&gt;，程序自动初始化数据库，开启后台定时巡检；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;控制台交互指令：	&lt;/p&gt;&lt;/li&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;输入&lt;code&gt;1&lt;/code&gt;立刻手动执行一轮巡检，不用等待定时周期；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;输入&lt;code&gt;2&lt;/code&gt;，填入商品编号，程序会统计该商品全部历史记录，输出最高价、最低价、平均价格；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;输入&lt;code&gt;q&lt;/code&gt;关闭程序。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/ol&gt;&lt;p&gt;采集的全部历史会保存在&lt;code&gt;goods_monitor.db&lt;/code&gt;文件，你可以使用数据库工具打开，导出为 Excel 表格做进一步处理。&lt;/p&gt;&lt;h2&gt;功能拓展方向&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;消息推送&lt;/strong&gt;：出现降价、库存变动告警的时候，可以增加钉钉机器人、企业微信 webhook，把告警信息推送到工作群，不用守着控制台；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;多规格 SKU 分析&lt;/strong&gt;：源码中只提取了基础价格，还可以读取多规格的数据，跟踪不同规格的价格、库存变化；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据可视化&lt;/strong&gt;：结合 matplotlib 绘制价格变化曲线图，直观观察一段时间的价格走势；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;导出报表&lt;/strong&gt;：定时把每日数据导出 csv 文件，方便周报复盘；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;部署服务器&lt;/strong&gt;：Linux 服务器后台运行，实现全天候无人值守监控。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;实际业务场景&lt;/h2&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;竞品跟踪&lt;/strong&gt;：电商运营持续盯对手商品调价、库存情况，把握促销时机；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;选品比价&lt;/strong&gt;：跟踪多款目标商品，等待合适价位再入手；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;店铺自检&lt;/strong&gt;：跟踪自家店铺多款商品，监控异常改价、库存异常；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;大促备战&lt;/strong&gt;：618、双 11 大促期间高频巡检，捕捉短期促销活动。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h2&gt;写在最后&lt;/h2&gt;&lt;p&gt;这套方案避开了复杂的页面解析工作，把重点放在业务逻辑开发，新手也可以快速落地。实际使用的时候注意控制巡检间隔，不要设置过于频繁，避免超出额度。如果后续需要更大规模的批量任务，可以在这个基础上做任务队列优化。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Mon, 24 Aug 2026 17:46:33 +0800</pubDate></item><item><title>告别人工盯品，基于 Open Claw 快速搭建电商商品监控与数据分析实战方案</title><link>https://www.shopapi.com.cn/?id=220</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;h2&gt;前言&lt;/h2&gt;&lt;p&gt;做电商运营、竞品调研的朋友，几乎都遇到过这样的困境。&lt;/p&gt;&lt;p&gt;想要盯紧竞品的售价变动、库存状态、规格上新，大促的时候更是需要时刻关注价格波动。如果靠人工打开网页一个个复制记录，不仅耗费大量时间精力，还很容易错过临时降价、库存告急这类关键信号。&lt;/p&gt;&lt;p&gt;如果自己从零写页面解析脚本，又会碰到一大堆麻烦：网页动态渲染、频繁访问触发风控限制、页面改版之后脚本直接失效，还要处理图片、多规格款式、店铺信息等复杂内容，调试成本非常高。&lt;/p&gt;&lt;p&gt;最近在实践中发现 Open Claw 这套工具可以很好解决这类痛点。不需要处理复杂的页面解析、反访问限制相关的底层工作，只需要简单编写少量代码，就可以拿到商品完整结构化信息，实现定时监控异动、存储历史快照，完成基础的数据分析工作。&lt;/p&gt;&lt;p&gt;本文会完整分享从数据获取、异动告警、本地存储到数据分析的完整实操流程，代码可以直接拿来修改运行，适合个人调研以及中小团队的竞品跟踪场景。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;温馨提示：本方案仅用于学习研究，使用过程中请遵守对应平台相关使用规范，合理控制访问频次。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;整体实现思路&lt;/h2&gt;&lt;p&gt;整套工具分为 4 个模块，逻辑简单清晰，方便后续迭代扩展：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;4&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E6%95%B0%E6%8D%AE%E8%8E%B7%E5%8F%96%E6%A8%A1%E5%9D%97%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%22zUKIub-1787278887975%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;数据获取模块&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;数据获取模块&quot;&gt;数据获取模块&lt;/a&gt;&lt;/span&gt;&lt;/strong&gt;&lt;/strong&gt;：输入商品编号，拿到标准化的商品全量信息，包含标题、售价、原价、多规格款式、库存、品牌、店铺、图片链接等内容。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;定时监控模块&lt;/strong&gt;：循环定时获取商品最新状态，和上一次保存的历史快照做对比。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;异动告警模块&lt;/strong&gt;：识别涨价、降价、库存清空、补货等变化，控制台输出提醒，后续可以拓展企业微信、邮件消息推送。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;数据分析模块&lt;/strong&gt;：把每次采集的数据保存本地，基于历史记录统计价格区间、高低价、规格分布，简单完成竞品分析。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;环境准备&lt;/h2&gt;&lt;p&gt;本地需要安装 Python3.7 以上版本，依赖基础网络请求库。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%20pandas%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&amp;nbsp;pandas&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;提前准备好工具分配的凭证信息，包含两组密钥字符串，用于身份校验。我们只需要传入商品编号，就可以获取完整的商品结构化数据。&lt;/p&gt;&lt;h2&gt;完整代码实战&lt;/h2&gt;&lt;h3&gt;1. 基础数据获取封装&lt;/h3&gt;&lt;p&gt;先封装基础函数，输入商品编号，返回清洗之后的商品字典数据，过滤无效空字段。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20-*-%20coding%3Autf-8%20-*-%5Cnimport%20requests%5Cnimport%20time%5Cnimport%20json%5Cnimport%20pandas%20as%20pd%5Cnfrom%20datetime%20import%20datetime%5Cn%5Cn%23%20--------------------------%E9%85%8D%E7%BD%AE%E5%8C%BA%EF%BC%8C%E8%AF%B7%E4%BF%AE%E6%94%B9%E4%B8%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%87%AD%E8%AF%81-------------------------%5CnACCESS_KEY%20%3D%20%5C%22%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81key%5C%22%5CnACCESS_SECRET%20%3D%20%5C%22%E4%BD%A0%E7%9A%84%E5%87%AD%E8%AF%81secret%5C%22%5CnMONITOR_GOODS%20%3D%20%5B%5C%2210335871600%5C%22%5D%20%20%23%20%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%E5%88%97%E8%A1%A8%5CnMONITOR_INTERVAL%20%3D%20300%20%20%23%20%E7%9B%91%E6%8E%A7%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E7%A4%BA%E4%BE%8B5%E5%88%86%E9%92%9F%E9%87%87%E9%9B%86%E4%B8%80%E6%AC%A1%5CnSAVE_CSV_PATH%20%3D%20%5C%22.%2Fgoods_monitor_record.csv%5C%22%5Cn%23%20---------------------------------------------------------------------------%5Cn%5Cndef%20get_goods_detail(goods_id%3A%20str)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%E5%AE%8C%E6%95%B4%E7%BB%93%E6%9E%84%E5%8C%96%E4%BF%A1%E6%81%AF%5Cn%20%20%20%20%3Aparam%20goods_id%3A%20%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%5Cn%20%20%20%20%3Areturn%3A%20dict%20%E5%95%86%E5%93%81%E6%95%B0%E6%8D%AE%EF%BC%8C%E5%A4%B1%E8%B4%A5%E8%BF%94%E5%9B%9ENone%5Cn%20%20%20%20%5C%22%5C%22%5C%22%5Cn%20%20%20%20base_url%20%3D%20%5C%22https%3A%2F%2Fapi-gw.onebound.cn%2Fjd%2Fitem_get_pro%2F%5C%22%5Cn%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20ACCESS_KEY%2C%5Cn%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20ACCESS_SECRET%2C%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%2C%20%20%23%20%E5%85%B3%E9%97%AD%E7%BC%93%E5%AD%98%EF%BC%8C%E5%B0%BD%E9%87%8F%E6%8B%BF%E5%88%B0%E5%AE%9E%E6%97%B6%E6%95%B0%E6%8D%AE%5Cn%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%20%5C%22json%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(base_url%2C%20params%3Dparams%2C%20headers%3D%7B%5C%22Accept-Encoding%5C%22%3A%20%5C%22gzip%5C%22%7D%2C%20timeout%3D20)%5Cn%20%20%20%20%20%20%20%20res_json%20%3D%20resp.json()%5Cn%20%20%20%20%20%20%20%20if%20res_json.get(%5C%22error_code%5C%22)%20!%3D%20%5C%220000%5C%22%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E8%8E%B7%E5%8F%96%E5%95%86%E5%93%81%7Bgoods_id%7D%E5%BC%82%E5%B8%B8%EF%BC%8C%E5%8E%9F%E5%9B%A0%EF%BC%9A%7Bres_json.get(&amp;#39;reason&amp;#39;%2C&amp;#39;%E6%9C%AA%E7%9F%A5%E9%94%99%E8%AF%AF&amp;#39;)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%20None%5Cn%20%20%20%20%20%20%20%20item_data%20%3D%20res_json.get(%5C%22item%5C%22%2C%20%7B%7D)%5Cn%20%20%20%20%20%20%20%20return%20item_data%5Cn%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E7%BD%91%E7%BB%9C%E8%AF%B7%E6%B1%82%E5%BC%82%E5%B8%B8%3A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;-*-&amp;nbsp;coding:utf-8&amp;nbsp;-*-
import&amp;nbsp;requests
import&amp;nbsp;time
import&amp;nbsp;json
import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd
from&amp;nbsp;datetime&amp;nbsp;import&amp;nbsp;datetime

#&amp;nbsp;--------------------------配置区，请修改为自己的凭证-------------------------
ACCESS_KEY&amp;nbsp;=&amp;nbsp;&amp;quot;你的凭证key&amp;quot;
ACCESS_SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;你的凭证secret&amp;quot;
MONITOR_GOODS&amp;nbsp;=&amp;nbsp;[&amp;quot;10335871600&amp;quot;]&amp;nbsp;&amp;nbsp;#&amp;nbsp;需要监控的商品编号列表
MONITOR_INTERVAL&amp;nbsp;=&amp;nbsp;300&amp;nbsp;&amp;nbsp;#&amp;nbsp;监控间隔，单位秒，示例5分钟采集一次
SAVE_CSV_PATH&amp;nbsp;=&amp;nbsp;&amp;quot;./goods_monitor_record.csv&amp;quot;
#&amp;nbsp;---------------------------------------------------------------------------

def&amp;nbsp;get_goods_detail(goods_id:&amp;nbsp;str):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;获取商品完整结构化信息
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:param&amp;nbsp;goods_id:&amp;nbsp;商品编号
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;:return:&amp;nbsp;dict&amp;nbsp;商品数据，失败返回None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base_url&amp;nbsp;=&amp;nbsp;&amp;quot;https://api-gw.onebound.cn/jd/item_get_pro/&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;ACCESS_KEY,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;ACCESS_SECRET,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;,&amp;nbsp;&amp;nbsp;#&amp;nbsp;关闭缓存，尽量拿到实时数据
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;nbsp;&amp;quot;json&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(base_url,&amp;nbsp;params=params,&amp;nbsp;headers={&amp;quot;Accept-Encoding&amp;quot;:&amp;nbsp;&amp;quot;gzip&amp;quot;},&amp;nbsp;timeout=20)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;res_json&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;res_json.get(&amp;quot;error_code&amp;quot;)&amp;nbsp;!=&amp;nbsp;&amp;quot;0000&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;获取商品{goods_id}异常，原因：{res_json.get(&amp;#39;reason&amp;#39;,&amp;#39;未知错误&amp;#39;)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_data&amp;nbsp;=&amp;nbsp;res_json.get(&amp;quot;item&amp;quot;,&amp;nbsp;{})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;item_data
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;网络请求异常:{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h3&gt;2. 监控异动检测逻辑&lt;/h3&gt;&lt;p&gt;缓存上一轮采集的数据，对比当前最新数据，捕捉价格、库存发生的变化，打印告警提示，同时把每一轮数据写入本地 CSV 文件留存历史记录。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20%E5%86%85%E5%AD%98%E7%BC%93%E5%AD%98%EF%BC%8C%E4%BF%9D%E5%AD%98%E4%B8%8A%E4%B8%80%E8%BD%AE%E9%87%87%E9%9B%86%E7%BB%93%E6%9E%9C%5Cnlast_snapshot%20%3D%20%7B%7D%5Cn%5Cn%5Cndef%20monitor_one_goods(goods_id%3A%20str)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E7%9B%91%E6%8E%A7%E5%8D%95%E4%B8%AA%E5%95%86%E5%93%81%EF%BC%8C%E6%A3%80%E6%B5%8B%E4%BB%B7%E6%A0%BC%E5%BA%93%E5%AD%98%E5%BC%82%E5%8A%A8%5C%22%5C%22%5C%22%5Cn%20%20%20%20current%20%3D%20get_goods_detail(goods_id)%5Cn%20%20%20%20if%20not%20current%3A%5Cn%20%20%20%20%20%20%20%20return%5Cn%5Cn%20%20%20%20title%20%3D%20current.get(%5C%22title%5C%22%2C%20%5C%22%E6%9C%AA%E7%9F%A5%E5%95%86%E5%93%81%5C%22)%5Cn%20%20%20%20now_price%20%3D%20float(current.get(%5C%22price%5C%22%2C%200))%5Cn%20%20%20%20origin_price%20%3D%20float(current.get(%5C%22orginal_price%5C%22%2C%200))%5Cn%20%20%20%20stock%20%3D%20int(current.get(%5C%22num%5C%22%2C%200))%5Cn%20%20%20%20shop_name%20%3D%20current.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22%2C%20%5C%22%5C%22)%5Cn%20%20%20%20curr_time%20%3D%20datetime.now().strftime(%5C%22%25Y-%25m-%25d%20%25H%3A%25M%3A%25S%5C%22)%5Cn%5Cn%20%20%20%20%23%20%E5%86%99%E5%85%A5%E6%9C%AC%E5%9C%B0%E5%8E%86%E5%8F%B2%E8%AE%B0%E5%BD%95%E6%96%87%E4%BB%B6%5Cn%20%20%20%20record_row%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22capture_time%5C%22%3A%20curr_time%2C%5Cn%20%20%20%20%20%20%20%20%5C%22goods_id%5C%22%3A%20goods_id%2C%5Cn%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20title%2C%5Cn%20%20%20%20%20%20%20%20%5C%22current_price%5C%22%3A%20now_price%2C%5Cn%20%20%20%20%20%20%20%20%5C%22origin_price%5C%22%3A%20origin_price%2C%5Cn%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3A%20stock%2C%5Cn%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20shop_name%5Cn%20%20%20%20%7D%5Cn%20%20%20%20df_row%20%3D%20pd.DataFrame(%5Brecord_row%5D)%5Cn%20%20%20%20df_row.to_csv(SAVE_CSV_PATH%2C%20mode%3D%5C%22a%5C%22%2C%20header%3Dnot%20os.path.exists(SAVE_CSV_PATH)%2C%20index%3DFalse%2C%20encoding%3D%5C%22utf_8_sig%5C%22)%5Cn%5Cn%20%20%20%20print(f%5C%22%5C%5Cn%E3%80%90%7Bcurr_time%7D%E3%80%91%E5%95%86%E5%93%81%3A%7Btitle%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E5%BA%97%E9%93%BA%3A%7Bshop_name%7D%EF%BD%9C%E7%8E%B0%E4%BB%B7%3A%7Bnow_price%7D%EF%BD%9C%E5%8E%9F%E4%BB%B7%3A%7Borigin_price%7D%EF%BD%9C%E5%BA%93%E5%AD%98%3A%7Bstock%7D%5C%22)%5Cn%5Cn%20%20%20%20%23%20%E7%AC%AC%E4%B8%80%E6%AC%A1%E9%87%87%E9%9B%86%EF%BC%8C%E5%8F%AA%E5%81%9A%E7%BC%93%E5%AD%98%EF%BC%8C%E4%B8%8D%E5%81%9A%E5%AF%B9%E6%AF%94%5Cn%20%20%20%20if%20goods_id%20not%20in%20last_snapshot%3A%5Cn%20%20%20%20%20%20%20%20last_snapshot%5Bgoods_id%5D%20%3D%20%7B%5C%22price%5C%22%3A%20now_price%2C%20%5C%22stock%5C%22%3A%20stock%7D%5Cn%20%20%20%20%20%20%20%20return%5Cn%5Cn%20%20%20%20old%20%3D%20last_snapshot%5Bgoods_id%5D%5Cn%20%20%20%20%23%20%E9%99%8D%E4%BB%B7%E6%8F%90%E9%86%92%5Cn%20%20%20%20if%20now_price%20%3C%20old%5B%5C%22price%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20drop%20%3D%20old%5B%5C%22price%5C%22%5D%20-%20now_price%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%F0%9F%94%94%E9%99%8D%E4%BB%B7%E9%A2%84%E8%AD%A6%EF%BC%81%E7%94%B1%7Bold%5B&amp;#39;price&amp;#39;%5D%7D%E4%B8%8B%E9%99%8D%E5%88%B0%7Bnow_price%7D%EF%BC%8C%E9%99%8D%E5%B9%85%7Bdrop%3A.2f%7D%E5%85%83%5C%22)%5Cn%20%20%20%20%23%20%E6%B6%A8%E4%BB%B7%E6%8F%90%E9%86%92%5Cn%20%20%20%20if%20now_price%20%3E%20old%5B%5C%22price%5C%22%5D%3A%5Cn%20%20%20%20%20%20%20%20rise%20%3D%20now_price%20-%20old%5B%5C%22price%5C%22%5D%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%F0%9F%93%88%E6%B6%A8%E4%BB%B7%E6%8F%90%E9%86%92%EF%BC%81%E7%94%B1%7Bold%5B&amp;#39;price&amp;#39;%5D%7D%E4%B8%8A%E6%B6%A8%E5%88%B0%7Bnow_price%7D%EF%BC%8C%E6%B6%A8%E5%B9%85%7Brise%3A.2f%7D%E5%85%83%5C%22)%5Cn%20%20%20%20%23%20%E5%BA%93%E5%AD%98%E5%91%8A%E8%AD%A6%5Cn%20%20%20%20if%20stock%20%3C%3D%200%20and%20old%5B%5C%22stock%5C%22%5D%20%3E%200%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%F0%9F%9A%A8%E5%BA%93%E5%AD%98%E5%91%8A%E6%80%A5%EF%BC%8C%E5%95%86%E5%93%81%E5%B7%B2%E7%BB%8F%E7%BC%BA%E8%B4%A7%EF%BC%81%5C%22)%5Cn%20%20%20%20if%20stock%20%3E%200%20and%20old%5B%5C%22stock%5C%22%5D%20%3C%3D%200%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%F0%9F%93%A6%E5%95%86%E5%93%81%E9%87%8D%E6%96%B0%E8%A1%A5%E8%B4%A7%EF%BC%8C%E5%BD%93%E5%89%8D%E5%BA%93%E5%AD%98%7Bstock%7D%5C%22)%5Cn%5Cn%20%20%20%20%23%20%E6%9B%B4%E6%96%B0%E5%86%85%E5%AD%98%E7%BC%93%E5%AD%98%E5%BF%AB%E7%85%A7%5Cn%20%20%20%20last_snapshot%5Bgoods_id%5D%5B%5C%22price%5C%22%5D%20%3D%20now_price%5Cn%20%20%20%20last_snapshot%5Bgoods_id%5D%5B%5C%22stock%5C%22%5D%20%3D%20stock%5Cn%5Cn%5Cndef%20run_monitor_loop()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E5%BE%AA%E7%8E%AF%E6%89%A7%E8%A1%8C%E5%85%A8%E9%83%A8%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%5C%22%5C%22%5C%22%5Cn%20%20%20%20import%20os%5Cn%20%20%20%20if%20not%20os.path.exists(SAVE_CSV_PATH)%3A%5Cn%20%20%20%20%20%20%20%20pd.DataFrame().to_csv(SAVE_CSV_PATH%2C%20index%3DFalse%2C%20encoding%3D%5C%22utf_8_sig%5C%22)%5Cn%20%20%20%20print(%5C%22%3D%3D%3D%3D%3D%E5%90%AF%E5%8A%A8%E5%95%86%E5%93%81%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20for%20gid%20in%20MONITOR_GOODS%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20monitor_one_goods(gid)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20except%20Exception%20as%20err%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%A4%84%E7%90%86%7Bgid%7D%E5%8F%91%E7%94%9F%E5%BC%82%E5%B8%B8%7Berr%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E7%AD%89%E5%BE%85%7BMONITOR_INTERVAL%7D%E7%A7%92%E8%BF%9B%E5%85%A5%E4%B8%8B%E4%B8%80%E8%BD%AE%E6%A3%80%E6%B5%8B...%5C%22)%5Cn%20%20%20%20%20%20%20%20time.sleep(MONITOR_INTERVAL)%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;内存缓存，保存上一轮采集结果
last_snapshot&amp;nbsp;=&amp;nbsp;{}


def&amp;nbsp;monitor_one_goods(goods_id:&amp;nbsp;str):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;监控单个商品，检测价格库存异动&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;current&amp;nbsp;=&amp;nbsp;get_goods_detail(goods_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;current:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;=&amp;nbsp;current.get(&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;未知商品&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;now_price&amp;nbsp;=&amp;nbsp;float(current.get(&amp;quot;price&amp;quot;,&amp;nbsp;0))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;origin_price&amp;nbsp;=&amp;nbsp;float(current.get(&amp;quot;orginal_price&amp;quot;,&amp;nbsp;0))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stock&amp;nbsp;=&amp;nbsp;int(current.get(&amp;quot;num&amp;quot;,&amp;nbsp;0))
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;shop_name&amp;nbsp;=&amp;nbsp;current.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;curr_time&amp;nbsp;=&amp;nbsp;datetime.now().strftime(&amp;quot;%Y-%m-%d&amp;nbsp;%H:%M:%S&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;写入本地历史记录文件
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;record_row&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;capture_time&amp;quot;:&amp;nbsp;curr_time,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;goods_id&amp;quot;:&amp;nbsp;goods_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;title,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;current_price&amp;quot;:&amp;nbsp;now_price,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;origin_price&amp;quot;:&amp;nbsp;origin_price,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;stock,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;shop_name
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_row&amp;nbsp;=&amp;nbsp;pd.DataFrame([record_row])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_row.to_csv(SAVE_CSV_PATH,&amp;nbsp;mode=&amp;quot;a&amp;quot;,&amp;nbsp;header=not&amp;nbsp;os.path.exists(SAVE_CSV_PATH),&amp;nbsp;index=False,&amp;nbsp;encoding=&amp;quot;utf_8_sig&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n【{curr_time}】商品:{title}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;店铺:{shop_name}｜现价:{now_price}｜原价:{origin_price}｜库存:{stock}&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;第一次采集，只做缓存，不做对比
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;goods_id&amp;nbsp;not&amp;nbsp;in&amp;nbsp;last_snapshot:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_snapshot[goods_id]&amp;nbsp;=&amp;nbsp;{&amp;quot;price&amp;quot;:&amp;nbsp;now_price,&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;stock}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;old&amp;nbsp;=&amp;nbsp;last_snapshot[goods_id]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;降价提醒
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;now_price&amp;nbsp;&amp;lt;&amp;nbsp;old[&amp;quot;price&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;drop&amp;nbsp;=&amp;nbsp;old[&amp;quot;price&amp;quot;]&amp;nbsp;-&amp;nbsp;now_price
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;🔔降价预警！由{old[&amp;#39;price&amp;#39;]}下降到{now_price}，降幅{drop:.2f}元&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;涨价提醒
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;now_price&amp;nbsp;&amp;gt;&amp;nbsp;old[&amp;quot;price&amp;quot;]:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;rise&amp;nbsp;=&amp;nbsp;now_price&amp;nbsp;-&amp;nbsp;old[&amp;quot;price&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;📈涨价提醒！由{old[&amp;#39;price&amp;#39;]}上涨到{now_price}，涨幅{rise:.2f}元&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;库存告警
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;stock&amp;nbsp;&amp;lt;=&amp;nbsp;0&amp;nbsp;and&amp;nbsp;old[&amp;quot;stock&amp;quot;]&amp;nbsp;&amp;gt;&amp;nbsp;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;🚨库存告急，商品已经缺货！&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;stock&amp;nbsp;&amp;gt;&amp;nbsp;0&amp;nbsp;and&amp;nbsp;old[&amp;quot;stock&amp;quot;]&amp;nbsp;&amp;lt;=&amp;nbsp;0:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;📦商品重新补货，当前库存{stock}&amp;quot;)

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;更新内存缓存快照
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_snapshot[goods_id][&amp;quot;price&amp;quot;]&amp;nbsp;=&amp;nbsp;now_price
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;last_snapshot[goods_id][&amp;quot;stock&amp;quot;]&amp;nbsp;=&amp;nbsp;stock


def&amp;nbsp;run_monitor_loop():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;循环执行全部监控任务&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import&amp;nbsp;os
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;os.path.exists(SAVE_CSV_PATH):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;pd.DataFrame().to_csv(SAVE_CSV_PATH,&amp;nbsp;index=False,&amp;nbsp;encoding=&amp;quot;utf_8_sig&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;=====启动商品监控任务=====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;gid&amp;nbsp;in&amp;nbsp;MONITOR_GOODS:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;monitor_one_goods(gid)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;err:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;处理{gid}发生异常{err}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n等待{MONITOR_INTERVAL}秒进入下一轮检测...&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(MONITOR_INTERVAL)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h3&gt;3. 历史数据分析函数&lt;/h3&gt;&lt;p&gt;读取本地保存的 CSV 历史文件，做简单统计分析：计算最高价、最低价、平均售价，输出价格波动概况。&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;3&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22def%20analysis_history_data()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E8%AF%BB%E5%8F%96%E6%9C%AC%E5%9C%B0%E5%8E%86%E5%8F%B2%E6%96%87%E4%BB%B6%EF%BC%8C%E5%81%9A%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E7%BB%9F%E8%AE%A1%5C%22%5C%22%5C%22%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20df%20%3D%20pd.read_csv(SAVE_CSV_PATH%2C%20encoding%3D%5C%22utf_8_sig%5C%22)%5Cn%20%20%20%20%20%20%20%20if%20df.empty%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(%5C%22%E6%9A%82%E6%97%A0%E5%8E%86%E5%8F%B2%E7%9B%91%E6%8E%A7%E6%95%B0%E6%8D%AE%EF%BC%8C%E8%AF%B7%E5%85%88%E8%BF%90%E8%A1%8C%E7%9B%91%E6%8E%A7%E9%87%87%E9%9B%86%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20return%5Cn%20%20%20%20%20%20%20%20print(%5C%22%5C%5Cn%3D%3D%3D%3D%3D%3D%3D%3D%E5%8E%86%E5%8F%B2%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A%3D%3D%3D%3D%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20%20%20%20%20for%20goods_id%20in%20df%5B%5C%22goods_id%5C%22%5D.unique()%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20sub_df%20%3D%20df%5Bdf%5B%5C%22goods_id%5C%22%5D%20%3D%3D%20goods_id%5D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20title%20%3D%20sub_df.iloc%5B0%5D%5B%5C%22title%5C%22%5D%5Cn%20%20%20%20%20%20%20%20%20%20%20%20max_p%20%3D%20sub_df%5B%5C%22current_price%5C%22%5D.max()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20min_p%20%3D%20sub_df%5B%5C%22current_price%5C%22%5D.min()%5Cn%20%20%20%20%20%20%20%20%20%20%20%20avg_p%20%3D%20round(sub_df%5B%5C%22current_price%5C%22%5D.mean()%2C2)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20capture_count%20%3D%20len(sub_df)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E5%95%86%E5%93%81%EF%BC%9A%7Btitle%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%E6%9D%A1%E6%95%B0%EF%BC%9A%7Bcapture_count%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%8E%86%E5%8F%B2%E6%9C%80%E9%AB%98%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bmax_p%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%8E%86%E5%8F%B2%E6%9C%80%E4%BD%8E%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bmin_p%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%B9%B3%E5%9D%87%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bavg_p%7D%5C%22)%5Cn%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E8%AF%BB%E5%8F%96%E6%96%87%E4%BB%B6%E5%A4%B1%E8%B4%A5%EF%BC%9A%7Be%7D%5C%22)%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20import%20os%5Cn%20%20%20%20%23%20%E5%90%AF%E5%8A%A8%E5%BE%AA%E7%8E%AF%E7%9B%91%E6%8E%A7%5Cn%20%20%20%20run_monitor_loop()%5Cn%5Cn%20%20%20%20%23%20%E5%A6%82%E6%9E%9C%E4%B8%8D%E9%9C%80%E8%A6%81%E5%BE%AA%E7%8E%AF%E7%9B%91%E6%8E%A7%EF%BC%8C%E5%8F%AA%E5%81%9A%E5%8D%95%E6%AC%A1%E9%87%87%E9%9B%86%E5%88%86%E6%9E%90%EF%BC%8C%E5%8F%AF%E4%BB%A5%E6%B3%A8%E9%87%8A%E4%B8%8A%E9%9D%A2run_monitor_loop()%EF%BC%8C%E6%89%93%E5%BC%80%E4%B8%8B%E9%9D%A2%E4%B8%A4%E8%A1%8C%5Cn%20%20%20%20%23%20monitor_one_goods(MONITOR_GOODS%5B0%5D)%5Cn%20%20%20%20%23%20analysis_history_data()%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;def&amp;nbsp;analysis_history_data():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;读取本地历史文件，做数据分析统计&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.read_csv(SAVE_CSV_PATH,&amp;nbsp;encoding=&amp;quot;utf_8_sig&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;df.empty:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;暂无历史监控数据，请先运行监控采集&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n========历史数据分析报告========&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;goods_id&amp;nbsp;in&amp;nbsp;df[&amp;quot;goods_id&amp;quot;].unique():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;sub_df&amp;nbsp;=&amp;nbsp;df[df[&amp;quot;goods_id&amp;quot;]&amp;nbsp;==&amp;nbsp;goods_id]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title&amp;nbsp;=&amp;nbsp;sub_df.iloc[0][&amp;quot;title&amp;quot;]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;max_p&amp;nbsp;=&amp;nbsp;sub_df[&amp;quot;current_price&amp;quot;].max()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;min_p&amp;nbsp;=&amp;nbsp;sub_df[&amp;quot;current_price&amp;quot;].min()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;avg_p&amp;nbsp;=&amp;nbsp;round(sub_df[&amp;quot;current_price&amp;quot;].mean(),2)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;capture_count&amp;nbsp;=&amp;nbsp;len(sub_df)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n商品：{title}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;采集记录条数：{capture_count}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;历史最高售价：{max_p}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;历史最低售价：{min_p}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;平均售价：{avg_p}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;数据分析读取文件失败：{e}&amp;quot;)


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;import&amp;nbsp;os
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;启动循环监控
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;run_monitor_loop()

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;如果不需要循环监控，只做单次采集分析，可以注释上面run_monitor_loop()，打开下面两行
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;monitor_one_goods(MONITOR_GOODS[0])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;analysis_history_data()&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;使用说明&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;修改代码最上方配置区，填入自己的两组凭证，填入需要监控的商品编号列表。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;MONITOR_INTERVAL&lt;/code&gt;为采集轮询间隔，不建议设置过小，避免高频请求。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;脚本运行之后，控制台会打印每一轮商品信息，出现价格库存异动输出提醒。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;所有采集记录自动保存到&lt;code&gt;goods_monitor_record.csv&lt;/code&gt;，可以用 Excel 直接打开查看。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果不需要持续循环监控，可以注释&lt;code&gt;run_monitor_loop()&lt;/code&gt;，调用单次采集函数，再执行&lt;code&gt;analysis_history_data()&lt;/code&gt;输出统计报告。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;拓展方向&lt;/h2&gt;&lt;p&gt;上面是最小可用版本，基于这个脚本还可以继续拓展很多实用功能：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;消息推送&lt;/strong&gt;：捕捉到降价缺货之后，对接钉钉 / 企业微信机器人，把告警消息推送到工作群。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;多规格分析&lt;/strong&gt;：解析返回的多规格列表，跟踪每一个不同款式的价格与库存变化。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;可视化图表&lt;/strong&gt;：使用 matplotlib 或者 plotly，读取 csv 历史数据绘制价格趋势折线图，直观看到一段时间价格走势。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;多商品批量管理&lt;/strong&gt;：把监控商品列表放到配置文件或者轻量数据库，不用修改代码就可以增删监控商品。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;阈值告警&lt;/strong&gt;：设置心理价位，当商品价格低于目标价格的时候才触发提醒。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;踩坑小结&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;工具返回的主价格字段有时候会存在偏差，如果业务对价格精度要求高，优先读取规格列表里面对应款式的价格。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;部分自营和非自营店铺返回字段会有差异，部分字段为空属于正常现象，代码要做好空值容错，避免脚本直接崩溃。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;如果出现获取数据失败，优先检查密钥凭证是否填写正确，网络是否正常连通。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;不要把轮询间隔设置得很短，合理控制采集频次，兼顾稳定性与实际业务需求。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;写在最后&lt;/h2&gt;&lt;p&gt;对于中小运营团队或者个人开发者来说，直接从零做商品数据跟踪，要投入不少精力去处理页面变化、访问限制。Open Claw 把底层繁杂的工作封装完成，我们只需要聚焦业务逻辑，几行代码就能搭建一套完整监控分析系统。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Fri, 21 Aug 2026 10:47:18 +0800</pubDate></item><item><title>竞品监控与商品数据分析实战：用 Open‑Claw 搭建自动化商品跟踪系统</title><link>https://www.shopapi.com.cn/?id=219</link><description>&lt;p&gt;&lt;span data-cke-copybin-start=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;摘要：做电商运营、选品、竞品调研，最头疼的就是手动刷页面记录价格、库存、规格变化。反复复制粘贴，效率低下还容易漏掉关键变动。本文分享一套可直接落地的实战方案，借助 Open‑Claw 工具快速完成商品信息自动化抓取，实现价格、库存、SKU 规格持续监控，搭配 Python 完成数据清洗、本地存储与简单统计分析，完整代码可直接运行，适合个人开发者、中小商家做市场情报收集。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;一、业务痛点：为什么需要自动化商品监控&lt;/h2&gt;&lt;p&gt;很多做电商调研的朋友日常会遇到这些问题：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;跟踪几十款竞品，每天手动打开页面记录价格、库存，耗费大量时间；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;错过竞品调价、上新规格、库存清零的节点，错失市场决策时机；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;拿到一堆零散复制的文本，没有结构化数据，很难做价格带统计、规格对比；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;直接写浏览器模拟采集，经常遇到访问限制、页面改版、签名校验，调试成本极高。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;传统的页面模拟采集，要处理 Cookie、设备指纹、页面结构变动，维护成本很高。Open‑Claw 封装好了底层的访问适配能力，我们只需要传入商品编号，就能拿到结构化完整商品信息，省去大量逆向、调试的时间，把精力聚焦在数据分析业务逻辑上。&lt;/p&gt;&lt;p&gt;我们可以实现这些能力：&lt;/p&gt;&lt;ul class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;批量输入商品编号，批量拉取商品标题、售价、原价、店铺信息、SKU 规格、库存、主图、发货地等全套信息；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;定时循环执行，持续监控价格变动、SKU 增减、库存状态；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;将数据保存本地 CSV，方便 Excel 做透视分析；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;简单逻辑判断，检测到价格变动、缺货时输出提醒日志。&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;温馨提示：本教程仅用于个人学习研究，请遵守平台相关使用规范，控制请求频率，不要高频大规模批量调用。&lt;/p&gt;&lt;/blockquote&gt;&lt;h2&gt;二、整体流程思路&lt;/h2&gt;&lt;p&gt;整体流程非常清晰：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;准备工具凭证，&lt;span aria-label=&quot;a 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_inline cke_widget_csdnlink cke_widget_wrapper_link-info cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;a&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;3&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;link-info cke_widget_editable cke_widget_element&quot; contenteditable=&quot;true&quot; data-cke-enter-mode=&quot;2&quot; data-cke-widget-data=&quot;%7B%22url%22%3A%22https%3A%2F%2Fo0b.cn%2Ficris%22%2C%22text%22%3A%22%E8%8E%B7%E5%8F%96%E8%BA%AB%E4%BB%BD%E5%AF%86%E9%92%A5%22%2C%22desc%22%3A%22%22%2C%22icon%22%3A%22%22%2C%22isCard%22%3Afalse%2C%22hasResquest%22%3Atrue%2C%22iconDefault%22%3A%22https%3A%2F%2Fcsdnimg.cn%2Frelease%2Fblog_editor_html%2Frelease2.4.6%2Fckeditor%2Fplugins%2FCsdnLink%2Ficons%2Ficon-default.png%3Ft%3DQ239%22%2C%22id%22%3A%22c2ct2o-1787189129388%22%2C%22classes%22%3A%7B%22link-info%22%3A1%7D%7D&quot; data-cke-widget-editable=&quot;text&quot; data-cke-widget-keep-attr=&quot;0&quot; data-link-icon=&quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=Q239&quot; data-link-title=&quot;获取身份密钥&quot; data-widget=&quot;csdnlink&quot; href=&quot;https://o0b.cn/icris&quot; title=&quot;获取身份密钥&quot;&gt;&lt;strong&gt;获取身份密钥&lt;/strong&gt;&lt;/a&gt;&lt;/span&gt;；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Python 脚本组装请求，传入商品编号获取完整结构化数据；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;解析返回结果，提取业务关心字段；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;数据写入本地 CSV 文件持久化；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;增加循环延时，实现定时监控；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;简单统计，做价格、库存维度的数据分析。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;需要提前安装依赖包：&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;0&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22pip%20install%20requests%20pandas%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;pip&amp;nbsp;install&amp;nbsp;requests&amp;nbsp;pandas&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;三、完整可运行 Python 代码&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;1&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20-*-%20coding%3Autf-8%20-*-%5Cnimport%20requests%5Cnimport%20time%5Cnimport%20pandas%20as%20pd%5Cnimport%20json%5Cn%5Cn%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%20%E9%85%8D%E7%BD%AE%E5%8C%BA%E5%9F%9F%EF%BC%8C%E8%AF%B7%E4%BF%AE%E6%94%B9%E4%B8%BA%E8%87%AA%E5%B7%B1%E7%9A%84%E5%87%AD%E8%AF%81%E5%92%8C%E7%9B%AE%E6%A0%87%E5%95%86%E5%93%81%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5CnKEY%20%3D%20%5C%22%E4%BD%A0%E7%9A%84%E8%BA%AB%E4%BB%BDkey%5C%22%5CnSECRET%20%3D%20%5C%22%E4%BD%A0%E7%9A%84%E8%BA%AB%E4%BB%BD%E5%AF%86%E9%92%A5%5C%22%5Cn%23%20%E9%9C%80%E8%A6%81%E7%9B%91%E6%8E%A7%E7%9A%84%E5%95%86%E5%93%81ID%E5%88%97%E8%A1%A8%EF%BC%8C%E5%8F%AF%E4%BB%A5%E5%A1%AB%E5%85%A5%E5%A4%9A%E4%B8%AA%E5%95%86%E5%93%81%E7%BC%96%E5%8F%B7%5CnMONITOR_ITEM_IDS%20%3D%20%5B%5C%2210335871600%5C%22%2C%20%5C%2210057467958584%5C%22%5D%5Cn%23%20%E7%9B%91%E6%8E%A7%E9%97%B4%E9%9A%94%EF%BC%8C%E5%8D%95%E4%BD%8D%E7%A7%92%EF%BC%8C%E7%A4%BA%E4%BE%8B%E8%AE%BE%E7%BD%AE30%E5%88%86%E9%92%9F%E6%89%AB%E6%8F%8F%E4%B8%80%E6%AC%A1%EF%BC%8C%E8%AF%B7%E5%8B%BF%E8%AE%BE%E7%BD%AE%E8%BF%87%E5%B0%8F%5CnMONITOR_INTERVAL%20%3D%201800%5Cn%23%20%E6%9C%AC%E5%9C%B0%E5%AD%98%E5%82%A8%E6%96%87%E4%BB%B6%5CnSAVE_CSV%20%3D%20%5C%22goods_monitor_data.csv%5C%22%5Cn%23%20%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%3D%5Cn%5Cndef%20fetch_goods_data(item_id)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E8%8E%B7%E5%8F%96%E5%8D%95%E6%AC%BE%E5%95%86%E5%93%81%E7%BB%93%E6%9E%84%E5%8C%96%E6%95%B0%E6%8D%AE%5C%22%5C%22%5C%22%5Cn%20%20%20%20base_url%20%3D%20%5C%22https%3A%2F%2Fapi%E2%80%91gw.onebound.cn%2Fjd%2Fitem_get_pro%5C%22%5Cn%20%20%20%20params%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22key%5C%22%3A%20KEY%2C%5Cn%20%20%20%20%20%20%20%20%5C%22secret%5C%22%3A%20SECRET%2C%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20item_id%2C%5Cn%20%20%20%20%20%20%20%20%5C%22result_type%5C%22%3A%20%5C%22json%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22cache%5C%22%3A%20%5C%22no%5C%22%20%20%23%20%E8%AE%BE%E7%BD%AEno%E8%8E%B7%E5%8F%96%E6%9C%80%E6%96%B0%E6%95%B0%E6%8D%AE%EF%BC%9Byes%E8%AF%BB%E5%8F%96%E7%BC%93%E5%AD%98%E9%80%9F%E5%BA%A6%E6%9B%B4%E5%BF%AB%5Cn%20%20%20%20%7D%5Cn%20%20%20%20headers%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22Accept%E2%80%91Encoding%5C%22%3A%20%5C%22gzip%5C%22%2C%5Cn%20%20%20%20%20%20%20%20%5C%22Connection%5C%22%3A%20%5C%22close%5C%22%5Cn%20%20%20%20%7D%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20resp%20%3D%20requests.get(base_url%2C%20params%3Dparams%2C%20headers%3Dheaders%2C%20timeout%3D20)%5Cn%20%20%20%20%20%20%20%20result%20%3D%20resp.json()%5Cn%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%20%7Bitem_id%7D%20%E8%AF%B7%E6%B1%82%E5%BC%82%E5%B8%B8%EF%BC%9A%7Bstr(e)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20error_code%20%3D%20result.get(%5C%22error_code%5C%22)%5Cn%20%20%20%20if%20error_code%20!%3D%20%5C%220000%5C%22%3A%5Cn%20%20%20%20%20%20%20%20err_msg%20%3D%20result.get(%5C%22error%5C%22%2C%20%5C%22%E6%9C%AA%E7%9F%A5%E9%94%99%E8%AF%AF%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%20%7Bitem_id%7D%20%E8%8E%B7%E5%8F%96%E5%A4%B1%E8%B4%A5%EF%BC%8C%E9%94%99%E8%AF%AF%EF%BC%9A%7Berr_msg%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20item_data%20%3D%20result.get(%5C%22item%5C%22%2C%20%7B%7D)%5Cn%20%20%20%20if%20not%20item_data%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%20%7Bitem_id%7D%20%E8%BF%94%E5%9B%9E%E5%86%85%E5%AE%B9%E4%B8%BA%E7%A9%BA%5C%22)%5Cn%20%20%20%20%20%20%20%20return%20None%5Cn%5Cn%20%20%20%20%23%20%E6%8F%90%E5%8F%96%E6%A0%B8%E5%BF%83%E5%AD%97%E6%AE%B5%5Cn%20%20%20%20output%20%3D%20%7B%5Cn%20%20%20%20%20%20%20%20%5C%22crawl_time%5C%22%3A%20time.strftime(%5C%22%25Y%E2%80%91%25m%E2%80%91%25d%20%25H%3A%25M%3A%25S%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22num_iid%5C%22%3A%20item_data.get(%5C%22num_iid%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22title%5C%22%3A%20item_data.get(%5C%22title%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22brand%5C%22%3A%20item_data.get(%5C%22brand%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22price%5C%22%3A%20item_data.get(%5C%22price%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22orginal_price%5C%22%3A%20item_data.get(%5C%22orginal_price%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22stock%5C%22%3A%20item_data.get(%5C%22num%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22shop_name%5C%22%3A%20item_data.get(%5C%22seller_info%5C%22%2C%20%7B%7D).get(%5C%22shop_name%5C%22%2C%20%5C%22%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22detail_url%5C%22%3A%20item_data.get(%5C%22detail_url%5C%22)%2C%5Cn%20%20%20%20%20%20%20%20%5C%22pic_url%5C%22%3A%20item_data.get(%5C%22pic_url%5C%22)%5Cn%20%20%20%20%7D%5Cn%20%20%20%20return%20output%5Cn%5Cn%5Cndef%20save_to_csv(data_list)%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E6%8A%8A%E9%87%87%E9%9B%86%E5%88%B0%E7%9A%84%E6%95%B0%E6%8D%AE%E8%BF%BD%E5%8A%A0%E5%86%99%E5%85%A5CSV%5C%22%5C%22%5C%22%5Cn%20%20%20%20df_new%20%3D%20pd.DataFrame(data_list)%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20df_old%20%3D%20pd.read_csv(SAVE_CSV%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%20%20%20%20%20%20%20%20df_total%20%3D%20pd.concat(%5Bdf_old%2C%20df_new%5D%2C%20ignore_index%3DTrue)%5Cn%20%20%20%20except%20FileNotFoundError%3A%5Cn%20%20%20%20%20%20%20%20df_total%20%3D%20df_new%5Cn%20%20%20%20df_total.to_csv(SAVE_CSV%2C%20index%3DFalse%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%20%20%20%20print(f%5C%22%E2%9C%85%20%7Blen(data_list)%7D%20%E6%9D%A1%E6%95%B0%E6%8D%AE%E5%B7%B2%E5%86%99%E5%85%A5%E6%9C%AC%E5%9C%B0%E6%96%87%E4%BB%B6%20%7BSAVE_CSV%7D%5C%22)%5Cn%5Cn%5Cndef%20analysis_data()%3A%5Cn%20%20%20%20%5C%22%5C%22%5C%22%E7%AE%80%E5%8D%95%E6%9C%AC%E5%9C%B0%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E7%A4%BA%E4%BE%8B%EF%BC%8C%E8%AF%BB%E5%8F%96CSV%E5%81%9A%E5%9F%BA%E7%A1%80%E7%BB%9F%E8%AE%A1%5C%22%5C%22%5C%22%5Cn%20%20%20%20try%3A%5Cn%20%20%20%20%20%20%20%20df%20%3D%20pd.read_csv(SAVE_CSV%2C%20encoding%3D%5C%22utf%E2%80%918%E2%80%91sig%5C%22)%5Cn%20%20%20%20%20%20%20%20df%5B%5C%22price%5C%22%5D%20%3D%20pd.to_numeric(df%5B%5C%22price%5C%22%5D%2C%20errors%3D%5C%22coerce%5C%22)%5Cn%20%20%20%20%20%20%20%20print(%5C%22%5C%5Cn%3D%3D%3D%3D%20%E7%AE%80%E5%8D%95%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90%E6%8A%A5%E5%91%8A%20%3D%3D%3D%3D%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E6%80%BB%E9%87%87%E9%9B%86%E8%AE%B0%E5%BD%95%E6%95%B0%EF%BC%9A%7Blen(df)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E7%9B%91%E6%8E%A7%E5%95%86%E5%93%81%E6%95%B0%E9%87%8F%EF%BC%9A%7Bdf%5B&amp;#39;num_iid&amp;#39;%5D.nunique()%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%95%86%E5%93%81%E5%B9%B3%E5%9D%87%E5%94%AE%E4%BB%B7%EF%BC%9A%7Bdf%5B&amp;#39;price&amp;#39;%5D.mean()%3A.2f%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20print(%5C%22%5C%5Cn%E5%90%84%E5%95%86%E5%93%81%E6%9C%80%E6%96%B0%E4%BB%B7%E6%A0%BC%EF%BC%9A%5C%22)%5Cn%20%20%20%20%20%20%20%20latest%20%3D%20df.sort_values(%5C%22crawl_time%5C%22).groupby(%5C%22num_iid%5C%22).last()%5Cn%20%20%20%20%20%20%20%20print(latest%5B%5B%5C%22title%5C%22%2C%20%5C%22price%5C%22%2C%20%5C%22stock%5C%22%5D%5D)%5Cn%20%20%20%20except%20Exception%20as%20e%3A%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%E5%88%86%E6%9E%90%E6%95%B0%E6%8D%AE%E5%BC%82%E5%B8%B8%EF%BC%9A%7Bstr(e)%7D%EF%BC%8C%E5%8F%AF%E8%83%BD%E8%BF%98%E6%B2%A1%E6%9C%89%E9%87%87%E9%9B%86%E5%88%B0%E6%95%B0%E6%8D%AE%5C%22)%5Cn%5Cn%5Cndef%20main_loop()%3A%5Cn%20%20%20%20print(%5C%22%F0%9F%9A%80%20%E5%90%AF%E5%8A%A8%E5%95%86%E5%93%81%E7%9B%91%E6%8E%A7%E4%BB%BB%E5%8A%A1%5C%22)%5Cn%20%20%20%20while%20True%3A%5Cn%20%20%20%20%20%20%20%20collect_result%20%3D%20%5B%5D%5Cn%20%20%20%20%20%20%20%20for%20goods_id%20in%20MONITOR_ITEM_IDS%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20item_info%20%3D%20fetch_goods_data(goods_id)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20if%20item_info%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20collect_result.append(item_info)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f%5C%22%E5%B7%B2%E8%8E%B7%E5%8F%96%EF%BC%9A%7Bitem_info.get(&amp;#39;title&amp;#39;)%7D%20%7C%20%E5%BD%93%E5%89%8D%E4%BB%B7%EF%BC%9A%7Bitem_info.get(&amp;#39;price&amp;#39;)%7D%5C%22)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20time.sleep(2)%20%20%23%20%E6%AF%8F%E4%B8%AA%E8%AF%B7%E6%B1%82%E4%B9%8B%E9%97%B4%E9%97%B4%E9%9A%94%EF%BC%8C%E9%81%BF%E5%85%8D%E8%AE%BF%E9%97%AE%E8%BF%87%E4%BA%8E%E5%AF%86%E9%9B%86%5Cn%20%20%20%20%20%20%20%20if%20collect_result%3A%5Cn%20%20%20%20%20%20%20%20%20%20%20%20save_to_csv(collect_result)%5Cn%20%20%20%20%20%20%20%20%20%20%20%20analysis_data()%5Cn%20%20%20%20%20%20%20%20print(f%5C%22%5C%5Cn%E2%8F%B3%20%E7%AD%89%E5%BE%85%20%7BMONITOR_INTERVAL%7D%20%E7%A7%92%E5%90%8E%E8%BF%9B%E8%A1%8C%E4%B8%8B%E4%B8%80%E8%BD%AE%E6%89%AB%E6%8F%8F...%5C%5Cn%5C%22)%5Cn%20%20%20%20%20%20%20%20time.sleep(MONITOR_INTERVAL)%5Cn%5Cn%5Cnif%20__name__%20%3D%3D%20%5C%22__main__%5C%22%3A%5Cn%20%20%20%20main_loop()%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;-*-&amp;nbsp;coding:utf-8&amp;nbsp;-*-
import&amp;nbsp;requests
import&amp;nbsp;time
import&amp;nbsp;pandas&amp;nbsp;as&amp;nbsp;pd
import&amp;nbsp;json

#&amp;nbsp;==========&amp;nbsp;配置区域，请修改为自己的凭证和目标商品&amp;nbsp;==========
KEY&amp;nbsp;=&amp;nbsp;&amp;quot;你的身份key&amp;quot;
SECRET&amp;nbsp;=&amp;nbsp;&amp;quot;你的身份密钥&amp;quot;
#&amp;nbsp;需要监控的商品ID列表，可以填入多个商品编号
MONITOR_ITEM_IDS&amp;nbsp;=&amp;nbsp;[&amp;quot;10335871600&amp;quot;,&amp;nbsp;&amp;quot;10057467958584&amp;quot;]
#&amp;nbsp;监控间隔，单位秒，示例设置30分钟扫描一次，请勿设置过小
MONITOR_INTERVAL&amp;nbsp;=&amp;nbsp;1800
#&amp;nbsp;本地存储文件
SAVE_CSV&amp;nbsp;=&amp;nbsp;&amp;quot;goods_monitor_data.csv&amp;quot;
#&amp;nbsp;=========================================================

def&amp;nbsp;fetch_goods_data(item_id):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;获取单款商品结构化数据&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;base_url&amp;nbsp;=&amp;nbsp;&amp;quot;https://api‑gw.onebound.cn/jd/item_get_pro&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;params&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;key&amp;quot;:&amp;nbsp;KEY,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;secret&amp;quot;:&amp;nbsp;SECRET,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;item_id,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;result_type&amp;quot;:&amp;nbsp;&amp;quot;json&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;cache&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;&amp;nbsp;&amp;nbsp;#&amp;nbsp;设置no获取最新数据；yes读取缓存速度更快
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Accept‑Encoding&amp;quot;:&amp;nbsp;&amp;quot;gzip&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Connection&amp;quot;:&amp;nbsp;&amp;quot;close&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;resp&amp;nbsp;=&amp;nbsp;requests.get(base_url,&amp;nbsp;params=params,&amp;nbsp;headers=headers,&amp;nbsp;timeout=20)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;result&amp;nbsp;=&amp;nbsp;resp.json()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品&amp;nbsp;{item_id}&amp;nbsp;请求异常：{str(e)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;error_code&amp;nbsp;=&amp;nbsp;result.get(&amp;quot;error_code&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;error_code&amp;nbsp;!=&amp;nbsp;&amp;quot;0000&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;err_msg&amp;nbsp;=&amp;nbsp;result.get(&amp;quot;error&amp;quot;,&amp;nbsp;&amp;quot;未知错误&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品&amp;nbsp;{item_id}&amp;nbsp;获取失败，错误：{err_msg}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_data&amp;nbsp;=&amp;nbsp;result.get(&amp;quot;item&amp;quot;,&amp;nbsp;{})
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;item_data:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品&amp;nbsp;{item_id}&amp;nbsp;返回内容为空&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;None

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;提取核心字段
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;output&amp;nbsp;=&amp;nbsp;{
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;crawl_time&amp;quot;:&amp;nbsp;time.strftime(&amp;quot;%Y‑%m‑%d&amp;nbsp;%H:%M:%S&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;num_iid&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;num_iid&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;title&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;title&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;brand&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;brand&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;price&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;price&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;orginal_price&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;orginal_price&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;stock&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;num&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;shop_name&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;seller_info&amp;quot;,&amp;nbsp;{}).get(&amp;quot;shop_name&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;detail_url&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;detail_url&amp;quot;),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;pic_url&amp;quot;:&amp;nbsp;item_data.get(&amp;quot;pic_url&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;output


def&amp;nbsp;save_to_csv(data_list):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;把采集到的数据追加写入CSV&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_new&amp;nbsp;=&amp;nbsp;pd.DataFrame(data_list)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_old&amp;nbsp;=&amp;nbsp;pd.read_csv(SAVE_CSV,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_total&amp;nbsp;=&amp;nbsp;pd.concat([df_old,&amp;nbsp;df_new],&amp;nbsp;ignore_index=True)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;FileNotFoundError:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_total&amp;nbsp;=&amp;nbsp;df_new
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df_total.to_csv(SAVE_CSV,&amp;nbsp;index=False,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;✅&amp;nbsp;{len(data_list)}&amp;nbsp;条数据已写入本地文件&amp;nbsp;{SAVE_CSV}&amp;quot;)


def&amp;nbsp;analysis_data():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;&amp;quot;&amp;quot;简单本地数据分析示例，读取CSV做基础统计&amp;quot;&amp;quot;&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df&amp;nbsp;=&amp;nbsp;pd.read_csv(SAVE_CSV,&amp;nbsp;encoding=&amp;quot;utf‑8‑sig&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;df[&amp;quot;price&amp;quot;]&amp;nbsp;=&amp;nbsp;pd.to_numeric(df[&amp;quot;price&amp;quot;],&amp;nbsp;errors=&amp;quot;coerce&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n====&amp;nbsp;简单数据分析报告&amp;nbsp;====&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;总采集记录数：{len(df)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;监控商品数量：{df[&amp;#39;num_iid&amp;#39;].nunique()}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;商品平均售价：{df[&amp;#39;price&amp;#39;].mean():.2f}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;\n各商品最新价格：&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;latest&amp;nbsp;=&amp;nbsp;df.sort_values(&amp;quot;crawl_time&amp;quot;).groupby(&amp;quot;num_iid&amp;quot;).last()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(latest[[&amp;quot;title&amp;quot;,&amp;nbsp;&amp;quot;price&amp;quot;,&amp;nbsp;&amp;quot;stock&amp;quot;]])
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;分析数据异常：{str(e)}，可能还没有采集到数据&amp;quot;)


def&amp;nbsp;main_loop():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(&amp;quot;🚀&amp;nbsp;启动商品监控任务&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while&amp;nbsp;True:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;collect_result&amp;nbsp;=&amp;nbsp;[]
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;for&amp;nbsp;goods_id&amp;nbsp;in&amp;nbsp;MONITOR_ITEM_IDS:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;item_info&amp;nbsp;=&amp;nbsp;fetch_goods_data(goods_id)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;item_info:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;collect_result.append(item_info)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;已获取：{item_info.get(&amp;#39;title&amp;#39;)}&amp;nbsp;|&amp;nbsp;当前价：{item_info.get(&amp;#39;price&amp;#39;)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(2)&amp;nbsp;&amp;nbsp;#&amp;nbsp;每个请求之间间隔，避免访问过于密集
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;collect_result:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;save_to_csv(collect_result)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;analysis_data()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;\n⏳&amp;nbsp;等待&amp;nbsp;{MONITOR_INTERVAL}&amp;nbsp;秒后进行下一轮扫描...\n&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;time.sleep(MONITOR_INTERVAL)


if&amp;nbsp;__name__&amp;nbsp;==&amp;nbsp;&amp;quot;__main__&amp;quot;:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;main_loop()&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;h2&gt;四、代码讲解&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;fetch_goods_data 函数&lt;/strong&gt;：负责传入商品编号，拉取完整商品信息，处理网络异常、返回状态判断，提取我们关心的标题、价格、库存、店铺等核心字段。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;cache 参数：设置&lt;code&gt;yes&lt;/code&gt;优先读取缓存，响应速度更快；设置&lt;code&gt;no&lt;/code&gt;拉取实时最新数据，适合监控价格变动场景。&lt;/p&gt;&lt;/blockquote&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;save_to_csv 函数&lt;/strong&gt;：将每一轮抓取的数据追加保存到本地 CSV 文件，&lt;code&gt;utf‑8‑sig&lt;/code&gt;编码保证 Excel 打开不会乱码。后续可以直接用 Excel 打开，做数据透视、价格对比。&lt;/p&gt;&lt;p&gt;
	&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;analysis_data 函数&lt;/strong&gt;：简单的本地数据分析演示，统计总记录、平均价格，输出每个商品最新状态。实际业务可以在此扩展，例如检测价格波动，当本次价格和上一轮记录不一致，打印告警提示。&lt;/p&gt;&lt;p&gt;
	&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;main_loop 主循环&lt;/strong&gt;：循环遍历所有待监控商品，间隔一定时间重复执行，实现持续监控。&lt;strong&gt;不要把 MONITOR_INTERVAL 设置过小，避免短时间大量请求。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;
	&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;运行脚本后，项目目录下会生成&lt;code&gt;goods_monitor_data.csv&lt;/code&gt;文件，每一轮采集都会追加新的一行，附带采集时间戳，可以完整记录商品随时间变化的轨迹。&lt;/p&gt;&lt;h2&gt;五、拓展：SKU 规格拆解与深度分析&lt;/h2&gt;&lt;p&gt;上面示例只提取了商品基础信息，如果需要分析多规格变体，返回数据中自带 SKU 列表，我们可以简单改造解析 SKU。 示例片段（放在&lt;code&gt;fetch_goods_data&lt;/code&gt;函数内部）：&lt;/p&gt;&lt;div aria-label=&quot;代码段 小部件&quot; class=&quot;cke_widget_wrapper cke_widget_block cke_widget_codeSnippet cke_widget_selected&quot; contenteditable=&quot;false&quot; data-cke-display-name=&quot;代码段&quot; data-cke-filter=&quot;off&quot; data-cke-widget-id=&quot;2&quot; data-cke-widget-wrapper=&quot;1&quot; role=&quot;region&quot; tabindex=&quot;-1&quot;&gt;&lt;pre class=&quot;cke_widget_element&quot; data-cke-widget-data=&quot;%7B%22classes%22%3Anull%2C%22lang%22%3Anull%2C%22code%22%3A%22%23%20%E8%A7%A3%E6%9E%90SKU%E8%A7%84%E6%A0%BC%E7%A4%BA%E4%BE%8B%5Cnsku_list%20%3D%20item_data.get(%5C%22skus%5C%22%2C%20%7B%7D).get(%5C%22sku%5C%22%2C%20%5B%5D)%5Cnfor%20sku%20in%20sku_list%3A%5Cn%20%20%20%20print(f%5C%22%E8%A7%84%E6%A0%BC%E5%90%8D%E7%A7%B0%EF%BC%9A%7Bsku.get(&amp;#39;properties_name&amp;#39;)%7D%5C%22)%5Cn%20%20%20%20print(f%5C%22%E8%A7%84%E6%A0%BC%E4%BB%B7%E6%A0%BC%EF%BC%9A%7Bsku.get(&amp;#39;price&amp;#39;)%7D%EF%BC%8C%E5%BA%93%E5%AD%98%EF%BC%9A%7Bsku.get(&amp;#39;quantity&amp;#39;)%7D%5C%22)%22%7D&quot; data-cke-widget-keep-attr=&quot;0&quot; data-widget=&quot;codeSnippet&quot;&gt;#&amp;nbsp;解析SKU规格示例
sku_list&amp;nbsp;=&amp;nbsp;item_data.get(&amp;quot;skus&amp;quot;,&amp;nbsp;{}).get(&amp;quot;sku&amp;quot;,&amp;nbsp;[])
for&amp;nbsp;sku&amp;nbsp;in&amp;nbsp;sku_list:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;规格名称：{sku.get(&amp;#39;properties_name&amp;#39;)}&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;print(f&amp;quot;规格价格：{sku.get(&amp;#39;price&amp;#39;)}，库存：{sku.get(&amp;#39;quantity&amp;#39;)}&amp;quot;)&lt;/pre&gt;&lt;span class=&quot;cke_reset cke_widget_drag_handler_container&quot; style=&quot;background: url(&amp;quot;https://csdnimg.cn/release/blog_editor_html/release2.4.6/ckeditor/plugins/widget/images/handle.png&amp;quot;) rgba(220, 220, 220, 0.5); top: 0px; left: 0px;&quot;&gt;&lt;/span&gt;&lt;/div&gt;&lt;p&gt;拿到每个规格的价格、库存后，就可以做更细粒度的分析：比如统计一款商品不同规格的价格梯度，观察哪些规格长期缺货，辅助选品和竞品定价分析。&lt;/p&gt;&lt;h2&gt;六、踩坑经验分享&lt;/h2&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;价格偶尔出现偏差&lt;/strong&gt;：部分场景返回的价格和页面展示会有出入，属于数据源本身的情况，适合做趋势监控，不要直接当做 100% 精准成交价格。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;商品下架&lt;/strong&gt;：当商品已经被删除、下架，程序会捕获错误并打印日志，可以据此做商品状态跟踪。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;凭证密钥保护&lt;/strong&gt;：不要直接硬编码提交到代码仓库，生产环境建议放到环境变量中读取。&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;频率控制&lt;/strong&gt;：不要把轮询间隔设置很短，高频请求不仅消耗额度，也容易触发限制，做监控一般 15‑60 分钟一轮足够业务使用。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;七、落地应用场景总结&lt;/h2&gt;&lt;p&gt;这套方案可以服务于很多业务场景：&lt;/p&gt;&lt;ol class=&quot; list-paddingleft-2&quot;&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;竞品跟踪&lt;/strong&gt;：持续跟踪同行多款商品的调价、库存、规格迭代，及时捕捉对手动作；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;选品调研&lt;/strong&gt;：批量收集一批候选商品，导出到表格分析价格区间、库存水平，筛选潜力款；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;自家商品巡检&lt;/strong&gt;：监控自己上架商品，快速发现异常缺货、价格异常；&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;个人比价&lt;/strong&gt;：跟踪心仪商品，观察历史价格变化。&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;写在最后：工具只是手段，真正价值在于结构化之后的数据。当我们拥有时间序列的商品记录，不再靠肉眼刷网页，很多市场趋势、竞品策略，都可以从数据中直观看到。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;如果你需要进一步扩展，可以加上告警逻辑，检测价格变动后发送消息通知；也可以把 CSV 替换成 SQLite、MySQL 数据库，支持更大规模的数据存储。&lt;/p&gt;&lt;p&gt;&lt;span data-cke-copybin-end=&quot;1&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Thu, 20 Aug 2026 09:33:03 +0800</pubDate></item></channel></rss>