<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ayan Pahwa</title>
    <description>The latest articles on DEV Community by Ayan Pahwa (@iayanpahwa).</description>
    <link>https://dev.to/iayanpahwa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F873075%2F4be7029b-bbfa-4a1e-8090-9e7e561d96a0.jpeg</url>
      <title>DEV Community: Ayan Pahwa</title>
      <link>https://dev.to/iayanpahwa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/iayanpahwa"/>
    <language>en</language>
    <item>
      <title>Headless web exploration is the way to go!</title>
      <dc:creator>Ayan Pahwa</dc:creator>
      <pubDate>Mon, 16 Mar 2026 09:21:25 +0000</pubDate>
      <link>https://dev.to/iayanpahwa/headless-web-exploration-is-the-way-to-go-3kim</link>
      <guid>https://dev.to/iayanpahwa/headless-web-exploration-is-the-way-to-go-3kim</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/extractdata" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F11159%2F9b0ab14b-3550-4e5e-b996-02b33c0912fa.jpg" alt="Extract by Zyte" width="800" height="800"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F873075%2F4be7029b-bbfa-4a1e-8090-9e7e561d96a0.jpeg" alt="" width="460" height="460"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/extractdata/i-built-a-claude-code-skill-that-screenshots-any-website-and-it-handles-anti-bot-sites-too-2m4b" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;I built a Claude Code skill that screenshots any website (and it handles anti-bot sites too)&lt;/h2&gt;
      &lt;h3&gt;Ayan Pahwa for Extract by Zyte ・ Mar 6&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#claude&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webscraping&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#python&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ai&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>claude</category>
      <category>webscraping</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>I built a Claude Code skill that screenshots any website (and it handles anti-bot sites too)</title>
      <dc:creator>Ayan Pahwa</dc:creator>
      <pubDate>Fri, 06 Mar 2026 14:40:32 +0000</pubDate>
      <link>https://dev.to/extractdata/i-built-a-claude-code-skill-that-screenshots-any-website-and-it-handles-anti-bot-sites-too-2m4b</link>
      <guid>https://dev.to/extractdata/i-built-a-claude-code-skill-that-screenshots-any-website-and-it-handles-anti-bot-sites-too-2m4b</guid>
      <description>&lt;p&gt;TLDR;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Automate screenshot capture for any URL with JavaScript rendering and anti-ban protection — straight from your AI assistant.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/P2HhnFRXm-I"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Taking a screenshot of a webpage sounds trivial, until you need to do it at scale. Modern websites throw every obstacle imaginable in your way: JavaScript-rendered content that only appears after a React bundle loads, bot-detection systems that serve blank pages to automated headless browsers, geo-blocked content, and CAPTCHAs that appear the moment traffic patterns look non-human. For a handful of URLs you can get away with Puppeteer or Playwright. For hundreds or thousands? You need infrastructure built for the job.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnca4lwviajb7vavqwio5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnca4lwviajb7vavqwio5.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Zyte API was designed specifically for this problem. It handles JavaScript rendering, anti-bot fingerprinting, rotating proxies, and headless browser management so you don't have to and what better way to do it straight from your LLM supplying the URLs? Hence I created this zyte-screenshots Claude Skill, which you can use to trigger the entire workflow- API call, base64 decode, PNG save on your filesystem, all just by chatting with Claude.&lt;/p&gt;

&lt;p&gt;In this tutorial, we'll walk through exactly how the skill works, how to set it up, and how to use it to capture production-quality screenshots of any URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Use the Zyte API for Screenshots?
&lt;/h2&gt;

&lt;p&gt;Before diving into the skill itself, it's worth understanding what makes the Zyte API uniquely suited to screenshot capture at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Full JavaScript Rendering
&lt;/h3&gt;

&lt;p&gt;Single-page applications built with React, Vue, Angular, or Next.js don't serve their content in the raw HTML response, they render it client-side after the page loads. Tools that capture the raw HTTP response will get a blank shell. Zyte's screenshot endpoint fires a real headless browser, waits for the DOM to fully settle, then captures the final rendered state.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Anti-Bot and Anti-Ban Protection
&lt;/h3&gt;

&lt;p&gt;Enterprise-grade sites use fingerprinting libraries to detect automation. They check TLS fingerprints, browser headers, canvas rendering patterns, mouse movement entropy, and dozens of other signals. Zyte's infrastructure is battle-tested to pass these checks so your screenshots won't return a "Access Denied" page.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Scale Without Infrastructure
&lt;/h3&gt;

&lt;p&gt;Managing a fleet of headless browser instances, proxy rotation, retries, and residential IP pools is a serious engineering investment. Zyte abstracts all of this into a single API call.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. One API, Any URL
&lt;/h3&gt;

&lt;p&gt;Whether the target is a static HTML page, a JS-heavy SPA, a behind-login dashboard (with session cookies), or a geo-restricted site, the same API call structure works. The skill you're about to install uses this endpoint.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://app.zyte.com/account/signup/zyteapi" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9a7qsf6b03h1rrfzw5g.png" alt="Zyte API signup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is the zyte-screenshots Claude Skill?
&lt;/h2&gt;

