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

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

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

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Scrapy

Scrapyは、Pythonで開発されたオープンソースソフトウェアです。スクレイピングという、Webサービスから必要な情報を取り出したり自動操作をしたりする技術を使うものです。

Q&A

解決済

1回答

584閲覧

[Yahooニュースのリスト] PythonのScrapyで二つ目のcallbackを呼ぶ方法

LIATARET

総合スコア17

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Scrapy

Scrapyは、Pythonで開発されたオープンソースソフトウェアです。スクレイピングという、Webサービスから必要な情報を取り出したり自動操作をしたりする技術を使うものです。

0グッド

1クリップ

投稿2018/04/29 13:35

編集2018/05/01 13:20

前提・実現したいこと

Yahooニュースの主要リストタブの1件目の情報を取得しようとしています。
※主要リストタブの1件目が動画ありのものだと失敗しますが、対象は動画なしのものです。

取得したいのは下記の5つ、
①タイトル 「title」、
②カテゴリ 「category」、
③タイトルのURL 「url」、
④③のURL先の(リンク付きの)見出し文 「summary」、
⑤④のリンク先の『○○/○○(曜日)○○:○○配信』という文字列 「haisintext」
です。

$ scrapy crawl ynews -o news.json 1> one.txt 2> two.txt

①〜④は問題なくファイルに出力されているのですが、⑤には「日時配信」の文字列ではなく、
その文字列があるページのURLが出力されます。
'haisinlink': '〜'ではなく、'haisintext': '4/29(日) 18:02配信'としたいです。

{'category': 'スポーツ',
'haisinlink': 'https://headlines.yahoo.co.jp/hl?a=20180429-00000132-dal-base',
'summary': '巨人が逆転で2年ぶり7連勝、代打・阿部の今季初安打で同点「最高です!」も今季初',
'title': 'G7連勝 阿部「やっと打てた」',
'url': 'https://news.yahoo.co.jp/pickup/6280827'}

parseメソッドからgetArticleメソッドへのcallback(summary取得)は問題ないのですが、
getArticleメソッドからgetTextメソッドへのcallback(haisintext取得希望)のやり方が分かりません。
どのように記述すれば良いのでしょうか。ご教示お願いいたします。

settings.py

ROBOTSTXT_OBEY = True DOWNLOAD_DELAY = 3 HTTPCACHE_ENABLED = False FEED_EXPORT_ENCODING = 'utf-8'

発生している問題・エラーメッセージ

