Packages
kreuzcrawl
0.3.0
0.3.0
0.3.0-rc.88
0.3.0-rc.87
0.3.0-rc.86
0.3.0-rc.85
0.3.0-rc.84
0.3.0-rc.83
0.3.0-rc.82
0.3.0-rc.81
0.3.0-rc.80
0.3.0-rc.79
0.3.0-rc.77
0.3.0-rc.72
0.3.0-rc.71
0.3.0-rc.70
0.3.0-rc.55
0.3.0-rc.51
0.3.0-rc.50
0.3.0-rc.49
0.3.0-rc.48
0.3.0-rc.47
0.3.0-rc.46
0.3.0-rc.45
0.3.0-rc.44
0.3.0-rc.43
0.3.0-rc.42
0.3.0-rc.37
0.3.0-rc.19
0.3.0-rc.18
0.3.0-rc.17
0.3.0-rc.15
0.3.0-rc.14
0.3.0-rc.13
0.3.0-rc.12
0.3.0-rc.11
0.3.0-rc.10
0.3.0-rc.9
0.3.0-rc.8
0.3.0-rc.7
0.3.0-rc.3
0.3.0-rc.2
0.2.0
0.1.2
0.1.1
0.1.0-rc.9
0.1.0-rc.8
0.1.0-rc.7
0.1.0-rc.6
0.1.0-rc.5
0.1.0-rc.2
High-performance web crawling engine
Current section
Files
Jump to
Current section
Files
lib/kreuzcrawl.ex
# This file is auto-generated by alef — DO NOT EDIT.
# alef:hash:c71da313df7b9a37bdd4f997f05150b92dfa5e723d3e2a4db0adafea6f951837
# To regenerate: alef generate
# To verify freshness: alef verify --exit-code
defmodule Kreuzcrawl do
@moduledoc "High-level API for kreuzcrawl"
@doc "Convert markdown links to numbered citations."
@spec generate_citations(String.t()) :: map()
def generate_citations(markdown) do
Kreuzcrawl.Native.generate_citations(markdown)
end
@doc "Create a new crawl engine with the given configuration."
@spec create_engine() :: {:ok, reference()} | {:error, atom, String.t()}
def create_engine do
Kreuzcrawl.Native.create_engine(nil)
end
@doc "Create a new crawl engine with the given configuration."
@spec create_engine(String.t() | nil) :: {:ok, reference()} | {:error, atom, String.t()}
def create_engine(config) do
Kreuzcrawl.Native.create_engine(config)
end
@doc "Scrape a single URL, returning extracted page data."
@spec scrape_async(reference(), String.t()) :: {:ok, map()} | {:error, atom, String.t()}
def scrape_async(engine, url) do
Kreuzcrawl.Native.scrape_async(engine, url)
end
@doc "Crawl a website starting from `url`, following links up to the configured depth."
@spec crawl_async(reference(), String.t()) :: {:ok, map()} | {:error, atom, String.t()}
def crawl_async(engine, url) do
Kreuzcrawl.Native.crawl_async(engine, url)
end
@doc "Discover all pages on a website by following links and sitemaps."
@spec map_urls_async(reference(), String.t()) :: {:ok, map()} | {:error, atom, String.t()}
def map_urls_async(engine, url) do
Kreuzcrawl.Native.map_urls_async(engine, url)
end
@doc "Execute browser actions on a single page."
@spec interact_async(reference(), String.t(), [map()]) ::
{:ok, map()} | {:error, atom, String.t()}
def interact_async(engine, url, actions) do
Kreuzcrawl.Native.interact_async(engine, url, Jason.encode!(Enum.map(actions, &encode_page_action/1)))
end
@doc "Scrape multiple URLs concurrently."
@spec batch_scrape_async(reference(), [String.t()]) :: {:ok, map()} | {:error, atom, String.t()}
def batch_scrape_async(engine, urls) do
Kreuzcrawl.Native.batch_scrape_async(engine, urls)
end
@doc "Crawl multiple seed URLs concurrently, each following links to configured depth."
@spec batch_crawl_async(reference(), [String.t()]) :: {:ok, map()} | {:error, atom, String.t()}
def batch_crawl_async(engine, urls) do
Kreuzcrawl.Native.batch_crawl_async(engine, urls)
end
@doc false
def crawlenginehandle_crawl_stream_start(client, req) do
Kreuzcrawl.Native.crawlenginehandle_crawl_stream_start(client, req)
end
@doc false
def crawlenginehandle_crawl_stream_next(handle) do
Kreuzcrawl.Native.crawlenginehandle_crawl_stream_next(handle)
end
@doc "Streaming `crawl_stream` — returns an `Enumerable` of decoded chunk maps."
def crawl_stream(client, req) do
req_json =
case req do
nil -> nil
s when is_binary(s) -> s
other -> Jason.encode!(other)
end
case Kreuzcrawl.Native.crawlenginehandle_crawl_stream_start(client, req_json) do
{:ok, handle} ->
stream =
Stream.unfold(handle, fn h ->
case Kreuzcrawl.Native.crawlenginehandle_crawl_stream_next(h) do
{:ok, nil} ->
nil
{:ok, chunk_json} ->
{Jason.decode!(chunk_json, keys: :atoms), h}
{:error, reason} ->
raise Kreuzcrawl.StreamError,
message: "crawl_stream stream failed: #{inspect(reason)}",
reason: reason,
adapter: :crawl_stream
end
end)
{:ok, stream}
{:error, reason} ->
{:error, reason}
end
end
@doc false
def crawlenginehandle_batch_crawl_stream_start(client, req) do
Kreuzcrawl.Native.crawlenginehandle_batch_crawl_stream_start(client, req)
end
@doc false
def crawlenginehandle_batch_crawl_stream_next(handle) do
Kreuzcrawl.Native.crawlenginehandle_batch_crawl_stream_next(handle)
end
@doc "Streaming `batch_crawl_stream` — returns an `Enumerable` of decoded chunk maps."
def batch_crawl_stream(client, req) do
req_json =
case req do
nil -> nil
s when is_binary(s) -> s
other -> Jason.encode!(other)
end
case Kreuzcrawl.Native.crawlenginehandle_batch_crawl_stream_start(client, req_json) do
{:ok, handle} ->
stream =
Stream.unfold(handle, fn h ->
case Kreuzcrawl.Native.crawlenginehandle_batch_crawl_stream_next(h) do
{:ok, nil} ->
nil
{:ok, chunk_json} ->
{Jason.decode!(chunk_json, keys: :atoms), h}
{:error, reason} ->
raise Kreuzcrawl.StreamError,
message: "batch_crawl_stream stream failed: #{inspect(reason)}",
reason: reason,
adapter: :batch_crawl_stream
end
end)
{:ok, stream}
{:error, reason} ->
{:error, reason}
end
end
defp encode_page_action({:click, %{} = data}) do
data
|> Enum.reduce(%{}, fn {k, v}, acc ->
key =
case k do
k when is_atom(k) -> Atom.to_string(k)
k when is_binary(k) -> k
end
Map.put(acc, key, v)
end)
|> Map.put("type", "click")
end
defp encode_page_action({:type_text, %{} = data}) do
data
|> Enum.reduce(%{}, fn {k, v}, acc ->
key =
case k do
k when is_atom(k) -> Atom.to_string(k)
k when is_binary(k) -> k
end
Map.put(acc, key, v)
end)
|> Map.put("type", "type")
end
defp encode_page_action({:press, %{} = data}) do
data
|> Enum.reduce(%{}, fn {k, v}, acc ->
key =
case k do
k when is_atom(k) -> Atom.to_string(k)
k when is_binary(k) -> k
end
Map.put(acc, key, v)
end)
|> Map.put("type", "press")
end
defp encode_page_action({:scroll, %{} = data}) do
data
|> Enum.reduce(%{}, fn {k, v}, acc ->
key =
case k do
k when is_atom(k) -> Atom.to_string(k)
k when is_binary(k) -> k
end
Map.put(acc, key, v)
end)
|> Map.put("type", "scroll")
end
defp encode_page_action({:wait, %{} = data}) do
data
|> Enum.reduce(%{}, fn {k, v}, acc ->
key =
case k do
k when is_atom(k) -> Atom.to_string(k)
k when is_binary(k) -> k
end
Map.put(acc, key, v)
end)
|> Map.put("type", "wait")
end
defp encode_page_action({:screenshot, %{} = data}) do
data
|> Enum.reduce(%{}, fn {k, v}, acc ->
key =
case k do
:full_page -> "fullPage"
k when is_atom(k) -> Atom.to_string(k)
k when is_binary(k) -> k
end
Map.put(acc, key, v)
end)
|> Map.put("type", "screenshot")
end
defp encode_page_action({:execute_js, %{} = data}) do
data
|> Enum.reduce(%{}, fn {k, v}, acc ->
key =
case k do
k when is_atom(k) -> Atom.to_string(k)
k when is_binary(k) -> k
end
Map.put(acc, key, v)
end)
|> Map.put("type", "executeJs")
end
defp encode_page_action(:scrape), do: %{"type" => "scrape"}
defp encode_page_action({:scrape, _}), do: %{"type" => "scrape"}
defp encode_page_action(%{} = m), do: m
defp encode_page_action(other),
do: raise(ArgumentError, "expected PageAction (atom, {atom, map}, or map), got: " <> inspect(other))
end
defmodule Kreuzcrawl.StreamError do
@moduledoc false
defexception [:message, :reason, :adapter]
@impl true
def message(%__MODULE__{message: msg}), do: msg
end