Packages
floki
0.4.0
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
13
files changed
+1093
additions
-220
deletions
| @@ -91,7 +91,7 @@ You can install Floki by adding a dependency to your mix file (mix.exs): | |
| 91 91 | ```elixir |
| 92 92 | defp deps do |
| 93 93 | [ |
| 94 | - {:floki, "~> 0.3"} |
| 94 | + {:floki, "~> 0.4"} |
| 95 95 | ] |
| 96 96 | end |
| 97 97 | ``` |
| @@ -5,9 +5,13 @@ | |
| 5 5 | <<"A HTML parser and searcher.\n\nYou can search inside HTML documents using CSS like selectors.">>}. |
| 6 6 | {<<"elixir">>,<<">= 1.0.0">>}. |
| 7 7 | {<<"files">>, |
| 8 | - [<<"lib/floki.ex">>,<<"lib/floki/deep_text.ex">>,<<"lib/floki/finder.ex">>, |
| 9 | - <<"lib/floki/flat_text.ex">>,<<"lib/floki/matchers.ex">>, |
| 10 | - <<"lib/floki/parser.ex">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}. |
| 8 | + [<<"lib/floki.ex">>,<<"lib/floki/attribute_selector.ex">>, |
| 9 | + <<"lib/floki/combinator.ex">>,<<"lib/floki/deep_text.ex">>, |
| 10 | + <<"lib/floki/finder.ex">>,<<"lib/floki/flat_text.ex">>, |
| 11 | + <<"lib/floki/parser.ex">>,<<"lib/floki/selector.ex">>, |
| 12 | + <<"lib/floki/selector_parser.ex">>,<<"lib/floki/selector_tokenizer.ex">>, |
| 13 | + <<"src/floki_selector_lexer.erl">>,<<"src/floki_selector_lexer.xrl">>, |
| 14 | + <<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}. |
| 11 15 | {<<"licenses">>,[<<"MIT">>]}. |
| 12 16 | {<<"links">>, |
| 13 17 | [{<<"Docs">>,<<"http://hexdocs.pm/floki">>}, |
| @@ -18,4 +22,4 @@ | |
| 18 22 | [{<<"app">>,<<"mochiweb">>}, |
| 19 23 | {<<"optional">>,false}, |
| 20 24 | {<<"requirement">>,<<"~> 2.12.2">>}]}]}. |
| 21 | - {<<"version">>,<<"0.3.3">>}. |
| 25 | + {<<"version">>,<<"0.4.0">>}. |
| @@ -81,18 +81,13 @@ defmodule Floki do | |
| 81 81 | Finds elements inside a HTML tree or string. |
| 82 82 | You can search by class, tag name or id. |
| 83 83 | |
| 84 | - It is possible to compose searches: |
| 85 | - |
| 86 | - Floki.find(html_string, ".class") |
| 87 | - |> Floki.find(".another-class-inside-small-scope") |
| 88 | - |
| 89 84 | ## Examples |
| 90 85 | |
| 91 86 | iex> Floki.find("<p><span class=hint>hello</span></p>", ".hint") |
| 92 87 | [{"span", [{"class", "hint"}], ["hello"]}] |
| 93 88 | |
| 94 | - iex> "<body><div id=important><div>Content</div></div></body>" |> Floki.find("#important") |
| 95 | - {"div", [{"id", "important"}], [{"div", [], ["Content"]}]} |
| 89 | + iex> Floki.find("<body><div id=important><div>Content</div></div></body>", "#important") |
| 90 | + [{"div", [{"id", "important"}], [{"div", [], ["Content"]}]}] |
| 96 91 | |
| 97 92 | iex> Floki.find("<p><a href='https://google.com'>Google</a></p>", "a") |
| 98 93 | [{"a", [{"href", "https://google.com"}], ["Google"]}] |
| @@ -101,49 +96,13 @@ defmodule Floki do | |
| 101 96 | |
| 102 97 | @spec find(binary | html_tree, binary) :: html_tree |
| 103 98 | |
| 99 | + def find(html, selector) when is_binary(html) do |
| 100 | + Floki.Parser.parse(html) |> Finder.find(selector) |
| 101 | + end |
| 104 102 | def find(html, selector) do |
| 105 103 | Finder.find(html, selector) |
| 106 104 | end |
| 107 105 | |
| 108 | - @doc """ |
| 109 | - Returns a list with attribute values for a given selector. |
| 110 | - |
| 111 | - ## Examples |
| 112 | - |
| 113 | - iex> Floki.attribute("<a href='https://google.com'>Google</a>", "a", "href") |
| 114 | - ["https://google.com"] |
| 115 | - |
| 116 | - """ |
| 117 | - |
| 118 | - @spec attribute(binary | html_tree, binary, binary) :: list |
| 119 | - |
| 120 | - def attribute(html, selector, attribute_name) do |
| 121 | - html |
| 122 | - |> find(selector) |
| 123 | - |> Finder.attribute_values(attribute_name) |
| 124 | - end |
| 125 | - |
| 126 | - @doc """ |
| 127 | - Returns a list with attribute values from elements. |
| 128 | - |
| 129 | - ## Examples |
| 130 | - |
| 131 | - iex> Floki.attribute("<a href=https://google.com>Google</a>", "href") |
| 132 | - ["https://google.com"] |
| 133 | - |
| 134 | - """ |
| 135 | - |
| 136 | - @spec attribute(binary | html_tree, binary) :: list |
| 137 | - |
| 138 | - def attribute(html_tree, attribute_name) when is_binary(html_tree) do |
| 139 | - html_tree |
| 140 | - |> parse |
| 141 | - |> Finder.attribute_values(attribute_name) |
| 142 | - end |
| 143 | - def attribute(elements, attribute_name) do |
| 144 | - Finder.attribute_values(elements, attribute_name) |
| 145 | - end |
| 146 | - |
| 147 106 | @doc """ |
| 148 107 | Returns the text nodes from a HTML tree. |
| 149 108 | By default, it will perform a deep search through the HTML tree. |
| @@ -176,4 +135,65 @@ defmodule Floki do | |
| 176 135 | |
| 177 136 | search_strategy.get(html_tree) |
| 178 137 | end |
| 138 | + |
| 139 | + @doc """ |
| 140 | + Returns a list with attribute values for a given selector. |
| 141 | + |
| 142 | + ## Examples |
| 143 | + |
| 144 | + iex> Floki.attribute("<a href='https://google.com'>Google</a>", "a", "href") |
| 145 | + ["https://google.com"] |
| 146 | + |
| 147 | + """ |
| 148 | + |
| 149 | + @spec attribute(binary | html_tree, binary, binary) :: list |
| 150 | + |
| 151 | + def attribute(html, selector, attribute_name) do |
| 152 | + html |
| 153 | + |> find(selector) |
| 154 | + |> attribute_values(attribute_name) |
| 155 | + end |
| 156 | + |
| 157 | + @doc """ |
| 158 | + Returns a list with attribute values from elements. |
| 159 | + |
| 160 | + ## Examples |
| 161 | + |
| 162 | + iex> Floki.attribute("<a href=https://google.com>Google</a>", "href") |
| 163 | + ["https://google.com"] |
| 164 | + |
| 165 | + """ |
| 166 | + |
| 167 | + @spec attribute(binary | html_tree, binary) :: list |
| 168 | + |
| 169 | + def attribute(html_tree, attribute_name) when is_binary(html_tree) do |
| 170 | + html_tree |
| 171 | + |> parse |
| 172 | + |> attribute_values(attribute_name) |
| 173 | + end |
| 174 | + def attribute(elements, attribute_name) do |
| 175 | + attribute_values(elements, attribute_name) |
| 176 | + end |
| 177 | + |
| 178 | + defp attribute_values(element, attr_name) when is_tuple(element) do |
| 179 | + attribute_values([element], attr_name) |
| 180 | + end |
| 181 | + defp attribute_values(elements, attr_name) do |
| 182 | + values = Enum.reduce elements, [], fn({_, attributes, _}, acc) -> |
| 183 | + case attribute_match?(attributes, attr_name) do |
| 184 | + {_attr_name, value} -> |
| 185 | + [value|acc] |
| 186 | + _ -> |
| 187 | + acc |
| 188 | + end |
| 189 | + end |
| 190 | + |
| 191 | + Enum.reverse(values) |
| 192 | + end |
| 193 | + |
| 194 | + defp attribute_match?(attributes, attribute_name) do |
| 195 | + Enum.find attributes, fn({attr_name, _}) -> |
| 196 | + attr_name == attribute_name |
| 197 | + end |
| 198 | + end |
| 179 199 | end |
| @@ -0,0 +1,45 @@ | |
| 1 | + defmodule Floki.AttributeSelector do |
| 2 | + alias Floki.AttributeSelector |
| 3 | + |
| 4 | + defstruct match_type: nil, attribute: nil, value: nil |
| 5 | + |
| 6 | + def match?(attributes, s = %AttributeSelector{match_type: nil, value: nil}) do |
| 7 | + attribute_present?(s.attribute, attributes) |
| 8 | + end |
| 9 | + def match?(attributes, s = %AttributeSelector{match_type: :equal}) do |
| 10 | + get_value(s.attribute, attributes) == s.value |
| 11 | + end |
| 12 | + def match?(attributes, s = %AttributeSelector{match_type: :includes}) do |
| 13 | + value = get_value(s.attribute, attributes) |
| 14 | + |
| 15 | + whitespace_values = String.split(value, " ") |
| 16 | + |
| 17 | + Enum.any? whitespace_values, fn(v) -> v == s.value end |
| 18 | + end |
| 19 | + def match?(attributes, s = %AttributeSelector{match_type: :dash_match}) do |
| 20 | + value = get_value(s.attribute, attributes) |
| 21 | + |
| 22 | + value == s.value || String.starts_with?(value, "#{s.value}-") |
| 23 | + end |
| 24 | + def match?(attributes, s = %AttributeSelector{match_type: :prefix_match}) do |
| 25 | + get_value(s.attribute, attributes) |> String.starts_with?(s.value) |
| 26 | + end |
| 27 | + def match?(attributes, s = %AttributeSelector{match_type: :sufix_match}) do |
| 28 | + get_value(s.attribute, attributes) |> String.ends_with?(s.value) |
| 29 | + end |
| 30 | + def match?(attributes, s = %AttributeSelector{match_type: :substring_match}) do |
| 31 | + get_value(s.attribute, attributes) |> String.contains?(s.value) |
| 32 | + end |
| 33 | + |
| 34 | + defp get_value(attr_name, attributes) do |
| 35 | + {_attr_name, value} = Enum.find attributes, {attr_name, ""}, fn({k, _v}) -> |
| 36 | + k == attr_name |
| 37 | + end |
| 38 | + |
| 39 | + value |
| 40 | + end |
| 41 | + |
| 42 | + defp attribute_present?(name, attributes) do |
| 43 | + Enum.any? attributes, fn({k, _v}) -> k == name end |
| 44 | + end |
| 45 | + end |
| @@ -0,0 +1,3 @@ | |
| 1 | + defmodule Floki.Combinator do |
| 2 | + defstruct match_type: nil, selector: nil |
| 3 | + end |
Loading more files…