yasuri 3.1.0 → 3.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3542a2cc0959a4534520f6104fc2922bdf0dbd368fcd4c149c3d251c2fc2198
4
- data.tar.gz: 6fdb960db697e9a4ec1d87f2b83bf0e9914e3c9efe90764536bbee6d68774353
3
+ metadata.gz: cd5fc7327c6d09b37771ac1c3ec40db2c052bf49ec9a1627e9ae49e047102856
4
+ data.tar.gz: a645f1e09ce72b73c54e2055af6fbf81bb145c8823e1d8428bb19c042bbb661d
5
5
  SHA512:
6
- metadata.gz: 9df576243bea289f4c285c46f1bd2137b7b69b79b24e0c657e4ac952114dd7bcf82a5f95cd2dae88c6eac4e3e468273b7dbd6ead9d05ffdc8d25861921702333
7
- data.tar.gz: 13f2ae72b3e8fa6d3ef58932daa2acad49f5d4f57c80f34e5215394940fc2305bc016d949760efe9f43ae2b8c3796064a1b0bd9bccf236cfe3789c2c291dfd8b
6
+ metadata.gz: 654bd6cfe8012811283b1aa03e0dcc1200ce957ef4641eed2b5fa65956fb974070157b832e42f340d7299031756848c5118a7f43019ff94f088c49974e2304e8
7
+ data.tar.gz: 5ad07b82672ea2ceebfb8154bb91631c095e9ad8d69f3d62c0bf8d528c4c539fab2597f4112b4212bffe7ad641b30d913686e8e2bfea7dfdbdd9a4468311b6c0
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  [![Build Status](https://github.com/tac0x2a/yasuri/actions/workflows/ruby.yml/badge.svg)](https://github.com/tac0x2a/yasuri/actions/workflows/ruby.yml)
3
3
  [![Coverage Status](https://coveralls.io/repos/tac0x2a/yasuri/badge.svg?branch=master)](https://coveralls.io/r/tac0x2a/yasuri?branch=master) [![Maintainability](https://api.codeclimate.com/v1/badges/c29480fea1305afe999f/maintainability)](https://codeclimate.com/github/tac0x2a/yasuri/maintainability)
4
4
 
5
- Yasuri (鑢) is an easy web-scraping library for supporting "[Mechanize](https://github.com/sparklemotion/mechanize)".
5
+ Yasuri (鑢) is an easy web-scraping library for supporting "[Mechanize](https://github.com/sparklemotion/mechanize)", and CLI tool using it.
6
6
 
7
7
  Yasuri can reduce frequently processes in Scraping.
8
8
 
@@ -33,10 +33,10 @@ or
33
33
 
34
34
  ```ruby
35
35
  # for Ruby 1.9.3 or lower
36
- gem 'yasuri', '~> 1.9'
36
+ gem 'yasuri', '~> 2.0', '>= 2.0.13'
37
37
 
38
38
  # for Ruby 3.0.0 or lower
39
- gem 'yasuri', '~> 3.0.1'
39
+ gem 'yasuri', '~> 3.1'
40
40
  ```
41
41
 
42
42
 
@@ -49,6 +49,7 @@ Or install it yourself as:
49
49
  $ gem install yasuri
50
50
 
51
51
  ## Usage
52
+ ### Use as library
52
53
 
53
54
  ```ruby
54
55
  # Node tree constructing by DSL
@@ -60,40 +61,64 @@ root = Yasuri.links_root '//*[@id="menu"]/ul/li/a' do
60
61
 
61
62
  # Node tree constructing by YAML
62
63
  src = <<-EOYAML
63
- root:
64
- node: links
64
+ links_root:
65
65
  path: "//*[@id='menu']/ul/li/a"
66
- children:
67
- - title: { node: text, path: "//*[@id='contents']/h2" }
68
- - content: { node: text, path: "//*[@id='contents']/p[1]" }
66
+ text_title: "//*[@id='contents']/h2"
67
+ text_content: "//*[@id='contents']/p[1]"
69
68
  EOYAML
70
69
  root = Yasuri.yaml2tree(src)
71
70
 
72
71
 
73
72
  # Node tree constructing by JSON
74
73
  src = <<-EOJSON
75
- { "node" : "links",
76
- "name" : "root",
77
- "path" : "//*[@id='menu']/ul/li/a",
78
- "children" : [
79
- { "node" : "text",
80
- "name" : "title",
81
- "path" : "//*[@id='contents']/h2"
82
- },
83
- { "node" : "text",
84
- "name" : "content",
85
- "path" : "//*[@id='contents']/p[1]"
86
- }
87
- ]
88
- }
74
+ {
75
+ "links_root": {
76
+ "path": "//*[@id='menu']/ul/li/a",
77
+ "text_title": "//*[@id='contents']/h2",
78
+ "text_content": "//*[@id='contents']/p[1]"
79
+ }
80
+ }
89
81
  EOJSON
90
82
  root = Yasuri.json2tree(src)
91
83
 
84
+
85
+ # Execution and getting scraped result
92
86
  agent = Mechanize.new
93
- root_page = agent.get("http://some.scraping.page.net/")
87
+ root_page = agent.get("http://some.scraping.page.tac42.net/")
94
88
 
95
89
  result = root.inject(agent, root_page)
96
- # => [ {"title" => "PageTitle", "content" => "Page Contents" }, ... ]
90
+ # => [
91
+ # {"title" => "PageTitle 01", "content" => "Page Contents 01" },
92
+ # {"title" => "PageTitle 02", "content" => "Page Contents 02" },
93
+ # ...
94
+ # {"title" => "PageTitle N", "content" => "Page Contents N" }
95
+ # ]
96
+ ```
97
+
98
+ ### Use as CLI
99
+
100
+ ```sh
101
+ # After gem installation..
102
+ $ yasuri help scrape
103
+ Usage:
104
+ yasuri scrape <URI> [[--file <TREE_FILE>] or [--json <JSON>]]
105
+
106
+ Options:
107
+ f, [--file=FILE] # path to file that written yasuri tree as json or yaml
108
+ j, [--json=JSON] # yasuri tree format json string
109
+
110
+ Getting from <URI> and scrape it. with <JSON> or json/yml from <TREE_FILE>. They should be Yasuri's format json or yaml string.
111
+ ```
112
+
113
+ Example
114
+ ```sh
115
+ $ yasuri scrape "https://www.ruby-lang.org/en/" -j '
116
+ {
117
+ "text_title": "/html/head/title",
118
+ "text_desc": "//*[@id=\"intro\"]/p"
119
+ }'
120
+
121
+ {"title":"Ruby Programming Language","desc":"\n A dynamic, open source programming language with a focus on\n simplicity and productivity. It has an elegant syntax that is\n natural to read and easy to write.\n "}
97
122
  ```
98
123
 
99
124
  ## Dev
@@ -108,6 +133,11 @@ $ rake
108
133
  $ rspec spec/*spec.rb
109
134
  ```
110
135
 
136
+ ### Test gem in local
137
+ ```sh
138
+ $ gem build yasuri.gemspec
139
+ $ gem install yasuri-*.gem
140
+ ```
111
141
  ### Release RubyGems
112
142
  ```sh
113
143
  # Only first time
data/USAGE.ja.md CHANGED
@@ -1,24 +1,31 @@
1
- # Yasuri の使い方
1
+ # Yasuri
2
2
 
3
3
  ## Yasuri とは
4
- Yasuri (鑢) は簡単にWebスクレイピングを行うための、"[Mechanize](https://github.com/sparklemotion/mechanize)" をサポートするライブラリです.
4
+ Yasuri (鑢) Webスクレイピングを宣言的に行うためのライブラリと、それを用いたスクレイピングのコマンドラインツールです。
5
+ 簡単な宣言的記法で期待結果を記述するだけで、"[Mechanize](https://github.com/sparklemotion/mechanize)" によるスクレイピングを実行します。
5
6
 
6
7
  Yasuriは、スクレイピングにおける、よくある処理を簡単に記述することができます.
7
- 例えば、
8
+ 例えば、以下のような処理を簡単に実現することができます.
8
9
 
9
- + ページ内の複数のリンクを開いて、各ページをスクレイピングした結果をHashで取得する
10
10
  + ページ内の複数のテキストをスクレイピングし、名前をつけてHashにする
11
+ + ページ内の複数のリンクを開いて、各ページをスクレイピングした結果をHashで取得する
11
12
  + ページ内に繰り返し出現するテーブルをそれぞれスクレイピングして、配列として取得する
12
- + ページネーションで提供される各ページのうち、上位3つだけを順にスクレイピングする
13
-
14
- これらを簡単に実装することができます.
13
+ + ページネーションで提供される各ページのうち、最初の3ページだけをスクレイピングする
15
14
 
16
15
  ## クイックスタート
17
16
 
17
+ #### インストール
18
+ ```sh
19
+ # for Ruby 2.3.2
20
+ $ gem 'yasuri', '~> 2.0', '>= 2.0.13'
18
21
  ```
22
+ または
23
+ ```sh
24
+ # for Ruby 3.0.0 or upper
19
25
  $ gem install yasuri
20
26
  ```
21
27
 
28
+ #### ライブラリとして使う
22
29
  ```ruby
23
30
  require 'yasuri'
24
31
  require 'machinize'
@@ -30,88 +37,59 @@ root = Yasuri.links_root '//*[@id="menu"]/ul/li/a' do
30
37
  end
31
38
 
32
39
  agent = Mechanize.new
33
- root_page = agent.get("http://some.scraping.page.net/")
40
+ root_page = agent.get("http://some.scraping.page.tac42.net/")
34
41
 
35
42
  result = root.inject(agent, root_page)
36
- # => [ {"title" => "PageTitle1", "content" => "Page Contents1" },
37
- # {"title" => "PageTitle2", "content" => "Page Contents2" }, ... ]
38
-
43
+ # => [
44
+ # {"title" => "PageTitle 01", "content" => "Page Contents 01" },
45
+ # {"title" => "PageTitle 02", "content" => "Page Contents 02" },
46
+ # ...
47
+ # {"title" => "PageTitle N", "content" => "Page Contents N" }
48
+ # ]
39
49
  ```
50
+
40
51
  この例では、 LinkNode(`links_root`)の xpath で指定された各リンク先のページから、TextNode(`text_title`,`text_content`) の xpath で指定された2つのテキストをスクレイピングする例です.
41
52
 
42
53
  (言い換えると、`//*[@id="menu"]/ul/li/a` で示される各リンクを開いて、`//*[@id="contents"]/h2` と `//*[@id="contents"]/p[1]` で指定されたテキストをスクレイピングします)
43
54
 
44
- ## 基本
45
-
46
- 1. パースツリーを作る
47
- 2. Mechanize の agent と対象のページを与えてパースを開始する
48
-
49
55
 
50
- ### パースツリーを作る
51
-
52
- ```ruby
53
- require 'mechanize'
54
- require 'yasuri'
56
+ #### CLIツールとして使う
57
+ 上記と同じことを、CLIのコマンドとして実行できます。
55
58
 
59
+ ```sh
60
+ $ yasuri scrape "http://some.scraping.page.tac42.net/" -j '
61
+ {
62
+ "links_root": {
63
+ "path": "//*[@id=\"menu\"]/ul/li/a",
64
+ "text_title": "//*[@id=\"contents\"]/h2",
65
+ "text_content": "//*[@id=\"contents\"]/p[1]"
66
+ }
67
+ }'
56
68
 
57
- # 1. パースツリーを作る
58
- tree = Yasuri.links_title '/html/body/a' do
59
- text_name '/html/body/p'
60
- end
61
-
62
- # 2. Mechanize の agent と対象のページを与えてパースを開始する
63
- agent = Mechanize.new
64
- page = agent.get(uri)
65
-
66
-
67
- tree.inject(agent, page)
69
+ [
70
+ {"title":"PageTitle 01","content":"Page Contents 01"},
71
+ {"title":"PageTitle 02","content":"Page Contents 02"},
72
+ ...,
73
+ {"title":"PageTitle N","content":"Page Contents N"}
74
+ ]
68
75
  ```
69
76
 
70
- ツリーは、json,yaml,またはDSLで定義することができます.上の例ではDSLで定義しています.
71
- 以下は、jsonで上記と等価な解析ツリーを定義した例です.
77
+ 結果はjson形式の文字列として取得できます。
72
78
 
73
- ```ruby
74
- # json で構成する場合
75
- src = <<-EOJSON
76
- { "node" : "links",
77
- "name" : "title",
78
- "path" : "/html/body/a",
79
- "children" : [
80
- { "node" : "text",
81
- "name" : "name",
82
- "path" : "/html/body/p"
83
- }
84
- ]
85
- }
86
- EOJSON
87
- tree = Yasuri.json2tree(src)
88
- ```
79
+ ----------------------------
80
+ ## パースツリー
89
81
 
90
- ```ruby
91
- # yaml で構成する場合
92
- src = <<-EOYAML
93
- title:
94
- node: links
95
- path: "/html/body/a"
96
- children:
97
- - name:
98
- node: text
99
- path: "/html/body/p"
100
- EOYAML
101
- tree = Yasuri.yaml2tree(src)
102
- ```
82
+ パースツリーとは、スクレイピングする要素と出力構造を宣言的に定義するための木構造データです。
83
+ パースツリーは入れ子になった Node で構成されます.Node は `Type`, `Name`, `Path`, `Childlen`, `Options` 属性を持っており、その `Type` に応じたスクレイピング処理を行います.(ただし、`MapNode` のみ `Path` を持ちません)
103
84
 
104
- ### Node
105
- ツリーは入れ子になった *Node* で構成されます.
106
- Node は `Type`, `Name`, `Path`, `Childlen`, `Options` を持っています.
107
- (ただし、`MapNode` のみ `Path` を持ちません)
108
85
 
109
- Nodeは以下のフォーマットで定義されます.
86
+ パースツリーは以下のフォーマットで定義されます.
110
87
 
111
88
  ```ruby
89
+ # 1ノードからなる単純なツリー
112
90
  Yasuri.<Type>_<Name> <Path> [,<Options>]
113
91
 
114
- # 入れ子になっている場合
92
+ # 入れ子になっているツリー
115
93
  Yasuri.<Type>_<Name> <Path> [,<Options>] do
116
94
  <Type>_<Name> <Path> [,<Options>] do
117
95
  <Type>_<Name> <Path> [,<Options>]
@@ -120,12 +98,13 @@ Yasuri.<Type>_<Name> <Path> [,<Options>] do
120
98
  end
121
99
  ```
122
100
 
123
-
101
+ **例**
124
102
 
125
103
  ```ruby
104
+ # 1ノードからなる単純なツリー
126
105
  Yasuri.text_title '/html/head/title', truncate:/^[^,]+/
127
106
 
128
- # 入れ子になっている場合
107
+ # 入れ子になっているツリー
129
108
  Yasuri.links_root '//*[@id="menu"]/ul/li/a' do
130
109
  struct_table './tr' do
131
110
  text_title './td[1]'
@@ -135,6 +114,71 @@ end
135
114
  ```
136
115
 
137
116
 
117
+ パースツリーはRubyのDSL、JSON、YAMLのいずれかで定義することができます。
118
+ 以下は、上記と同じパースツリーをそれぞれの記法で定義した例です。
119
+
120
+ **Ruby DSLで定義する場合**
121
+ ```ruby
122
+ Yasuri.links_title '/html/body/a' do
123
+ text_name '/html/body/p'
124
+ end
125
+ ```
126
+
127
+ **JSONで定義する場合**
128
+ ```json
129
+ {
130
+ links_title": {
131
+ "path": "/html/body/a",
132
+ "text_name": "/html/body/p"
133
+ }
134
+ }
135
+ ```
136
+
137
+ **YAMLで定義する場合**
138
+ ```yaml
139
+ links_title:
140
+ path: "/html/body/a"
141
+ text_name: "/html/body/p"
142
+ ```
143
+
144
+ **パースツリーの特殊なケース**
145
+
146
+ rootの直下の要素が1つだけの場合、Hash(Object)ではなく、その要素を直接返します。
147
+ ```json
148
+ {
149
+ "text_title": "/html/head/title",
150
+ "text_body": "/html/body",
151
+ }
152
+ # => {"title": "Welcome to yasuri!", "body": "Yasuri is ..."}
153
+
154
+ {
155
+ "text_title": "/html/head/title"}
156
+ }
157
+ # => Welcome to yasuri!
158
+ ```
159
+
160
+
161
+ jsonまたはyaml形式では、子Nodeを持たない場合、`path` を直接値に指定することができます。以下の2つのjsonは同じパースツリーになります。
162
+
163
+ ```json
164
+ {
165
+ "text_name": "/html/body/p"
166
+ }
167
+
168
+ {
169
+ "text_name": {
170
+ "path": "/html/body/p"
171
+ }
172
+ }
173
+ ```
174
+
175
+
176
+ --------------------------
177
+ ## Node
178
+
179
+ Nodeはパースツリーの節または葉となる要素で、`Type`, `Name`, `Path`, `Childlen`, `Options` を持っており、その `Type` に応じてスクレイピングを行います.(ただし、`MapNode` のみ `Path` を持ちません)
180
+
181
+
138
182
  #### Type
139
183
  *Type* は Nodeの振る舞いを示します.Typeには以下のものがあります.
140
184
 
@@ -144,6 +188,8 @@ end
144
188
  - *Paginate*
145
189
  - *Map*
146
190
 
191
+ 詳細は各ノードの説明を参照してください。
192
+
147
193
  #### Name
148
194
  *Name* は 解析結果のHashにおけるキーになります.
149
195
 
@@ -193,7 +239,8 @@ p1t.inject(agent, page) #=> "Hello"
193
239
  p2u.inject(agent, page) #=> "HELLO,WORLD"
194
240
  ```
195
241
 
196
- なお、同じページ内の複数の要素を一度にスクレイピングする場合は、`MapNode`を使用します。
242
+ なお、同じページ内の複数の要素を一度にスクレイピングする場合は、`MapNode`を使用します。詳細は、`MapNode`の例を参照してください。
243
+
197
244
 
198
245
  ### オプション
199
246
  ##### `truncate`
@@ -549,3 +596,100 @@ tree.inject(agent, page) #=> {
549
596
 
550
597
  ### オプション
551
598
  なし
599
+
600
+
601
+ -------------------------
602
+ ## 使い方
603
+
604
+ #### ライブラリとして使用する場合
605
+ ライブラリとして使用する場合は、DSL, json, yaml の形式でツリーを定義できます。
606
+ ```ruby
607
+ require 'mechanize'
608
+ require 'yasuri'
609
+
610
+
611
+ # 1. パースツリーを作る
612
+ # DSLで定義する倍
613
+ tree = Yasuri.links_title '/html/body/a' do
614
+ text_name '/html/body/p'
615
+ end
616
+
617
+ # jsonで定義する場合
618
+ src = <<-EOJSON
619
+ {
620
+ links_title": {
621
+ "path": "/html/body/a",
622
+ "text_name": "/html/body/p"
623
+ }
624
+ }
625
+ EOJSON
626
+ tree = Yasuri.json2tree(src)
627
+
628
+
629
+ # yamlで定義する場合
630
+ src = <<-EOYAML
631
+ links_title:
632
+ path: "/html/body/a"
633
+ text_name: "/html/body/p"
634
+ EOYAML
635
+ tree = Yasuri.yaml2tree(src)
636
+
637
+
638
+
639
+ # 2. Mechanize の agent と対象のページを与えてパースを開始する
640
+ agent = Mechanize.new
641
+ page = agent.get(uri)
642
+
643
+
644
+ tree.inject(agent, page)
645
+ ```
646
+
647
+ #### CLIツールとして使用する場合
648
+
649
+ **ヘルプ表示**
650
+ ```sh
651
+ $ yasuri help scrape
652
+ Usage:
653
+ yasuri scrape <URI> [[--file <TREE_FILE>] or [--json <JSON>]]
654
+
655
+ Options:
656
+ f, [--file=FILE] # path to file that written yasuri tree as json or yaml
657
+ j, [--json=JSON] # yasuri tree format json string
658
+
659
+ Getting from <URI> and scrape it. with <JSON> or json/yml from <TREE_FILE>. They should be Yasuri's format json or yaml string.
660
+ ```
661
+
662
+ CLIツールでは以下のどちらかの方法でパースツリーを指定します。
663
+ + `--file`, `-f` オプションで、ファイルに出力されたjson形式またはyaml形式のパースツリーを読み込む
664
+ + `--json`, `-j` オプションで、パースツリーを文字列として直接指定する
665
+
666
+
667
+ **パースツリーをファイルで指定する例**
668
+ ```sh
669
+ % cat sample.yml
670
+ text_title: "/html/head/title"
671
+ text_desc: "//*[@id=\"intro\"]/p"
672
+
673
+ % yasuri scrape "https://www.ruby-lang.org/en/" --file sample.yml
674
+ {"title":"Ruby Programming Language","desc":"\n A dynamic, open source programming language with a focus on\n simplicity and productivity. It has an elegant syntax that is\n natural to read and easy to write.\n "}
675
+
676
+ % cat sample.json
677
+ {
678
+ "text_title": "/html/head/title",
679
+ "text_desc": "//*[@id=\"intro\"]/p"
680
+ }
681
+
682
+ % yasuri scrape "https://www.ruby-lang.org/en/" --file sample.json
683
+ {"title":"Ruby Programming Language","desc":"\n A dynamic, open source programming language with a focus on\n simplicity and productivity. It has an elegant syntax that is\n natural to read and easy to write.\n "}
684
+ ```
685
+
686
+ **パースツリーをjsonで直接指定する例**
687
+ ```sh
688
+ $ yasuri scrape "https://www.ruby-lang.org/en/" -j '
689
+ {
690
+ "text_title": "/html/head/title",
691
+ "text_desc": "//*[@id=\"intro\"]/p"
692
+ }'
693
+
694
+ {"title":"Ruby Programming Language","desc":"\n A dynamic, open source programming language with a focus on\n simplicity and productivity. It has an elegant syntax that is\n natural to read and easy to write.\n "}
695
+ ```