2018-04-29 20:48:15 [scrapy.utils.log] INFO: Scrapy 1.5.0 started (bot: nobel_winners) 2018-04-29 20:48:15 [scrapy.utils.log] INFO: Versions: lxml 4.1.1.0, libxml2 2.9.7, cssselect 1.0.3, parsel 1.4.0, w3lib 1.19.0, Twisted 17.9.0, Python 3.6.4 (default, Feb 10 2018, 20:17:51) - [GCC 4.2.1 Compatible Apple LLVM 9.0.0 (clang-900.0.39.2)], pyOpenSSL 17.5.0 (OpenSSL 1.1.0h 27 Mar 2018), cryptography 2.2.2, Platform Darwin-16.7.0-x86_64-i386-64bit 2018-04-29 20:48:15 [scrapy.crawler] INFO: Overridden settings: {'BOT_NAME': 'nobel_winners', 'DEPTH_LIMIT': 5, 'DOWNLOAD_DELAY': 3, 'FEED_EXPORT_ENCODING': 'utf-8', 'FEED_FORMAT': 'json', 'FEED_URI': 'news.json', 'NEWSPIDER_MODULE': 'nobel_winners.spiders', 'ROBOTSTXT_OBEY': True, 'SPIDER_MODULES': ['nobel_winners.spiders']} 2018-04-29 20:48:15 [scrapy.middleware] INFO: Enabled extensions: ['scrapy.extensions.corestats.CoreStats', 'scrapy.extensions.telnet.TelnetConsole', 'scrapy.extensions.memusage.MemoryUsage', 'scrapy.extensions.feedexport.FeedExporter', 'scrapy.extensions.logstats.LogStats'] 2018-04-29 20:48:15 [scrapy.middleware] INFO: Enabled downloader middlewares: ['scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware', 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware', 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware', 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware', 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware', 'scrapy.downloadermiddlewares.retry.RetryMiddleware', 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware', 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware', 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware', 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware', 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware', 'scrapy.downloadermiddlewares.stats.DownloaderStats'] 2018-04-29 20:48:15 [scrapy.middleware] INFO: Enabled spider middlewares: ['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware', 'scrapy.spidermiddlewares.offsite.OffsiteMiddleware', 'scrapy.spidermiddlewares.referer.RefererMiddleware', 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware', 'scrapy.spidermiddlewares.depth.DepthMiddleware'] 2018-04-29 20:48:15 [scrapy.middleware] INFO: Enabled item pipelines: [] 2018-04-29 20:48:15 [scrapy.core.engine] INFO: Spider opened 2018-04-29 20:48:15 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 2018-04-29 20:48:15 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6024 2018-04-29 20:48:15 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://news.yahoo.co.jp/robots.txt> (referer: None) 2018-04-29 20:48:19 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://news.yahoo.co.jp/list> (referer: None) 2018-04-29 20:48:23 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://news.yahoo.co.jp/pickup/6280827> (referer: https://news.yahoo.co.jp/list) 2018-04-29 20:48:23 [scrapy.core.scraper] DEBUG: Scraped from <200 https://news.yahoo.co.jp/pickup/6280827> {'category': 'スポーツ', 'haisinlink': 'https://headlines.yahoo.co.jp/hl?a=20180429-00000132-dal-base', 'summary': '巨人が逆転で2年ぶり7連勝、代打・阿部の今季初安打で同点「最高です!」も今季初', 'title': 'G7連勝 阿部「やっと打てた」', 'url': 'https://news.yahoo.co.jp/pickup/6280827'} 2018-04-29 20:48:23 [scrapy.core.engine] INFO: Closing spider (finished) 2018-04-29 20:48:23 [scrapy.extensions.feedexport] INFO: Stored json feed (1 items) in: news.json 2018-04-29 20:48:23 [scrapy.statscollectors] INFO: Dumping Scrapy stats: {'downloader/request_bytes': 759, 'downloader/request_count': 3, 'downloader/request_method_count/GET': 3, 'downloader/response_bytes': 22206, 'downloader/response_count': 3, 'downloader/response_status_count/200': 3, 'finish_reason': 'finished', 'finish_time': datetime.datetime(2018, 4, 29, 11, 48, 23, 584196), 'item_scraped_count': 1, 'log_count/DEBUG': 5, 'log_count/INFO': 8, 'memusage/max': 53182464, 'memusage/startup': 53182464, 'request_depth_max': 1, 'response_received_count': 3, 'scheduler/dequeued': 2, 'scheduler/dequeued/memory': 2, 'scheduler/enqueued': 2, 'scheduler/enqueued/memory': 2, 'start_time': datetime.datetime(2018, 4, 29, 11, 48, 15, 328173)} 2018-04-29 20:48:23 [scrapy.core.engine] INFO: Spider closed (finished)

ファイル出力結果 (news.json)

[ {"title": "G7連勝 阿部「やっと打てた」", "category": "スポーツ", "url": "https://news.yahoo.co.jp/pickup/6280827", "summary": "巨人が逆転で2年ぶり7連勝、代打・阿部の今季初安打で同点「最高です!」も今季初", "haisinlink": "https://headlines.yahoo.co.jp/hl?a=20180429-00000132-dal-base"} ]

該当のソースコード

import scrapy class NewsItem(scrapy.Item): title = scrapy.Field() category = scrapy.Field() url = scrapy.Field() summary = scrapy.Field() haisintext = scrapy.Field() haisinlink = scrapy.Field() class YnewsSpider(scrapy.Spider): name = "ynews" start_urls = ['https://news.yahoo.co.jp/list'] def parse(self, response): for selectElements in response.xpath('//*[@id="main"]/div[1]/div[1]/div[4]/ul'): item = NewsItem()    # 先頭の記事が動画ありだと失敗するので、下3行分の.//li[1]を[2]とかにずらしてやり過ごす item['title'] = selectElements.xpath('.//li[1]/a/dl/dt/text()').extract_first() item['category'] = selectElements.xpath('.//li[1]/a/dl/dd/text()').extract_first() item['url'] = selectElements.xpath('.//li[1]/a/@href').extract_first() request = scrapy.Request(item['url'], callback = self.getArticle, dont_filter = True) request.meta['item'] = item yield request def getArticle(self, response): item = response.meta['item'] item['summary'] = response.xpath('//*[@id="link"]/text()').extract_first() item['haisinlink'] = response.xpath('//*[@id="link"]/@href').extract_first() request = scrapy.Request(item['haisinlink'], callback = self.getText, dont_filter = True) yield item def getText(self, response): item = response.meta['item'] item['haisintext'] = response.xpath('//*[@id="ym_newsarticle"]/div[1]/div/p[1]/text()').extract_first() yield item

