yasuri 3.2.0 → 3.3.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: cd5fc7327c6d09b37771ac1c3ec40db2c052bf49ec9a1627e9ae49e047102856
4
- data.tar.gz: a645f1e09ce72b73c54e2055af6fbf81bb145c8823e1d8428bb19c042bbb661d
3
+ metadata.gz: a7bf438a08fc83fec7e78cb5543577c98f6cc98b4f5fae7b0dd969f2049c0531
4
+ data.tar.gz: e399c6b57589b7d8ba2e8eff7a1d204fa7f8e676f82f631057e19a9377333060
5
5
  SHA512:
6
- metadata.gz: 654bd6cfe8012811283b1aa03e0dcc1200ce957ef4641eed2b5fa65956fb974070157b832e42f340d7299031756848c5118a7f43019ff94f088c49974e2304e8
7
- data.tar.gz: 5ad07b82672ea2ceebfb8154bb91631c095e9ad8d69f3d62c0bf8d528c4c539fab2597f4112b4212bffe7ad641b30d913686e8e2bfea7dfdbdd9a4468311b6c0
6
+ metadata.gz: 56f39994972657712cb7d95e5ceaadefca8de41e06c2cd4759363b496d7c8531fad7517f9df99bf2446c144f01c5cd82cbc94146c432d6b5b552f092b975ecd7
7
+ data.tar.gz: cf74a25615187ecbe5f8ca5f2072679fa9cc1902dfa3bf2190b87e11104f332688cdaee16f4be6cb00f9ed63fa18f2ec8f27cf32b0b27389f81d98229fa212e6
data/README.md CHANGED
@@ -81,12 +81,8 @@ src = <<-EOJSON
81
81
  EOJSON
82
82
  root = Yasuri.json2tree(src)
83
83
 
84
-
85
84
  # Execution and getting scraped result
86
- agent = Mechanize.new
87
- root_page = agent.get("http://some.scraping.page.tac42.net/")
88
-
89
- result = root.inject(agent, root_page)
85
+ result = root.scrape("http://some.scraping.page.tac42.net/")
90
86
  # => [
91
87
  # {"title" => "PageTitle 01", "content" => "Page Contents 01" },
92
88
  # {"title" => "PageTitle 02", "content" => "Page Contents 02" },
@@ -104,8 +100,9 @@ Usage:
104
100
  yasuri scrape <URI> [[--file <TREE_FILE>] or [--json <JSON>]]
105
101
 
106
102
  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
103
+ f, [--file=FILE] # path to file that written yasuri tree as json or yaml
104
+ j, [--json=JSON] # yasuri tree format json string
105
+ i, [--interval=N] # interval each request [ms]
109
106
 
110
107
  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
108
  ```
data/USAGE.ja.md CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  ## Yasuri とは
4
4
  Yasuri (鑢) はWebスクレイピングを宣言的に行うためのライブラリと、それを用いたスクレイピングのコマンドラインツールです。
5
- 簡単な宣言的記法で期待結果を記述するだけで、"[Mechanize](https://github.com/sparklemotion/mechanize)" によるスクレイピングを実行します。
5
+
6
+ 簡単な宣言的記法で期待結果を記述するだけでスクレイピングした結果を得られます。
6
7
 
7
8
  Yasuriは、スクレイピングにおける、よくある処理を簡単に記述することができます.
8
9
  例えば、以下のような処理を簡単に実現することができます.
@@ -36,10 +37,7 @@ root = Yasuri.links_root '//*[@id="menu"]/ul/li/a' do
36
37
  text_content '//*[@id="contents"]/p[1]'
37
38
  end
38
39
 
39
- agent = Mechanize.new
40
- root_page = agent.get("http://some.scraping.page.tac42.net/")
41
-
42
- result = root.inject(agent, root_page)
40
+ result = root.scrape("http://some.scraping.page.tac42.net/")
43
41
  # => [
44
42
  # {"title" => "PageTitle 01", "content" => "Page Contents 01" },
45
43
  # {"title" => "PageTitle 02", "content" => "Page Contents 02" },
@@ -171,7 +169,51 @@ jsonまたはyaml形式では、子Nodeを持たない場合、`path` を直接
171
169
  }
172
170
  }
173
171
  ```
172
+ ### ツリーを実行する
173
+ パースツリーのルートノードで`Node#scrape(uri, opt={})`メソッドをコールします。
174
+
175
+ **例**
176
+ ```ruby
177
+ root = Yasuri.links_root '//*[@id="menu"]/ul/li/a' do
178
+ text_title '//*[@id="contents"]/h2'
179
+ text_content '//*[@id="contents"]/p[1]'
180
+ end
181
+
182
+ result = root.scrape("http://some.scraping.page.tac42.net/", interval_ms: 1000)
183
+ ```
174
184
 
