質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

3回答

4626閲覧

Mechanizeのlink_withメソッドについて

退会済みユーザー

退会済みユーザー

総合スコア0

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

1クリップ

投稿2015/05/30 09:29

編集2015/05/30 15:57

lang

1require 'mechanize' 2 3agent = Mechanize.new 4page = agent.get('http://hoge.com/') 5regex = /<a href="([^"]+)"><img src/ 6link = page.link_with(:href => /$1/).click 7hai = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')

これはhttp://example.com/というサイトにある画像を保存するためのプログラムです
しかしこのプログラムを実行しても以下のエラーが出ます

undefined method `fetch' for nil:NilClass (NoMethodError)

これはプログラム中の以下の部分の書き方が誤っているからだと思います

lang

1link = page.link_with(:href => /$1/).click

この/$1/に正規表現ではなく、実際にあるURLを書き込むとエラーは出ません
また、他のプログラムで試したので正規表現自体が間違っている、ということもないと思います
link_withメソッドのなかで以上のような、正規表現を実現するためにはどうすれば良いのでしょうか
教えてください

lang

1require 'mechanize' 2 3agent = Mechanize.new 4page = agent.get('http://example.com/') 5regex = /<a href="([^"]+)"><img src/ 6link = page.link_with(:href => /$1/).click 7img = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')

これはhttp://example.com/というサイトにある画像を保存するためのプログラムです
しかしこのプログラムを実行しても以下のエラーが出ます

undefined method `fetch' for nil:NilClass (NoMethodError)

これはプログラム中の以下の部分の書き方が誤っているからだと思います

lang

1link = page.link_with(:href => /$1/).click

この/$1/に正規表現ではなく、実際にあるURLを書き込むとエラーは出ません
また、他のプログラムで試したので正規表現自体が間違っている、ということもないと思います
link_withメソッドのなかで以上のような、正規表現を実現するためにはどうすれば良いのでしょうか
教えてください

追記
これはhttp://example.com/というサイトにある画像を保存するためのプロクラムです
http://example.com/には以下のソースがあり、そのaタグのなかをlink_withメソッドでクリックしたいです
このとき、自分の必要とするリンク先は任意のURLで、次に来るHTMLタグが<img src> からはじまっているものなのでそれを利用した正規表現を書きます

<a href="http://random.com/randomstring"><img src="http://img.example.com/example.jpg">

プログラム中に記述した正規表現では以下にマッチしますが$1ではhttp://hoge.com/randomstringにマッチします(おそらくこの辺が自分でよくわかっていません)

<a href="http://random.com/randomstring"><img src

そのためlink_withメソッドの正規表現に$1を渡せばうまくいくかなと思ったのですが、やはりだめでした

説明がうまくできずすみません
やりたいことを端的に述べると以下になります
http://example.comのリンク先にある画像を取得したい
・そのためにまず、リンク先を正規表現で探してlink_withメソッドでクリックする
・最後にimage_withとfetch.save_asをもちいて保存

たとえば、以下のプログラムだとhttp://random.com/randomstringにある画像は、保存できるのですが実際は任意のURLなので、正規表現でURLを指定したいです

lang

1require 'mechanize' 2 3agent = Mechanize.new 4page = agent.get('http://exmaple.com/') 5link = page.link_with(:href => "http://random.com/randomstring").click 6img = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')

以下のプログラムの場合は正規表現がうまくマッチせず使えません

lang

1require 'mechanize' 2 3agent = Mechanize.new 4page = agent.get('http://exmaple.com/') 5link = page.link_with(:href => /<a href="([^"]+)"><img src/).click 6img = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

そのためlink_withメソッドの正規表現に$1を渡せばうまくいくかなと思ったのですが、やはりだめでした

正規表現自体の知識はおありのようですが、それをRubyの中でどう使えるのかについての知識が不足しています。$1についての説明は前回の回答に書いた通り。

どうやるかの1つの案です。
該当aタグや、imgタグは複数あるのでは?と思ったので、そういう前提。

lang

1require "mechanize" 2 3agent = Mechanize.new 4page = agent.get("http://exmaple.com/") 5n=0 6page.root.xpath("//a/img/..").each do |a| 7 page = agent.get(a["href"]) 8 if page.respond_to? :images_with 9 page.images_with(src: /jpg\Z/).each do |img| 10 img.fetch.save_as("img#{n+=1}.jpg") 11 end 12 end 13end

投稿2015/05/31 01:09

otn

総合スコア84423

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2015/05/31 07:56

回答ありがとうございます タグが複数あるときの書き方を知らなかったので、大変勉強になりました ありがとうございました
guest

0

ベストアンサー

こんにちは。

追記を拝読しての回答となります。

追記から、実現したいこととしては、

(1)指定されたURLのページに記述されている、ある条件を満たすURLを
オリジナル画像
が表示されるページへのリンクであるとして、そのリンク先からオリジナルの画像を手元
のPCにダウンロードして保存したい。

(2)上記の(1)の「ある条件」とは、正規表現

<a href="([^"]+)"><img src

にマッチする行のhref属性の値として取得する、1個目の丸括弧 ( ) で
囲まれた部分である。

というものであると把握しました。(違っていたら、ご指摘ください。)

また、上記の要件(1)で「オリジナル画像」という言い方を
している理由は、上記の(2)の正規表現が

<a href="([^"]+)"><img src

