google_rest 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +22 -0
- data/init.rb +3 -0
- data/lib/google_rest.rb +231 -0
- metadata +82 -0
data/README
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
GoogleRest
|
2
|
+
==========
|
3
|
+
|
4
|
+
A plugin to manipulate some Google REST API. For now it partially support:
|
5
|
+
* Google Ajax Feed Api (find a feed, retrieve articles from a feed)
|
6
|
+
* Google Search API (inbound links, number of indexed page)
|
7
|
+
|
8
|
+
Setup
|
9
|
+
=====
|
10
|
+
|
11
|
+
You must add a config/google_rest.yml with your api key and referer (requested by Google Terms of use)
|
12
|
+
Ex config/google_rest.yml
|
13
|
+
---
|
14
|
+
api_key: XXXXX
|
15
|
+
referer: http://www.mydomain.com
|
16
|
+
|
17
|
+
|
18
|
+
Example
|
19
|
+
=======
|
20
|
+
|
21
|
+
|
22
|
+
Copyright (c) 2008 Olivier Ruffin, http://www.veilleperso.com , released under the MIT license
|
data/init.rb
ADDED
data/lib/google_rest.rb
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
require 'json/add/rails'
|
2
|
+
|
3
|
+
module GoogleRest
|
4
|
+
PER_PAGE = 8
|
5
|
+
|
6
|
+
class Results
|
7
|
+
|
8
|
+
attr_accessor :raw, :results, :cursor
|
9
|
+
|
10
|
+
def initialize(raw)
|
11
|
+
self.raw = raw
|
12
|
+
data = (raw || {})
|
13
|
+
self.cursor = data["cursor"]
|
14
|
+
self.results = data["results"] || []
|
15
|
+
end
|
16
|
+
|
17
|
+
def empty?
|
18
|
+
raw.blank?
|
19
|
+
end
|
20
|
+
|
21
|
+
def each
|
22
|
+
results.each { |r| yield r }
|
23
|
+
end
|
24
|
+
|
25
|
+
def map
|
26
|
+
results.map { |r| yield r }
|
27
|
+
end
|
28
|
+
|
29
|
+
def page
|
30
|
+
cursor["currentPageIndex"].blank? ? 1 : cursor["currentPageIndex"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def pages
|
34
|
+
(cursor["pages"] || [])
|
35
|
+
end
|
36
|
+
|
37
|
+
def total_pages
|
38
|
+
(cursor["pages"] || []).length
|
39
|
+
end
|
40
|
+
|
41
|
+
def total_entries
|
42
|
+
if pages.last
|
43
|
+
pages.last["start"].to_i + GoogleRest::PER_PAGE - 1
|
44
|
+
else
|
45
|
+
0
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def per_page
|
50
|
+
8
|
51
|
+
end
|
52
|
+
|
53
|
+
def paginated(current_page = nil)
|
54
|
+
results.paginate(:page => current_page || page, :per_page => per_page, :total_entries => total_entries)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
class Request
|
60
|
+
if defined?(HTTParty)
|
61
|
+
include HTTParty
|
62
|
+
base_uri "http://ajax.googleapis.com/ajax/services"
|
63
|
+
format :json
|
64
|
+
parser Proc.new { |data, format|
|
65
|
+
begin
|
66
|
+
Crack::JSON.parse(data)
|
67
|
+
rescue Crack::ParseError => err
|
68
|
+
ActiveSupport::JSON.decode(data)
|
69
|
+
end
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_accessor :api_key
|
74
|
+
attr_accessor :referer
|
75
|
+
attr_accessor :userip
|
76
|
+
|
77
|
+
@@ascii_available = "".respond_to?(:to_ascii)
|
78
|
+
|
79
|
+
API_VERSION = "1.0"
|
80
|
+
API_URL = {
|
81
|
+
:feed_lookup => "/feed/lookup",
|
82
|
+
:feed_load => "/feed/load",
|
83
|
+
:web => "/search/web",
|
84
|
+
:blog => "/search/blogs",
|
85
|
+
:news => "/search/news",
|
86
|
+
:image => "/search/images",
|
87
|
+
:local => "/search/local",
|
88
|
+
:video => "/search/video"
|
89
|
+
}
|
90
|
+
|
91
|
+
def initialize(userip = nil)
|
92
|
+
path = Rails.root.join("config/google_rest.yml")
|
93
|
+
if !File.exists?(path)
|
94
|
+
raise StandardError, "Missing config file: #{path}"
|
95
|
+
else
|
96
|
+
config = YAML.load_file(path) || {}
|
97
|
+
data = config[Rails.env.to_s] || config
|
98
|
+
self.api_key = data['api_key']
|
99
|
+
self.referer = data['referer']
|
100
|
+
self.userip = userip
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def feed_lookup(website)
|
105
|
+
return nil if website.blank?
|
106
|
+
res = google_request(:feed_lookup, {:q => website.to_s.gsub(/^https?:\/\//i, '')})
|
107
|
+
res.empty? ? nil : res.raw["url"]
|
108
|
+
end
|
109
|
+
|
110
|
+
def feed_load(feed_url, count_entries = false)
|
111
|
+
return nil if feed_url.blank?
|
112
|
+
if count_entries == false
|
113
|
+
# we retrieve the current feed entries
|
114
|
+
res = google_request(:feed_load, {:q => feed_url, :num => -1})
|
115
|
+
else
|
116
|
+
res = google_request(:feed_load, {:q => feed_url, :num => count_entries, :scoring => 'h'} )
|
117
|
+
end
|
118
|
+
res.empty? ? nil : res.raw["feed"]
|
119
|
+
end
|
120
|
+
|
121
|
+
def blog_search(query, options = {})
|
122
|
+
common_search(:blog, {:scoring => 'd', :rsz => 'large', :q => query}.merge(options))
|
123
|
+
end
|
124
|
+
|
125
|
+
def web_search(query, options = {})
|
126
|
+
common_search(:web, {:rsz => 'large', :q => query}.merge(options))
|
127
|
+
end
|
128
|
+
|
129
|
+
def news_search(query, options = {})
|
130
|
+
common_search(:news, {:rsz => 'large', :scoring => 'd', :q => query}.merge(options))
|
131
|
+
end
|
132
|
+
|
133
|
+
def image_search(query, options = {})
|
134
|
+
common_search(:image, {:q => query}.merge(options))
|
135
|
+
end
|
136
|
+
|
137
|
+
def video_search(query, options = {})
|
138
|
+
common_search(:video, {:q => query}.merge(options))
|
139
|
+
end
|
140
|
+
|
141
|
+
def local_search(query, options = {})
|
142
|
+
common_search(:local, {:q => query}.merge(options))
|
143
|
+
end
|
144
|
+
|
145
|
+
def inbound_links(url)
|
146
|
+
res = common_search(:web, {:q => "link:#{url.gsub(/^https?:\/\//,'')}"})
|
147
|
+
res.cursor.blank? ? 0 : res.cursor["estimatedResultCount"].to_i
|
148
|
+
end
|
149
|
+
|
150
|
+
def indexed_pages(url)
|
151
|
+
res = common_search(:web, {:q => "site:#{url.gsub(/^https?:\/\//,'')}"})
|
152
|
+
res.cursor.blank? ? 0 : res.cursor["estimatedResultCount"].to_i
|
153
|
+
end
|
154
|
+
|
155
|
+
def api_key?
|
156
|
+
!self.api_key.blank?
|
157
|
+
end
|
158
|
+
|
159
|
+
def userip?
|
160
|
+
!self.userip.blank?
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
def common_search(type, query = {})
|
165
|
+
if !(lang=query.delete(:lang)).blank?
|
166
|
+
lang.downcase!
|
167
|
+
if type == :news
|
168
|
+
query.reverse_merge!(:hl => lang, :ned => lang)
|
169
|
+
else
|
170
|
+
query.reverse_merge!(:hl => lang, :lr => "lang_#{lang}")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
query.delete_if {|k,v| v.blank?}
|
174
|
+
google_request(type, query)
|
175
|
+
end
|
176
|
+
|
177
|
+
def google_request(type, query = {})
|
178
|
+
# HTTParty now use Crack instead of ActiveSupport::JSON to do the decoding
|
179
|
+
# But it doesn't seems to work properly with some Google Results so we hack it to not parse
|
180
|
+
# google results and we decode them by ourselves
|
181
|
+
begin
|
182
|
+
Timeout::timeout(15) do
|
183
|
+
no_escape = query.delete(:no_escape)
|
184
|
+
query[:v] = API_VERSION
|
185
|
+
query[:key] = api_key if api_key?
|
186
|
+
query[:userip] = userip if userip?
|
187
|
+
self.class.headers({'Referer' => self.referer})
|
188
|
+
res = self.class.get(API_URL[type], :query => query)
|
189
|
+
if !res["responseData"].blank?
|
190
|
+
GoogleRest::Results.new(no_escape ? res["responseData"] : Util.json_recursive_unescape(res["responseData"]))
|
191
|
+
else
|
192
|
+
GoogleRest::Results.new(nil)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
rescue Timeout::Error => err
|
196
|
+
GoogleRest::Results.new(nil)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
module Util
|
201
|
+
JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C', '=' => '\u003D' }
|
202
|
+
|
203
|
+
# A utility method for unescaping HTML entities in JSON strings.
|
204
|
+
# puts json_unescape("\u003E 0 \u0026 a \u003C 10?")
|
205
|
+
# # => is a > 0 & a < 10?
|
206
|
+
def json_unescape(s)
|
207
|
+
JSON_ESCAPE.inject(s.to_s) { |str, (k,v)| str.gsub!(/#{Regexp.escape(v)}/i, k); str }
|
208
|
+
end
|
209
|
+
|
210
|
+
# A utility method for escaping HTML entities in JSON strings.
|
211
|
+
# puts json_escape("is a > 0 & a < 10?")
|
212
|
+
# # => is a \u003E 0 \u0026 a \u003C 10?
|
213
|
+
def json_escape(s)
|
214
|
+
s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
|
215
|
+
end
|
216
|
+
|
217
|
+
# A utility method for unescaping recursively HTML entities in JSON array or hash.
|
218
|
+
def json_recursive_unescape(data)
|
219
|
+
case data
|
220
|
+
when String : json_unescape(data)
|
221
|
+
when Hash : data.inject({}) { |hsh, (k,v)| hsh[k] = json_recursive_unescape(v);hsh }
|
222
|
+
when Array : data.collect { |v| json_recursive_unescape(v) }
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
module_function :json_escape
|
227
|
+
module_function :json_unescape
|
228
|
+
module_function :json_recursive_unescape
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: google_rest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Olivier Ruffin
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-02-17 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: httparty
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Wrapper around google rest api
|
36
|
+
email: olivier@muweb.fr
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- README
|
45
|
+
- init.rb
|
46
|
+
- lib/google_rest.rb
|
47
|
+
has_rdoc: true
|
48
|
+
homepage: https://github.com/veilleperso/google_rest
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.4.2
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Google REST API
|
81
|
+
test_files: []
|
82
|
+
|