bookmeter_exporter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +19 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +23 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +82 -0
- data/Rakefile +12 -0
- data/_config.yml +1 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/bookmeter_exporter.gemspec +29 -0
- data/exe/bookmeter_exporter +7 -0
- data/lib/bookmeter_exporter.rb +9 -0
- data/lib/bookmeter_exporter/books.rb +26 -0
- data/lib/bookmeter_exporter/cli.rb +35 -0
- data/lib/bookmeter_exporter/crawler.rb +101 -0
- data/lib/bookmeter_exporter/version.rb +5 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6bdd15cf3580a2b9112053f97c67b50388f8d0c6341a0f73f77f81665fca568d
|
4
|
+
data.tar.gz: 906480de373aacdf3ab810151cdf7887b2cabcf3969fa4082f7213c36e282891
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cdf26fd81c41dc05cf5875515f0422332b43e249f5bf89c6b099a22e1b1af346e7574b865ac3f8d6ef42a9476434ae25d5055ba90b191a9c8fea4070c6e924ca
|
7
|
+
data.tar.gz: 11f56ecd657fff8879b6299012125bf9f0e64eb9d61d09f425f38217a182f018ee1658bf6d2cc49f894332d44c7bd035758366f302e7209153c68f6c09242a8c
|
@@ -0,0 +1,19 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push,pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
strategy:
|
9
|
+
matrix:
|
10
|
+
ruby: [2.6, 2.7, 3.0]
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
- name: Set up Ruby
|
14
|
+
uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: ${{ matrix.ruby}}
|
17
|
+
bundler-cache: true
|
18
|
+
- name: Run the default task
|
19
|
+
run: bundle exec rake
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
NewCops: enable
|
4
|
+
|
5
|
+
Metrics/AbcSize:
|
6
|
+
Max: 30
|
7
|
+
|
8
|
+
Metrics/MethodLength:
|
9
|
+
Max: 20
|
10
|
+
|
11
|
+
Style/Documentation:
|
12
|
+
Enabled: false
|
13
|
+
|
14
|
+
Style/StringLiterals:
|
15
|
+
Enabled: true
|
16
|
+
EnforcedStyle: double_quotes
|
17
|
+
|
18
|
+
Style/StringLiteralsInInterpolation:
|
19
|
+
Enabled: true
|
20
|
+
EnforcedStyle: double_quotes
|
21
|
+
|
22
|
+
Layout/LineLength:
|
23
|
+
Max: 120
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.2
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 Ikuo Degawa
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# bookmeter_exporter
|
2
|
+
|
3
|
+
A command line tool to export read books data from https://bookmeter.com as CSV
|
4
|
+
|
5
|
+
[読書メーター](https://bookmeter.com)から読み終わった本をエクスポートするCLIツール
|
6
|
+
|
7
|
+
## Requirements
|
8
|
+
|
9
|
+
* Ruby >= 2.6 and bundler
|
10
|
+
* `chromedriver` in $PATH and same version of Google Chrome
|
11
|
+
* Download: https://chromedriver.chromium.org/downloads
|
12
|
+
* 読書メーターのアカウントにメールアドレスとパスワードが設定されていること
|
13
|
+
|
14
|
+
## Installation
|
15
|
+
|
16
|
+
```bash
|
17
|
+
$ gem install bookmeter_exporter
|
18
|
+
```
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
```bash
|
23
|
+
$ bookmeter_exporter
|
24
|
+
Commands:
|
25
|
+
bookmeter_exporter export EMAIL # This command exports all read books of an account as CSV.
|
26
|
+
bookmeter_exporter help [COMMAND] # Describe available commands or one specific command
|
27
|
+
bookmeter_exporter version # Display version info
|
28
|
+
```
|
29
|
+
|
30
|
+
Example:
|
31
|
+
|
32
|
+
```bash
|
33
|
+
$ bookmeter_exporter export ikuwow@example.com
|
34
|
+
Password for EMAIL: # Type the password of your account
|
35
|
+
Starting Chrome... # Chrome window opens
|
36
|
+
Login success.
|
37
|
+
Book count: XXX
|
38
|
+
XX books fetched...
|
39
|
+
YY books fetched...
|
40
|
+
[...]
|
41
|
+
Books are successfully exported as './books.csv'.
|
42
|
+
```
|
43
|
+
|
44
|
+
## CSV Format
|
45
|
+
|
46
|
+
|ASIN|Read Date|Review|
|
47
|
+
|---|---|---|
|
48
|
+
|ex. B071K5WM6P|ex. 2021/07/31|ex. This book is awesome!|
|
49
|
+
|
50
|
+
Ex.
|
51
|
+
|
52
|
+
```csv
|
53
|
+
4408167967,2021/06/02,ここまで読んだ
|
54
|
+
B09366V8V2,2021/05/13,""
|
55
|
+
B093KCX58C,2021/05/12,""
|
56
|
+
```
|
57
|
+
|
58
|
+
## Miscellaneous
|
59
|
+
|
60
|
+
### [ブクログ](https://booklog.jp)へのインポート
|
61
|
+
|
62
|
+
ブクログはCSVファイルから本をインポートすることが出来ます:
|
63
|
+
https://booklog.zendesk.com/hc/ja/articles/360048930533-他の読書管理サイトからブクログへデータを移行したいです
|
64
|
+
|
65
|
+
bookmeter_exporterで出力したCSVをExcel等でフォーマットを整えるとよいです。
|
66
|
+
|
67
|
+
## Development
|
68
|
+
|
69
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
70
|
+
|
71
|
+
## Contributing
|
72
|
+
|
73
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ikuwow/bookmeter_exporter.
|
74
|
+
|
75
|
+
## Wishlist
|
76
|
+
|
77
|
+
* More tests
|
78
|
+
* More fields in CSV
|
79
|
+
|
80
|
+
## License
|
81
|
+
|
82
|
+
MIT
|
data/Rakefile
ADDED
data/_config.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
theme: jekyll-theme-cayman
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "bookmeter_exporter"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require "pry"
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require "irb"
|
15
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/bookmeter_exporter/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "bookmeter_exporter"
|
7
|
+
spec.version = BookmeterExporter::VERSION
|
8
|
+
spec.authors = ["Ikuo Degawa"]
|
9
|
+
spec.email = ["degawa@ikuwow.com"]
|
10
|
+
|
11
|
+
spec.summary = "Export user history of bookmeter.com"
|
12
|
+
spec.homepage = "https://ikuwow.github.io/bookmeter_exporter/"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.required_ruby_version = ">= 2.6"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/ikuwow/bookmeter_exporter"
|
18
|
+
spec.metadata["changelog_uri"] = "https://github.com/ikuwow/bookmeter_exporter/blob/master/CHANGELOG.md"
|
19
|
+
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
22
|
+
end
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
|
27
|
+
spec.add_dependency "selenium-webdriver", "~> 3.142.2"
|
28
|
+
spec.add_dependency "thor", "~> 1.1.0"
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module BookmeterExporter
|
6
|
+
Book = Struct.new(:asin, :read_date, :review)
|
7
|
+
|
8
|
+
class Books
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def_delegator :@books, :[]
|
12
|
+
def_delegator :@books, :<<
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@books = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_csv
|
19
|
+
CSV.generate do |csv|
|
20
|
+
@books.each do |book|
|
21
|
+
csv << [book.asin, book.read_date, book.review]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "thor"
|
4
|
+
require "csv"
|
5
|
+
require_relative "crawler"
|
6
|
+
require_relative "version"
|
7
|
+
|
8
|
+
module BookmeterExporter
|
9
|
+
class CLI < Thor
|
10
|
+
class << self
|
11
|
+
def exit_on_failure?
|
12
|
+
true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "export EMAIL", "This command exports all read books of an account as CSV."
|
17
|
+
option :destination, aliases: :d, desc: "path where CSV file is saved to"
|
18
|
+
def export(email)
|
19
|
+
password = ask("Password for #{email}:", echo: false)
|
20
|
+
puts ""
|
21
|
+
|
22
|
+
crawler = BookmeterExporter::Crawler.new(email, password)
|
23
|
+
books = crawler.crawl
|
24
|
+
|
25
|
+
destination = options[:destination] || "./books.csv"
|
26
|
+
IO.write(destination, books.to_csv)
|
27
|
+
puts "Books are successfully exported as '#{destination}'."
|
28
|
+
end
|
29
|
+
|
30
|
+
desc "version", "Display version info"
|
31
|
+
def version
|
32
|
+
puts VERSION
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "selenium-webdriver"
|
4
|
+
require "uri"
|
5
|
+
require "cgi"
|
6
|
+
|
7
|
+
require_relative "books"
|
8
|
+
|
9
|
+
module BookmeterExporter
|
10
|
+
class Crawler
|
11
|
+
def initialize(email, password)
|
12
|
+
@email = email
|
13
|
+
@password = password
|
14
|
+
@root_url = URI("https://bookmeter.com/")
|
15
|
+
@user_id = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def crawl
|
19
|
+
start_webdriver
|
20
|
+
login
|
21
|
+
book_urls = collect_book_urls
|
22
|
+
fetch_books(book_urls)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def start_webdriver
|
28
|
+
puts "Starting Chrome..."
|
29
|
+
@driver = Selenium::WebDriver.for :chrome
|
30
|
+
@wait = Selenium::WebDriver::Wait.new(timeout: 10)
|
31
|
+
end
|
32
|
+
|
33
|
+
def login
|
34
|
+
@driver.get @root_url.merge("/login")
|
35
|
+
@driver.find_element(:id, "session_email_address").send_keys @email
|
36
|
+
@driver.find_element(:id, "session_password").send_keys @password
|
37
|
+
@driver.find_element(:css, "form[action='/login'] button[type=submit]").click
|
38
|
+
@wait.until { @driver.current_url == @root_url.merge("/home").to_s }
|
39
|
+
|
40
|
+
puts "Login success."
|
41
|
+
|
42
|
+
@user_id = @driver.find_element(:css, ".personal-account__data__link").attribute("href")[%r{/([0-9]+)$}, 1]
|
43
|
+
end
|
44
|
+
|
45
|
+
def fetch_books(book_urls)
|
46
|
+
books = Books.new
|
47
|
+
fetched_books_count = 0
|
48
|
+
book_urls.each do |url|
|
49
|
+
books << fetch_book(url)
|
50
|
+
fetched_books_count += 1
|
51
|
+
puts "#{fetched_books_count} books fetched..." if (fetched_books_count % 10).zero?
|
52
|
+
end
|
53
|
+
puts "All books fetched."
|
54
|
+
books
|
55
|
+
end
|
56
|
+
|
57
|
+
def collect_book_urls
|
58
|
+
go_read_books
|
59
|
+
|
60
|
+
last_page_url = @driver.find_element(:css, "ul.bm-pagination").find_element(:link_text, "最後").attribute("href")
|
61
|
+
last_page = CGI.parse(URI.parse(last_page_url).query)["page"].shift.to_i
|
62
|
+
|
63
|
+
urls = []
|
64
|
+
(1..last_page).each do |page|
|
65
|
+
page_url = @root_url.merge("/users/#{@user_id}/books/read?page=#{page}")
|
66
|
+
@driver.get page_url
|
67
|
+
|
68
|
+
book_list_css_selector = ".book-list--grid ul.book-list__group"
|
69
|
+
@wait.until { @driver.current_url == page_url.to_s }
|
70
|
+
@driver.find_elements(:css, book_list_css_selector).each do |ul|
|
71
|
+
ul.find_elements(:css, "li .detail__title").each do |li|
|
72
|
+
urls << li.find_element(:tag_name, "a").attribute("href")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
puts "Book count: #{urls.count}"
|
78
|
+
urls
|
79
|
+
end
|
80
|
+
|
81
|
+
def go_read_books
|
82
|
+
read_books_url = @root_url.merge("/users/#{@user_id}/books/read")
|
83
|
+
@driver.get read_books_url
|
84
|
+
@wait.until { sleep 3 }
|
85
|
+
end
|
86
|
+
|
87
|
+
def fetch_book(url)
|
88
|
+
@driver.get url
|
89
|
+
@wait.until do
|
90
|
+
%r{/books/[0-9]+$}.match(@driver.current_url)
|
91
|
+
end
|
92
|
+
|
93
|
+
book_asin = @driver.find_element(:css, ".sidebar__group .group__image a").attribute("href")
|
94
|
+
.gsub(%r{https://www.amazon.co.jp/dp/product/(.+)/.*}, '\1')
|
95
|
+
read_date = @driver.find_element(:css, ".read-book__date").text
|
96
|
+
review_text = @driver.find_element(:css, ".read-book__content").text
|
97
|
+
|
98
|
+
Book.new(book_asin, read_date, review_text)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
metadata
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bookmeter_exporter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ikuo Degawa
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-07-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: selenium-webdriver
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.142.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.142.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: thor
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.0
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.1.0
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- degawa@ikuwow.com
|
44
|
+
executables:
|
45
|
+
- bookmeter_exporter
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".github/workflows/main.yml"
|
50
|
+
- ".gitignore"
|
51
|
+
- ".rspec"
|
52
|
+
- ".rubocop.yml"
|
53
|
+
- ".ruby-version"
|
54
|
+
- CHANGELOG.md
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- _config.yml
|
60
|
+
- bin/console
|
61
|
+
- bin/setup
|
62
|
+
- bookmeter_exporter.gemspec
|
63
|
+
- exe/bookmeter_exporter
|
64
|
+
- lib/bookmeter_exporter.rb
|
65
|
+
- lib/bookmeter_exporter/books.rb
|
66
|
+
- lib/bookmeter_exporter/cli.rb
|
67
|
+
- lib/bookmeter_exporter/crawler.rb
|
68
|
+
- lib/bookmeter_exporter/version.rb
|
69
|
+
homepage: https://ikuwow.github.io/bookmeter_exporter/
|
70
|
+
licenses:
|
71
|
+
- MIT
|
72
|
+
metadata:
|
73
|
+
homepage_uri: https://ikuwow.github.io/bookmeter_exporter/
|
74
|
+
source_code_uri: https://github.com/ikuwow/bookmeter_exporter
|
75
|
+
changelog_uri: https://github.com/ikuwow/bookmeter_exporter/blob/master/CHANGELOG.md
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '2.6'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
requirements: []
|
91
|
+
rubygems_version: 3.2.25
|
92
|
+
signing_key:
|
93
|
+
specification_version: 4
|
94
|
+
summary: Export user history of bookmeter.com
|
95
|
+
test_files: []
|