『○○/○○(曜日)○○:○○配信』別の方法で取得

しましたが、結局二つ目callback呼び出しは解決出来ないまま...

import scrapy import urllib, lxml class NewsItem(scrapy.Item): title = scrapy.Field() category = scrapy.Field() url = scrapy.Field() summary = scrapy.Field() haisintext = scrapy.Field() # haisinlink = scrapy.Field() class YnewsSpider(scrapy.Spider): name = "ynews" start_urls = ['https://news.yahoo.co.jp/list'] def parse(self, response): for selectElements in response.xpath('//*[@id="main"]/div[1]/div[1]/div[4]/ul'): item = NewsItem() # 先頭の記事が動画ありだと失敗するので、下3行分の.//li[1]を[2]とかにずらしてやり過ごす item['title'] = selectElements.xpath('.//li[3]/a/dl/dt/text()').extract_first() item['category'] = selectElements.xpath('.//li[3]/a/dl/dd/text()').extract_first() item['url'] = selectElements.xpath('.//li[3]/a/@href').extract_first() request = scrapy.Request(item['url'], callback = self.getArticle, dont_filter = True) request.meta['item'] = item yield request def getArticle(self, response): item = response.meta['item'] item['summary'] = response.xpath('//*[@id="link"]/text()').extract_first() haisinlink = response.xpath('//*[@id="link"]/@href').extract_first() target_url = haisinlink res = urllib.request.urlopen(target_url) dom = lxml.html.fromstring(res.read()) haisin_txt = dom.xpath('//*[@id="ym_newsarticle"]/div[1]/div/p[1]/text()') result = haisin_txt[0].strip() item['haisintext'] = result yield item

補足情報(FW/ツールのバージョンなど)

Python 3.6.4
scrapy 1.5.0

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

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

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

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

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

guest

回答1

0

ベストアンサー

{'category': 'スポーツ', 'haisintext': '5/3(木) 9:34 掲載', 'summary': '白星無し3連敗…ダルビッシュに地元ファンからブーイング', 'title': 'ダル3敗目 地元でブーイング', 'url': 'https://news.yahoo.co.jp/pickup/6281189'}

このようなものを取得するのが目的でしょうか。
でしたら、haisintextにあたるもののxpathが

//*[@id="main"]/div[@class="mainBox"]/div[@class="topicsDetail"]/div[@class="topicsHead"]/div[@class="topicsName"/span[@class="date"]

となっていますので、getArticle 中でhaisintext取得を行えばよいかと思います。例えば下記のように

item['haisintext'] = response.xpath('//div[@class="topicsName"]/span[@class="date"]/text()').extract_first()

投稿2018/05/03 01:13

calkinos

総合スコア452

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

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

LIATARET

2018/05/03 06:15

回答ありがとうございます。 分かり難くて申し訳ありません。目的の文字列を取得するページの場所ですが、頂いた回答の例で挙げますと、 ’url’: ‘http://news.yahoo.co.jp/pickup/6281189’ のページ内の ’summary’: ‘白星〜(略)〜ブーイング’ をクリックしたリンク先のページ内にある、 xpathが『//*[@id=“ym_newsarticle”]/div[1]/div/p[1]/text()』の 『○○/○○(曜日)○○:○○配信』という文字列になります。 こちらを getArticle()内のcallback=self.getText()を使って取得する(getTextにデータを渡す)方法が質問の趣旨となります。
calkinos

2018/05/10 01:30

それでしたら ``` def getArticle(self, response): item = response.meta['item'] item['summary'] = response.xpath('//*[@id="link"]/text()').extract_first() item['haisinlink'] = response.xpath('//*[@id="link"]/@href').extract_first() request = scrapy.Request(item['haisinlink'], callback = self.getText, dont_filter = True) yield item ``` ここの最後を ``` request = scrapy.Request(item['haisinlink'], callback = self.getText, dont_filter = True) request.meta["item"]=item yield request ``` とするのではダメなんでしょうか
LIATARET

2018/05/11 15:03

ご指摘の箇所を変更し実行したところ取得出来ました。いまいちyieldが分からず、ググっても解決方法がなかったので諦めておりました。ありがとうございます。助かりました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問