となっていて、<a href="オリジの画像があるページのURL">
リンクで囲まれた部分が<img>要素なので、この<img>要素が
ダウンロードしたい画像のサムネイルになっているようなページなのかな
と思ったからです。
このサムネイルからオリジ画像だけがあるページにいって、そこから
画像データを取り込みたいという状況と想定しました。

上記の想定のもとに、挙げられているコードの問題はどこかを検討しますと、

link = page.link_with(:href => /<a href="([^"]+)"><img src/).click

の、link_with の引数で渡している
:href => /<a href="([^"]+)"><img src/
の部分ですね。

私は、Mechanizeは使ったことがないのですが、ざっとマニュアルを読んでみた
ところ、ここの :href にはリンク先のURLそのものにマッチする
ものでないといけないようです。
なので、ご質問の与件では、ダウンロードしてくる画像のあるページのURL
つまり、正規表現

<a href="([^"]+)"><img src

の ( )で取れるURL自体のパターンについて調べて、それを、:href に記述します。

たとえば仮に、上記の正規表現で取れる、オリジ画像のあるページの
URLが

http://exmaple.com/images/半角数字5桁.html

というものであるとしましょう。
これにマッチする正規表現(オブジェクト)は

/http://exmaple.com/images/[0-9]{5}.html/

と書けるので、

link = page.link_with(:href => /http://exmaple.com/images/[0-9]{5}.html/).click

とすればよい、ということになるかと思います。

**・・・なのですが、**より工夫しなければならないのは、
正規表現

<a href="([^"]+)"><img src

の ( )で取れるURL自体のパターンについては分からない、
という状況の場合です。
つまり、

ダウンロードしたい画像のあるページのURLは、
元のページの<img>を囲んでいる<a>のhref属性の値である。

ということしか分からない場合です。

この場合は、まず、<a>に囲まれている、(つまり親要素が<a>であるような)<img>要素を
検索して、そのような<img>があれば、それの親である<a>のhrefを取得する、というような
処理が必要になると思いました。

・・・という考えのもと、私は、Mechanizeは使ったことないのですが
ちょっと調べたところ、pageのsearchメソッドにXPathを渡して、
以下のような感じでいけるのではないかなと思いました。

lang

1require 'mechanize' 2 3agent = Mechanize.new 4page = agent.get('http://exmaple.com/') 5 6thumbnails = page.search(‘//a/img’) 7if thumbnails && thumbnails.length > 0 8 link_to_original_image = thumbnails[0].parent 9 page_of_original_image = agent.get(link_to_original_image[:href]) 10 img = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg') 11end

上記のコードをテンプレートにして、ちょっと自分の勉強がてら、
映画「イミテーションゲーム」のまとめページから、一番上の
画像を取ってくるコードを書いてみました。

lang

1# coding: utf-8 2 3require 'mechanize' 4 5agent = Mechanize.new 6page = agent.get('http://matome.naver.jp/odai/2141968391848272501') 7 8thumbnails = page.search('//a/img[@class="MTMItemThumb"]') 9 10if thumbnails && thumbnails.length > 0 11 link_to_original_image = thumbnails[0].parent 12 puts "href:[" + link_to_original_image[:href] + "]" 13 page_of_original_image = agent.get(link_to_original_image[:href]) 14 img = page_of_original_image.image_with(src: /jpg\Z/).fetch.save_as('img.jpg') 15end

上記を実行すると、まとめの一番上にある画像のオリジ画像が
ローカルに img.jpgとしてダウンロードできました。

蛇足ですが、rubyでこういったスクレイピングを色々やりたいときには
以下の本が詳しいです。

Rubyによるクローラー開発技法

以上、参考になれば幸いです。

※以下追記です。

OTNさんのご回答のコードにあるように、XPathでは
"//a/img/.."
の末尾のように、".."を使えるので、私が最後に挙げたコードは
以下のように少し短くできます。

lang

1# coding: utf-8 2 3require 'mechanize' 4 5agent = Mechanize.new 6page = agent.get('http://matome.naver.jp/odai/2141968391848272501') 7 8links = page.search('//a/img[@class="MTMItemThumb"]/..') 9 10if links && links.length > 0 11 puts "href:[" + links[0][:href] + "]" #確認のための表示 12 page_of_original_image = agent.get(links[0][:href]) 13 img = page_of_original_image.image_with(src: /jpg\Z/).fetch.save_as('img.jpg') 14end

望ましい動きをしてくれるスクレイピングのプログラムを書けるようになるためには、
今回の $1 の使い方含めて正規表現を使いこなせるようになることに加えて、
XPathも自分のものにしておくとよいと思います。

以上です。

投稿2015/05/31 03:15

編集2015/05/31 08:11
jun68ykt

総合スコア9058

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2015/05/31 07:54

回答ありがとうございます サンプルのコードや、関連書籍のリンクまで貼っていただき、大変参考になりました 他の回答も含め、試行錯誤した結果自分の期待するプログラムをなんとか書けそうです かさねてお礼申し上げます、ありがとうございました
guest

0

$1というのは、正規表現のマッチを行った場合に、最初の括弧に囲まれた部分を指します。
このコードだと、$1登場以前に正規表現のマッチを行っていないので、nilのはずです。

コードを見ても何をしたいのか分からないので、具体的に「こう直せば良い」というアドバイスが出来ません。

投稿2015/05/30 15:06

otn

総合スコア84423

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2015/05/30 15:58

回答ありがとうございます かなり駄文になってしまいましたが詳細を追記しましたので、是非回答をお願いしたいです
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問