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

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

ただいまの
回答率

90.48%

  • Ruby

    9643questions

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

Mechanizeのlink_withメソッドについて

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,966
退会済みユーザー

退会済みユーザー

require 'mechanize'

agent = Mechanize.new
page = agent.get('http://hoge.com/')
regex = /<a href="([^"]+)"><img src/
link = page.link_with(:href => /$1/).click
hai = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')
これはhttp://example.com/というサイトにある画像を保存するためのプログラムです
しかしこのプログラムを実行しても以下のエラーが出ます
undefined method `fetch' for nil:NilClass (NoMethodError)
これはプログラム中の以下の部分の書き方が誤っているからだと思います
link = page.link_with(:href => /$1/).click
この/$1/に正規表現ではなく、実際にあるURLを書き込むとエラーは出ません
また、他のプログラムで試したので正規表現自体が間違っている、ということもないと思います
link_withメソッドのなかで以上のような、正規表現を実現するためにはどうすれば良いのでしょうか
教えてください

require 'mechanize'

agent = Mechanize.new
page = agent.get('http://example.com/')
regex = /<a href="([^"]+)"><img src/
link = page.link_with(:href => /$1/).click
img = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')
これはhttp://example.com/というサイトにある画像を保存するためのプログラムです
しかしこのプログラムを実行しても以下のエラーが出ます
undefined method `fetch' for nil:NilClass (NoMethodError)
これはプログラム中の以下の部分の書き方が誤っているからだと思います
link = 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を指定したいです
require 'mechanize'

agent = Mechanize.new
page = agent.get('http://exmaple.com/')
link = page.link_with(:href => "http://random.com/randomstring").click
img = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')
以下のプログラムの場合は正規表現がうまくマッチせず使えません
require 'mechanize'

agent = Mechanize.new
page = agent.get('http://exmaple.com/')
link = page.link_with(:href => /<a href="([^"]+)"><img src/).click
img = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

そのためlink_withメソッドの正規表現に$1を渡せばうまくいくかなと思ったのですが、やはりだめでした 
正規表現自体の知識はおありのようですが、それをRubyの中でどう使えるのかについての知識が不足しています。$1についての説明は前回の回答に書いた通り。

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

require "mechanize"

agent = Mechanize.new
page = agent.get("http://exmaple.com/")
n=0
page.root.xpath("//a/img/..").each do |a|
  page = agent.get(a["href"])
  if page.respond_to? :images_with
    page.images_with(src: /jpg\Z/).each do |img|
      img.fetch.save_as("img#{n+=1}.jpg")
    end
  end
end

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/05/31 16:56

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

    キャンセル

checkベストアンサー

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を渡して、
以下のような感じでいけるのではないかなと思いました。

require 'mechanize'

agent = Mechanize.new
page = agent.get('http://exmaple.com/')

thumbnails = page.search(‘//a/img’)
if thumbnails && thumbnails.length > 0
  link_to_original_image = thumbnails[0].parent
  page_of_original_image = agent.get(link_to_original_image[:href])
  img = link.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')
end
上記のコードをテンプレートにして、ちょっと自分の勉強がてら、
映画「イミテーションゲーム」のまとめページから、一番上の
画像を取ってくるコードを書いてみました。
# coding: utf-8

require 'mechanize'

agent = Mechanize.new
page = agent.get('http://matome.naver.jp/odai/2141968391848272501')

thumbnails = page.search('//a/img[@class="MTMItemThumb"]')

if thumbnails && thumbnails.length > 0 
  link_to_original_image = thumbnails[0].parent
  puts "href:[" + link_to_original_image[:href]  + "]" 
  page_of_original_image = agent.get(link_to_original_image[:href])
  img = page_of_original_image.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')
end

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

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

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


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

※以下追記です。

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

require 'mechanize'

agent = Mechanize.new
page = agent.get('http://matome.naver.jp/odai/2141968391848272501')

links = page.search('//a/img[@class="MTMItemThumb"]/..')

if links && links.length > 0 
  puts "href:[" + links[0][:href]  + "]"  #確認のための表示
  page_of_original_image = agent.get(links[0][:href])
  img = page_of_original_image.image_with(src: /jpg\Z/).fetch.save_as('img.jpg')
end

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

以上です。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/05/31 16:54

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

    キャンセル

0

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/05/31 00:58

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

    キャンセル

同じタグがついた質問を見る

  • Ruby

    9643questions

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