resilientlink 1.0.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 +7 -0
- data/README.md +83 -0
- data/lib/resilientlink.rb +117 -0
- metadata +46 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: dba098b1c03af355a388cf7f641024563505947a79641f22bc3e18a765dd1395
|
|
4
|
+
data.tar.gz: bff0570aea14931fa8b7380901b1862523352cfb9ec8cb592ac3f6acada8dee9
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 99289b1ea9a89b2329e20aba7b0fd9eb3db042481ba8718a6cc4d1ecd46985222fe90a93fa68a132918ce111fdcdfc11323d00d8c6dad119b0346301d5562f36
|
|
7
|
+
data.tar.gz: 5a1ab1c3ae48d811101a733bb93f102a9075ee6bc795460e15305f1827d906c649b20608ba7cadc76e11de9d5f0cd600678963d4f4ddb34b1b0cf471d511340c
|
data/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# ResilientLink Ruby SDK
|
|
2
|
+
|
|
3
|
+
Official Ruby gem for the [ResilientLink](https://resilientlink.silentgode.com) Web Scraping API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gem install resilientlink
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or add to your `Gemfile`:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'resilientlink'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
require 'resilientlink'
|
|
21
|
+
|
|
22
|
+
client = Resilientlink::Client.new(api_key: 'YOUR_API_KEY')
|
|
23
|
+
|
|
24
|
+
result = client.scrape('https://example.com')
|
|
25
|
+
|
|
26
|
+
puts result['data']['title'] # "Example Domain"
|
|
27
|
+
puts result['data']['description'] # meta description
|
|
28
|
+
puts result['data']['image'] # OG image URL
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Options
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
result = client.scrape('https://example.com',
|
|
35
|
+
return_html: true, # include raw HTML
|
|
36
|
+
screenshot: true, # base64 PNG (Pro/Enterprise)
|
|
37
|
+
pdf: true, # base64 PDF (Pro/Enterprise)
|
|
38
|
+
pdf_format: 'A4',
|
|
39
|
+
bypass_cache: true, # force fresh scrape
|
|
40
|
+
js_render: true, # JS rendering (Pro/Enterprise)
|
|
41
|
+
wait_for_selector: '#app', # wait for CSS selector
|
|
42
|
+
wait_ms: 2000, # wait 2s before scraping
|
|
43
|
+
custom_headers: { 'Accept-Language' => 'en-US' },
|
|
44
|
+
timeout: 30_000 # ms (max 60000)
|
|
45
|
+
)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Response
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
{
|
|
52
|
+
'success' => true,
|
|
53
|
+
'cached' => false,
|
|
54
|
+
'tier' => '...',
|
|
55
|
+
'responseTime' => 412,
|
|
56
|
+
'data' => {
|
|
57
|
+
'url' => 'https://example.com',
|
|
58
|
+
'title' => 'Example Domain',
|
|
59
|
+
'description' => '...',
|
|
60
|
+
'image' => '...',
|
|
61
|
+
'domain' => 'example.com',
|
|
62
|
+
'og' => { 'title' => '...', 'description' => '...' },
|
|
63
|
+
'content' => { 'wordCount' => 423, 'readTimeMinutes' => 2 },
|
|
64
|
+
'scrapedAt' => '2026-...'
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Error Handling
|
|
70
|
+
|
|
71
|
+
```ruby
|
|
72
|
+
begin
|
|
73
|
+
result = client.scrape('https://example.com')
|
|
74
|
+
rescue Resilientlink::Error => e
|
|
75
|
+
puts e.message # human-readable error
|
|
76
|
+
puts e.status_code # 429 = rate limit, 401 = bad key, 451 = blocked
|
|
77
|
+
puts e.body # full response hash
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Get Your API Key
|
|
82
|
+
|
|
83
|
+
Sign up at [resilientlink](https://resilientlink.silentgode.com) → Dashboard → API Key.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
module Resilientlink
|
|
8
|
+
VERSION = '1.0.0'
|
|
9
|
+
DEFAULT_URL = 'https://api.resilientlink.silentgode.com'
|
|
10
|
+
DEFAULT_TIMEOUT = 60
|
|
11
|
+
|
|
12
|
+
# Raised when the ResilientLink API returns an error.
|
|
13
|
+
class Error < StandardError
|
|
14
|
+
attr_reader :status_code, :body
|
|
15
|
+
|
|
16
|
+
def initialize(message, status_code: nil, body: nil)
|
|
17
|
+
super(message)
|
|
18
|
+
@status_code = status_code
|
|
19
|
+
@body = body
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Client
|
|
24
|
+
# @param api_key [String] Your ResilientLink API key (required)
|
|
25
|
+
# @param base_url [String] Override API base URL
|
|
26
|
+
# @param timeout [Integer] Request timeout in seconds (default: 60)
|
|
27
|
+
def initialize(api_key:, base_url: DEFAULT_URL, timeout: DEFAULT_TIMEOUT)
|
|
28
|
+
raise ArgumentError, 'api_key is required.' if api_key.nil? || api_key.empty?
|
|
29
|
+
|
|
30
|
+
@api_key = api_key
|
|
31
|
+
@base_url = base_url.chomp('/')
|
|
32
|
+
@timeout = timeout
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Scrape a URL and return structured metadata.
|
|
36
|
+
#
|
|
37
|
+
# @param url [String] The URL to scrape (required)
|
|
38
|
+
# @param opts [Hash] Scrape options:
|
|
39
|
+
# :return_html [Boolean] Include raw HTML in response
|
|
40
|
+
# :screenshot [Boolean] Return base64 PNG screenshot (Pro/Enterprise)
|
|
41
|
+
# :full_page [Boolean] Full-page screenshot (default: true)
|
|
42
|
+
# :pdf [Boolean] Return base64 PDF (Pro/Enterprise)
|
|
43
|
+
# :pdf_format [String] 'A4', 'Letter', etc. (default: 'A4')
|
|
44
|
+
# :pdf_background [Boolean] Include background (default: true)
|
|
45
|
+
# :pdf_landscape [Boolean] Landscape PDF
|
|
46
|
+
# :bypass_cache [Boolean] Skip cache, force fresh scrape
|
|
47
|
+
# :js_render [Boolean] Enable JS rendering (Pro/Enterprise)
|
|
48
|
+
# :wait_for_selector [String] CSS selector to wait for
|
|
49
|
+
# :wait_until [String] 'networkidle0', 'load', 'domcontentloaded'
|
|
50
|
+
# :wait_ms [Integer] Extra ms to wait before scraping
|
|
51
|
+
# :custom_headers [Hash] HTTP headers to forward
|
|
52
|
+
# :custom_js [String] JavaScript to execute (Enterprise)
|
|
53
|
+
# :return_cookies [Boolean] Return cookies from the page
|
|
54
|
+
# :block_resources [Array] Resource types to block: ['media','font']
|
|
55
|
+
# :timeout [Integer] Per-request timeout in ms (max 60000)
|
|
56
|
+
#
|
|
57
|
+
# @return [Hash] Scrape result
|
|
58
|
+
# @raise [Resilientlink::Error]
|
|
59
|
+
def scrape(url, **opts)
|
|
60
|
+
raise ArgumentError, 'url is required.' if url.nil? || url.empty?
|
|
61
|
+
|
|
62
|
+
body = { url: url }
|
|
63
|
+
body[:return_html] = opts[:return_html] unless opts[:return_html].nil?
|
|
64
|
+
body[:screenshot] = opts[:screenshot] unless opts[:screenshot].nil?
|
|
65
|
+
body[:full_page] = opts[:full_page] unless opts[:full_page].nil?
|
|
66
|
+
body[:pdf] = opts[:pdf] unless opts[:pdf].nil?
|
|
67
|
+
body[:pdf_format] = opts[:pdf_format] unless opts[:pdf_format].nil?
|
|
68
|
+
body[:pdf_background] = opts[:pdf_background] unless opts[:pdf_background].nil?
|
|
69
|
+
body[:pdf_landscape] = opts[:pdf_landscape] unless opts[:pdf_landscape].nil?
|
|
70
|
+
body[:bypass_cache] = opts[:bypass_cache] unless opts[:bypass_cache].nil?
|
|
71
|
+
body[:js_render] = opts[:js_render] unless opts[:js_render].nil?
|
|
72
|
+
body[:wait_for_selector] = opts[:wait_for_selector] unless opts[:wait_for_selector].nil?
|
|
73
|
+
body[:wait_until] = opts[:wait_until] unless opts[:wait_until].nil?
|
|
74
|
+
body[:wait_ms] = opts[:wait_ms] unless opts[:wait_ms].nil?
|
|
75
|
+
body[:custom_headers] = opts[:custom_headers] unless opts[:custom_headers].nil?
|
|
76
|
+
body[:custom_js] = opts[:custom_js] unless opts[:custom_js].nil?
|
|
77
|
+
body[:return_cookies] = opts[:return_cookies] unless opts[:return_cookies].nil?
|
|
78
|
+
body[:block_resources] = opts[:block_resources] unless opts[:block_resources].nil?
|
|
79
|
+
body[:timeout] = opts[:timeout] unless opts[:timeout].nil?
|
|
80
|
+
|
|
81
|
+
request('POST', '/api/scrape', body)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
private
|
|
85
|
+
|
|
86
|
+
def request(method, path, body = {})
|
|
87
|
+
uri = URI.parse(@base_url + path)
|
|
88
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
89
|
+
http.use_ssl = uri.scheme == 'https'
|
|
90
|
+
http.read_timeout = @timeout
|
|
91
|
+
http.open_timeout = 10
|
|
92
|
+
|
|
93
|
+
payload = body.to_json
|
|
94
|
+
|
|
95
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
|
96
|
+
req['X-API-Key'] = @api_key
|
|
97
|
+
req['Content-Type'] = 'application/json'
|
|
98
|
+
req['User-Agent'] = "resilientlink-ruby/#{VERSION}"
|
|
99
|
+
req['Content-Length'] = payload.bytesize.to_s
|
|
100
|
+
req.body = payload
|
|
101
|
+
|
|
102
|
+
response = http.request(req)
|
|
103
|
+
parsed = JSON.parse(response.body) rescue { 'raw' => response.body }
|
|
104
|
+
|
|
105
|
+
if response.code.to_i >= 400
|
|
106
|
+
message = parsed['error'] || "HTTP #{response.code}"
|
|
107
|
+
raise Error.new(message, status_code: response.code.to_i, body: parsed)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
parsed
|
|
111
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
112
|
+
raise Error.new("Request timed out: #{e.message}")
|
|
113
|
+
rescue SocketError, Errno::ECONNREFUSED => e
|
|
114
|
+
raise Error.new("Connection failed: #{e.message}")
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: resilientlink
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Silent God Enterprise
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-08 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: ResilientLink delivers structured metadata from any URL with sub-second
|
|
14
|
+
latency — Residential proxy infrastructure, intelligent caching, and an API engineered
|
|
15
|
+
for enterprise scale.
|
|
16
|
+
email: resilientlink@silentgode.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- README.md
|
|
22
|
+
- lib/resilientlink.rb
|
|
23
|
+
homepage: https://resilientlink.silentgode.com
|
|
24
|
+
licenses:
|
|
25
|
+
- MIT
|
|
26
|
+
metadata: {}
|
|
27
|
+
post_install_message:
|
|
28
|
+
rdoc_options: []
|
|
29
|
+
require_paths:
|
|
30
|
+
- lib
|
|
31
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
32
|
+
requirements:
|
|
33
|
+
- - ">="
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: 2.6.0
|
|
36
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
requirements: []
|
|
42
|
+
rubygems_version: 3.4.19
|
|
43
|
+
signing_key:
|
|
44
|
+
specification_version: 4
|
|
45
|
+
summary: Official Ruby SDK for the ResilientLink Web Scraping API
|
|
46
|
+
test_files: []
|