capture_page 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +13 -0
- data/lib/capture.rb +83 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3217118ed9497ccaaff219c4b5fd9345320790d99d88d252d14f363921832a4e
|
|
4
|
+
data.tar.gz: 3c96668a060f2771e1210583c2e9aeb65fbf75b9d508d5f475c1fc4098cdd6f7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cf1928267a630bc2f2e26cf5fc2db0443c995e523641f86ab8816038a52a5d388c0093973d3a33f97dbf5c509e6f054575cea7b2b5658575d46e96245338a1a8
|
|
7
|
+
data.tar.gz: 3b62f1f2d90d24af537bd56fa87a1303eef91bb3e3e5355e2ac7a2e4027d797d46220ebf6a8c9bad97bde5440ac234497ce5e7b1441d651e5f94741bd109a1da
|
data/README.md
CHANGED
|
@@ -34,6 +34,7 @@ puts image_url
|
|
|
34
34
|
- **Content Extraction**: Extract HTML and text content from web pages
|
|
35
35
|
- **Metadata Extraction**: Get page metadata (title, description, og tags, etc.)
|
|
36
36
|
- **Animated GIFs**: Create animated GIFs of page interactions
|
|
37
|
+
- **Browser Sessions**: Create stateful browser sessions and run actions
|
|
37
38
|
- **Zero Dependencies**: Uses only Ruby standard library
|
|
38
39
|
|
|
39
40
|
## Usage
|
|
@@ -132,6 +133,18 @@ gif_data = client.fetch_animated("https://example.com")
|
|
|
132
133
|
File.binwrite("animation.gif", gif_data)
|
|
133
134
|
```
|
|
134
135
|
|
|
136
|
+
### Browser Sessions
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
session = client.create_session("maxTtlSeconds" => 300)
|
|
140
|
+
session_id = session["session"]["id"]
|
|
141
|
+
|
|
142
|
+
client.execute_action(session_id, "goto", "url" => "https://example.com")
|
|
143
|
+
screenshot = client.execute_action(session_id, "screenshot", "fullPage" => true)
|
|
144
|
+
|
|
145
|
+
client.close_session(session_id)
|
|
146
|
+
```
|
|
147
|
+
|
|
135
148
|
## Configuration Options
|
|
136
149
|
|
|
137
150
|
### Constructor Options
|
data/lib/capture.rb
CHANGED
|
@@ -5,6 +5,17 @@ require "net/http"
|
|
|
5
5
|
require "uri"
|
|
6
6
|
require "json"
|
|
7
7
|
|
|
8
|
+
class CaptureSessionsError < StandardError
|
|
9
|
+
attr_reader :status, :body
|
|
10
|
+
|
|
11
|
+
def initialize(status, body)
|
|
12
|
+
message = body.is_a?(Hash) && body["error"].is_a?(String) ? body["error"] : "Capture Sessions API request failed with status #{status}"
|
|
13
|
+
super(message)
|
|
14
|
+
@status = status
|
|
15
|
+
@body = body
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
8
19
|
class Capture
|
|
9
20
|
API_URL = "https://cdn.capture.page"
|
|
10
21
|
EDGE_URL = "https://edge.capture.page"
|
|
@@ -59,6 +70,28 @@ class Capture
|
|
|
59
70
|
fetch_binary(build_animated_url(url, options))
|
|
60
71
|
end
|
|
61
72
|
|
|
73
|
+
def create_session(options = {})
|
|
74
|
+
options = options.nil? ? {} : options
|
|
75
|
+
raise TypeError, "options must be a Hash" unless options.is_a?(Hash)
|
|
76
|
+
|
|
77
|
+
sessions_request("", :post, options)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def get_session(session_id)
|
|
81
|
+
sessions_request("/#{escape_path(session_id)}", :get)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def close_session(session_id)
|
|
85
|
+
sessions_request("/#{escape_path(session_id)}", :delete)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def execute_action(session_id, action_type, payload = {})
|
|
89
|
+
payload = payload.nil? ? {} : payload
|
|
90
|
+
raise TypeError, "payload must be a Hash" unless payload.is_a?(Hash)
|
|
91
|
+
|
|
92
|
+
sessions_request("/#{escape_path(session_id)}/actions", :post, "type" => action_type, "payload" => payload)
|
|
93
|
+
end
|
|
94
|
+
|
|
62
95
|
private
|
|
63
96
|
|
|
64
97
|
def build_url(url, request_type, options)
|
|
@@ -110,4 +143,54 @@ class Capture
|
|
|
110
143
|
|
|
111
144
|
JSON.parse(response.body)
|
|
112
145
|
end
|
|
146
|
+
|
|
147
|
+
def sessions_bearer_token
|
|
148
|
+
raise TypeError, "key and secret must be strings" unless @key.is_a?(String) && @secret.is_a?(String)
|
|
149
|
+
raise ArgumentError, "Key and Secret is required" if @key.empty? || @secret.empty?
|
|
150
|
+
|
|
151
|
+
["#{@key}:#{@secret}"].pack("m0")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def session_url(path = "")
|
|
155
|
+
"#{EDGE_URL}/v1/sessions#{path}"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def sessions_request(path, method, body = nil)
|
|
159
|
+
uri = URI.parse(session_url(path))
|
|
160
|
+
request = case method
|
|
161
|
+
when :post then Net::HTTP::Post.new(uri)
|
|
162
|
+
when :get then Net::HTTP::Get.new(uri)
|
|
163
|
+
when :delete then Net::HTTP::Delete.new(uri)
|
|
164
|
+
else raise ArgumentError, "unsupported sessions request method"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
request["Authorization"] = "Bearer #{sessions_bearer_token}"
|
|
168
|
+
unless body.nil?
|
|
169
|
+
request["Content-Type"] = "application/json"
|
|
170
|
+
request.body = JSON.generate(body)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
|
|
174
|
+
http.request(request)
|
|
175
|
+
end
|
|
176
|
+
response_body = parse_json_response(response.body)
|
|
177
|
+
|
|
178
|
+
raise CaptureSessionsError.new(response.code.to_i, response_body) unless response.is_a?(Net::HTTPSuccess)
|
|
179
|
+
|
|
180
|
+
response_body
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def parse_json_response(body)
|
|
184
|
+
return {} if body.nil? || body.empty?
|
|
185
|
+
|
|
186
|
+
JSON.parse(body)
|
|
187
|
+
rescue JSON::ParserError
|
|
188
|
+
{}
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def escape_path(value)
|
|
192
|
+
raise ArgumentError, "session_id is required" if value.nil? || value.to_s.empty?
|
|
193
|
+
|
|
194
|
+
URI.encode_www_form_component(value.to_s)
|
|
195
|
+
end
|
|
113
196
|
end
|