scrapeunblocker 0.2.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 52fcc1782b1ae470a517b26515674f570071abefa9183613f772f54c002cd050
4
- data.tar.gz: 688d1524ef190967afcd2272e7ca6cc17fc0a36417fec75a839f0cc2b607fde7
3
+ metadata.gz: fbe2fc1ce79fbf6b9293fe17403a6293599bf6cb04fe775d3596537a40fd5acb
4
+ data.tar.gz: 4b9dce4731d710996fe0796766bc67829d1cdd5753e6a812f3fc74796cdf7c48
5
5
  SHA512:
6
- metadata.gz: f60f1a37fcf62e63fe8195904a50464d7a06f6a1fcc4aac3cffe435fe5a9af0c7fa6de4a89ea0dc0658ed7e7c527f7b731bcb14389e5581913d1173f893f505d
7
- data.tar.gz: c044299c381b979ad904cf1e0ecb67687e5e93ed91b44f4bc06af06bcf11cd11bda3e7bbabe08c3a0e63cac3474b24b5384141fe2999c9e5f06b548905f84e77
6
+ metadata.gz: bca9218a5cee0b1bff9b9160860cc2d1cdd69e0b115abf3db35d449d005828e1f52099395f9d9ff0072ee4b509155cdc9088690d947a964179a3cb57823fffe2
7
+ data.tar.gz: 14a9b61b2f89d2bd23d2356a7fdc35fe16c075862251173aa29cb2463633705132f19f4f87a8e76e7dd637d7f955f23078151389007c0a6860f8b21bddc6a3a0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 (2026-09-16)
4
+
5
+ - Added `southwest.flights(origin:, dest:, depart_date:, ...)` for the new Southwest Airlines plugin (`POST /flights/southwest-quotes`), reached through the new `su.southwest` namespace and mirroring `su.skyscanner`. `origin` and `dest` are IATA airport codes; `depart_date` and the optional `return_date` are `YYYY-MM-DD` (omit `return_date` for a one-way search). Optional `adults` (1-8, default 1), `fare_type` (`"dollars"` default or `"points"`), `proxy_country` (default `"US"`) and `max_attempts` (1-5, default 3). Returns the raw booking / shopping JSON as a Hash.
6
+
7
+ No breaking changes.
8
+
9
+ ## 0.3.0 (2026-09-08)
10
+
11
+ - Added `tiktok_profile`, `tiktok_video`, `tiktok_hashtag`, `tiktok_search` and `tiktok_comments` for the new TikTok plugin: a creator's exact follower / like / video counts with their newest videos (up to 200), any video or photo post with exact plays, likes, comments, shares, saves and reposts, hashtags, music, play / download URLs, subtitle tracks and an optional transcript, a hashtag's total views and videos with its videos, keyword search in TikTok's own ranking, and the comments of any post. No login.
12
+
3
13
  ## 0.2.1 (2026-09-02)
4
14
 
5
15
  - Added `meta_ad_library(advertiser, ...)` for the new Meta Ad Library plugin (`POST /ads/meta-ad-library`) - returns an advertiser's Meta (Facebook) Ad Library ads as a Hash. `advertiser` is required; optional filters `country`, `active_status`, `media_type` and `max_ads` are dropped when unset so the API applies its own defaults.
data/README.md CHANGED
@@ -178,6 +178,19 @@ end
178
178
 
179
179
  `marketplace` is any of the 19 regional eBay hosts (`ebay.com` default). `condition` is one of `"new"`, `"open_box"`, `"refurbished"`, `"used"` or `"for_parts"`; `sort` is one of `"best_match"` (default), `"newly_listed"`, `"ending_soon"`, `"price_asc"` or `"price_desc"`; `page_size` is 60, 120 or 240. `exactMatches` is `false` when eBay found nothing for the keyword and answered with its own loosely-related suggestions instead, so check it before using the listings.
180
180
 
