google-player 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af507d0570fd674c2188cdee281e3a262033370c
4
+ data.tar.gz: f4ce7fba5f37af259da20126d96e82d13453034e
5
+ SHA512:
6
+ metadata.gz: 9ae690bc88e28b539f9654c9830a674febace3006b0c3057e74aa52e1eadd2a11c8cdf3116a962496b7623122b97bbf0de2d88e9618f81c8a731944255cd255b
7
+ data.tar.gz: de9c1c39c770ca17ded77d1c29fe3aafcfc6c2faff29e9f2e599d97d1ac9d88f7c91feebce068e1b0d0f807a53a8c68291d6a5943be76b1cd7767b2c4aa6ef5e
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in google-player.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 mako2x
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # GooglePlayer
2
+
3
+ Ruby gem for fetching app info and reviews from Google Play.
4
+
5
+ ## Dependencies
6
+
7
+ - Nokogiri
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'google-player'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install google-player
22
+
23
+ ## Usage
24
+
25
+ ```ruby
26
+ gp = GooglePlay.new
27
+
28
+ # App
29
+ app = gp.app('your.app.id')
30
+ puts app.name
31
+ puts app.image_url
32
+ puts app.developer
33
+ puts app.developer_mail
34
+ puts app.developer_web
35
+ puts app.category
36
+ puts app.rating_count
37
+ puts app.rating_counts
38
+ puts app.rating_average
39
+ puts app.description
40
+ puts app.recent_change
41
+ puts app.last_update
42
+ puts app.file_size
43
+ puts app.downloads
44
+ puts app.version
45
+ puts app.os_requried
46
+
47
+
48
+ # Review
49
+ reviews = gp.reviews('your.app.id')
50
+ reviews = gp.reviews('your.app.id', page: 0)
51
+ reviews = gp.reviews('your.app.id') { |r| (Date.today - r.date) < 7 }
52
+ reviews = gp.reviews('your.app.id', sort_order: :rating) { |r| r.rating == 5 }
53
+
54
+ reviews.each do |review|
55
+ puts review.id
56
+ puts review.user
57
+ puts review.user_id
58
+ puts review.title
59
+ puts review.text
60
+ puts review.date
61
+ puts review.rating
62
+ end
63
+ ```
64
+
65
+ ## Contributing
66
+
67
+ 1. Fork it
68
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
69
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
70
+ 4. Push to the branch (`git push origin my-new-feature`)
71
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'google-play/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'google-player'
8
+ spec.version = GooglePlay::VERSION
9
+ spec.authors = ['mako2x']
10
+ spec.email = ['arekara3nen@gmail.com']
11
+ spec.description = %q{Ruby gem for fetching app info and reviews from Google Play.}
12
+ spec.summary = %q{Fetchs app info and reviews from Google Play.}
13
+ spec.homepage = 'https://github.com/mako2x/google-player'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'nokogiri', '>=1.5.6'
22
+ spec.add_dependency 'hashie'
23
+ spec.add_dependency 'httpclient'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.3'
26
+ spec.add_development_dependency 'rake'
27
+ end
@@ -0,0 +1,62 @@
1
+ require 'json'
2
+ require 'httpclient'
3
+ require 'google-play/app'
4
+ require 'google-play/review'
5
+ require 'google-play/parser'
6
+
7
+ class GooglePlay
8
+ BASE_URL = 'https://play.google.com'
9
+ SORT_ORDERS = [:newest, :rating, :helpfulness]
10
+
11
+ include Parser
12
+
13
+ def initialize
14
+ @client = HTTPClient.new
15
+ end
16
+
17
+ def app(id)
18
+ res = @client.get("#{GooglePlay::BASE_URL}/store/apps/details", {:id => id})
19
+ raise GooglePlay::App::NotFoundError.new("'#{id}' is not found") if res.status_code == 404
20
+ parse_app(res.content)
21
+ end
22
+
23
+ def reviews(id, opts = {})
24
+ page = opts[:page] || :all
25
+ sort_order = opts[:sort_order] || :newest
26
+
27
+ if page.is_a?(Integer)
28
+ return fetch_reviews(id, page, sort_order)
29
+ else
30
+ all_reviews = []
31
+ i = 0
32
+ loop do
33
+ reviews = fetch_reviews(id, i, sort_order)
34
+ return all_reviews if (all_reviews.size > 0) and (all_reviews.last.id == reviews.last.id)
35
+ if block_given?
36
+ reviews.each do |r|
37
+ return all_reviews unless yield(r)
38
+ all_reviews << r
39
+ end
40
+ else
41
+ all_reviews += reviews
42
+ end
43
+ i += 1
44
+ end
45
+ return all_reviews
46
+ end
47
+ end
48
+
49
+ private
50
+ def fetch_reviews(id, page, sort_order)
51
+ sort_order_num = GooglePlay::SORT_ORDERS.find_index { |s| sort_order == s } || 0
52
+ res = @client.post("#{GooglePlay::BASE_URL}/store/getreviews", {
53
+ :id => id,
54
+ :pageNum => page,
55
+ :reviewSortOrder => sort_order_num,
56
+ :reviewType => 1
57
+ })
58
+ html = JSON.parse(res.content.split("\n")[2] + ']')[0][2]
59
+ raise GooglePlay::App::NotFoundError.new("'#{id}' is not found") if html.size == 0
60
+ parse_review(html)
61
+ end
62
+ end
@@ -0,0 +1,7 @@
1
+ require 'hashie'
2
+
3
+ class GooglePlay
4
+ class App < Hashie::Mash
5
+ class NotFoundError < StandardError; end
6
+ end
7
+ end
@@ -0,0 +1,164 @@
1
+ require 'nokogiri'
2
+
3
+ class GooglePlay
4
+ module Parser
5
+ def parse_app(html)
6
+ doc = Nokogiri.HTML(html)
7
+ GooglePlay::App.new(
8
+ :name => parse_app_name(doc),
9
+ :image_url => parse_app_image_url(doc),
10
+ :developer => parse_app_developer(doc),
11
+ :developer_mail => parse_app_developer_mail(doc),
12
+ :developer_web => parse_app_developer_web(doc),
13
+ :category => parse_app_category(doc),
14
+ :rating_count => parse_app_rating_count(doc),
15
+ :rating_counts => parse_app_rating_counts(doc),
16
+ :rating_average => parse_app_rating_average(doc),
17
+ :description => parse_app_descrption(doc),
18
+ :recent_change => parse_app_recent_change(doc),
19
+ :last_update => parse_app_last_update(doc),
20
+ :file_size => parse_app_file_size(doc),
21
+ :downloads => parse_app_downloads(doc),
22
+ :version => parse_app_version(doc),
23
+ :os_requried => parse_app_os_required(doc)
24
+ )
25
+ end
26
+
27
+ def parse_review(html)
28
+ doc = Nokogiri.HTML(html)
29
+ doc.xpath("//div[@class='single-review']").map do |node|
30
+ GooglePlay::Review.new(
31
+ :id => parse_review_id(node),
32
+ :user => parse_review_user(node),
33
+ :user_id => parse_review_user_id(node),
34
+ :date => parse_review_date(node),
35
+ :rating => parse_review_rating(node),
36
+ :title => parse_review_title(node),
37
+ :text => parse_review_text(node)
38
+ )
39
+ end
40
+ end
41
+
42
+ private
43
+ def parse_review_id(node)
44
+ a = node.xpath(".//a[@class='reviews-permalink']").first
45
+ a['href'].match(/reviewId=(\w+)/)[1]
46
+ end
47
+
48
+ def parse_review_user(node)
49
+ a = node.xpath(".//span[@class='author-name']/a").first
50
+ a.nil? ? '' : a['title']
51
+ end
52
+
53
+ def parse_review_user_id(node)
54
+ a = node.xpath(".//span[@class='author-name']/a").first
55
+ a.nil? ? nil : a['data-userid']
56
+ end
57
+
58
+ def parse_review_date(node)
59
+ text = node.xpath(".//span[@class='review-date']").text
60
+ text =~ /(\d+)\D+(\d+)\D+(\d+)/
61
+ Date.new($1.to_i, $2.to_i, $3.to_i)
62
+ end
63
+
64
+ def parse_review_rating(node)
65
+ div = node.xpath(".//div[@class='current-rating']").first
66
+ width = div['style'].match(/width: (\d+)\.0%/)[1].to_i
67
+ width / 20
68
+ end
69
+
70
+ def parse_review_title(node)
71
+ node.xpath(".//span[@class='review-title']").text
72
+ end
73
+
74
+ def parse_review_text(node)
75
+ dup = node.dup
76
+ dup.xpath(".//div[@class='review-link']").remove
77
+ dup.xpath(".//span[@class='review-title']").remove
78
+ dup.xpath(".//div[@class='review-body']").text.strip
79
+ end
80
+
81
+
82
+ def parse_app_name(node)
83
+ node.xpath("//div[@class='document-title']/div").text
84
+ end
85
+
86
+ def parse_app_image_url(node)
87
+ node.xpath("//img[@class='cover-image']").first['src']
88
+ end
89
+
90
+ def parse_app_developer(node)
91
+ node.xpath("//div[@itemprop='author']/a[@itemprop='name']").text
92
+ end
93
+
94
+ def parse_app_developer_web(node)
95
+ a = node.xpath("//a[@class='dev-link']")
96
+ (a.size == 2) ? a.first['href'] : nil
97
+ end
98
+
99
+ def parse_app_developer_mail(node)
100
+ a = node.xpath("//a[@class='dev-link']")
101
+ if a.size == 0
102
+ nil
103
+ else
104
+ a[a.size - 1]['href'].sub('mailto:', '')
105
+ end
106
+ end
107
+
108
+ def parse_app_category(node)
109
+ a = node.xpath("//a[@class='document-subtitle category']").first
110
+ a['href'].match(/category\/(.+)$/)[1].downcase
111
+ end
112
+
113
+ def parse_app_rating_count(node)
114
+ node.xpath("//div[@class='stars-count']").text.match(/(\d+)/)[1]
115
+ end
116
+
117
+ def parse_app_rating_counts(node)
118
+ node.xpath("//span[@class='bar-number']").map { |n| n.text.to_i }
119
+ end
120
+
121
+ def parse_app_rating_average(node)
122
+ node.xpath("//div[@class='score']").text.to_f
123
+ end
124
+
125
+ def parse_app_descrption(node)
126
+ node.xpath("//div[@class='app-orig-desc']").inner_html
127
+ end
128
+
129
+ def parse_app_recent_change(node)
130
+ node.xpath("//div[@class='recent-change']")
131
+ .map { |n| n.text }
132
+ .join("\n")
133
+ end
134
+
135
+ def parse_app_last_update(node)
136
+ text = node.xpath("//div[@itemprop='datePublished']").text
137
+ text =~ /(\d+)\D+(\d+)\D+(\d+)/
138
+ Date.new($1.to_i, $2.to_i, $3.to_i)
139
+ end
140
+
141
+ def parse_app_file_size(node)
142
+ file_size = node.xpath("//div[@itemprop='fileSize']").text.strip
143
+ (file_size =~ /\d+/) ? file_size : :device_dependent
144
+ end
145
+
146
+ def parse_app_downloads(node)
147
+ node.xpath("//div[@itemprop='numDownloads']").text.strip
148
+ end
149
+
150
+ def parse_app_version(node)
151
+ version = node.xpath("//div[@itemprop='softwareVersion']").text.strip
152
+ (version =~ /\d+/) ? version : :device_dependent
153
+ end
154
+
155
+ def parse_app_os_required(node)
156
+ text = node.xpath("//div[@itemprop='operatingSystems']").text
157
+ if text =~ /([\d\.]+)/
158
+ $1
159
+ else
160
+ :device_dependent
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,5 @@
1
+ require 'hashie'
2
+
3
+ class GooglePlay
4
+ class Review < Hashie::Mash; end
5
+ end
@@ -0,0 +1,3 @@
1
+ class GooglePlay
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,125 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: google-player
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - mako2x
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.5.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.5.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: hashie
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: httpclient
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1.3'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1.3'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: Ruby gem for fetching app info and reviews from Google Play.
84
+ email:
85
+ - arekara3nen@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - .gitignore
91
+ - Gemfile
92
+ - LICENSE.txt
93
+ - README.md
94
+ - Rakefile
95
+ - google-player.gemspec
96
+ - lib/google-play.rb
97
+ - lib/google-play/app.rb
98
+ - lib/google-play/parser.rb
99
+ - lib/google-play/review.rb
100
+ - lib/google-play/version.rb
101
+ homepage: https://github.com/mako2x/google-player
102
+ licenses:
103
+ - MIT
104
+ metadata: {}
105
+ post_install_message:
106
+ rdoc_options: []
107
+ require_paths:
108
+ - lib
109
+ required_ruby_version: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - '>='
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ requirements: []
120
+ rubyforge_project:
121
+ rubygems_version: 2.0.3
122
+ signing_key:
123
+ specification_version: 4
124
+ summary: Fetchs app info and reviews from Google Play.
125
+ test_files: []