mischov

mischov

Meeseeks - A library for extracting data from HTML and XML with CSS or XPath selectors

import Meeseeks.CSS

html = HTTPoison.get!("https://news.ycombinator.com/").body

for story <- Meeseeks.all(html, css("tr.athing")) do
  title = Meeseeks.one(story, css(".title a"))
  %{title: Meeseeks.text(title),
    url: Meeseeks.attr(title, "href")}
end
#=> [%{title: "...", url: "..."}, %{title: "...", url: "..."}, ...]

Meeseeks is a library for parsing and extracting data from HTML and XML with CSS or XPath selectors.

GitHub: https://github.com/mischov/meeseeks
HexDocs: https://hexdocs.pm/meeseeks/Meeseeks.html

Features

  • Friendly API
  • Browser-grade HTML5 parser
  • Permissive XML parser
  • CSS and XPath selectors
  • Rich, extensible selector architecture
  • Helpers to extract data from selections

Why?

Meeseeks exists in the same space as an earlier library called Floki, so why was Meeseeks created and why would you use it instead of Floki?

Floki is a couple years older than Meeseeks, so why does Meeseeks even exist?

Meeseeks exists because Floki used to be unable to do what I needed.

When I started learning Elixir I reimplemented a small project I had written in another language. Part of that project involved extracting data from HTML, and unbeknownst to me some of the HTML I needed to extract data from was malformed.

This had never been a problem before because the HTML parser I was using in the other language was HTML5 spec compliant and handled the malformed HTML just as well as a browser. Unfortunately for me, Floki used (and still uses by default) the :mochiweb_html parser which is nowhere near HTML5 spec compliant, and just silently dropped the data I needed when parsing.

Meeseeks started out as an attempt to write an HTML5 spec compliant parser in Elixir (spoiler: it’s really hard), then switched to using Mozilla’s html5ever via Rustler after Hans wrote html5ever_elixir.

Floki gained optional support for using html5ever_elixir as its parser around the same time, but it still used :mochiweb_html (which doesn’t require Rust to be part of the build process) by default and I released Meeseeks as a safer alternative.

Why should I use Meeseeks instead of Floki?

When Meeseeks was released it came with a safer default HTML parser, a more complete collection of CSS selectors, and a more extensible selector architecture than Floki.

Since then Meeseeks has been further expanded with functionality Floki just doesn’t have, such as an XML parser and XPath selectors.

It won’t matter to most users, but the selection architecture is much richer than Floki’s, and permits the creation all kinds of interesting custom, stateful selectors (in fact, both the CSS and XPath selector strings compile down to the same selector structs that anybody can define).

What probably will matter more to users is the friendly API, extensive documentation, and the attention to the details of usability seen in such places as the custom formatting for result structs (#Meeseeks.Result<{ <p>1</p> }>) and the descriptive errors.

Is Floki ever a better choice than Meeseeks?

Yes, there are two main cases when Floki is clearly a better choice than Meeseeks.

Firstly, if you absolutely can’t include Rust in your build process AND you know that the HTML you’ll be working with is well-formed and won’t require an HTML5 spec compliant parser then using Floki with the :mochiweb_html parser is a reasonable choice.

However, if you have any doubts about the HTML you’ll be parsing you should probably figure out a way to use a better parser because using :mochiweb_html in that situation may be a timebomb.

Secondly, if you want to make updates to an HTML document Floki provides facilities to do so while Meeseeks, which is entirely focused on selecting and extracting data, does not.

How does performance compare between Floki and Meeseeks?

Performance is similar enough between the two that it’s probably not worth choosing one over the other for that reason.

For details and benchmarks, see Meeseeks vs. Floki Performance.

Most Liked

mischov

mischov

Release v0.16.0

Wow, it’s been over a year since the last release! Hope everybody has been as well as circumstances allow.

This release adds support for Elixir 1.12 and Erlang/OTP 24, and is brought to you mostly due to the excellent work of @jeroenvisser101 who figured out how to upgrade from Rustler v0.21 to v0.22.

This release also drops support for Elixir 1.6 and Erlang/OTP 20.

mischov

mischov

Release v0.5.0

XPATH SELECTORS!

They weren’t particularly easy to implement, but the library is stronger for the work that went into them. I doubled my test count trying to make sure the implementation was accurate, but XPath is… interesting and there’s every chance a few slipped by, so let me know if you find one.

Fun fact- in XPath 1.0 it’s valid to take a substring starting at -INFINITY and continuing for INFINITY characters.

This release also fixes some bugs related to element namespaces and a bug related to lack of html5ever version limits that was causing compilation problems for meeseeks_html5ever.

mischov

mischov

Release v0.7.3

A super minor release, just fixing a couple warnings that popped up with Elixir 1.5.

On a tangentially related note, the much slowed rate of releases has nothing to do with my commitment to the project- I just haven’t encountered any bugs or thought of any improvements.

The big next steps for the library (barring the appearance of a bug or improvement) will be a chores such as a style unification and further improving the documentation (if anybody has felt let down by some portion of the documentation, please speak up). I also plan to release some articles about web scraping with Elixir and Meeseeks (if you have any ideas for examples or articles you wish were written on the subject, get in touch).

mischov

mischov

Release v0.11.2

This release includes several fixes to CSS selectors.

Previously names, idents, and strings in CSS selectors would not accept escaped or unicode characters, now CSS selectors accept both escaped characters and Elixir-style unicode code points. A big thank you to @Ripster for creating the issue that led to these fixes.

iex(1)> import Meeseeks.CSS
Meeseeks.CSS

iex(2)> Meeseeks.one("<div id=\"~\"></div>", css("#\\~"))
#Meeseeks.Result<{ <div id="~"></div> }>

iex(3)> Meeseeks.one("<div class=\"❤\"></div>", css(".❤"))  
#Meeseeks.Result<{ <div class="❤"></div> }>

This release also includes some minor improvements to the CSS selector parser errors.

brightball

brightball

Just wanted to give you credit for a great name choice.

Where Next?

Popular in Libraries Top

tmbb
I’ve been working on two packages (not on hex.pm yet) to build admin interfaces for phoenix apps: bureaucrat - which contains a bunch ...
New
kevinlang
Hey all, We have made an Ecto3 Adapter for SQLite3, ecto_sqlite3! We have successfully on-boarded the full suite of integration tests (...
New
pkrawat1
Presenting Aviacommerce, open source e-commerce platform in Elixir Aviacommerce is an open source e-commerce platform in Elixir. We at...
New
mcrumm
If you would like to migrate away from node/npm/webpack while still using sass, the dart_sass package provides a installer and runner for...
New
New
mbuhot
EctoJob A transactional job queue built with Ecto, PostgreSQL and GenStage Available on Hex.pm: ecto_job | Hex Docs: API Reference — ec...
New
Jskalc
Hi! Today, after a couple weeks of development I’ve released v0.1 of LiveVue. It’s a seamless integration of Vue and Phoenix LiveView, i...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
Azolo
Hey everyone, I just released WebSockex which is a Elixir WebSocket client. WebSockex strives to work as a OTP special process, be RFC6...
New
wojtekmach
Hey everyone! Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Sub Categories:

We're in Beta

About us Mission Statement