181
+ ### TikTok
182
+
183
+ ```ruby
184
+ profile = su.tiktok_profile("nasa", max_videos: 5) # exact stats + newest videos
185
+ video = su.tiktok_video("https://www.tiktok.com/@nasa/video/7665075736742530317", include_transcript: true)
186
+ tag = su.tiktok_hashtag("nasa", max_videos: 10)
187
+ results = su.tiktok_search("space telescope", max_results: 25) # TikTok's own ranking
188
+ comments = su.tiktok_comments("https://www.tiktok.com/@nasa/video/7665075736742530317", max_comments: 40)
189
+ puts profile["stats"]["followers"], video["stats"]["plays"], tag["stats"]["views"], comments["totalComments"]
190
+ ```
191
+
192
+ Profiles and hashtags list up to 10 videos in a couple of seconds from TikTok's server-rendered widget; ask for more (up to 200) and the real grid is scrolled in a browser session.
193
+
181
194
  ## Cookies and the serving proxy
182
195
 
183
196
  ```ruby
@@ -210,6 +223,25 @@ hotels = su.skyscanner.hotels(destination: "Madrid", checkin: "2026-09-01", chec
210
223
  cars = su.skyscanner.carhire(pickup: "Madrid", pickup_datetime: "2026-09-01T10:00", dropoff_datetime: "2026-09-03T10:00")
211
224
  ```
212
225
 
226
+ ## Southwest plugin
227
+
228
+ Southwest Airlines flight quotes as JSON. `origin` and `dest` are IATA airport codes; omit `return_date` for a one-way search.
229
+
230
+ ```ruby
231
+ flights = su.southwest.flights(
232
+ origin: "DAL", dest: "HOU",
233
+ depart_date: "2026-10-20", return_date: "2026-10-27"
234
+ )
235
+
236
+ # One-way, priced in Rapid Rewards points
237
+ oneway = su.southwest.flights(
238
+ origin: "DAL", dest: "HOU",
239
+ depart_date: "2026-10-20", adults: 2, fare_type: "points"
240
+ )
241
+ ```
242
+
243
+ `adults` is 1-8 (default 1), `fare_type` is `"dollars"` (default) or `"points"`, `proxy_country` defaults to `"US"` and `max_attempts` is 1-5 (default 3). The call returns the raw booking / shopping JSON.
244
+
213
245
  ## Error handling
214
246
 
215
247
  Non-2xx responses raise typed errors, all subclasses of `ScrapeUnblocker::Error`.
@@ -268,7 +300,7 @@ end
268
300
 
269
301
  When more than one applies, the most serious wins: failed payment outranks credit limit, which outranks quota. All three lift by themselves once the billing state changes - access returns within about a minute, and the API key stays the same. One catch worth knowing: subscribing to a new plan does **not** clear `PaymentFailedError`, because the old unpaid invoice stays open until it is paid.
270
302
 
