Packages
floki
0.0.4
0.38.4
0.38.3
0.38.2
0.38.1
0.38.0
0.37.1
0.37.0
0.36.3
0.36.2
0.36.1
0.36.0
0.35.4
0.35.3
0.35.2
0.35.1
0.35.0
0.34.3
0.34.2
0.34.1
0.34.0
0.33.1
0.33.0
0.32.1
0.32.0
0.31.0
0.30.1
0.30.0
0.29.0
0.28.0
0.27.0
0.26.0
0.25.0
0.24.0
0.23.1
0.23.0
0.22.0
0.21.0
0.20.4
0.20.3
0.20.2
0.20.1
0.20.0
0.19.3
0.19.2
0.19.1
0.19.0
0.18.1
0.18.0
0.17.2
0.17.1
0.17.0
0.16.0
0.15.0
0.14.0
0.13.2
0.13.1
0.13.0
0.12.1
0.12.0
0.11.0
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.1
0.6.0
0.5.0
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.1
0.1.0
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
Floki is a simple HTML parser that enables search for nodes using CSS selectors.
Current section
86 Versions
Jump to
Current section
86 Versions
Compare versions
4
files changed
+187
additions
-21
deletions
| @@ -1,17 +1,59 @@ | |
| 1 | - Floki - search inside HTML documents |
| 1 | + Floki |
| 2 2 | ===== |
| 3 3 | |
| 4 4 | [](https://travis-ci.org/philss/floki) |
| 5 5 | |
| 6 | - Floki is useful to search inside HTML documents using query selectors (like jQuery). |
| 7 | - Under the hood, it uses the [Mochiweb](https://github.com/mochi/mochiweb) HTML parser. |
| 6 | + A HTML parser and seeker. |
| 8 7 | |
| 9 | - This version works with simple CSS selectors (without nesting or group). |
| 10 | - List of selectors: |
| 8 | + This is a simple HTML parser that enables searching using CSS like selectors. |
| 11 9 | |
| 12 | - * class selectors - Ex.: `.class-name` |
| 13 | - * id selectors - Ex.: `#element-id` |
| 14 | - * tag selectors - Ex.: `img` |
| 10 | + You can search elements by class, tag name and id. |
| 11 | + |
| 12 | + [Check the documentation](http://hexdocs.pm/floki). |
| 13 | + |
| 14 | + ## Example |
| 15 | + |
| 16 | + Assuming that you have the following HTML: |
| 17 | + |
| 18 | + ```html |
| 19 | + <html> |
| 20 | + <body> |
| 21 | + <section id="content"> |
| 22 | + <p class="headline">Floki</p> |
| 23 | + <a href="http://github.com/philss/floki">Github page</a> |
| 24 | + </section> |
| 25 | + </body> |
| 26 | + </html> |
| 27 | + ``` |
| 28 | + |
| 29 | + You can perform the following queries: |
| 30 | + |
| 31 | + * Floki.find(html, "#content") : returns the section with all children; |
| 32 | + * Floki.find(html, ".headline") : returns a list with the `p` element; |
| 33 | + * Floki.find(html, "a") : returns a list with the `a` element. |
| 34 | + |
| 35 | + Each HTML node is represented by a tuple like: |
| 36 | + |
| 37 | + {tag_name, attributes, chidren_nodes} |
| 38 | + |
| 39 | + Example of node: |
| 40 | + |
| 41 | + {"p", [{"class", "headline"}], ["Floki"]} |
| 42 | + |
| 43 | + So even if the only child node is the element text, it is represented |
| 44 | + inside a list. |
| 45 | + |
| 46 | + You can write a simple HTML crawler (with support of [HTTPoison](https://github.com/edgurgel/httpoison)) with a few lines of code: |
| 47 | + |
| 48 | + ```elixir |
| 49 | + html |
| 50 | + |> Floki.find(".pages") |
| 51 | + |> Floki.find("a") |
| 52 | + |> Floki.attribute("href") |
| 53 | + |> Enum.map(fn(url) -> HTTPoison.get!(url) end) |
| 54 | + ``` |
| 55 | + |
| 56 | + It is simple as that! |
| 15 57 | |
| 16 58 | ## API |
| @@ -2,14 +2,16 @@ | |
| 2 2 | {<<"build_tools">>,[<<"mix">>]}. |
| 3 3 | {<<"contributors">>,[<<"Philip Sampaio Silva">>]}. |
| 4 4 | {<<"description">>, |
| 5 | - <<"Floki is useful to search elements inside HTML documents using query selectors (like jQuery).\n">>}. |
| 5 | + <<"A HTML parser and seeker.\n\nYou can search inside HTML documents using CSS like selectors.\n">>}. |
| 6 6 | {<<"elixir">>,<<"~> 1.0.0">>}. |
| 7 7 | {<<"files">>, |
| 8 8 | [<<"lib/floki.ex">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>, |
| 9 9 | <<"src/mochinum.erl">>,<<"src/mochiutf8.erl">>, |
| 10 10 | <<"src/mochiweb_charref.erl">>,<<"src/mochiweb_html.erl">>]}. |
| 11 11 | {<<"licenses">>,[<<"MIT">>]}. |
| 12 | - {<<"links">>,#{<<"GitHub">> => <<"https://github.com/philss/floki">>}}. |
| 12 | + {<<"links">>, |
| 13 | + #{<<"Docs">> => <<"http://hexdocs.pm/floki">>, |
| 14 | + <<"GitHub">> => <<"https://github.com/philss/floki">>}}. |
| 13 15 | {<<"name">>,<<"floki">>}. |
| 14 16 | {<<"requirements">>,#{}}. |
| 15 | - {<<"version">>,<<"0.0.3">>}. |
| 17 | + {<<"version">>,<<"0.0.4">>}. |
| @@ -1,8 +1,93 @@ | |
| 1 1 | defmodule Floki do |
| 2 | + @moduledoc """ |
| 3 | + A HTML parser and seeker. |
| 4 | + |
| 5 | + This is a simple HTML parser that enables searching using CSS like selectors. |
| 6 | + |
| 7 | + You can search elements by class, tag name and id. |
| 8 | + |
| 9 | + ## Example |
| 10 | + |
| 11 | + Assuming that you have the following HTML: |
| 12 | + |
| 13 | + <!doctype html> |
| 14 | + <html> |
| 15 | + <body> |
| 16 | + <section id="content"> |
| 17 | + <p class="headline">Floki</p> |
| 18 | + <a href="http://github.com/philss/floki">Github page</a> |
| 19 | + </section> |
| 20 | + </body> |
| 21 | + </html> |
| 22 | + |
| 23 | + You can perform the following queries: |
| 24 | + |
| 25 | + * Floki.find(html, "#content") : returns the section with all children; |
| 26 | + * Floki.find(html, ".headline") : returns a list with the `p` element; |
| 27 | + * Floki.find(html, "a") : returns a list with the `a` element. |
| 28 | + |
| 29 | + Each HTML node is represented by a tuple like: |
| 30 | + |
| 31 | + {tag_name, attributes, chidren_nodes} |
| 32 | + |
| 33 | + Example of node: |
| 34 | + |
| 35 | + {"p", [{"class", "headline"}], ["Floki"]} |
| 36 | + |
| 37 | + So even if the only child node is the element text, it is represented |
| 38 | + inside a list. |
| 39 | + |
| 40 | + You can write a simple HTML crawler (with support of [HTTPoison](https://github.com/edgurgel/httpoison)) with a few lines of code: |
| 41 | + |
| 42 | + html |
| 43 | + |> Floki.find(".pages") |
| 44 | + |> Floki.find("a") |
| 45 | + |> Floki.attribute("href") |
| 46 | + |> Enum.map(fn(url) -> HTTPoison.get!(url) end) |
| 47 | + |
| 48 | + It is simple as that! |
| 49 | + """ |
| 50 | + |
| 51 | + @type html_tree :: tuple | list |
| 52 | + |
| 53 | + @doc """ |
| 54 | + Parses a HTML string. |
| 55 | + |
| 56 | + ## Examples |
| 57 | + |
| 58 | + iex> Floki.parse("<div class=js-action>hello world</div>") |
| 59 | + {"div", [{"class", "js-action" }], ["hello world"]} |
| 60 | + |
| 61 | + """ |
| 62 | + @spec parse(binary) :: html_tree |
| 2 63 | def parse(html) do |
| 3 64 | :mochiweb_html.parse(html) |
| 4 65 | end |
| 5 66 | |
| 67 | + @doc """ |
| 68 | + Finds elements inside a HTML tree or string. |
| 69 | + You can search by class, tag name or id. |
| 70 | + |
| 71 | + It is possible to compose searches: |
| 72 | + |
| 73 | + Floki.find(html_string, ".class") |
| 74 | + |> Floki.find(".another-class-inside-small-scope" |
| 75 | + |
| 76 | + ## Examples |
| 77 | + |
| 78 | + iex> Floki.find("<p><span class=hint>hello</span></p>", ".hint") |
| 79 | + [{"span", [{ "class", "hint" }], ["hello"]}] |
| 80 | + |
| 81 | + iex> "<body><div id=important><div>Content</div></div></body>" |> Floki.find("#important") |
| 82 | + {"div", [{"id", "important"}], [{"div", [], ["Content"]}]} |
| 83 | + |
| 84 | + iex> Floki.find("<p><a href='https://google.com'>Google</a></p>", "a") |
| 85 | + [{"a", [{"href", "https://google.com"}], ["Google"]}] |
| 86 | + |
| 87 | + """ |
| 88 | + |
| 89 | + @spec find(binary | html_tree, binary) :: html_tree |
| 90 | + |
| 6 91 | def find(html, selector) when is_binary(html) do |
| 7 92 | parse(html) |
| 8 93 | |> find(selector) |
| @@ -23,19 +108,46 @@ defmodule Floki do | |
| 23 108 | |> Enum.reverse |
| 24 109 | end |
| 25 110 | |
| 111 | + @doc """ |
| 112 | + Returns a list with attribute values for a given selector. |
| 113 | + |
| 114 | + ## Examples |
| 115 | + |
| 116 | + iex> Floki.attribute("<a href='https://google.com'>Google</a>", "a", "href") |
| 117 | + ["https://google.com"] |
| 118 | + |
| 119 | + """ |
| 120 | + |
| 121 | + @spec attribute(binary | html_tree, binary, binary) :: list |
| 122 | + |
| 26 123 | def attribute(html, selector, attribute_name) do |
| 27 124 | html |
| 28 125 | |> find(selector) |
| 29 126 | |> get_attribute_values(attribute_name) |
| 30 127 | end |
| 31 128 | |
| 129 | + @doc """ |
| 130 | + Returns a list with attribute values from elements. |
| 131 | + |
| 132 | + ## Examples |
| 133 | + |
| 134 | + iex> Floki.attribute("<a href='https://google.com'>Google</a>", "href") |
| 135 | + ["https://google.com"] |
| 136 | + |
| 137 | + """ |
| 138 | + |
| 139 | + @spec attribute(binary | html_tree, binary) :: list |
| 140 | + |
| 32 141 | def attribute(elements, attribute_name) do |
| 33 142 | elements |
| 34 143 | |> get_attribute_values(attribute_name) |
| 35 144 | end |
| 36 145 | |
| 37 | - defp class_match?(attributes, class) do |
| 38 | - attribute_match?(attributes, "class", class) |
| 146 | + |
| 147 | + defp attribute_match?(attributes, attribute_name) do |
| 148 | + attributes |> Enum.find(fn({attr_name, _}) -> |
| 149 | + attr_name == attribute_name |
| 150 | + end) |
| 39 151 | end |
| 40 152 | |
| 41 153 | defp attribute_match?(attributes, attribute_name, value) do |
| @@ -62,20 +174,24 @@ defmodule Floki do | |
| 62 174 | find_by_selector(selector, child_node, matcher, acc) |
| 63 175 | end |
| 64 176 | |
| 177 | + |
| 178 | + defp get_attribute_values(element, attr_name) when is_tuple(element) do |
| 179 | + get_attribute_values([element], attr_name) |
| 180 | + end |
| 65 181 | defp get_attribute_values(elements, attr_name) do |
| 66 182 | Enum.map(elements, fn(el) -> |
| 67 183 | { _name, attributes, _childs } = el |
| 68 184 | |
| 69 | - attribute_match?(attributes, attr_name, "") |
| 185 | + attribute_match?(attributes, attr_name) |
| 70 186 | end) |
| 71 187 | |> Enum.reject(fn(x) -> is_nil(x) end) |
| 72 188 | |> Enum.map(fn({_attr_name, value}) -> value end) |
| 73 189 | end |
| 74 190 | |
| 75 | - defp class_matcher(selector, node, acc) do |
| 191 | + defp class_matcher(class_name, node, acc) do |
| 76 192 | { _, attributes, _ } = node |
| 77 193 | |
| 78 | - if class_match?(attributes, selector) do |
| 194 | + if attribute_match?(attributes, "class", class_name) do |
| 79 195 | acc = [node|acc] |
| 80 196 | end |
| @@ -3,11 +3,11 @@ defmodule Floki.Mixfile do | |
| 3 3 | |
| 4 4 | def project do |
| 5 5 | [app: :floki, |
| 6 | - version: "0.0.3", |
| 6 | + version: "0.0.4", |
| 7 7 | elixir: "~> 1.0.0", |
| 8 8 | package: package, |
| 9 9 | description: description, |
| 10 | - docs: [readme: true, main: "README.md"], |
| 10 | + docs: [readme: true, main: "README"], |
| 11 11 | deps: deps] |
| 12 12 | end |
| 13 13 | |
| @@ -28,12 +28,15 @@ defmodule Floki.Mixfile do | |
| 28 28 | # |
| 29 29 | # Type `mix help deps` for more examples and options |
| 30 30 | defp deps do |
| 31 | - [] |
| 31 | + [{:earmark, "~> 0.1", only: :dev}, |
| 32 | + {:ex_doc, "~> 0.6", only: :dev}] |
| 32 33 | end |
| 33 34 | |
| 34 35 | defp description do |
| 35 36 | """ |
| 36 | - Floki is useful to search elements inside HTML documents using query selectors (like jQuery). |
| 37 | + A HTML parser and seeker. |
| 38 | + |
| 39 | + You can search inside HTML documents using CSS like selectors. |
| 37 40 | """ |
| 38 41 | end |
| 39 42 | |
| @@ -42,7 +45,10 @@ defmodule Floki.Mixfile do | |
| 42 45 | contributors: ["Philip Sampaio Silva"], |
| 43 46 | licenses: ["MIT"], |
| 44 47 | files: ["lib", "priv", "mix.exs", "README*", "readme*", "LICENSE*", "license*", "src"], |
| 45 | - links: %{"GitHub" => "https://github.com/philss/floki"} |
| 48 | + links: %{ |
| 49 | + "GitHub" => "https://github.com/philss/floki", |
| 50 | + "Docs" => "http://hexdocs.pm/floki" |
| 51 | + } |
| 46 52 | } |
| 47 53 | end |
| 48 54 | end |