185
+ + `uri` はスクレイピングする対象ページのURIです。
186
+ + `opt` はオプションをHashで指定します。以下のオプションを利用できます。
187
+
188
+ Yasuriはスクレイピングを行うエージェントとして、内部で`Mechanize`を使用しています。
189
+ このインスタンスを指定したい場合は、`Node#scrape_with_agent(uri, agent, opt={})`をコールします。
190
+
191
+ ```ruby
192
+ require 'logger'
193
+
194
+ agent = Mechanize.new
195
+ agent.log = Logger.new $stderr
196
+ agent.request_headers = {
197
+ # ...
198
+ }
199
+
200
+ result = root.scrape_with_agent(
201
+ "http://some.scraping.page.tac42.net/",
202
+ agent,
203
+ interval_ms: 1000)
204
+ ```
205
+
206
+ ### `opt`
207
+ #### `interval_ms`
208
+ 複数ページにリクエストする際の間隔[ミリ秒]です。
209
+
210
+ 省略した場合はインターバルなしで続けてリクエストしますが、多数のページへのリクエストが予想される場合、対象ホストが高負荷とならないよう、インターバル時間を指定することを強くお勧めします。
211
+
212
+ #### `retry_count`
213
+ ページ取得失敗時のリトライ回数です。省略した場合は5回リトライします。
214
+
215
+ #### `symbolize_names`
216
+ `true`のとき、結果セットのキーをシンボルとして返します。
175
217
 
176
218
  --------------------------
177
219
  ## Node
@@ -216,7 +258,7 @@ node.opt #=> {:truncate => /^[^,]+/, :proc => nil}
216
258
  ### 例
217
259
 
218
260
  ```html
219
- <!-- http://yasuri.example.net -->
261
+ <!-- http://yasuri.example.tac42.net -->
220
262
  <html>
221
263
  <head></head>
222
264
  <body>
@@ -227,28 +269,24 @@ node.opt #=> {:truncate => /^[^,]+/, :proc => nil}
227
269
  ```
228
270
 
229
271
  ```ruby
230
- agent = Mechanize.new
231
- page = agent.get("http://yasuri.example.net")
232
-
233
272
  p1 = Yasuri.text_title '/html/body/p[1]'
234
273
  p1t = Yasuri.text_title '/html/body/p[1]', truncate:/^[^,]+/
235
274
  p2u = Yasuri.text_title '/html/body/p[1]', proc: :upcase
236
275
 
237
- p1.inject(agent, page) #=> "Hello,World"
238
- p1t.inject(agent, page) #=> "Hello"
239
- p2u.inject(agent, page) #=> "HELLO,WORLD"
276
+ p1.scrape("http://yasuri.example.tac42.net") #=> "Hello,World"
277
+ p1t.scrape("http://yasuri.example.tac42.net") #=> "Hello"
278
+ p2u.scrape("http://yasuri.example.tac42.net") #=> "HELLO,WORLD"
240
279
  ```
241
280
 
242
281
  なお、同じページ内の複数の要素を一度にスクレイピングする場合は、`MapNode`を使用します。詳細は、`MapNode`の例を参照してください。
243
282
 
244
-
245
283
  ### オプション
246
284
  ##### `truncate`
247
285
  正規表現にマッチした文字列を取り出します.グループを指定した場合、最初にマッチしたグループだけを返します.
248
286
 
249
287
  ```ruby
250
288
  node = Yasuri.text_example '/html/body/p[1]', truncate:/H(.+)i/
251
- node.inject(agent, index_page)
289
+ node.scrape(uri)
252
290
  #=> { "example" => "ello,Yasur" }
253
291
  ```
254
292
 
@@ -259,7 +297,7 @@ node.inject(agent, index_page)
259
297
 
260
298
  ```ruby
261
299
  node = Yasuri.text_example '/html/body/p[1]', proc: :upcase, truncate:/H(.+)i/
262
- node.inject(agent, index_page)
300
+ node.scrape(uri)
263
301
  #=> { "example" => "ELLO,YASUR" }
264
302
  ```
265
303
 
@@ -274,7 +312,7 @@ Struct Node の `Path` が複数のタグにマッチする場合、配列とし
274
312
  ### 例
275
313
 
276
314
  ```html
277
- <!-- http://yasuri.example.net -->
315
+ <!-- http://yasuri.example.tac42.net -->
278
316
  <html>
279
317
  <head>
280
318
  <title>Books</title>
@@ -315,15 +353,12 @@ Struct Node の `Path` が複数のタグにマッチする場合、配列とし
315
353
  ```