271
- Full details for every status code: [developers.scrapeunblocker.com/errors](https://developers.scrapeunblocker.com/errors).
303
+ Full details for every status code: [docs.scrapeunblocker.com/errors](https://docs.scrapeunblocker.com/errors).
272
304
 
273
305
  ## Configuration
274
306
 
@@ -283,7 +315,7 @@ ScrapeUnblocker::Client.new(
283
315
 
284
316
  ## Links
285
317
 
286
- - Documentation: [developers.scrapeunblocker.com](https://developers.scrapeunblocker.com?utm_source=rubygems&utm_medium=integration&utm_campaign=ruby-sdk)
318
+ - Documentation: [docs.scrapeunblocker.com](https://docs.scrapeunblocker.com?utm_source=rubygems&utm_medium=integration&utm_campaign=ruby-sdk)
287
319
  - Website: [scrapeunblocker.com](https://scrapeunblocker.com?utm_source=rubygems&utm_medium=integration&utm_campaign=ruby-sdk)
288
320
  - Dashboard: [app.scrapeunblocker.com](https://app.scrapeunblocker.com?utm_source=rubygems&utm_medium=integration&utm_campaign=ruby-sdk)
289
321
 
@@ -7,6 +7,7 @@ require "json"
7
7
  require_relative "errors"
8
8
  require_relative "parsed_page"
9
9
  require_relative "skyscanner"
10
+ require_relative "southwest"
10
11
  require_relative "version"
11
12
 
12
13
  module ScrapeUnblocker
@@ -22,6 +23,9 @@ module ScrapeUnblocker
22
23
  # @return [Skyscanner] the Skyscanner plugin endpoints
23
24
  attr_reader :skyscanner
24
25
 
26
+ # @return [Southwest] the Southwest Airlines plugin endpoints
27
+ attr_reader :southwest
28
+
25
29
  def initialize(api_key: nil, base_url: DEFAULT_BASE_URL, timeout: 180, max_retries: 2, transport: nil)
26
30
  @api_key = api_key || ENV["SCRAPEUNBLOCKER_KEY"]
27
31
  if @api_key.nil? || @api_key.empty?
@@ -34,6 +38,7 @@ module ScrapeUnblocker
34
38
  @max_retries = max_retries
35
39
  @transport = transport || method(:net_http_transport)
36
40
  @skyscanner = Skyscanner.new(self)
41
+ @southwest = Southwest.new(self)
37
42
  end
38
43
 
39
44
  # Fetch a URL and return the fully rendered HTML.
@@ -190,6 +195,53 @@ module ScrapeUnblocker
190
195
  end
191
196
 
192
197
  # @api private
198
+ # Scrape a public TikTok creator profile and its newest videos.
199
+ #
200
+ # Returns the exact follower, following, like and video counts, bio, bio
201
+ # link, verified / private / organization / seller flags, avatar and a
202
+ # +videos+ array of the creator's newest posts (up to 200), each in the
203
+ # full #tiktok_video shape. No login.
204
+ def tiktok_profile(username, max_videos: 10, video_details: true, proxy_country: nil)
205
+ post_json("/social/tiktok-profile",
206
+ username: username, max_videos: max_videos,
207
+ video_details: video_details ? nil : false,
208
+ proxy_country: proxy_country)
209
+ end
210
+
211
+ # Scrape one TikTok video or photo post: exact plays, likes, comments,
212
+ # shares, saves and reposts, hashtags, mentions, author, music, play /
213
+ # download URLs, subtitle tracks and, with +include_transcript+, the
214
+ # transcript as text.
215
+ def tiktok_video(url, include_transcript: false, transcript_language: nil, proxy_country: nil)
216
+ post_json("/social/tiktok-video",
217
+ url: url, include_transcript: include_transcript ? true : nil,
218
+ transcript_language: transcript_language, proxy_country: proxy_country)
219
+ end
220
+
221
+ # Scrape a TikTok hashtag: total views and videos plus its videos (up to 200).
222
+ def tiktok_hashtag(hashtag, max_videos: 10, video_details: true, proxy_country: nil)
223
+ post_json("/social/tiktok-hashtag",
224
+ hashtag: hashtag, max_videos: max_videos,
225
+ video_details: video_details ? nil : false,
226
+ proxy_country: proxy_country)
227
+ end
228
+
229
+ # Search TikTok videos by keyword. Runs in a browser session that clears
230
+ # TikTok's captcha, so +results+ carry TikTok's own ranking (region via
231
+ # +proxy_country+), each in the full #tiktok_video shape. 20-45 s.
232
+ def tiktok_search(query, max_results: 20, proxy_country: nil)
233
+ post_json("/social/tiktok-search",
234
+ query: query, max_results: max_results, proxy_country: proxy_country)
235
+ end
236
+
237
+ # Scrape the comments of a TikTok post (text, date, likes, reply count,
238
+ # author, creator flags, preloaded replies) plus +totalComments+ and
239
+ # +hasMore+. Runs in a browser session that clears TikTok's captcha. 20-45 s.
240
+ def tiktok_comments(url, max_comments: 50, proxy_country: nil)
241
+ post_json("/social/tiktok-comments",
242
+ url: url, max_comments: max_comments, proxy_country: proxy_country)
243
+ end
244
+
193
245
  def post_json(path, params)
194
246
  JSON.parse(request(path, params)[:body])
195
247
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ScrapeUnblocker
4
+ # Southwest Airlines plugin endpoints (flights).
5
+ class Southwest
6
+ # @api private
7
+ def initialize(client)
8
+ @client = client
9
+ end
10
+
11
+ # Fetch Southwest Airlines flight quotes and return the raw booking JSON.
12
+ #
13
+ # +origin+ and +dest+ are IATA airport codes (e.g. "DAL", "HOU").
14
+ # +depart_date+ (and the optional +return_date+) are "YYYY-MM-DD"; omit
15
+ # +return_date+ for a one-way search. +adults+ is 1-8. +fare_type+ is
16
+ # "dollars" (default) or "points". +proxy_country+ pins the exit (default
17
+ # "US"). +max_attempts+ is 1-5.
18
+ def flights(origin:, dest:, depart_date:, return_date: nil, adults: 1,
19
+ fare_type: "dollars", proxy_country: "US", max_attempts: 3)
20
+ @client.post_json("/flights/southwest-quotes",
21
+ origin: origin, dest: dest, depart_date: depart_date,
22
+ return_date: return_date, adults: adults,
23
+ fare_type: fare_type, proxy_country: proxy_country,
24
+ max_attempts: max_attempts)
25
+ end
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ScrapeUnblocker
4
- VERSION = "0.2.1"
4
+ VERSION = "0.4.0"
5
5
  end
@@ -4,6 +4,7 @@ require_relative "scrapeunblocker/version"
4
4
  require_relative "scrapeunblocker/errors"
5
5
  require_relative "scrapeunblocker/parsed_page"
6
6
  require_relative "scrapeunblocker/skyscanner"
7
+ require_relative "scrapeunblocker/southwest"
7
8
  require_relative "scrapeunblocker/client"
8
9
 
9
10
  # Official Ruby client for the ScrapeUnblocker web scraping API.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scrapeunblocker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ScrapeUnblocker
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-09-02 00:00:00.000000000 Z
11
+ date: 2026-09-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: JS-rendered pages that bypass Cloudflare, DataDome, PerimeterX and Akamai,
14
14
  plus Google SERP and Skyscanner flights/hotels/car-hire scraping as JSON.
@@ -26,6 +26,7 @@ files:
26
26
  - lib/scrapeunblocker/errors.rb
27
27
  - lib/scrapeunblocker/parsed_page.rb
28
28
  - lib/scrapeunblocker/skyscanner.rb
29
+ - lib/scrapeunblocker/southwest.rb
29
30
  - lib/scrapeunblocker/version.rb
30
31
  homepage: https://scrapeunblocker.com?utm_source=rubygems&utm_medium=integration&utm_campaign=ruby-sdk
31
32
  licenses:
@@ -33,7 +34,7 @@ licenses:
33
34
  metadata:
34
35
  homepage_uri: https://scrapeunblocker.com?utm_source=rubygems&utm_medium=integration&utm_campaign=ruby-sdk
35
36
  source_code_uri: https://github.com/ScrapeUnblocker/scrapeunblocker-ruby
36
- documentation_uri: https://developers.scrapeunblocker.com?utm_source=rubygems&utm_medium=integration&utm_campaign=ruby-sdk
37
+ documentation_uri: https://docs.scrapeunblocker.com?utm_source=rubygems&utm_medium=integration&utm_campaign=ruby-sdk
37
38
  changelog_uri: https://github.com/ScrapeUnblocker/scrapeunblocker-ruby/blob/main/CHANGELOG.md
38
39
  bug_tracker_uri: https://github.com/ScrapeUnblocker/scrapeunblocker-ruby/issues
39
40
  rubygems_mfa_required: 'true'