&lt;p&gt;Claude Skills are reusable instruction packages that extend Claude's capabilities with domain-specific workflows. The &lt;strong&gt;zyte-screenshots&lt;/strong&gt; skill teaches Claude how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accept a URL from the user in natural language&lt;/li&gt;
&lt;li&gt;Read the ZYTE_API_KEY environment variable&lt;/li&gt;
&lt;li&gt;Construct and execute the correct curl command against &lt;a href="https://api.zyte.com/v1/extract" rel="noopener noreferrer"&gt;https://api.zyte.com/v1/extract&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pipe the JSON response through jq and base64 --decode to produce a PNG file&lt;/li&gt;
&lt;li&gt;Derive a clean filename from the URL (e.g. &lt;a href="https://quotes.toscrape.com" rel="noopener noreferrer"&gt;https://quotes.toscrape.com&lt;/a&gt; becomes quotes.toscrape.png)&lt;/li&gt;
&lt;li&gt;Report the exact file path and describe what's visible in the screenshot in one sentence&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, this means you can open Claude, say &lt;strong&gt;"screenshot &lt;a href="https://example.com" rel="noopener noreferrer"&gt;https://example.com&lt;/a&gt;"&lt;/strong&gt;, and have a pixel-perfect PNG on your filesystem in seconds, no browser, no script, no Puppeteer config.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before installing the skill, make sure you have the following:&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;curl&lt;/strong&gt;: Pre-installed on macOS and most Linux distributions. On Windows, use WSL or Git Bash.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;jq&lt;/strong&gt;: A lightweight JSON processor. Install via &lt;code&gt;brew install jq&lt;/code&gt; (macOS) or &lt;code&gt;sudo apt install jq&lt;/code&gt; (Ubuntu/Debian).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;base64&lt;/strong&gt;: Standard on all Unix-like systems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude desktop app&lt;/strong&gt; with Skills support enabled.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A Zyte API Key
&lt;/h3&gt;

&lt;p&gt;Sign up at &lt;a href="https://www.zyte.com/" rel="noopener noreferrer"&gt;zyte.com&lt;/a&gt; and navigate to your API credentials. The free tier includes enough credits to get started with testing. Copy your API key, you'll set it as an environment variable.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Pro tip:&lt;/strong&gt; Set your ZYTE_API_KEY in your shell profile (~/.zshrc or ~/.bashrc) so it's always available: &lt;code&gt;export ZYTE_API_KEY="your_key_here"&lt;/code&gt; or pass it along your prompt&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Installing the Skill
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Download the Skill from GitHub
&lt;/h3&gt;

&lt;p&gt;The skill is open source and available at &lt;a href="https://github.com/apscrapes/claude-zyte-screenshots" rel="noopener noreferrer"&gt;github.com/apscrapes/claude-zyte-screenshots&lt;/a&gt;. Download the latest release ZIP from the repository's Releases page, or clone it directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/apscrapes/claude-zyte-screenshots.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Import into Claude
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Open the Claude desktop app or go to Claude.ai&lt;/li&gt;
&lt;li&gt;Navigate to Settings → Skills&lt;/li&gt;
&lt;li&gt;Click Import Skill&lt;/li&gt;
&lt;li&gt;Select the SKILL.md file from the downloaded ZIP&lt;/li&gt;
&lt;li&gt;Claude will confirm the skill has been installed&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 3: Set Your API Key
&lt;/h3&gt;

&lt;p&gt;In your terminal, export your Zyte API key or pass it along your prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ZYTE_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your_zyte_api_key_here"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make this permanent across terminal sessions, add the export line to your &lt;code&gt;~/.zshrc&lt;/code&gt; or &lt;code&gt;~/.bashrc&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using the Skill: Step-by-Step
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Basic Screenshot
&lt;/h3&gt;

&lt;p&gt;Once the skill is installed and your API key is set, using it is as simple as asking Claude:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Screenshot https://quotes.toscrape.com using zyte-screenshots skill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Claude will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Verify ZYTE_API_KEY is available in the environment&lt;/li&gt;
&lt;li&gt;Derive the output filename: quotes.toscrape.png&lt;/li&gt;
&lt;li&gt;Execute the following command:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.zyte.com/v1/extract &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ZYTE_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;: &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "url": "https://quotes.toscrape.com",
    "screenshot": true
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.screenshot'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; quotes.toscrape.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Verify the file is non-empty&lt;/li&gt;
&lt;li&gt;Open and inspect the image&lt;/li&gt;
&lt;li&gt;Return the file location and a one-line visual description&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Example output from Claude:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ Screenshot captured!

📁 Location: /home/you/quotes.toscrape.png

🖼️  What's in it: A clean light-themed page listing inspirational quotes
   with author attributions and tag filters in the sidebar.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Under the Hood: How the API Call Works
&lt;/h2&gt;

&lt;p&gt;Let's break down the exact curl command the skill executes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.zyte.com/v1/extract &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ZYTE_API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;: &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "url": "https://target-site.com",
    "screenshot": true
  }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.screenshot'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
| &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; output.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;curl -s&lt;/code&gt;&lt;/strong&gt; — Silent mode; suppresses progress output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;-u "$ZYTE_API_KEY":&lt;/code&gt;&lt;/strong&gt; — HTTP Basic Auth. Zyte uses the API key as the username with an empty password.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;-H "Content-Type: application/json"&lt;/code&gt;&lt;/strong&gt; — Tells the API to expect a JSON body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;-d '{...}'&lt;/code&gt;&lt;/strong&gt; — The JSON request body. Setting &lt;code&gt;screenshot: true&lt;/code&gt; instructs Zyte to return a base64-encoded PNG of the fully rendered page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;| jq -r '.screenshot'&lt;/code&gt;&lt;/strong&gt; — Extracts the raw base64 string from the JSON response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;| base64 --decode&lt;/code&gt;&lt;/strong&gt; — Decodes the base64 string into binary PNG data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;&amp;gt; output.png&lt;/code&gt;&lt;/strong&gt; — Writes the binary data to a PNG file.&lt;/p&gt;

&lt;p&gt;The Zyte API handles everything in between — spinning up a headless Chromium instance, loading the page with real browser fingerprints, waiting for JavaScript execution to complete, and rendering the final DOM to a pixel buffer.&lt;/p&gt;

&lt;p&gt;This was a fun weekend project I put together, let me know your thoughts on our Discord and feel free to play around with it. I'd also love to know if you create any useful claude skills or mcp server, so say hi on our discord.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: web scraping • Zyte API • screenshots at scale • JavaScript rendering • anti-bot • Claude AI • Claude Skills • automation • headless browser • site APIs&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claude</category>
      <category>webscraping</category>
      <category>python</category>
      <category>ai</category>
    </item>
    <item>
      <title>Raspberry Pi &amp; E-ink scrapes &amp; displays the price of Gold today</title>
      <dc:creator>Ayan Pahwa</dc:creator>
      <pubDate>Tue, 24 Feb 2026 08:39:31 +0000</pubDate>
      <link>https://dev.to/extractdata/how-i-trade-gold-using-e-ink-live-data-and-an-old-raspberry-pi-42ag</link>
      <guid>https://dev.to/extractdata/how-i-trade-gold-using-e-ink-live-data-and-an-old-raspberry-pi-42ag</guid>
      <description>&lt;p&gt;They say that “data is the new oil”, but there’s another hot commodity that’s setting markets alight - precious metals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtbqu72m01w8auk29wfd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtbqu72m01w8auk29wfd.jpg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the last 12 months, the &lt;a href="https://uk.finance.yahoo.com/quote/GC%3DF/" rel="noopener noreferrer"&gt;value of gold&lt;/a&gt; has surged about 75%, while &lt;a href="https://www.gold.co.uk/silver-price/" rel="noopener noreferrer"&gt;silver has boomed&lt;/a&gt; more than 200%. That’s why I, like a growing number of others, now trade in the metal markets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdanyckktjsbtvn1e2b3p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdanyckktjsbtvn1e2b3p.png" alt=" " width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These days, it is possible to buy &lt;em&gt;digital&lt;/em&gt; versions of precious metals. But I think of myself as a &lt;em&gt;collector&lt;/em&gt; - I like to buy &lt;em&gt;real&lt;/em&gt;, solid coins or bullions whenever I get a chance.&lt;/p&gt;