316
354
 
317
355
  ```ruby
318
- agent = Mechanize.new
319
- page = agent.get("http://yasuri.example.net")
320
-
321
356
  node = Yasuri.struct_table '/html/body/table[1]/tr' do
322
357
  text_title './td[1]'
323
358
  text_pub_date './td[2]'
324
- ])
359
+ end
325
360
 
326
- node.inject(agent, page)
361
+ node.scrape("http://yasuri.example.tac42.net")
327
362
  #=> [ { "title" => "The Perfect Insider",
328
363
  # "pub_date" => "1996/4/5" },
329
364
  # { "title" => "Doctors in Isolated Room",
@@ -337,23 +372,19 @@ Struct Node は xpath `'/html/body/table[1]/tr'` によって、最初の `<tabl
337
372
  この場合は、最初の `<table>` は 3つの `<tr>`タグを持っているため、3つのHashを返します.(`<thead><tr>` は `Path` にマッチしないため4つではないことに注意)
338
373
  各HashはTextNodeによってパースされたテキストを含んでいます.
339
374
 
340
-
341
375
  また以下の例のように、Struct Node は TextNode以外のノードを子ノードとすることができます.
342
376
 
343
377
  ### 例
344
378
 
345
379
  ```ruby
346
- agent = Mechanize.new
347
- page = agent.get("http://yasuri.example.net")
348
-
349
380
  node = Yasuri.strucre_tables '/html/body/table' do
350
381
  struct_table './tr' do
351
382
  text_title './td[1]'
352
383
  text_pub_date './td[2]'
353
384
  end
354
- ])
385
+ end
355
386
 
356
- node.inject(agent, page)
387
+ node.scrape("http://yasuri.example.tac42.net")
357
388
 
358
389
  #=> [ { "table" => [ { "title" => "The Perfect Insider",
359
390
  # "pub_date" => "1996/4/5" },
@@ -385,8 +416,8 @@ node.inject(agent, page)
385
416
  Links Node は リンクされた各ページをパースして結果を返します.
386
417
 
387
418
  ### 例
388
- ```
389
- <!-- http://yasuri.example.net -->
419
+ ```html
420
+ <!-- http://yasuri.example.tac42.net -->
390
421
  <html>
391
422
  <head><title>Yasuri Test</title></head>
392
423
  <body>
@@ -398,8 +429,8 @@ Links Node は リンクされた各ページをパースして結果を返し
398
429
  <title>
399
430
  ```
400
431
 
401
- ```
402
- <!-- http://yasuri.example.net/child01.html -->
432
+ ```html
433
+ <!-- http://yasuri.example.tac42.net/child01.html -->
403
434
  <html>
404
435
  <head><title>Child 01 Test</title></head>
405
436
  <body>
@@ -412,8 +443,8 @@ Links Node は リンクされた各ページをパースして結果を返し
412
443
  <title>
413
444
  ```
414
445
 
415
- ```
416
- <!-- http://yasuri.example.net/child02.html -->
446
+ ```html
447
+ <!-- http://yasuri.example.tac42.net/child02.html -->
417
448
  <html>
418
449
  <head><title>Child 02 Test</title></head>
419
450
  <body>
@@ -422,8 +453,8 @@ Links Node は リンクされた各ページをパースして結果を返し
422
453
  <title>
423
454
  ```
424
455
 
425
- ```
426
- <!-- http://yasuri.example.net/child03.html -->
456
+ ```html
457
+ <!-- http://yasuri.example.tac42.net/child03.html -->
427
458
  <html>
428
459
  <head><title>Child 03 Test</title></head>
429
460
  <body>
@@ -435,22 +466,19 @@ Links Node は リンクされた各ページをパースして結果を返し
435
466
  <title>
436
467
  ```
437
468
 
438
- ```
439
- agent = Mechanize.new
440
- page = agent.get("http://yasuri.example.net")
441
-
469
+ ```ruby
442
470
  node = Yasuri.links_title '/html/body/a' do
443
471
  text_content '/html/body/p'
444
472
  end
445
473
 
446
- node.inject(agent, page)
474
+ node.scrape("http://yasuri.example.tac42.net")
447
475
  #=> [ {"content" => "Child 01 page."},
448
476
  {"content" => "Child 02 page."},
449
477
  {"content" => "Child 03 page."}]
450
478
  ```
451
479
 
452
480
  まず、 LinksNode は `Path` にマッチするすべてのリンクを最初のページから探します.
