A small Zig project for learning through a practical price checker. It reads products from pages.config, fetches each page, extracts a price between configured HTML markers, and prints the result in minor units.
Note
As this project is for learning the language / syntax. I decided to do a lot of string manipulations manually instead
of using std.mem
The project currently supports:
- Reading product names and URL extraction rules from
pages.config. - Fetching HTTP pages and extracting text between start and end markers.
- Removing a leading
$and commas before converting a price to minor units. - Configuring the currency and whether a value is already in minor units.
Price monitoring, persistence, and reporting price changes are still to be built.
src/main.zig: executable entry point. Reads the config and extracts every product URL.src/file_reader.zig: parsespages.configintoProductandProductUrlvalues.src/html.zig: fetches a URL and returns its response body.src/extract.zig: defines products and extracts, formats, and converts prices.pages.config: product and extraction-rule configuration.product-sites/: React Router practice storefronts for local scraper testing.
Each product starts with a bracketed name. Add one or more URL rules below it:
[Product name]
URL | start marker | end marker | currency | is_minor
currency and is_minor are optional:
URL | start marker | end marker
URL | start marker | end marker | USD
URL | start marker | end marker | USD | true
The defaults are AUD and false. is_minor accepts only true or false. Whitespace around fields is ignored.
For example, this extracts the value of data-price from a local product page:
[VOLTAGE 9 Neural Deck]
http://127.0.0.1:5173/product-1 | data-price=" | " | USD | false
When is_minor is true, the parsed value is already a minor-unit amount and is not multiplied by 100.
Build the executable:
zig buildRun it:
./zig-out/bin/price-watchStart the React Router practice sites from product-sites/ in a separate terminal:
bun run dev -- --host 127.0.0.1The explicit IPv4 host is required so the Zig HTTP client can reach the site at http://127.0.0.1:5173.
/product-1: formatteddata-pricevalue./product-2:data-quote-pricevalue./product-3: cents stored indata-price; configure it withis_minorset totrue./product-4: formatteddata-current-pricevalue.
Verify the React app from product-sites/ with:
bun run typecheck
bun run buildzig test src/file_reader.zig
zig test src/extract.zigparse_file copies product names, URLs, markers, and configured currencies with the allocator it receives. In main.zig, those copies are owned by the arena and remain valid until arena.deinit().
html.get_page_content also allocates response bodies with the passed allocator. The current program uses the same arena for all work, releasing parser data, URL lists, and response bodies together at shutdown.
- Products and price history are not persisted.
- Price extraction depends on retailer-specific HTML markers, which can change when a page is redesigned.
- Each configuration line must fit in the parser's 1024-byte read buffer and end with a newline.