&lt;p&gt;In the last two years, I have acquired a small collection of gold bullions and silver coins, which have appreciated healthily. But I am not planning to sell and book a profit just yet. In fact, I want to buy more, especially when there’s a dip in the price.&lt;/p&gt;

&lt;p&gt;There’s just one problem that hits this hobby - prices of actual physical gold and silver bullions are very different in the retail market from stock exchanges’ spot prices and keeping track of them manually is cumbersome specially with a full time job.&lt;/p&gt;

&lt;p&gt;To take advantage of the dips and price arbitrage, I need to &lt;em&gt;automate&lt;/em&gt; my decisions. To buy gold old-style, I need a key resource from the modern trading toolset - data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn data into gold
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="http://gjc.org.in/" rel="noopener noreferrer"&gt;All-India Gem And Jewellery Domestic Council&lt;/a&gt; (GJC), a national trade federation for the promotion and growth of trade in gems and jewellery across, is the go-to site listing latest retail rates for gold and silver.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsgjjdai9hw0oyihva84n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsgjjdai9hw0oyihva84n.png" alt=" " width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alas, it doesn’t offer an API to access that data. But fear not - with web scraping skills and &lt;a href="https://www.zyte.com/zyte-api/" rel="noopener noreferrer"&gt;Zyte API&lt;/a&gt;, I can extract these prices quickly and regularly.&lt;/p&gt;

&lt;p&gt;And I can do it using some of the tech I love to tinker with.&lt;/p&gt;

&lt;p&gt;I call it &lt;a href="https://github.com/apscrapes/ExtractToInk" rel="noopener noreferrer"&gt;ExtractToInk&lt;/a&gt; - a custom project that pulls the latest prices on a two-inch, 250x122 e-ink display powered by a retired Raspberry Pi (total cost under US$50).&lt;/p&gt;

&lt;p&gt;This is the story of how I power my quest for rapid riches using cheap old hardware and the &lt;a href="https://www.zyte.com/blog/zyte-leads-proxyway-2025-web-scraping-api-report/" rel="noopener noreferrer"&gt;world’s best web scraping engine&lt;/a&gt; - and how you can, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mining for data
&lt;/h2&gt;

&lt;p&gt;Like many modern sites, GJC’s includes both JavaScript rendering for HTML and protection mechanisms- technologies that can break brittle traditional scraping solutions.&lt;/p&gt;

&lt;p&gt;This project connects all the dots:&lt;/p&gt;

&lt;p&gt;Web → Extract → Parse → Render → Physical display&lt;/p&gt;

&lt;h3&gt;
  
  
  Tech stack
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Hardware&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry Pi (tested on Pi Zero 2 W), it should run on any Raspberry Pi Board
&lt;/li&gt;
&lt;li&gt;Pimoroni Inky pHAT (Black, SSD1608)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Software&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3
&lt;/li&gt;
&lt;li&gt;Zyte API: to get rendered HTML
&lt;/li&gt;
&lt;li&gt;BeautifulSoup: to parse HTML
&lt;/li&gt;
&lt;li&gt;Pillow and Inky Python libraries: for e-ink display stuff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now let’s get building.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Prepare hardware
&lt;/h2&gt;

&lt;p&gt;Setup your Raspberry Pi. In my case, I am using Raspberry Pi OS &lt;a href="https://www.raspberrypi.com/documentation/computers/getting-started.html" rel="noopener noreferrer"&gt;booted from the SD card&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furl2uiive3xweo4opha6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furl2uiive3xweo4opha6.png" alt=" " width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Depending on which display you use, it most probably will be connected to the Pi over i2c bus or SPI bus protocol - so, enable your display type by entering:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo raspi-config&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Now attach your e-ink display and do a quick reboot &lt;/p&gt;

&lt;p&gt;You might need to install libraries to use your e-ink display.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd77cowp2x5kj1hmvupf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd77cowp2x5kj1hmvupf.png" alt=" " width="800" height="373"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Fetching rendered HTML with Zyte API
&lt;/h2&gt;

&lt;p&gt;The source site, GJC, renders prices dynamically, using JavaScript - something which can make plain HTTP requests unreliable.&lt;/p&gt;

&lt;p&gt;No problem. By accessing the page through Zyte API, we can set &lt;code&gt;browserHTML&lt;/code&gt; mode to return the page content as though rendered in an actual browser.&lt;/p&gt;