453
- この例では、LinksNodeは `/html/body/a` にマッチするすべてのタグを `http://yasuri.example.net` から探します.
481
+ この例では、LinksNodeは `/html/body/a` にマッチするすべてのタグを `http://yasuri.example.tac42.net` から探します.
454
482
  次に、見つかったタグのhref属性で指定されたページを開きます.(`./child01.html`, `./child02.html`, `./child03.html`)
455
483
 
456
484
  開いた各ページに対して、子ノードによる解析を行います.LinksNodeは 各ページに対するパース結果をHashの配列として返します.
@@ -463,7 +491,7 @@ PaginateNodeは ページネーション(パジネーション, Pagination) で
463
491
  `page02.html` から `page04.html` も同様です.
464
492
 
465
493
  ```html
466
- <!-- http://yasuri.example.net/page01.html -->
494
+ <!-- http://yasuri.example.tac42.net/page01.html -->
467
495
  <html>
468
496
  <head><title>Page01</title></head>
469
497
  <body>
@@ -483,17 +511,14 @@ PaginateNodeは ページネーション(パジネーション, Pagination) で
483
511
  ```
484
512
 
485
513
  ```ruby
486
- agent = Mechanize.new
487
- page = agent.get("http://yasuri.example.net/page01.html")
488
-
489
514
  node = Yasuri.pages_root "/html/body/nav/span/a[@class='next']" , limit:3 do
490
515
  text_content '/html/body/p'
491
516
  end
492
517
 
493
- node.inject(agent, page)
518
+ node.scrape("http://yasuri.example.tac42.net/page01.html")
494
519
  #=> [ {"content" => "Patination01"},
495
- {"content" => "Patination02"},
496
- {"content" => "Patination03"}]
520
+ # {"content" => "Patination02"},
521
+ # {"content" => "Patination03"}]
497
522
  ```
498
523
  PaginateNodeは 次のページ を指すリンクを`Path`として指定する必要があります.
499
524
  この例では、`NextPage` (`/html/body/nav/span/a[@class='next']`)が、次のページを指すリンクに該当します.
@@ -506,7 +531,7 @@ PaginateNodeは 次のページ を指すリンクを`Path`として指定する
506
531
  node = Yasuri.pages_root "/html/body/nav/span/a[@class='next']" , limit:2 do
507
532
  text_content '/html/body/p'
508
533
  end
509
- node.inject(agent, page)
534
+ node.scrape(uri)
510
535
  #=> [ {"content" => "Pagination01"}, {"content" => "Pagination02"}]
511
536
  ```
512
537
  この場合、PaginateNode は最大2つまでのページを開いてパースします.ページネーションは4つのページを持っているようですが、`limit:2`が指定されているため、結果の配列には2つの結果のみが含まれています.
@@ -515,35 +540,32 @@ node.inject(agent, page)
515
540
  取得した各ページの結果を展開します.
516
541
 
517
542
  ```ruby
518
- agent = Mechanize.new
519
- page = agent.get("http://yasuri.example.net/page01.html")
520
-
521
543
  node = Yasuri.pages_root "/html/body/nav/span/a[@class='next']" , flatten:true do
522
544
  text_title '/html/head/title'
523
545
  text_content '/html/body/p'
524
546
  end
525
- node.inject(agent, page)
547
+ node.scrape("http://yasuri.example.tac42.net/page01.html")
526
548
 
527
549
  #=> [ {"title" => "Page01",
528
- "content" => "Patination01"},
529
- {"title" => "Page01",
530
- "content" => "Patination02"},
531
- {"title" => "Page01",
532
- "content" => "Patination03"}]
550
+ # "content" => "Patination01"},
551
+ # {"title" => "Page01",
552
+ # "content" => "Patination02"},
553
+ # {"title" => "Page01",
554
+ # "content" => "Patination03"}]
533
555
 
534
556
 
535
557
  node = Yasuri.pages_root "/html/body/nav/span/a[@class='next']" , flatten:true do
536
558
  text_title '/html/head/title'
537
559
  text_content '/html/body/p'
538
560
  end
539
- node.inject(agent, page)
561
+ node.scrape("http://yasuri.example.tac42.net/page01.html")
540
562
 
541
563
  #=> [ "Page01",
542
- "Patination01",
543
- "Page02",
544
- "Patination02",
545
- "Page03",
546
- "Patination03"]
564
+ # "Patination01",
565
+ # "Page02",
566
+ # "Patination02",
567
+ # "Page03",
568
+ # "Patination03"]
547
569
  ```
548
570
 
549
571
  ## Map Node
@@ -552,7 +574,7 @@ node.inject(agent, page)
552
574
  ### 例
553
575
 
554
576
  ```html
