pinterest-dl 1.0.0 → 1.0.3
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 +60 -9
- data/lib/pinterest-dl.rb +75 -18
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 73cae48cd87360325168ee188b8a67b25944402f33857a61dee4ea3a13457f91
|
|
4
|
+
data.tar.gz: 313a17fd00772aa8980ee8389dc5bd934b8217225f4e4b11f130ad4882e629c0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e08fc0e08bec2a47a91659d10b1ba3a87e6d2c70e81fc27134366768108ebf3e1a5f10bfea2dc3391c63fa038eb9daba58594ffd8c5997338b240dd21fcaa558
|
|
7
|
+
data.tar.gz: 7bcfe71875c82f31ddaf9e27a0f648b646bb6381045830d03d6ee0a8fb561283e8a0eb4e85036915a6d71e6f5865e05f0aa4a5e0658f601b593f8e5a6f42f714
|
data/README.md
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
1
3
|
# pinterest-dl
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
---
|
|
4
6
|
|
|
7
|
+
[](https://rubygems.org/gems/pinterest-dl)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://www.ruby-lang.org/)
|
|
5
10
|
|
|
11
|
+
<img src="https://img.icons8.com/color/96/000000/pinterest--v1.png" width="80">
|
|
6
12
|
|
|
7
|
-
|
|
8
|
-
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
Get direct image and video URLs from Pinterest.
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
> **Note:** Users in Iran may need a VPN to access Pinterest.
|
|
9
22
|
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
gem install pinterest-dl
|
|
10
27
|
```
|
|
11
28
|
|
|
12
29
|
## Usage
|
|
@@ -15,11 +32,45 @@ gem install pinterest-dl
|
|
|
15
32
|
require 'pinterest-dl'
|
|
16
33
|
```
|
|
17
34
|
|
|
35
|
+
### Get Image URL
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
url = PinterestDL.get_image_url('https://www.pinterest.com/pin/example/')
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Get Video URL
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
url = PinterestDL.get_video_url('https://www.pinterest.com/pin/example/')
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Search Pins
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
# Search for pins with a specific query
|
|
51
|
+
results = PinterestDL.search('nature landscape', limit: 10)
|
|
52
|
+
|
|
53
|
+
# Returns an array of pin objects with:
|
|
54
|
+
# id, pin_url, title, description, image_url, video_url
|
|
55
|
+
|
|
56
|
+
# Get only image URLs from search results
|
|
57
|
+
images = PinterestDL.search_images('sunset', limit: 5)
|
|
58
|
+
# => ["https://i.pinimg.com/originals/...", ...]
|
|
59
|
+
|
|
60
|
+
# Get only video URLs from search results
|
|
61
|
+
videos = PinterestDL.search_videos('tutorial', limit: 3)
|
|
62
|
+
# => ["https://v.pinimg.com/videos/...", ...]
|
|
63
|
+
|
|
64
|
+
# Get search results as JSON
|
|
65
|
+
json_results = PinterestDL.search_json('anime art', limit: 20)
|
|
66
|
+
# => Returns formatted JSON string
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Links
|
|
70
|
+
|
|
71
|
+
- [RubyGems](https://rubygems.org/gems/pinterest-dl)
|
|
72
|
+
- [GitHub](https://github.com/monji024/pinterest-dl)
|
|
18
73
|
|
|
19
|
-
|
|
20
|
-
url = PinterestDL.get_image_url('link')
|
|
21
|
-
# => "https://i.pinimg.com/originals/8e/2a/..."
|
|
74
|
+
## License
|
|
22
75
|
|
|
23
|
-
|
|
24
|
-
url = PinterestDL.get_video_url('link')
|
|
25
|
-
# => "https://v.pinimg.com/videos/..."
|
|
76
|
+
MIT © [monji024](https://github.com/monji024)
|
data/lib/pinterest-dl.rb
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
|
-
# pinterest-dl.rb
|
|
3
2
|
# Pinterest downloader
|
|
4
3
|
# creator: Monji
|
|
5
|
-
# ver: 1.0.
|
|
4
|
+
# ver: 1.0.3
|
|
6
5
|
# github: https://github.com/monji024/pinterest-dl
|
|
7
6
|
|
|
8
7
|
require 'net/http'
|
|
@@ -12,11 +11,10 @@ require 'openssl'
|
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
module PinterestDL
|
|
15
|
-
ver = '1.0.
|
|
16
|
-
craetor = 'Monji'.freeze
|
|
14
|
+
ver = '1.0.3'.freeze
|
|
17
15
|
github = 'https://github.com/monji024/pinterest-dl'.freeze
|
|
18
16
|
|
|
19
|
-
puts "pinterest-dl v#{ver}
|
|
17
|
+
puts "pinterest-dl v#{ver} (#{github})\n\n"
|
|
20
18
|
|
|
21
19
|
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'.freeze
|
|
22
20
|
|
|
@@ -28,7 +26,6 @@ module PinterestDL
|
|
|
28
26
|
# 1 -> json-ld
|
|
29
27
|
url = extract_json_ld(html, 'ImageObject')
|
|
30
28
|
return url if url
|
|
31
|
-
|
|
32
29
|
# 2 -> open graph
|
|
33
30
|
match = html.match(%r{<meta[^>]*property="og:image"[^>]*content="([^"]+)"})
|
|
34
31
|
if match
|
|
@@ -36,7 +33,6 @@ module PinterestDL
|
|
|
36
33
|
puts "image url: #{url}"
|
|
37
34
|
return url
|
|
38
35
|
end
|
|
39
|
-
|
|
40
36
|
# 3 -> direct i.pinimg.com patterns
|
|
41
37
|
match = html.match(%r{https://i\.pinimg\.com/(?:originals|474x|236x|736x)/[^"\s]+})
|
|
42
38
|
if match
|
|
@@ -46,8 +42,7 @@ module PinterestDL
|
|
|
46
42
|
puts "#{green}image url :#{reset} #{url}"
|
|
47
43
|
return url
|
|
48
44
|
end
|
|
49
|
-
|
|
50
|
-
puts 'No found'
|
|
45
|
+
puts 'No found:)'
|
|
51
46
|
nil
|
|
52
47
|
end
|
|
53
48
|
|
|
@@ -63,7 +58,6 @@ module PinterestDL
|
|
|
63
58
|
puts "ok : #{url}"
|
|
64
59
|
return url
|
|
65
60
|
end
|
|
66
|
-
|
|
67
61
|
# 2 -> Open Graph video
|
|
68
62
|
match = html.match(%r{<meta[^>]*property="og:video"[^>]*content="([^"]+)"})
|
|
69
63
|
if match
|
|
@@ -71,7 +65,6 @@ module PinterestDL
|
|
|
71
65
|
puts "video url: #{url}"
|
|
72
66
|
return url
|
|
73
67
|
end
|
|
74
|
-
|
|
75
68
|
# 3 -> contentUrl in page source
|
|
76
69
|
match = html.match(/"contentUrl"\s*:\s*"([^"]+\.mp4[^"]*)"/)
|
|
77
70
|
if match
|
|
@@ -85,7 +78,76 @@ module PinterestDL
|
|
|
85
78
|
puts 'No found'
|
|
86
79
|
nil
|
|
87
80
|
end
|
|
88
|
-
|
|
81
|
+
def search(query, limit: 25)
|
|
82
|
+
source_path = "/search/pins/?q=#{URI.encode_www_form_component(query)}"
|
|
83
|
+
home_uri = URI("https://www.pinterest.com#{source_path}")
|
|
84
|
+
http = Net::HTTP.new(home_uri.host, home_uri.port)
|
|
85
|
+
http.use_ssl = true
|
|
86
|
+
http.open_timeout = 10
|
|
87
|
+
http.read_timeout = 20
|
|
88
|
+
home_req = Net::HTTP::Get.new(home_uri, 'User-Agent' => USER_AGENT)
|
|
89
|
+
home_resp = http.request(home_req)
|
|
90
|
+
cookies = (home_resp.get_fields('set-cookie') || []).map { |c| c.split(';').first }
|
|
91
|
+
cookie_header = cookies.join('; ')
|
|
92
|
+
csrf = cookies.find { |c| c.start_with?('csrftoken=') }&.split('=', 2)&.last || 'undefined'
|
|
93
|
+
data = {
|
|
94
|
+
options: { query: query, scope: 'pins', page_size: limit },
|
|
95
|
+
context: {}}.to_json
|
|
96
|
+
res_uri = URI('https://www.pinterest.com/resource/BaseSearchResource/get/')
|
|
97
|
+
res_uri.query = URI.encode_www_form('source_url' => source_path, 'data' => data)
|
|
98
|
+
req = Net::HTTP::Get.new(res_uri)
|
|
99
|
+
req['User-Agent'] = USER_AGENT
|
|
100
|
+
req['Accept'] = 'application/json, text/javascript, */*, q=0.01'
|
|
101
|
+
req['X-Requested-With'] = 'XMLHttpRequest'
|
|
102
|
+
req['X-Pinterest-PWS-Handler'] = 'www/search/[scope].js'
|
|
103
|
+
req['X-CSRFToken'] = csrf
|
|
104
|
+
req['Referer'] = home_uri.to_s
|
|
105
|
+
req['Cookie'] = cookie_header
|
|
106
|
+
resp = http.request(req)
|
|
107
|
+
unless resp.is_a?(Net::HTTPSuccess)
|
|
108
|
+
puts "\e[31msearch http err!:\e[0m #{resp.code}"
|
|
109
|
+
return []
|
|
110
|
+
end
|
|
111
|
+
json = begin
|
|
112
|
+
JSON.parse(resp.body)
|
|
113
|
+
rescue JSON::ParserError
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
return [] unless json
|
|
117
|
+
raw_results = (json.dig('resource_response', 'data', 'results') || []).first(limit)
|
|
118
|
+
pins = raw_results.filter_map do |r|
|
|
119
|
+
id = r['id']
|
|
120
|
+
next nil unless id
|
|
121
|
+
orig_image = r.dig('images', 'orig', 'url')
|
|
122
|
+
fallback_image = r['images']&.values&.map { |v| v.is_a?(Hash) ? v['url'] : nil }&.compact&.last
|
|
123
|
+
image_url = orig_image || fallback_image
|
|
124
|
+
video_url = r.dig('videos', 'video_list')&.values&.first&.dig('url')
|
|
125
|
+
{
|
|
126
|
+
id: id,
|
|
127
|
+
pin_url: "https://www.pinterest.com/pin/#{id}/",
|
|
128
|
+
title: r['title'] || r['grid_title'],
|
|
129
|
+
description: r['description'],
|
|
130
|
+
image_url: image_url,
|
|
131
|
+
video_url: video_url
|
|
132
|
+
}
|
|
133
|
+
end
|
|
134
|
+
green = "\e[32m"
|
|
135
|
+
reset = "\e[0m"
|
|
136
|
+
puts "#{green}found #{pins.size} pin(s) for \"#{query}\"#{reset}"
|
|
137
|
+
pins
|
|
138
|
+
rescue StandardError => e
|
|
139
|
+
puts "\e[31msearch err!:\e[0m #{e.message}"
|
|
140
|
+
[]
|
|
141
|
+
end
|
|
142
|
+
def search_json(query, limit: 25)
|
|
143
|
+
JSON.pretty_generate(search(query, limit: limit))
|
|
144
|
+
end
|
|
145
|
+
def search_images(query, limit: 25)
|
|
146
|
+
search(query, limit: limit).filter_map { |p| p[:image_url] }
|
|
147
|
+
end
|
|
148
|
+
def search_videos(query, limit: 25)
|
|
149
|
+
search(query, limit: limit).filter_map { |p| p[:video_url] }
|
|
150
|
+
end
|
|
89
151
|
private
|
|
90
152
|
|
|
91
153
|
def fetch_html(url)
|
|
@@ -95,9 +157,7 @@ module PinterestDL
|
|
|
95
157
|
http.max_retries = 2
|
|
96
158
|
http.open_timeout = 10
|
|
97
159
|
http.read_timeout = 20
|
|
98
|
-
|
|
99
160
|
resp = http.request(Net::HTTP::Head.new(uri, 'User-Agent' => USER_AGENT))
|
|
100
|
-
|
|
101
161
|
5.times do
|
|
102
162
|
break unless resp.is_a?(Net::HTTPRedirection)
|
|
103
163
|
location = resp['location']
|
|
@@ -107,11 +167,9 @@ module PinterestDL
|
|
|
107
167
|
http.use_ssl = (uri.scheme == 'https')
|
|
108
168
|
resp = http.request(Net::HTTP::Head.new(uri, 'User-Agent' => USER_AGENT))
|
|
109
169
|
end
|
|
110
|
-
|
|
111
170
|
# get final page
|
|
112
171
|
req = Net::HTTP::Get.new(uri, 'User-Agent' => USER_AGENT)
|
|
113
172
|
resp = http.request(req)
|
|
114
|
-
|
|
115
173
|
return resp.body if resp.is_a?(Net::HTTPSuccess)
|
|
116
174
|
nil
|
|
117
175
|
rescue StandardError => e
|
|
@@ -123,7 +181,6 @@ module PinterestDL
|
|
|
123
181
|
def extract_json_ld(html, type)
|
|
124
182
|
match = html.match(%r{<script[^>]*type="application/ld\+json"[^>]*>(.*?)</script>}m)
|
|
125
183
|
return nil unless match
|
|
126
|
-
|
|
127
184
|
begin
|
|
128
185
|
data = JSON.parse(match[1])
|
|
129
186
|
data = data[0] if data.is_a?(Array) && !data.empty?
|
|
@@ -133,4 +190,4 @@ module PinterestDL
|
|
|
133
190
|
nil
|
|
134
191
|
end
|
|
135
192
|
end
|
|
136
|
-
end
|
|
193
|
+
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: pinterest-dl
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Monji
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-07-28 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
12
|
description: Simple Pinterest Downloader Tool:)
|
|
13
13
|
email: hoseinmonjiofficial@gmail.com
|
|
@@ -39,5 +39,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
39
39
|
requirements: []
|
|
40
40
|
rubygems_version: 3.6.3
|
|
41
41
|
specification_version: 4
|
|
42
|
-
summary: Extract direct image/video URLs from Pinterest!!!
|
|
42
|
+
summary: Extract direct image/video/search URLs from Pinterest!!!
|
|
43
43
|
test_files: []
|