yasuri 2.0.11 → 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 +5 -5
- data/.github/workflows/ruby.yml +35 -0
- data/.gitignore +1 -2
- data/.ruby-version +1 -0
- data/.travis.yml +1 -3
- data/README.md +88 -19
- data/USAGE.ja.md +325 -63
- data/USAGE.md +335 -69
- data/exe/yasuri +5 -0
- data/lib/yasuri.rb +1 -0
- data/lib/yasuri/version.rb +1 -1
- data/lib/yasuri/yasuri.rb +80 -39
- data/lib/yasuri/yasuri_cli.rb +64 -0
- data/lib/yasuri/yasuri_links_node.rb +10 -6
- data/lib/yasuri/yasuri_map_node.rb +39 -0
- data/lib/yasuri/yasuri_node.rb +24 -3
- data/lib/yasuri/yasuri_node_generator.rb +16 -11
- data/lib/yasuri/yasuri_paginate_node.rb +18 -6
- data/lib/yasuri/yasuri_struct_node.rb +8 -4
- data/lib/yasuri/yasuri_text_node.rb +11 -4
- data/spec/cli_resources/tree.json +8 -0
- data/spec/cli_resources/tree.yml +5 -0
- data/spec/cli_resources/tree_wrong.json +9 -0
- data/spec/cli_resources/tree_wrong.yml +6 -0
- data/spec/htdocs/struct/structual_links.html +30 -0
- data/spec/htdocs/{structual_text.html → struct/structual_text.html} +0 -0
- data/spec/spec_helper.rb +1 -6
- data/spec/yasuri_cli_spec.rb +83 -0
- data/spec/yasuri_links_node_spec.rb +12 -4
- data/spec/yasuri_map_spec.rb +76 -0
- data/spec/yasuri_paginate_node_spec.rb +43 -0
- data/spec/yasuri_spec.rb +199 -84
- data/spec/yasuri_struct_node_spec.rb +42 -1
- data/yasuri.gemspec +5 -3
- metadata +52 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: cd5fc7327c6d09b37771ac1c3ec40db2c052bf49ec9a1627e9ae49e047102856
|
4
|
+
data.tar.gz: a645f1e09ce72b73c54e2055af6fbf81bb145c8823e1d8428bb19c042bbb661d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 654bd6cfe8012811283b1aa03e0dcc1200ce957ef4641eed2b5fa65956fb974070157b832e42f340d7299031756848c5118a7f43019ff94f088c49974e2304e8
|
7
|
+
data.tar.gz: 5ad07b82672ea2ceebfb8154bb91631c095e9ad8d69f3d62c0bf8d528c4c539fab2597f4112b4212bffe7ad641b30d913686e8e2bfea7dfdbdd9a4468311b6c0
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
+
|
8
|
+
name: Ruby
|
9
|
+
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [ master ]
|
13
|
+
pull_request:
|
14
|
+
branches: [ master ]
|
15
|
+
|
16
|
+
jobs:
|
17
|
+
test:
|
18
|
+
|
19
|
+
runs-on: ubuntu-latest
|
20
|
+
strategy:
|
21
|
+
matrix:
|
22
|
+
ruby-version: ['2.6', '2.7', '3.0']
|
23
|
+
|
24
|
+
steps:
|
25
|
+
- uses: actions/checkout@v2
|
26
|
+
- name: Set up Ruby
|
27
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
28
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
29
|
+
# uses: ruby/setup-ruby@v1
|
30
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
31
|
+
with:
|
32
|
+
ruby-version: ${{ matrix.ruby-version }}
|
33
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
34
|
+
- name: Run tests
|
35
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.0.0
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
# Yasuri
|
1
|
+
# Yasuri
|
2
|
+
[](https://github.com/tac0x2a/yasuri/actions/workflows/ruby.yml)
|
3
|
+
[](https://coveralls.io/r/tac0x2a/yasuri?branch=master) [](https://codeclimate.com/github/tac0x2a/yasuri/maintainability)
|
2
4
|
|
3
|
-
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.
|
4
6
|
|
5
7
|
Yasuri can reduce frequently processes in Scraping.
|
6
8
|
|
@@ -31,7 +33,10 @@ or
|
|
31
33
|
|
32
34
|
```ruby
|
33
35
|
# for Ruby 1.9.3 or lower
|
34
|
-
gem 'yasuri', '~>
|
36
|
+
gem 'yasuri', '~> 2.0', '>= 2.0.13'
|
37
|
+
|
38
|
+
# for Ruby 3.0.0 or lower
|
39
|
+
gem 'yasuri', '~> 3.1'
|
35
40
|
```
|
36
41
|
|
37
42
|
|
@@ -44,6 +49,7 @@ Or install it yourself as:
|
|
44
49
|
$ gem install yasuri
|
45
50
|
|
46
51
|
## Usage
|
52
|
+
### Use as library
|
47
53
|
|
48
54
|
```ruby
|
49
55
|
# Node tree constructing by DSL
|
@@ -52,32 +58,95 @@ root = Yasuri.links_root '//*[@id="menu"]/ul/li/a' do
|
|
52
58
|
text_content '//*[@id="contents"]/p[1]'
|
53
59
|
end
|
54
60
|
|
61
|
+
|
62
|
+
# Node tree constructing by YAML
|
63
|
+
src = <<-EOYAML
|
64
|
+
links_root:
|
65
|
+
path: "//*[@id='menu']/ul/li/a"
|
66
|
+
text_title: "//*[@id='contents']/h2"
|
67
|
+
text_content: "//*[@id='contents']/p[1]"
|
68
|
+
EOYAML
|
69
|
+
root = Yasuri.yaml2tree(src)
|
70
|
+
|
71
|
+
|
55
72
|
# Node tree constructing by JSON
|
56
73
|
src = <<-EOJSON
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
},
|
65
|
-
{ "node" : "text",
|
66
|
-
"name" : "content",
|
67
|
-
"path" : "//*[@id='contents']/p[1]"
|
68
|
-
}
|
69
|
-
]
|
70
|
-
}
|
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
|
+
}
|
71
81
|
EOJSON
|
72
82
|
root = Yasuri.json2tree(src)
|
73
83
|
|
84
|
+
|
85
|
+
# Execution and getting scraped result
|
74
86
|
agent = Mechanize.new
|
75
|
-
root_page = agent.get("http://some.scraping.page.net/")
|
87
|
+
root_page = agent.get("http://some.scraping.page.tac42.net/")
|
76
88
|
|
77
89
|
result = root.inject(agent, root_page)
|
78
|
-
# => [
|
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.
|
79
111
|
```
|
80
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 "}
|
122
|
+
```
|
123
|
+
|
124
|
+
## Dev
|
125
|
+
```sh
|
126
|
+
$ gem install bundler
|
127
|
+
$ bundle install
|
128
|
+
```
|
129
|
+
### Test
|
130
|
+
```sh
|
131
|
+
$ rake
|
132
|
+
# or
|
133
|
+
$ rspec spec/*spec.rb
|
134
|
+
```
|
135
|
+
|
136
|
+
### Test gem in local
|
137
|
+
```sh
|
138
|
+
$ gem build yasuri.gemspec
|
139
|
+
$ gem install yasuri-*.gem
|
140
|
+
```
|
141
|
+
### Release RubyGems
|
142
|
+
```sh
|
143
|
+
# Only first time
|
144
|
+
$ curl -u <user_name> https://rubygems.org/api/v1/api_key.yaml > ~/.gem/credentials
|
145
|
+
$ chmod 0600 ~/.gem/credentials
|
146
|
+
|
147
|
+
$ nano lib/yasuri/version.rb # edit gem version
|
148
|
+
$ rake release
|
149
|
+
```
|
81
150
|
|
82
151
|
## Contributing
|
83
152
|
|
data/USAGE.ja.md
CHANGED
@@ -1,24 +1,31 @@
|
|
1
|
-
# Yasuri
|
1
|
+
# Yasuri
|
2
2
|
|
3
3
|
## Yasuri とは
|
4
|
-
Yasuri (鑢)
|
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
|
-
+
|
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,82 +37,148 @@ 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
|
-
# => [
|
37
|
-
# {"title" => "
|
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
55
|
|
46
|
-
|
47
|
-
|
56
|
+
#### CLIツールとして使う
|
57
|
+
上記と同じことを、CLIのコマンドとして実行できます。
|
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
|
+
}'
|
68
|
+
|
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
|
+
]
|
75
|
+
```
|
48
76
|
|
77
|
+
結果はjson形式の文字列として取得できます。
|
49
78
|
|
50
|
-
|
79
|
+
----------------------------
|
80
|
+
## パースツリー
|
51
81
|
|
52
|
-
|
53
|
-
|
54
|
-
require 'yasuri'
|
82
|
+
パースツリーとは、スクレイピングする要素と出力構造を宣言的に定義するための木構造データです。
|
83
|
+
パースツリーは入れ子になった Node で構成されます.Node は `Type`, `Name`, `Path`, `Childlen`, `Options` 属性を持っており、その `Type` に応じたスクレイピング処理を行います.(ただし、`MapNode` のみ `Path` を持ちません)
|
55
84
|
|
56
85
|
|
57
|
-
|
58
|
-
tree = Yasuri.links_title '/html/body/a' do
|
59
|
-
text_name '/html/body/p'
|
60
|
-
end
|
86
|
+
パースツリーは以下のフォーマットで定義されます.
|
61
87
|
|
62
|
-
|
63
|
-
|
64
|
-
|
88
|
+
```ruby
|
89
|
+
# 1ノードからなる単純なツリー
|
90
|
+
Yasuri.<Type>_<Name> <Path> [,<Options>]
|
65
91
|
|
92
|
+
# 入れ子になっているツリー
|
93
|
+
Yasuri.<Type>_<Name> <Path> [,<Options>] do
|
94
|
+
<Type>_<Name> <Path> [,<Options>] do
|
95
|
+
<Type>_<Name> <Path> [,<Options>]
|
96
|
+
...
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
66
100
|
|
67
|
-
|
101
|
+
**例**
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
# 1ノードからなる単純なツリー
|
105
|
+
Yasuri.text_title '/html/head/title', truncate:/^[^,]+/
|
106
|
+
|
107
|
+
# 入れ子になっているツリー
|
108
|
+
Yasuri.links_root '//*[@id="menu"]/ul/li/a' do
|
109
|
+
struct_table './tr' do
|
110
|
+
text_title './td[1]'
|
111
|
+
text_pub_date './td[2]'
|
112
|
+
end
|
113
|
+
end
|
68
114
|
```
|
69
115
|
|
70
|
-
ツリーは、DSLまたはjsonで定義することができます.上の例ではDSLで定義しています.
|
71
|
-
以下は、jsonで上記と等価な解析ツリーを定義した例です.
|
72
116
|
|
117
|
+
パースツリーはRubyのDSL、JSON、YAMLのいずれかで定義することができます。
|
118
|
+
以下は、上記と同じパースツリーをそれぞれの記法で定義した例です。
|
119
|
+
|
120
|
+
**Ruby DSLで定義する場合**
|
73
121
|
```ruby
|
74
|
-
|
75
|
-
|
76
|
-
|
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)
|
122
|
+
Yasuri.links_title '/html/body/a' do
|
123
|
+
text_name '/html/body/p'
|
124
|
+
end
|
88
125
|
```
|
89
126
|
|
127
|
+
**JSONで定義する場合**
|
128
|
+
```json
|
129
|
+
{
|
130
|
+
links_title": {
|
131
|
+
"path": "/html/body/a",
|
132
|
+
"text_name": "/html/body/p"
|
133
|
+
}
|
134
|
+
}
|
135
|
+
```
|
90
136
|
|
91
|
-
|
92
|
-
|
93
|
-
|
137
|
+
**YAMLで定義する場合**
|
138
|
+
```yaml
|
139
|
+
links_title:
|
140
|
+
path: "/html/body/a"
|
141
|
+
text_name: "/html/body/p"
|
142
|
+
```
|
94
143
|
|
95
|
-
|
144
|
+
**パースツリーの特殊なケース**
|
96
145
|
|
97
|
-
|
98
|
-
|
99
|
-
|
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 ..."}
|
100
153
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
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
|
+
}
|
107
173
|
```
|
108
174
|
|
175
|
+
|
176
|
+
--------------------------
|
177
|
+
## Node
|
178
|
+
|
179
|
+
Nodeはパースツリーの節または葉となる要素で、`Type`, `Name`, `Path`, `Childlen`, `Options` を持っており、その `Type` に応じてスクレイピングを行います.(ただし、`MapNode` のみ `Path` を持ちません)
|
180
|
+
|
181
|
+
|
109
182
|
#### Type
|
110
183
|
*Type* は Nodeの振る舞いを示します.Typeには以下のものがあります.
|
111
184
|
|
@@ -113,18 +186,21 @@ end
|
|
113
186
|
- *Struct*
|
114
187
|
- *Links*
|
115
188
|
- *Paginate*
|
189
|
+
- *Map*
|
190
|
+
|
191
|
+
詳細は各ノードの説明を参照してください。
|
116
192
|
|
117
|
-
|
193
|
+
#### Name
|
118
194
|
*Name* は 解析結果のHashにおけるキーになります.
|
119
195
|
|
120
|
-
|
196
|
+
#### Path
|
121
197
|
*Path* は xpath あるいは css セレクタによって、HTML上の特定のノードを指定します.
|
122
198
|
これは Machinize の `search` で使用されます.
|
123
199
|
|
124
|
-
|
200
|
+
#### Childlen
|
125
201
|
入れ子になっているノードの子ノードです.TextNodeはツリーの葉に当たるため、子ノードを持ちません.
|
126
202
|
|
127
|
-
|
203
|
+
#### Options
|
128
204
|
パースのオプションです.オプションはTypeごとに異なります.
|
129
205
|
各ノードに対して、`opt`メソッドをコールすることで、利用可能なオプションを取得できます.
|
130
206
|
|
@@ -156,13 +232,16 @@ page = agent.get("http://yasuri.example.net")
|
|
156
232
|
|
157
233
|
p1 = Yasuri.text_title '/html/body/p[1]'
|
158
234
|
p1t = Yasuri.text_title '/html/body/p[1]', truncate:/^[^,]+/
|
159
|
-
p2u = Yasuri.text_title '/html/body/p[
|
235
|
+
p2u = Yasuri.text_title '/html/body/p[1]', proc: :upcase
|
160
236
|
|
161
|
-
p1.inject(agent, page) #=>
|
162
|
-
p1t.inject(agent, page) #=>
|
163
|
-
|
237
|
+
p1.inject(agent, page) #=> "Hello,World"
|
238
|
+
p1t.inject(agent, page) #=> "Hello"
|
239
|
+
p2u.inject(agent, page) #=> "HELLO,WORLD"
|
164
240
|
```
|
165
241
|
|
242
|
+
なお、同じページ内の複数の要素を一度にスクレイピングする場合は、`MapNode`を使用します。詳細は、`MapNode`の例を参照してください。
|
243
|
+
|
244
|
+
|
166
245
|
### オプション
|
167
246
|
##### `truncate`
|
168
247
|
正規表現にマッチした文字列を取り出します.グループを指定した場合、最初にマッチしたグループだけを返します.
|
@@ -431,3 +510,186 @@ node.inject(agent, page)
|
|
431
510
|
#=> [ {"content" => "Pagination01"}, {"content" => "Pagination02"}]
|
432
511
|
```
|
433
512
|
この場合、PaginateNode は最大2つまでのページを開いてパースします.ページネーションは4つのページを持っているようですが、`limit:2`が指定されているため、結果の配列には2つの結果のみが含まれています.
|
513
|
+
|
514
|
+
##### `flatten`
|
515
|
+
取得した各ページの結果を展開します.
|
516
|
+
|
517
|
+
```ruby
|
518
|
+
agent = Mechanize.new
|
519
|
+
page = agent.get("http://yasuri.example.net/page01.html")
|
520
|
+
|
521
|
+
node = Yasuri.pages_root "/html/body/nav/span/a[@class='next']" , flatten:true do
|
522
|
+
text_title '/html/head/title'
|
523
|
+
text_content '/html/body/p'
|
524
|
+
end
|
525
|
+
node.inject(agent, page)
|
526
|
+
|
527
|
+
#=> [ {"title" => "Page01",
|
528
|
+
"content" => "Patination01"},
|
529
|
+
{"title" => "Page01",
|
530
|
+
"content" => "Patination02"},
|
531
|
+
{"title" => "Page01",
|
532
|
+
"content" => "Patination03"}]
|
533
|
+
|
534
|
+
|
535
|
+
node = Yasuri.pages_root "/html/body/nav/span/a[@class='next']" , flatten:true do
|
536
|
+
text_title '/html/head/title'
|
537
|
+
text_content '/html/body/p'
|
538
|
+
end
|
539
|
+
node.inject(agent, page)
|
540
|
+
|
541
|
+
#=> [ "Page01",
|
542
|
+
"Patination01",
|
543
|
+
"Page02",
|
544
|
+
"Patination02",
|
545
|
+
"Page03",
|
546
|
+
"Patination03"]
|
547
|
+
```
|
548
|
+
|
549
|
+
## Map Node
|
550
|
+
*MapNode* はスクレイピングした結果をまとめるノードです.このノードはパースツリーにおいて常に節です.
|
551
|
+
|
552
|
+
### 例
|
553
|
+
|
554
|
+
```html
|
555
|
+
<!-- http://yasuri.example.net -->
|
556
|
+
<html>
|
557
|
+
<head><title>Yasuri Example</title></head>
|
558
|
+
<body>
|
559
|
+
<p>Hello,World</p>
|
560
|
+
<p>Hello,Yasuri</p>
|
561
|
+
</body>
|
562
|
+
</html>
|
563
|
+
```
|
564
|
+
|
565
|
+
```ruby
|
566
|
+
agent = Mechanize.new
|
567
|
+
page = agent.get("http://yasuri.example.net")
|
568
|
+
|
569
|
+
|
570
|
+
tree = Yasuri.map_root do
|
571
|
+
text_title '/html/head/title'
|
572
|
+
text_body_p '/html/body/p[1]'
|
573
|
+
end
|
574
|
+
|
575
|
+
tree.inject(agent, page) #=> { "title" => "Yasuri Example", "body_p" => "Hello,World" }
|
576
|
+
|
577
|
+
|
578
|
+
tree = Yasuri.map_root do
|
579
|
+
map_group1 { text_child01 '/html/body/a[1]' }
|
580
|
+
map_group2 do
|
581
|
+
text_child01 '/html/body/a[1]'
|
582
|
+
text_child03 '/html/body/a[3]'
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
tree.inject(agent, page) #=> {
|
587
|
+
# "group1" => {
|
588
|
+
# "child01" => "child01"
|
589
|
+
# },
|
590
|
+
# "group2" => {
|
591
|
+
# "child01" => "child01",
|
592
|
+
# "child03" => "child03"
|
593
|
+
# }
|
594
|
+
# }
|
595
|
+
```
|
596
|
+
|
597
|
+
### オプション
|
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
|
+
```
|