scrape_creators 0.7.0 → 0.8.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 +31 -1
- data/lib/scrape_creators/client.rb +50 -6
- data/lib/scrape_creators/version.rb +1 -1
- 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: 81c09defede693eaa2d2a5096d7d9be96179e236e48ff2f49c4bce865be49773
|
|
4
|
+
data.tar.gz: d1978cf6dc4d1d3feaf1fbdb128be6aca6ed45d7269aa1b6991615b25207d509
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2db5b1d62794c71c70292bb4225e651ae84cd7558adcb8bb7db4be79b66324096defc3274f9d69c9db4fc86d6299a8b74d16c139bc125c47bca7c78a75f8dca4
|
|
7
|
+
data.tar.gz: ece660c23bb218af965016f85ca918b08c964939ecb3012ac8fb0a7b23bccf7383631975812d35230bef3170139442fd2f25be05b7a4f020c044f5a4cfb1cb97
|
data/README.md
CHANGED
|
@@ -7,7 +7,7 @@ A zero-dependency Ruby client for the [Scrape Creators API](https://docs.scrapec
|
|
|
7
7
|
Add this line to your application's Gemfile:
|
|
8
8
|
|
|
9
9
|
```ruby
|
|
10
|
-
gem 'scrape_creators', '0.
|
|
10
|
+
gem 'scrape_creators', '0.8.0'
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
And then execute:
|
|
@@ -85,6 +85,36 @@ info = scraper.post("https://www.instagram.com/p/DKSMEpKRd6h/", download_media:
|
|
|
85
85
|
comments = scraper.comments("https://www.instagram.com/p/DKSMEpKRd6h/")
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
+
### Comment pagination
|
|
89
|
+
|
|
90
|
+
`comments` returns the first page by default. Pass `pages:` to collect several pages:
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
comments = scraper.comments(video_url, source: 'instagram', pages: 5)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Use `comments_page` for an initial batch followed by a Load more button. It returns
|
|
97
|
+
`{ comments:, cursor:, has_more: }` for Instagram, TikTok, and YouTube:
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
page = scraper.comments_page(video_url, source: 'instagram')
|
|
101
|
+
|
|
102
|
+
# When the user asks for more:
|
|
103
|
+
if page[:has_more]
|
|
104
|
+
page = scraper.comments_page(video_url, source: 'instagram', cursor: page[:cursor])
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Pass the returned cursor unchanged. The client translates it to YouTube's
|
|
109
|
+
`continuationToken` parameter and uses `cursor` for Instagram and TikTok. TikTok's
|
|
110
|
+
`has_more` flag is respected; missing or unchanged cursors stop pagination.
|
|
111
|
+
Multi-page collection also stops if a cursor repeats. Failed API responses raise
|
|
112
|
+
`ScrapeCreators::APIError` so callers can retry without losing their place.
|
|
113
|
+
|
|
114
|
+
API documentation: [Instagram](https://docs.scrapecreators.com/v2/instagram/post/comments/),
|
|
115
|
+
[TikTok](https://docs.scrapecreators.com/v1/tiktok/video/comments/),
|
|
116
|
+
[YouTube](https://docs.scrapecreators.com/v1/youtube/video/comments/).
|
|
117
|
+
|
|
88
118
|
## License
|
|
89
119
|
|
|
90
120
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
|
@@ -137,12 +137,33 @@ module ScrapeCreators
|
|
|
137
137
|
end
|
|
138
138
|
|
|
139
139
|
def comments(url_or_code, options = {})
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
pages = options[:pages] || options[:max_pages] || 1
|
|
141
|
+
cursor = options[:cursor]
|
|
142
|
+
seen_cursors = {}
|
|
143
|
+
all_comments = []
|
|
144
|
+
|
|
145
|
+
pages.to_i.times do
|
|
146
|
+
break if seen_cursors[cursor.to_s]
|
|
147
|
+
|
|
148
|
+
seen_cursors[cursor.to_s] = true
|
|
149
|
+
page = comments_page(url_or_code, options.merge(cursor: cursor))
|
|
150
|
+
all_comments.concat(page[:comments])
|
|
151
|
+
break unless page[:has_more]
|
|
152
|
+
|
|
153
|
+
cursor = page[:cursor]
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
all_comments
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def comments_page(url_or_code, options = {})
|
|
160
|
+
source = (options[:source] || INSTAGRAM).to_s
|
|
161
|
+
raise ArgumentError, "Unsupported comments source: #{source}" unless [INSTAGRAM, TIKTOK, YOUTUBE].include?(source)
|
|
162
|
+
|
|
163
|
+
cursor = options[:cursor] || options['cursor']
|
|
164
|
+
params = comments_params(url_or_code, source, cursor, options)
|
|
165
|
+
response = get(comments_url_for(source: source), params)
|
|
166
|
+
comments_result(response, source, cursor)
|
|
146
167
|
end
|
|
147
168
|
|
|
148
169
|
def get(endpoint, params = {}, options = {})
|
|
@@ -185,6 +206,29 @@ module ScrapeCreators
|
|
|
185
206
|
|
|
186
207
|
private
|
|
187
208
|
|
|
209
|
+
def comments_cursor_key(source)
|
|
210
|
+
source == YOUTUBE ? 'continuationToken' : 'cursor'
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def comments_params(url_or_code, source, cursor, options)
|
|
214
|
+
params = options.reject { |key, _| %w[source cursor pages max_pages].include?(key.to_s) }
|
|
215
|
+
params[:url] = normalize_url(url_or_code)
|
|
216
|
+
params[comments_cursor_key(source)] = cursor unless cursor.nil? || cursor.to_s.empty?
|
|
217
|
+
params
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def comments_result(response, source, cursor)
|
|
221
|
+
unless response.is_a?(Hash) && response['success'] != false && response['comments'].is_a?(Array)
|
|
222
|
+
raise APIError, 'Unable to fetch comments'
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
next_cursor = response[comments_cursor_key(source)]&.to_s
|
|
226
|
+
next_cursor = nil if next_cursor.to_s.empty? || next_cursor == '0'
|
|
227
|
+
has_more = !next_cursor.nil? && next_cursor != cursor.to_s
|
|
228
|
+
has_more &&= [true, 1, '1'].include?(response['has_more']) if source == TIKTOK
|
|
229
|
+
{ comments: response['comments'], cursor: next_cursor, has_more: has_more }
|
|
230
|
+
end
|
|
231
|
+
|
|
188
232
|
def search_results(response)
|
|
189
233
|
unless response.is_a?(Hash) && response['success'] != false
|
|
190
234
|
raise APIError, 'Account search failed'
|