pinterest-dl 1.0.1 → 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 +75 -56
- data/lib/pinterest-dl.rb +193 -135
- metadata +4 -7
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,57 +1,76 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
# pinterest-dl
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
[](https://rubygems.org/gems/pinterest-dl)
|
|
8
|
-
[](LICENSE)
|
|
9
|
-
[]()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# pinterest-dl
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
[](https://rubygems.org/gems/pinterest-dl)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://www.ruby-lang.org/)
|
|
10
|
+
|
|
11
|
+
<img src="https://img.icons8.com/color/96/000000/pinterest--v1.png" width="80">
|
|
12
|
+
|
|
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.
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
gem install pinterest-dl
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
require 'pinterest-dl'
|
|
33
|
+
```
|
|
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)
|
|
73
|
+
|
|
74
|
+
## License
|
|
75
|
+
|
|
57
76
|
MIT © [monji024](https://github.com/monji024)
|
data/lib/pinterest-dl.rb
CHANGED
|
@@ -1,135 +1,193 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
require '
|
|
9
|
-
require '
|
|
10
|
-
require '
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
url
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
url
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
url
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Pinterest downloader
|
|
3
|
+
# creator: Monji
|
|
4
|
+
# ver: 1.0.3
|
|
5
|
+
# github: https://github.com/monji024/pinterest-dl
|
|
6
|
+
|
|
7
|
+
require 'net/http'
|
|
8
|
+
require 'uri'
|
|
9
|
+
require 'json'
|
|
10
|
+
require 'openssl'
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
module PinterestDL
|
|
14
|
+
ver = '1.0.3'.freeze
|
|
15
|
+
github = 'https://github.com/monji024/pinterest-dl'.freeze
|
|
16
|
+
|
|
17
|
+
puts "pinterest-dl v#{ver} (#{github})\n\n"
|
|
18
|
+
|
|
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
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def get_image_url(pin_url)
|
|
23
|
+
html = fetch_html(pin_url)
|
|
24
|
+
return nil unless html
|
|
25
|
+
|
|
26
|
+
# 1 -> json-ld
|
|
27
|
+
url = extract_json_ld(html, 'ImageObject')
|
|
28
|
+
return url if url
|
|
29
|
+
# 2 -> open graph
|
|
30
|
+
match = html.match(%r{<meta[^>]*property="og:image"[^>]*content="([^"]+)"})
|
|
31
|
+
if match
|
|
32
|
+
url = match[1]
|
|
33
|
+
puts "image url: #{url}"
|
|
34
|
+
return url
|
|
35
|
+
end
|
|
36
|
+
# 3 -> direct i.pinimg.com patterns
|
|
37
|
+
match = html.match(%r{https://i\.pinimg\.com/(?:originals|474x|236x|736x)/[^"\s]+})
|
|
38
|
+
if match
|
|
39
|
+
url = match[0]
|
|
40
|
+
reset = "\e[0m"
|
|
41
|
+
green = "\e[32m"
|
|
42
|
+
puts "#{green}image url :#{reset} #{url}"
|
|
43
|
+
return url
|
|
44
|
+
end
|
|
45
|
+
puts 'No found:)'
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_video_url(pin_url)
|
|
51
|
+
html = fetch_html(pin_url)
|
|
52
|
+
return nil unless html
|
|
53
|
+
|
|
54
|
+
# 1 -> json-ld
|
|
55
|
+
url = extract_json_ld(html, 'VideoObject')
|
|
56
|
+
if url
|
|
57
|
+
url = url.gsub('\\u002F', '/').gsub('\\', '')
|
|
58
|
+
puts "ok : #{url}"
|
|
59
|
+
return url
|
|
60
|
+
end
|
|
61
|
+
# 2 -> Open Graph video
|
|
62
|
+
match = html.match(%r{<meta[^>]*property="og:video"[^>]*content="([^"]+)"})
|
|
63
|
+
if match
|
|
64
|
+
url = match[1].gsub('\\u002F', '/').gsub('\\', '')
|
|
65
|
+
puts "video url: #{url}"
|
|
66
|
+
return url
|
|
67
|
+
end
|
|
68
|
+
# 3 -> contentUrl in page source
|
|
69
|
+
match = html.match(/"contentUrl"\s*:\s*"([^"]+\.mp4[^"]*)"/)
|
|
70
|
+
if match
|
|
71
|
+
url = match[1].gsub('\\u002F', '/').gsub('\\', '')
|
|
72
|
+
green = "\e[32m"
|
|
73
|
+
reset = "\e[0m"
|
|
74
|
+
puts "#{green}video url :#{reset} #{url}"
|
|
75
|
+
return url
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
puts 'No found'
|
|
79
|
+
nil
|
|
80
|
+
end
|
|
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
|
|
151
|
+
private
|
|
152
|
+
|
|
153
|
+
def fetch_html(url)
|
|
154
|
+
uri = URI(url)
|
|
155
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
156
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
157
|
+
http.max_retries = 2
|
|
158
|
+
http.open_timeout = 10
|
|
159
|
+
http.read_timeout = 20
|
|
160
|
+
resp = http.request(Net::HTTP::Head.new(uri, 'User-Agent' => USER_AGENT))
|
|
161
|
+
5.times do
|
|
162
|
+
break unless resp.is_a?(Net::HTTPRedirection)
|
|
163
|
+
location = resp['location']
|
|
164
|
+
location = URI.join(uri.to_s, location) if location.start_with?('/')
|
|
165
|
+
uri = URI(location)
|
|
166
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
167
|
+
http.use_ssl = (uri.scheme == 'https')
|
|
168
|
+
resp = http.request(Net::HTTP::Head.new(uri, 'User-Agent' => USER_AGENT))
|
|
169
|
+
end
|
|
170
|
+
# get final page
|
|
171
|
+
req = Net::HTTP::Get.new(uri, 'User-Agent' => USER_AGENT)
|
|
172
|
+
resp = http.request(req)
|
|
173
|
+
return resp.body if resp.is_a?(Net::HTTPSuccess)
|
|
174
|
+
nil
|
|
175
|
+
rescue StandardError => e
|
|
176
|
+
red = "\e[31m"
|
|
177
|
+
reset = "\e[0m"
|
|
178
|
+
puts "#{red}http err!:#{reset} #{e.message}"
|
|
179
|
+
nil
|
|
180
|
+
end
|
|
181
|
+
def extract_json_ld(html, type)
|
|
182
|
+
match = html.match(%r{<script[^>]*type="application/ld\+json"[^>]*>(.*?)</script>}m)
|
|
183
|
+
return nil unless match
|
|
184
|
+
begin
|
|
185
|
+
data = JSON.parse(match[1])
|
|
186
|
+
data = data[0] if data.is_a?(Array) && !data.empty?
|
|
187
|
+
return data['contentUrl'] if data['@type'] == type && data['contentUrl']
|
|
188
|
+
rescue JSON::ParserError
|
|
189
|
+
end
|
|
190
|
+
nil
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +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
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
10
|
+
date: 2026-07-28 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: Simple Pinterest Downloader Tool:)
|
|
14
13
|
email: hoseinmonjiofficial@gmail.com
|
|
@@ -24,7 +23,6 @@ licenses:
|
|
|
24
23
|
metadata:
|
|
25
24
|
source_code_uri: https://github.com/monji024/pinterest-dl
|
|
26
25
|
bug_tracker_uri: https://github.com/monji024/pinterest-dl/issues
|
|
27
|
-
post_install_message:
|
|
28
26
|
rdoc_options: []
|
|
29
27
|
require_paths:
|
|
30
28
|
- lib
|
|
@@ -39,8 +37,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
39
37
|
- !ruby/object:Gem::Version
|
|
40
38
|
version: '0'
|
|
41
39
|
requirements: []
|
|
42
|
-
rubygems_version: 3.
|
|
43
|
-
signing_key:
|
|
40
|
+
rubygems_version: 3.6.3
|
|
44
41
|
specification_version: 4
|
|
45
|
-
summary: Extract direct image/video URLs from Pinterest!!!
|
|
42
|
+
summary: Extract direct image/video/search URLs from Pinterest!!!
|
|
46
43
|
test_files: []
|