555
- <!-- http://yasuri.example.net -->
577
+ <!-- http://yasuri.example.tac42.net -->
556
578
  <html>
557
579
  <head><title>Yasuri Example</title></head>
558
580
  <body>
@@ -563,16 +585,12 @@ node.inject(agent, page)
563
585
  ```
564
586
 
565
587
  ```ruby
566
- agent = Mechanize.new
567
- page = agent.get("http://yasuri.example.net")
568
-
569
-
570
588
  tree = Yasuri.map_root do
571
589
  text_title '/html/head/title'
572
590
  text_body_p '/html/body/p[1]'
573
591
  end
574
592
 
575
- tree.inject(agent, page) #=> { "title" => "Yasuri Example", "body_p" => "Hello,World" }
593
+ tree.scrape("http://yasuri.example.tac42.net") #=> { "title" => "Yasuri Example", "body_p" => "Hello,World" }
576
594
 
577
595
 
578
596
  tree = Yasuri.map_root do
@@ -583,7 +601,7 @@ tree = Yasuri.map_root do
583
601
  end
584
602
  end
585
603
 
586
- tree.inject(agent, page) #=> {
604
+ tree.scrape("http://yasuri.example.tac42.net") #=> {
587
605
  # "group1" => {
588
606
  # "child01" => "child01"
589
607
  # },
@@ -601,15 +619,14 @@ tree.inject(agent, page) #=> {
601
619
  -------------------------
602
620
  ## 使い方
603
621
 
604
- #### ライブラリとして使用する場合
622
+ ### ライブラリとして使う
605
623
  ライブラリとして使用する場合は、DSL, json, yaml の形式でツリーを定義できます。
624
+
606
625
  ```ruby
607
- require 'mechanize'
608
626
  require 'yasuri'
609
627
 
610
-
611
628
  # 1. パースツリーを作る
612
- # DSLで定義する倍
629
+ # DSLで定義する
613
630
  tree = Yasuri.links_title '/html/body/a' do
614
631
  text_name '/html/body/p'
615
632
  end
@@ -634,17 +651,11 @@ links_title:
634
651
  EOYAML
635
652
  tree = Yasuri.yaml2tree(src)
636
653
 
637
-
638
-
639
- # 2. Mechanize の agent と対象のページを与えてパースを開始する
640
- agent = Mechanize.new
641
- page = agent.get(uri)
642
-
643
-
644
- tree.inject(agent, page)
654
+ # 2. URLを与えてパースを開始する
655
+ tree.inject(uri)
645
656
  ```
646
657
 
647
- #### CLIツールとして使用する場合
658
+ ### CLIツールとして使う
648
659
 
649
660
  **ヘルプ表示**
650
661
  ```sh
@@ -655,13 +666,14 @@ Usage:
655
666
  Options:
656
667
  f, [--file=FILE] # path to file that written yasuri tree as json or yaml
657
668
  j, [--json=JSON] # yasuri tree format json string
669
+ i, [--interval=N] # interval each request [ms]
658
670
 
659
671
  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
672
  ```
661
673
 
662
674
  CLIツールでは以下のどちらかの方法でパースツリーを指定します。
663
- + `--file`, `-f` オプションで、ファイルに出力されたjson形式またはyaml形式のパースツリーを読み込む
664
- + `--json`, `-j` オプションで、パースツリーを文字列として直接指定する
675
+ + `--file`, `-f` : ファイルに出力されたjson形式またはyaml形式のパースツリーを読み込む
676
+ + `--json`, `-j` : パースツリーを文字列として直接指定する
665
677
 
666
678
 
667
679
  **パースツリーをファイルで指定する例**
@@ -683,6 +695,8 @@ text_desc: "//*[@id=\"intro\"]/p"
683
695
  {"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
696
  ```
685
697
 
698
+ ファイルがjsonまたはyamlのどちらで記載されているかについては自動判別されます。
699
+
686
700
  **パースツリーをjsonで直接指定する例**
687
701
  ```sh
688
702
  $ yasuri scrape "https://www.ruby-lang.org/en/" -j '
@@ -693,3 +707,10 @@ $ yasuri scrape "https://www.ruby-lang.org/en/" -j '
693
707
 
694
708
  {"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
709
  ```
710
+
711
+ #### その他のオプション
712
+ + `--interval`, `-i` : 複数ページにリクエストする際の間隔[ミリ秒]です。
713
+ **例: 1秒間隔でリクエストする**
714
+ ```sh
715
+ $ yasuri scrape "https://www.ruby-lang.org/en/" --file sample.yml --interval 1000
716
+ ```