Current section

86 Versions

Jump to

Compare versions

4 files changed
+67 additions
-21 deletions
  @@ -3,8 +3,6 @@ Floki
3 3
4 4 [![Build Status](https://travis-ci.org/philss/floki.svg?branch=master)](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