Packages
floki
0.0.5
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
+67
additions
-21
deletions
| @@ -3,8 +3,6 @@ Floki | |
| 3 3 | |
| 4 4 | [](https://travis-ci.org/philss/floki) |
| 5 5 | |
| 6 | - A HTML parser and seeker. |
| 7 | - |
| 8 6 | This is a simple HTML parser that enables searching using CSS like selectors. |
| 9 7 | |
| 10 8 | You can search elements by class, tag name and id. |
| @@ -16,6 +14,7 @@ You can search elements by class, tag name and id. | |
| 16 14 | Assuming that you have the following HTML: |
| 17 15 | |
| 18 16 | ```html |
| 17 | + <!doctype html> |
| 19 18 | <html> |
| 20 19 | <body> |
| 21 20 | <section id="content"> |
| @@ -90,10 +89,19 @@ You can also get attributes from elements that you already have: | |
| 90 89 | |
| 91 90 | ```elixir |
| 92 91 | Floki.find(html, ".example") |
| 93 | - |> Floki.attribute("class") |
| 92 | + |> Floki.attribute("class") |
| 94 93 | # => ["example"] |
| 95 94 | ``` |
| 96 95 | |
| 96 | + If you want to get the text from an element, try: |
| 97 | + |
| 98 | + ```elixir |
| 99 | + Floki.find(html, ".headline") |
| 100 | + |> Floki.text |
| 101 | + |
| 102 | + # => "Floki" |
| 103 | + ``` |
| 104 | + |
| 97 105 | ## License |
| 98 106 | |
| 99 107 | Floki is under MIT license. Check the `LICENSE` file for more details. |
| @@ -3,7 +3,7 @@ | |
| 3 3 | {<<"contributors">>,[<<"Philip Sampaio Silva">>]}. |
| 4 4 | {<<"description">>, |
| 5 5 | <<"A HTML parser and seeker.\n\nYou can search inside HTML documents using CSS like selectors.\n">>}. |
| 6 | - {<<"elixir">>,<<"~> 1.0.0">>}. |
| 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">>, |
| @@ -14,4 +14,4 @@ | |
| 14 14 | <<"GitHub">> => <<"https://github.com/philss/floki">>}}. |
| 15 15 | {<<"name">>,<<"floki">>}. |
| 16 16 | {<<"requirements">>,#{}}. |
| 17 | - {<<"version">>,<<"0.0.4">>}. |
| 17 | + {<<"version">>,<<"0.0.5">>}. |
| @@ -71,7 +71,7 @@ defmodule Floki do | |
| 71 71 | It is possible to compose searches: |
| 72 72 | |
| 73 73 | Floki.find(html_string, ".class") |
| 74 | - |> Floki.find(".another-class-inside-small-scope" |
| 74 | + |> Floki.find(".another-class-inside-small-scope") |
| 75 75 | |
| 76 76 | ## Examples |
| 77 77 | |
| @@ -90,22 +90,22 @@ defmodule Floki do | |
| 90 90 | |
| 91 91 | def find(html, selector) when is_binary(html) do |
| 92 92 | parse(html) |
| 93 | - |> find(selector) |
| 93 | + |> find(selector) |
| 94 94 | end |
| 95 95 | |
| 96 96 | def find(html_tree, "." <> class) do |
| 97 97 | find_by_selector(class, html_tree, &class_matcher/3, []) |
| 98 | - |> Enum.reverse |
| 98 | + |> Enum.reverse |
| 99 99 | end |
| 100 100 | |
| 101 101 | def find(html_tree, "#" <> id) do |
| 102 102 | find_by_selector(id, html_tree, &id_matcher/3, []) |
| 103 | - |> List.first |
| 103 | + |> List.first |
| 104 104 | end |
| 105 105 | |
| 106 106 | def find(html_tree, tag_name) do |
| 107 107 | find_by_selector(tag_name, html_tree, &tag_matcher/3, []) |
| 108 | - |> Enum.reverse |
| 108 | + |> Enum.reverse |
| 109 109 | end |
| 110 110 | |
| 111 111 | @doc """ |
| @@ -122,8 +122,8 @@ defmodule Floki do | |
| 122 122 | |
| 123 123 | def attribute(html, selector, attribute_name) do |
| 124 124 | html |
| 125 | - |> find(selector) |
| 126 | - |> get_attribute_values(attribute_name) |
| 125 | + |> find(selector) |
| 126 | + |> get_attribute_values(attribute_name) |
| 127 127 | end |
| 128 128 | |
| 129 129 | @doc """ |
| @@ -140,21 +140,52 @@ defmodule Floki do | |
| 140 140 | |
| 141 141 | def attribute(elements, attribute_name) do |
| 142 142 | elements |
| 143 | - |> get_attribute_values(attribute_name) |
| 143 | + |> get_attribute_values(attribute_name) |
| 144 | + end |
| 145 | + |
| 146 | + |
| 147 | + @doc """ |
| 148 | + Returns the text nodes from a html tree. |
| 149 | + |
| 150 | + ## Examples |
| 151 | + |
| 152 | + iex> Floki.text("<div><span>something else</span>hello world</div>") |
| 153 | + "hello world" |
| 154 | + |
| 155 | + """ |
| 156 | + |
| 157 | + @spec text(html_tree | binary) :: binary |
| 158 | + |
| 159 | + def text(html) when is_binary(html), do: parse(html) |> text |
| 160 | + def text(element) when is_tuple(element), do: text(element, "") |
| 161 | + def text(elements) do |
| 162 | + Enum.reduce elements, "", fn(element, str) -> |
| 163 | + text(element, str) |
| 164 | + end |
| 165 | + end |
| 166 | + |
| 167 | + defp text({_, _, children}, acc) do |
| 168 | + Enum.reduce children, acc, fn(child, istr) -> |
| 169 | + if is_binary(child) do |
| 170 | + (istr <> "\s" <> child) |> String.strip |
| 171 | + else |
| 172 | + istr |
| 173 | + end |
| 174 | + end |
| 144 175 | end |
| 145 176 | |
| 146 177 | |
| 147 178 | defp attribute_match?(attributes, attribute_name) do |
| 148 | - attributes |> Enum.find(fn({attr_name, _}) -> |
| 179 | + Enum.find(attributes, fn({ attr_name, _ }) -> |
| 149 180 | attr_name == attribute_name |
| 150 181 | end) |
| 151 182 | end |
| 152 183 | |
| 153 | - defp attribute_match?(attributes, attribute_name, value) do |
| 184 | + defp attribute_match?(attributes, attribute_name, selector_value) do |
| 154 185 | Enum.find(attributes, fn(attribute) -> |
| 155 186 | { attr_name, attr_value } = attribute |
| 156 187 | |
| 157 | - attr_name == attribute_name && String.contains?(attr_value, value) |
| 188 | + attr_name == attribute_name && value_match?(attr_value, selector_value) |
| 158 189 | end) |
| 159 190 | end |
| 160 191 | |
| @@ -165,6 +196,7 @@ defmodule Floki do | |
| 165 196 | acc = find_by_selector(selector, h, matcher, acc) |
| 166 197 | find_by_selector(selector, t, matcher, acc) |
| 167 198 | end |
| 199 | + # Ignores comments |
| 168 200 | defp find_by_selector(_selector, { :comment, _comment }, _, acc), do: acc |
| 169 201 | defp find_by_selector(selector, node, matcher, acc) when is_tuple(node) do |
| 170 202 | { _, _, child_node } = node |
| @@ -184,8 +216,8 @@ defmodule Floki do | |
| 184 216 | |
| 185 217 | attribute_match?(attributes, attr_name) |
| 186 218 | end) |
| 187 | - |> Enum.reject(fn(x) -> is_nil(x) end) |
| 188 | - |> Enum.map(fn({_attr_name, value}) -> value end) |
| 219 | + |> Enum.reject(fn(x) -> is_nil(x) end) |
| 220 | + |> Enum.map(fn({_attr_name, value}) -> value end) |
| 189 221 | end |
| 190 222 | |
| 191 223 | defp class_matcher(class_name, node, acc) do |
| @@ -217,4 +249,10 @@ defmodule Floki do | |
| 217 249 | |
| 218 250 | acc |
| 219 251 | end |
| 252 | + |
| 253 | + defp value_match?(attribute_value, selector_value) do |
| 254 | + attribute_value |
| 255 | + |> String.split |
| 256 | + |> Enum.any?(fn(x) -> x == selector_value end) |
| 257 | + end |
| 220 258 | end |
| @@ -3,11 +3,11 @@ defmodule Floki.Mixfile do | |
| 3 3 | |
| 4 4 | def project do |
| 5 5 | [app: :floki, |
| 6 | - version: "0.0.4", |
| 7 | - elixir: "~> 1.0.0", |
| 6 | + version: "0.0.5", |
| 7 | + elixir: ">= 1.0.0", |
| 8 8 | package: package, |
| 9 9 | description: description, |
| 10 | - docs: [readme: true, main: "README"], |
| 10 | + docs: [readme: "README.md"], |
| 11 11 | deps: deps] |
| 12 12 | end |