&lt;p&gt;Instead of fighting JavaScript, we let Zyte handle it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html = requests.post(`  
    `"https://api.zyte.com/v1/extract",`  
    `auth=(ZYTE_API_KEY, ""),`  
    `json={"url": URL, "browserHtml": True},`  
`).json()["browserHtml"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: there is no Selenium here, and no headless browsers. This is much more reliable for production-style scraping&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Parsing with CSS selectors
&lt;/h2&gt;

&lt;p&gt;Once we have clean HTML, parsing becomes straightforward.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzkwbp7ffd758ci8uu01r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzkwbp7ffd758ci8uu01r.png" alt=" " width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Gold prices
&lt;/h3&gt;

&lt;p&gt;Let’s locate the actual prices in the page mark-up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for row in soup.select(".gold_rate table tr"):`  
    `label = row.select_one("td strong")`  
    `values = row.select("td strong")`

    `if not label or len(values) &amp;lt; 2:`  
        `continue`

    `text = label.get_text(strip=True)`  
    `priceText = values[1].get_text()`

    `if "Standard Rate Buying" in text:`  
        `goldBuying = re.search(r"\d[\d,]*", priceText).group(0)`

    `if "Standard Rate Selling" in text:`  
        `goldSelling = re.search(r"\d[\d,]*", priceText).group(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’re deliberately using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS selectors (easy to find from your browser’s DevTools).
&lt;/li&gt;
&lt;li&gt;Minimal regular expressions (only for numeric extraction).
&lt;/li&gt;
&lt;li&gt;Defensive checks to avoid brittle parsing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Silver prices
&lt;/h3&gt;

&lt;p&gt;Silver appears outside the main table, so we filter it carefully:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for strong in soup.select("p &amp;gt; strong"):`  
    `text = strong.get_text(" ", strip=True)`

    `if "Standard Rate Selling" in text and not strong.find_parent("table"):`  
        `silver = re.search(r"\d[\d,]*", text).group(0)`  
        `break
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Rendering for e-ink
&lt;/h2&gt;

&lt;p&gt;For this project, I did not want to pipe data into a web dashboard on a computer monitor.&lt;/p&gt;

&lt;p&gt;E-ink is always-on, low power, distraction-free and perfect for “ambient information” like this.&lt;/p&gt;

&lt;p&gt;So, it’s a great fit for data like prices, weather, status indicators and system health.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwp8mf7r0lr5iibk3h8b5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwp8mf7r0lr5iibk3h8b5.png" alt=" " width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But e-ink displays are not normal screens.&lt;/p&gt;

&lt;p&gt;They are typically black-and-white, have high contrast and are slow to refresh.&lt;/p&gt;

&lt;p&gt;What’s more, no two e-ink displays are made the same way. Every vendor has different support packages so, whichever you end up using, make sure to read the documentation and change the code accordingly.&lt;/p&gt;

&lt;p&gt;In my case, I am using &lt;a href="https://learn.pimoroni.com/article/getting-started-with-inky-phat" rel="noopener noreferrer"&gt;Pimoroni inky PHat&lt;/a&gt;. The supplied Python library has great built-in examples to get you quickly up and running. I used the helper function to render texts on the display, ex, the build in draw.text() function comes handy:&lt;/p&gt;

&lt;h3&gt;
  
  
  Draw silver selling price
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    draw.text((x, y), f"Silver : {silverPrice}", fill=(0, 0, 0), font=fontBig)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Taking it furtherSection about the finished product
&lt;/h2&gt;

&lt;p&gt;I built this project to use web data thoughtfully, connecting it to the physical world, and building pipelines that feel calm, reliable, and purposeful. When I am at my work-desk the project actively tells me the current prices so I can buy new coins if I see a price drop.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdf1n493o7cimspvfjrjw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdf1n493o7cimspvfjrjw.jpg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can further extend this to place automatic orders on the website and secure me a coin at my desired strike price. &lt;/p&gt;

&lt;p&gt;If you want to take this further, you could also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run it via &lt;code&gt;cron&lt;/code&gt; every 10 minutes : The website I am targeting only refreshes prices twice a day, so my cron job runs every 12 hours, but, if you need faster data, you can scrape a site with more real-time updates.
&lt;/li&gt;
&lt;li&gt;Add more commodities or currencies.
&lt;/li&gt;
&lt;li&gt;Turn it into a &lt;code&gt;systemd&lt;/code&gt; service to run at start time.
&lt;/li&gt;
&lt;li&gt;Swap e-ink for another output (PDF, LED, dashboard).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re exploring Zyte API, or looking for real-world scraping examples beyond CSVs and JSON files, this project is a great place to start.&lt;/p&gt;

&lt;p&gt;You can get my code in the &lt;a href="https://github.com/apscrapes/ExtractToInk" rel="noopener noreferrer"&gt;ExtractToInk GitHub repository&lt;/a&gt; now.&lt;/p&gt;

</description>
      <category>python</category>
      <category>raspberrypi</category>
      <category>webdev</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>Holiday Gift Guide 2025: For Developers, Web Scrapers &amp; Everyone In Between</title>
      <dc:creator>Ayan Pahwa</dc:creator>
      <pubDate>Fri, 19 Dec 2025 07:55:12 +0000</pubDate>
      <link>https://dev.to/extractdata/holiday-gift-guide-2025-for-developers-web-scrapers-everyone-in-between-2hbp</link>
      <guid>https://dev.to/extractdata/holiday-gift-guide-2025-for-developers-web-scrapers-everyone-in-between-2hbp</guid>
      <description>&lt;p&gt;It’s that time of the year when the coffee gets stronger, commits get messier, and everyone agrees to finally refactor that script in January. And let’s be honest, most of us won’t. But while it lasts let’s celebrate the season of enjoying laid back family time and exchanging gifts. &lt;br&gt;
And to make gifting a little easier, we asked the Zyte team and community to share what they would love to receive. So if you’ve got a developer, a web scraper, or someone who just really enjoys arguing with APIs in your life… Here's your cheat sheet.&lt;br&gt;
Grab a hot drink, settle into your favourite debugging position, and let’s dive in. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer : It's not sponsored. All the recommendations are from community and author's personal experience using these products, hence no URL is provided but you should be able find these easily.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;1. Book: Soul of a New Machine&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsk3748t6ioitfnv4qms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhsk3748t6ioitfnv4qms.png" alt=" " width="572" height="892"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the dev who loves origin stories. It’s a classic engineering tale that reminds us why we fell in love with building things in the first place. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Logitech MX Master mouse&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkn5l6tujar1d04fvqyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbkn5l6tujar1d04fvqyf.png" alt=" " width="570" height="812"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Smooth scrolling. Perfect ergonomics. Side buttons that feel like cheat codes for productivity. This is the mouse equivalent of finally finding that one undocumented API endpoint you needed all year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. AeroPress + Coffee Subscription&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facfz2yudv63bxnq41916.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Facfz2yudv63bxnq41916.png" alt=" " width="734" height="866"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If coffee is the real runtime powering your favourite developer, this combo is basically a performance upgrade. AeroPress means fast, clean brews; fresh beans mean they might actually fix that bug before lunch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Spider Plush Toy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltyo0r4rwnfq2ybtb482.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltyo0r4rwnfq2ybtb482.png" alt=" " width="636" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For every web scraper who proudly identifies as “part human, part spider.” it sits silently on your desk reminding you to obey robots.txt (…most of the time).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Git Merch Based on Their Commit History&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdlexoulpxhdj1354vou5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdlexoulpxhdj1354vou5.png" alt=" " width="720" height="918"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A mug that says “I survived the merge conflict of 2025.”&lt;br&gt;
A T-shirt celebrating their 3,000-day streak.&lt;br&gt;
Or maybe a gentle reminder that “WIP” is not a personality.&lt;br&gt;
Funny, personal, and guaranteed to make them smile during standup.&lt;br&gt;
Link : &lt;a href="https://gitmerch.com/" rel="noopener noreferrer"&gt;https://gitmerch.com/&lt;/a&gt; (not-sponsored),  just enter their github username&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Nothing Headphones&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxulp8u0vgwuj70i6dhli.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxulp8u0vgwuj70i6dhli.png" alt=" " width="548" height="578"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sleek, transparent, and great for tuning out noisy offices or noisy families. Perfect for deep work, debugging, or pretending you can’t hear someone asking, “Can you take a quick look at this?”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Mechanical Keyboard (NuPhy / Keychron)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwvejewr2qxjcsgilo9p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdwvejewr2qxjcsgilo9p.png" alt=" " width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Developers don’t just type on these; they perform.&lt;br&gt;
Clicky keys, gorgeous layouts, RGB that could guide aircraft, what’s not to love? Warning: once they switch, they’ll never stop talking about actuation force.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;8. Bambu Lab A1 Mini 3D Printer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyve77kf7h4f7palr2hdt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyve77kf7h4f7palr2hdt.png" alt=" " width="540" height="730"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the dev who already has too many hobbies.&lt;br&gt;
They’ll print brackets, cable holders, figurines, replacement parts… and things no one can identify but everyone politely admires. A creator’s playground in miniature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. BenQ Monitor Light Bar&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesxgg5au2821ji4v9h01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesxgg5au2821ji4v9h01.png" alt=" " width="676" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Immediate upgrade to any desk setup. Reduces eye strain, looks clean, and helps developers see their code clearly even during late-night “just one more function” sessions without taking extra space on their desk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10. 100W GaN Charger&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feznqyferd6siq0nsnwa1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feznqyferd6siq0nsnwa1.png" alt=" " width="738" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tiny but absurdly powerful, just like that one script they wrote at 3 AM that still runs in production. A GaN charger keeps everything powered: laptop, phone, tablet, e-reader, existential dread, everything.&lt;/p&gt;




&lt;p&gt;If you love someone who spends their days coaxing data out of websites, obsessing over keyboard switches, or whispering sweet nothings to their terminal, this list has something that will make them light up.&lt;br&gt;
Here’s to a warm, restful holiday season… and to fewer bugs in 2026. Happy gifting, happy coding, and as always, happy scraping! 🕷️✨&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>holiday</category>
      <category>python</category>
      <category>developers</category>
    </item>
    <item>
      <title>Build Your Own Holiday Deal Tracker with Python, Zyte API &amp; IFTTT 🎁</title>
      <dc:creator>Ayan Pahwa</dc:creator>
      <pubDate>Fri, 21 Nov 2025 11:06:18 +0000</pubDate>
      <link>https://dev.to/extractdata/build-your-own-holiday-deal-tracker-with-python-zyte-api-ifttt-3p78</link>
      <guid>https://dev.to/extractdata/build-your-own-holiday-deal-tracker-with-python-zyte-api-ifttt-3p78</guid>
      <description>&lt;p&gt;&lt;em&gt;(A simple, developer-friendly project to help you catch Black Friday, Cyber Monday, and Christmas deals before anyone else)&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Watch the video tutorial : &lt;/p&gt;

&lt;h2&gt;
  
  
  

  &lt;iframe src="https://www.youtube.com/embed/7o-S-FY5-k8"&gt;
  &lt;/iframe&gt;



&lt;/h2&gt;

&lt;p&gt;It’s that time of the year again, everyone’s hunting for discounts, limited-time deals, and that one item you’ve been keeping an eye on all year. You know the drill: tabs open everywhere, price tracker sites breaking under traffic, browser extensions that promise magic but fail right when The Deal drops.&lt;/p&gt;

&lt;p&gt;So this year, I decided to build something different, something that actually works when I need it the most.&lt;/p&gt;

&lt;p&gt;A personal price-alert system, powered by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 🐍&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.zyte.com/" rel="noopener noreferrer"&gt;Zyte’s&lt;/a&gt; Automatic Extraction API 🕷️&lt;/li&gt;
&lt;li&gt;An &lt;a href="https://ifttt.com/" rel="noopener noreferrer"&gt;IFTTT&lt;/a&gt; mobile notification trigger 📱&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And honestly? It ended up being one of the simplest, useful and most reliable holiday project I’ve made in a long time. &lt;/p&gt;

&lt;p&gt;❌ No HTML parsing.&lt;br&gt;
❌ No complex CSS selectors.&lt;br&gt;
❌ No brittle scrapers that break during peak traffic.&lt;br&gt;
✅Just a clean API call, structured product data, and a push notification when the price hits your set target. &lt;/p&gt;

&lt;p&gt;Let me walk you through the whole thing :&lt;/p&gt;


&lt;h2&gt;
  
  
  🎁 Why Build Your Own Deal Tracker?
&lt;/h2&gt;

&lt;p&gt;Because holiday deals don’t wait for anyone. Every year I get messages from friends asking,&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“Bro, what’s the best price tracker? Nothing seems to work today.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;And they’re right. Most free services throttle, break, or go down completely during Black Friday and Cyber Monday because everyone hits them at once.&lt;/p&gt;

&lt;p&gt;Meanwhile, with a few lines of Python and Zyte’s AI-powered extraction, you can build something that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works only for you&lt;/li&gt;
&lt;li&gt;Deals with anti-ban, anti-bot and captchas &lt;/li&gt;
&lt;li&gt;Checks as often as you want (make it run on your pc, github-actions, server or a raspberry pi)&lt;/li&gt;
&lt;li&gt;Doesn’t rely on brittle selectors&lt;/li&gt;
&lt;li&gt;Doesn’t choke on JavaScript&lt;/li&gt;
&lt;li&gt;Doesn’t get rate-limited&lt;/li&gt;
&lt;li&gt;Sends you a mobile ping instantly when price drops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s the perfect mix of practical dev fun + a tool you’ll actually use.&lt;/p&gt;


&lt;h2&gt;
  
  
  🎄 Why Zyte?
&lt;/h2&gt;

&lt;p&gt;Scraping e-commerce sites for price data is usually a pain: blocking, JavaScript rendering, cookie rules, bot detection, dynamic DOMs, infinite variations in product page layouts... you get it.&lt;/p&gt;

&lt;p&gt;The magic here is &lt;strong&gt;&lt;a href="https://docs.zyte.com/zyte-api/usage/reference.html#operation/extract/response/200/product" rel="noopener noreferrer"&gt;Zyte’s Automatic Extraction&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You literally tell AI powered Zyte API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{ 
"url": "&amp;lt;product-url&amp;gt;", 
"product": true 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and it returns structured fields like: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;price &lt;/li&gt;
&lt;li&gt;currency &lt;/li&gt;
&lt;li&gt;product name &lt;/li&gt;
&lt;li&gt;sku &lt;/li&gt;
&lt;li&gt;images &lt;/li&gt;
&lt;li&gt;stock availability &lt;/li&gt;
&lt;li&gt;description &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don’t write selectors. You don’t parse HTML. You don’t chase CSS changes. You just get clean data. And that makes this holiday project absurdly simple.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎅 What We’re Building
&lt;/h2&gt;

&lt;p&gt;A script that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Takes:&lt;/li&gt;
&lt;li&gt;a product URL&lt;/li&gt;
&lt;li&gt;a target price&lt;/li&gt;
&lt;li&gt;your Zyte API key&lt;/li&gt;
&lt;li&gt;&lt;p&gt;your IFTTT Webhook key&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fetches structured product data using Zyte API&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Checks if the product price is ≤ your target&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If yes → triggers an IFTTT event&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your phone instantly notifies you with product URL:&lt;br&gt;
“Your product dropped to your target price. Go get it!”&lt;/p&gt;
&lt;h2&gt;
  
  
  You can run this:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;manually (when you want)&lt;/li&gt;
&lt;li&gt;on a cron job (in the background)&lt;/li&gt;
&lt;li&gt;in GitHub Actions&lt;/li&gt;
&lt;li&gt;on a Raspberry Pi&lt;/li&gt;
&lt;li&gt;on a cloud function
Your call.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🛠 Setup
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Clone your project
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/apscrapes/zyte-sale-alert.git
cd zyte-sale-alert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Create your virtual environment
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 -m venv venv
source venv/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Install dependencies
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Create and add secrets in .env file
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZYTE_API_KEY=your_zyte_key_here (obtained from zyte)
IFTTT_KEY=your_webhooks_key (obtained from IFTTT, see next step)
IFTTT_EVENT=price_drop (name of your ifttt applet, see next step)
TARGET_PRICE=149.99 (example)
PRODUCT_URL=https://example.com/product/123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ol&gt;
&lt;li&gt;Create an IFTTT Webhook Applet&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;a. Download &lt;a href="https://ifttt.com/" rel="noopener noreferrer"&gt;IFTTT&lt;/a&gt; mobile app, it’s paid but you get a 7-day trial and it has tons of automation you can build using no-code, so i think it’s worth it. &lt;/p&gt;

&lt;p&gt;b. Click create new automation / applet&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uje1n43d1odgh872t1g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7uje1n43d1odgh872t1g.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;c. In “IF” field, select “Webhooks” &amp;gt; Add Event Name &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j8wuf71g071i1wo949c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j8wuf71g071i1wo949c.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbtj4o3uiwrv3hq1yzee.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flbtj4o3uiwrv3hq1yzee.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;d. In “THEN” field select Notification &amp;gt; App Notification &amp;gt; JSON Payload &amp;gt; High Priority&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkbr84te5l7gsd86oilz.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkbr84te5l7gsd86oilz.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note : You can instead of notification can also set other automation like getting an email for example&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcao83gt5mwlzh1fy0sd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flcao83gt5mwlzh1fy0sd.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyabr1st8f5aia47sggtc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyabr1st8f5aia47sggtc.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;e. Here's how it should look like when it's successfully created:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bdxpa8pyosqkr39c431.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bdxpa8pyosqkr39c431.jpg" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;Once the automation is created add your IFTTT_KEY and IFTTT_EVENT to your .env file &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set product parameters 
The main script is at src/pricedrop.py, edit the following variables to add your:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRODUCT_URL = “ ”
DESIRED_PRICE = 250 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;PRODUCT_URL “ ” is the URL of the product you want to track and DESIRED_PRICE is the price at which you want to be notified, that is it. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the project
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python src/pricedrop.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can run it manually or set up a cronjob to run at regular intervals. Whenever the price drops equal or below your target price you’ll get a notification from the IFTTT app on your phone with product URL so you can order it right away. &lt;/p&gt;

&lt;p&gt;Let’s understand how it works. &lt;/p&gt;

&lt;p&gt;🧩 Core Logic (Short Version)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;resp = client.get({"url": url, "product": True})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;“product : True” tells zyte API that the webpage we’re scraping has a product so the Machine Learning powered scrapper gets you all the relevant parameters like price, quantity, description, currency etc. &lt;/p&gt;

&lt;p&gt;And that’s literally all you need.&lt;/p&gt;

&lt;p&gt;The reason this works so beautifully is Zyte is handling:&lt;br&gt;
JS rendering&lt;br&gt;
blocking&lt;br&gt;
retries&lt;br&gt;
browser simulation&lt;br&gt;
extraction logic&lt;br&gt;
AI-powered field detection&lt;/p&gt;




&lt;p&gt;In-detail : &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Importing all the necessary libraries
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import os
import sys
import requests
from zyte_api import ZyteAPI
from dotenv import load_dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Setting required variables
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRODUCT_URL = "https://outdoor.hylnd7.com/product/a1b2c3d4-e5f6-4a7b-8c9d-000000000293"
DESIRED_PRICE = 250
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we've setup a sample product link whose price we want to track and a price at which if it goes below we want to be notified. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading the API keys from .env file
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;load_dotenv()

# from Zyte API
ZYTE_API_KEY = os.getenv("ZYTE_API_KEY")

# from IFTTT Service applets
EVENT_NAME= os.getenv("EVENT_NAME")
IFTTT_KEY= os.getenv("IFTTT_KEY")

if not ZYTE_API_KEY:
    print("ERROR: ZYTE_API_KEY not found in environment.")
    sys.exit(1)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the project root directory, create a .env file and add ZYTE API Key you'll get after logging into zyte.com and IFTTT webhook API key you get after creating the automation applet&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Function to trigger the mobile notification :
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def trigger_ifttt(event_name, key, value1):
    url = f"https://maker.ifttt.com/trigger/{event_name}/json/with/key/{key}"

    payload = {
        "value1": value1,
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this funciton is doing is basically making an API call to IFTTT and IFTTT applet is set so whenever the API calls comes with payload it sends mobile notification with that payload, which in this case is product URL so you can directly click and open the product page and buy it before it goes out of stock, SMART right? 😉&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scraping init
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;client = ZyteAPI(api_key=ZYTE_API_KEY)

    payload = {
        "url": PRODUCT_URL,
        "product": True,          
    }

    resp = client.get(payload)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Making a GET request on Zyte API with product : True, we're asking zyte to treat the URL as product page and thus it's uses it's ML capabilities to fetch product relevant details, price in this case. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compare price to SETPOINT
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if price_float &amp;lt;= DESIRED_PRICE:
            trigger_ifttt(EVENT_NAME, IFTTT_KEY, value1 = PRODUCT_URL)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If price of the product reaches to or below our target price it will call the IFTTT function, thus triggering the notification.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌟 Make It Even Better
&lt;/h2&gt;

&lt;p&gt;You can extend this to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track multiple URLs&lt;/li&gt;
&lt;li&gt;Log daily prices to CSV&lt;/li&gt;
&lt;li&gt;Plot graphs&lt;/li&gt;
&lt;li&gt;Send WhatsApp alerts&lt;/li&gt;
&lt;li&gt;Push to a Telegram bot&lt;/li&gt;
&lt;li&gt;Use GitHub Actions to check every hour&lt;/li&gt;
&lt;li&gt;Deploy as a Streamlit dashboard&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Zyte handles the extraction. You build the magic on top.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧘 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;I love projects like this because they hit the sweet spot between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;seasonal usefulness &lt;/li&gt;
&lt;li&gt;real-world scraping challenges&lt;/li&gt;
&lt;li&gt;a clean developer experience&lt;/li&gt;
&lt;li&gt;a fun weekend build&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're new to the world of web scraping like me, this shows how powerful the right tools can be. If you're experienced, it’s refreshing to skip the boilerplate and let Zyte handle the messy parts.&lt;/p&gt;

&lt;p&gt;And honestly, there’s something fun about getting a custom alert on your phone saying:&lt;br&gt;
“Hey, that gadget you wanted all year just dropped to your target price.”&lt;/p&gt;

&lt;p&gt;Happy building, happy holidays, and happy deal-hunting! 🎄🎁&lt;br&gt;
Let me know what you end up tracking.&lt;/p&gt;

&lt;p&gt;Join Zyte Discord to share what you're building or get any support : &lt;br&gt;
&lt;a href="https://discord.com/invite/DwTnbrm83s" rel="noopener noreferrer"&gt;https://discord.com/invite/DwTnbrm83s&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a class="mentioned-user" href="https://dev.to/iayanpahwa"&gt;@iayanpahwa&lt;/a&gt; &lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>python</category>
      <category>webscraping</category>
    </item>
    <item>
      <title>Build a crypto miner using Raspberry Pi in 10 minutes</title>
      <dc:creator>Ayan Pahwa</dc:creator>
      <pubDate>Wed, 15 Jun 2022 13:44:17 +0000</pubDate>
      <link>https://dev.to/iayanpahwa/build-a-crypto-miner-using-raspberry-pi-in-10-minutes-3a8g</link>
      <guid>https://dev.to/iayanpahwa/build-a-crypto-miner-using-raspberry-pi-in-10-minutes-3a8g</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fth8kwip1qd7s8ex9n6ef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fth8kwip1qd7s8ex9n6ef.png" alt="Mine Monero on Raspberry Pi" width="800" height="506"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Lately I’ve been hearing a lot of hype around web3- cryptocurrencies, NFTs, DAOs, DeFi, GameFi and all of these cool jargons giving me a lot of FOMO (Fear of missing out), so I decided that I’m gonna try some of them for myself and see what’s it all about. &lt;/p&gt;

&lt;p&gt;I tried a few things like buying some cryptos on an exchange, made a couple of web3 projects and joined a gazillion of discord channels doing the morning ritual of gm(good morning!) and responding to WAGMI(we all gonna make it!) but this one specific project called &lt;a href="https://www.getmonero.org/" rel="noopener noreferrer"&gt;Monero&lt;/a&gt; caught my attention and I wanted to share it with the community here. &lt;/p&gt;

&lt;p&gt;Before proceeding I want to put out that this is in no way an endorsement of the project or whole blockchain/web3 ecosystem in general, nor it’s a financial, get rich by mining cryptocurrency advice. This is in fact a fun project which you can build along to learn more about web3 and being a developer advocate, I’m also gonna use this project to share about the concept of :&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Using a separate docker images for build-time and run-time within the same Dockerfile, we will see why it’s important and how to achieve that later in this post&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Nevertheless, this project is kind of a fun to build, it allows you to mine cryptocurrency called &lt;strong&gt;XMR&lt;/strong&gt; of Monero blockchain, profitable or not you can no doubt brag about mining cryptocurrency at your home to your friends, so let’s get started 😉&lt;/p&gt;

&lt;p&gt;You can skip the inner details and jump straight to the build part down in the post if you like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;Without going too much into the detail, for someone new into the web3 ecosystem - A blockchain is a distributed ledger which keeps records of every transaction happening over it. Just like how a bank keeps the record of who sent money to whom, the amount as well as the date and time, similarly in case of blockchain this immutable information is maintained within distributed blocks connected by a network.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi9glt1cff5zztlyiny7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi9glt1cff5zztlyiny7.png" alt="blockchain" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This validation of all the transactions on a blockchain is done by certain users who lend their compute power in the form of miners or validator nodes. There are also some programs called smart contracts which can run automatically on blockchain when certain conditions are met but that is out of scope for this blog so will not go into more details of smart contracts. &lt;/p&gt;

&lt;p&gt;When you think of Bitcoin or Ethereum miners you might imagine a big server room consisting of massive GPUs or ASICs machines dedicatedly solving complex cryptographic problems sent to them by blockchain and once they solve it they earn rewards in the form of cryptocurrencies The process of successful submission to earn the rewards follows certain consensus mechanism which can vary depending on the type of blockchain &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh5gjffhn9zrfh35xzc1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flh5gjffhn9zrfh35xzc1.png" alt="bitcoin miner farm" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example: Bitcoin blockchain works on a consensus mechanism known as Proof of Work (PoW), while Solana works on Proof of Stake (PoS) and Proof of History(PoH). &lt;br&gt;
You can read all about types of consensus mechanism &lt;a href="https://www.allerin.com/blog/8-blockchain-consensus-mechanisms-you-should-know-about" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gro8hnotu3fizohfnhk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gro8hnotu3fizohfnhk.png" alt="consensus mechanisms" width="800" height="1131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re feeling disheartened seeing the cost of these mining computers crushing your dream to have a mining rig of yourself, don’t be because the project we’re talking today called Monero allows or in fact encourages mining on CPUs, so even a small single board computer like Raspberry Pi 4 can become a miner to help validate the transactions and get the rewards in form of XMRs, the cryptocurrency of monero blockchain. &lt;/p&gt;

&lt;p&gt;The philosophy behind this is since the cost and entry barrier to mine is pretty high, the miners are usually owned by few persons or organizations which may not be good for blockchain’s decentralization (no ownership and trustlesness by design), so monero optimizes to allow more and more people to contribute making their blockchain more decentralized. Monero also works on Proof of Work but instead of your small device solving a really complex cryptography puzzle it can join a mining pool with other devices to lend the compute power and together they can solve it fast and depending on how much your device contributed to the solution it’ll be rewarded suitably for that.&lt;/p&gt;

&lt;p&gt;Is it profitable? Maybe not, given the device needs to be powered on and running but it surely is fun and maybe you can really earn if you add a lot of devices in your mining pool with good resources, say Rpi with 8 GB RAM; nevertheless it’s not financial advice so please do your due diligence :) &lt;/p&gt;

&lt;p&gt;You can read all about Monero project, how much it pays for mining, costs associated, etc all on official website: &lt;a href="https://www.getmonero.org/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Build
&lt;/h2&gt;


&lt;h3&gt;
  
  
  Hardware Needed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A single board computer like raspberry pi 4 (more ram the better). &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The following SBCs were tested by my friend Lambros and this was the hashrate result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Raspberry Pi 3  - 20 H/s
- Raspberry Pi 4, 1 GB RAM - 45 H/s
- Raspberry Pi 4, 4 GB RAM - 99 H/s
- Nvidia Jetson Nano 2GB (without GPU enabled) - 62 H/s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hash rate(H/s) is the number of hashes a device can solve per second. I’ve not enabled CUDA(GPU backend) for Nvidia Jetson since monero encourages mining on CPU though if you want to give it a shot I’d love to see how it performs.&lt;/p&gt;

&lt;p&gt;Other things you'll need are&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SD card&lt;/li&gt;
&lt;li&gt;Power supply&lt;/li&gt;
&lt;li&gt;Connectivity to Internet via LAN or WiFi &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Software needed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;monero wallet (this is where you’ll get the rewards) download from &lt;a href="https://www.getmonero.org/downloads/" rel="noopener noreferrer"&gt;https://www.getmonero.org/downloads/&lt;/a&gt;&lt;br&gt;
Information of mining pools : as mentioned earlier we will be joining a mining pool, the address of active mining pools can be found by a simple google query, it’s better to use one near you ex: google search monero mining pools in Los Angeles. Each mining pool has their own threshold after which they start paying out to the wallets. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://balena.io/cloud/" rel="noopener noreferrer"&gt;balenaCloud account&lt;/a&gt; to manage miners&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.balena.io/etcher/" rel="noopener noreferrer"&gt;balenaEtcher&lt;/a&gt; to flash the SD card&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The easy way (deploy with balena)
&lt;/h3&gt;

&lt;p&gt;Sign up for a free balenaCloud account. Your first ten devices are free and fully-featured! Then use the button below to create and deploy the application:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dashboard.balena-cloud.com/deploy?repoUrl=https://github.com/iayanpahwa/monero-miner" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbalena.io%2Fdeploy.svg" alt="deploy button" width="191" height="38"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Note: I have used a Raspberry Pi 4 in the image below but be sure to select the correct device type for the device you are using.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Select the Device&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add OS type: &lt;a href="https://www.balena.io/docs/reference/OS/overview/#development-vs-production-mode" rel="noopener noreferrer"&gt;Production vs Development&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(optionally) add in your home WiFi credentials, download and flash the OS to SD card using etcher&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Advanced way
&lt;/h3&gt;

&lt;p&gt;If you are already a balena user it might be better for you to use this way. You can clone the project from &lt;a href="https://github.com/iayanpahwa/monero-miner" rel="noopener noreferrer"&gt;this github repo&lt;/a&gt; and use the &lt;a href="https://github.com/balena-io/balena-cli" rel="noopener noreferrer"&gt;balena CLI&lt;/a&gt; command&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;balena push &amp;lt;fleet_name&amp;gt;&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
 to push the application to your devices in the fleet created on dashboard. This is the best option if you want to tinker with the project and have full control. The &lt;a href="https://www.balena.io/os/docs/raspberrypi3/getting-started/" rel="noopener noreferrer"&gt;Getting Started Guide&lt;/a&gt; covers this option. After you've created the application and pushed the code using the CLI, follow the steps below. &lt;/p&gt;
&lt;h4&gt;
  
  
  First device boot and configurations
&lt;/h4&gt;

&lt;p&gt;When the device boots for the first time, it connects to the balenaCloud dashboard, after which you’ll be able to see it listed online. In the meanwhile we need to Install Monero wallet on our computer where we will be getting the mining rewards &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdy8osmut0h3kxffqfnhr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdy8osmut0h3kxffqfnhr.gif" alt="boot the pi" width="500" height="281"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Software Setup
&lt;/h4&gt;

&lt;p&gt;Download the Monero wallet from &lt;a href="https://www.getmonero.org/downloads/" rel="noopener noreferrer"&gt;https://www.getmonero.org/downloads/&lt;/a&gt; GUI or CLI as per your preference. I recommend using a GUI wallet and create a new wallet using Simple Mode.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpvbeaqhobwokxqswc59.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwpvbeaqhobwokxqswc59.png" alt=" " width="800" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the wallet is created you’ll have a unique wallet address copy that to clipboard and head over to:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;balena cloud &amp;gt; your device &amp;gt; Device Variables&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
tab and add the following &lt;/p&gt;
&lt;h3&gt;
  
  
  Device Variables
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;VARIABLE NAME&lt;/th&gt;
&lt;th&gt;VALUE&lt;/th&gt;
&lt;th&gt;CHANGE TYPE&lt;/th&gt;
&lt;th&gt;DESCRIPTION&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;WALLET_ADDRESS&lt;/td&gt;
&lt;td&gt; from last step&lt;/td&gt;
&lt;td&gt;Must add&lt;/td&gt;
&lt;td&gt;This is the wallet address where you’ll earn your mining rewards in the form of XMRs which you can trade on any crypto exchange&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MINER_POOL&lt;/td&gt;
&lt;td&gt;Default value is:  &lt;a href="http://xmr.2miners.com:2222/" rel="noopener noreferrer"&gt;http://xmr.2miners.com:2222/&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;This is the miner pool you will join by default. You can change this to another miner pool by searching addresses of miner pools on Google. The one near to your location will be good. For ex:&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This will restart the container and your miner will be registered to the mining pool and start getting the jobs. All the rewards will be sent to your monero wallet after your device meets the threshold of the miner pool. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qejtzog7a5yc1m523uj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8qejtzog7a5yc1m523uj.png" alt="Monero Mining logs on balenaCloud" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now as I mentioned above this may not be profitable at all but it's definitely fun and let’s look at some totally unrelated things that we're gonna learn here.&lt;/p&gt;
&lt;h4&gt;
  
  
  Container Learnings
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/iayanpahwa/monero-miner/blob/main/Dockerfile.template" rel="noopener noreferrer"&gt;Here&lt;/a&gt; is the project’s Dockerfile.template. If you take a closer look at it, you’ll see there are two different base images being used. One of them is a build image and other one is the run image &lt;/p&gt;

&lt;p&gt;Why we do that is simply because we don't want our run image to be bloated with extra packages which were needed to build a source code. So we conduct the build in a separate image and copy the artifacts or binaries in the run image using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COPY --from=build /usr/src/app/xmrig/build/xmrig /usr/local/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way our main image is minimal and less bloated. All of the balena base images are available as build and run. The build image has additional packages such as &lt;em&gt;gcc&lt;/em&gt;, &lt;em&gt;build-essential&lt;/em&gt; which are needed to build from source whereas the run image is minimal. You can read more about it &lt;a href="https://www.balena.io/docs/reference/base-images/base-images/#run-vs-build" rel="noopener noreferrer"&gt;here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;So this was the container lesson from this blog. Now I hope you do use this project to dip into the world where IoT edge computing meets web3 and as always if you have any suggestions, feedbacks or questions, please write on &lt;a href="http://forums.balena.io" rel="noopener noreferrer"&gt;balenaForums&lt;/a&gt; or any social media channel. I'll be back with another interesting project and lesson soon 🍻&lt;/p&gt;




&lt;p&gt;Attribution&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/xmrig" rel="noopener noreferrer"&gt;XMRig project&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;===&lt;/p&gt;

</description>
      <category>web3</category>
      <category>tutorial</category>
      <category>blockchain</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
