# Streaming HTML parser?

**URL:** <https://forum.crystal-lang.org/t/streaming-html-parser/8885>\
**Category:** Help & Support\
**Created:** [April 15, 2026, 8:57pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885 "2026-04-15T20:57:49Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Xen](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/xen/32/2288_2.png) [@Xen](https://forum.crystal-lang.org/u/Xen)\
**Post date:** [April 15, 2026, 8:57pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/1 "2026-04-15T20:57:49Z")

</div>

As it seems the forum beats my googling skills every time (I must be getting old), I thought I’d ask:

Is there any HTML parser shard that works on a stream rather than chewing through the whole thing in one go?

I’ve always been fascinated by templating systems that work directly with the HTML structure ever since I stumbled across Amrita in Ruby.

---

<div class="post-metadata">

**Author:** ![kostya](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/kostya/32/3228_2.png) [@kostya](https://forum.crystal-lang.org/u/kostya)\
**Post date:** [April 15, 2026, 9:29pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/2 "2026-04-15T21:29:40Z")

</div>

lexbor, streaming parse method: [lexbor/src/lexbor/parser.cr at master · kostya/lexbor · GitHub](https://github.com/kostya/lexbor/blob/master/src/lexbor/parser.cr#L117)

---

<div class="post-metadata">

**Author:** ![MarioAriasC](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/marioariasc/32/1434_2.png) [@MarioAriasC](https://forum.crystal-lang.org/u/MarioAriasC)\
**Post date:** [April 16, 2026, 1:55am UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/3 "2026-04-16T01:55:12Z")

</div>

Google is getting worse by the day

---

<div class="post-metadata">

**Author:** ![yxhuvud](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/yxhuvud/32/27_2.png) [@yxhuvud](https://forum.crystal-lang.org/u/yxhuvud)\
**Post date:** [April 16, 2026, 4:37pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/4 "2026-04-16T16:37:04Z")

</div>

Oh my, if that existed for XML I’d be quite exited. We have this service at work that would be absolutely perfect for that.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [April 16, 2026, 4:46pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/5 "2026-04-16T16:46:54Z")

</div>

> [@yxhuvud](#):
>
> Oh my, if that existed for XML I’d be quite exited.

Wouldn’t this just be [XML::Reader - Crystal 1.20.0-dev](https://crystal-lang.org/api/master/XML/Reader.html)?

---

<div class="post-metadata">

**Author:** ![yxhuvud](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/yxhuvud/32/27_2.png) [@yxhuvud](https://forum.crystal-lang.org/u/yxhuvud)\
**Post date:** [April 16, 2026, 4:51pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/6 "2026-04-16T16:51:25Z")

</div>

I’d need to be able to modify the structure as I go. I don’t think that is possible with`Reader`? But thanks for the suggestion, there may be use cases for just parsing too.

---

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [April 16, 2026, 4:54pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/7 "2026-04-16T16:54:00Z")

</div>

Ahh gotcha. might be able to pair it with a `XML::Builder` where you create a reader using the source IO then the builder with the dest IO, then as you read call related write methods, passing things thru directly, or if you need to make a change then do that instead of the existing node.

EDIT: Did something similar with `XML::Reader`/`JSON::Builder` and `XML::Builder`/`JSON::PullParser` for [oq](https://github.com/Blacksmoke16/oq/blob/master/src/converters/xml.cr).

---

<div class="post-metadata">

**Author:** ![Xen](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/xen/32/2288_2.png) [@Xen](https://forum.crystal-lang.org/u/Xen)\
**Post date:** [April 16, 2026, 6:31pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/8 "2026-04-16T18:31:10Z")

</div>

> [@kostya](#):
>
> lexbor, streaming parse method

Oh, great, now I just need to figure out how to use that. 😁

> [@MarioAriasC](#):
>
> Google is getting worse by the day

Well, I lied a bit, it was DuckDuckGo, but I don’t think the difference is major.

---

<div class="post-metadata">

**Author:** ![naqvis](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/naqvis/32/906_2.png) [@naqvis](https://forum.crystal-lang.org/u/naqvis)\
**Post date:** [April 17, 2026, 12:59pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/9 "2026-04-17T12:59:57Z")

</div>

> [@Xen](#):
>
> Is there any HTML parser shard that works on a stream rather than chewing through the whole thing in one go?

[crystal-html5](https://github.com/naqvis/crystal-html5) also supports parse HTML stream at two different levels.

**Token-level** — no tree built, constant memory: Useful when one only need tokens without building the tree.

```crystal
HTML5.each_token(io) do |token|
  puts token.data if token.type.start_tag?
end

```

Also available as `HTML5.token_iterator(io)` if you want an `Iterator`.

**SAX-style** — event callbacks during tree construction: Useful when one need a tree-aware events

```crystal
class MyHandler
  include HTML5::StreamingHandler

  def on_element_open(tag, attrs, namespace)
    puts "<#{tag}>"
  end 
end

doc = HTML5.stream(io, MyHandler.new)

```

HIH  
//Ali

---

<div class="post-metadata">

**Author:** ![Xen](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/xen/32/2288_2.png) [@Xen](https://forum.crystal-lang.org/u/Xen)\
**Post date:** [April 17, 2026, 5:46pm UTC](https://forum.crystal-lang.org/t/streaming-html-parser/8885/10 "2026-04-17T17:46:24Z")

</div>

> [@naqvis](#):
>
> [crystal-html5](https://github.com/naqvis/crystal-html5) also supports parse HTML stream at two different levels.

Oooh, I’m getting spoilt for options here.

![image](https://canada1.discourse-cdn.com/flex036/uploads/crystal_lang/original/2X/3/319117e19df504070a870f18fa99a7c5158efaa2.png)

That’s why the forum beats google…
