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

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

新規登録して質問してみよう
ただいま回答率
85.35%
スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

2290閲覧

ニュースサイト(Yahooニュース)からのスクレイピングの際、URLの抽出ができません

zerokara.code

総合スコア3

スクレイピング

スクレイピングとは、公開されているWebサイトからページ内の情報を抽出する技術です。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/08/17 12:27

前提・実現したいこと

ニュースサイト(Yahooニュース)からのスクレイピングによる記事抽出

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

Traceback (most recent call last): File "200817_yahoonews_5pages.py", line 19, in <module> href = item.find('a')['href'] TypeError: 'NoneType' object is not subscriptable

該当のソースコード

python

1# 200817_yahoonews_5pages.py 2import csv 3import requests 4from bs4 import BeautifulSoup 5 6url = 'https://news.yahoo.co.jp/topics/top-picks' 7 8with open ('yahoonews_topics.csv', 'w') as f: 9 writer = csv.writer(f) 10 11 for i in range(5): 12 html = requests.get(url).text 13 soup = BeautifulSoup(html, 'html.parser') 14 next = soup.find('li', {'class' : 'pagination_item pagination_item-next'}) 15 url = 'https://news.yahoo.co.jp/' + next.find('a')['href'] 16 items = soup.find_all("li", {'class':'newsFeed_item'}) 17 for item in items: 18 title = item.text 19 href = item.find('a')['href'] 20 writer.writerow([title, href, ]) 21

試したこと

titleが抽出できているところまでは分かったのですが、
urlの抽出でエラーが出ます。
エラーメッセージでググっていくつかのサイトを見ましたが、今回のケースでどういった理由でこのエラーが出ているのか突き止められていません。

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

ここにより詳細な情報を記載してください。

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

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

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

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

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

meg_

2020/08/17 12:47

itemsに値は入っていますか?
zerokara.code

2020/08/17 13:05

itemsには値は入っています。 要素を1つだけ書き出すと、例えば以下のようなものです <li class="newsFeed_item"><a class="newsFeed_item_link" data-ylk="rsec:st_topics;slk:title;pos:25;" href="https://news.yahoo.co.jp/pickup/6368532"><div class="newsFeed_item_thumbnail"><div class="sc-jAaTju yxion thumbnail thumbnail-small"><img alt="" class="sc-jDwBTQ ieGgsp" src="https://giwiz-tpc.c.yimg.jp/q/iwiz-tpc/images/tpc/2020/08/17/1597635001_20200817-28160045-nksports-000-view.jpg"/></div></div><div class="newsFeed_item_text"><div class="newsFeed_item_title">速報 尽誠学園vs.智弁和歌山</div><div class="newsFeed_item_sub"><div class="newsFeed_item_sourceWrap"><time class="newsFeed_item_date">8/17(月) 12:50</time></div></div></div></a></li>]
guest

回答3

0

先日もご説明した様に
item.find('a')['href'] → "/article/DGXMZ~~~"
item.get('href') →None
こちら['href']と.get('href')はどちらも同じ挙動のコードである為
.get('href)だとNoneが出るというものではありません。

これらはどちらも正常に抽出出来るはずです。
item.find('a').get('href')
item.find('a')['href']

そしてこれらはどちらもNoneが返ってくるはずです。
item.get('href')
item['href']

抽出するのはどちらのコードを使用しても構いません。
コードではなく抽出しようとしている対象(item)が問題となっているのです。

.get('href') / ['href']は自動的に、親要素からhref属性を取得する仕組みになっています。
親要素とはどれと定義されているものではなく、
簡易的に説明するならば一番外側の要素が親要素です。

html

1<div class="sample"> //親要素 2 <img src="http://sample.com/sample.jpg"> //子要素 3 <h3 class="hoge"> //子要素 4 <a href="http://sample.com/">これはサンプルです</a> //孫要素 5 </h3> 6</div>

極端にわかりやすく申し上げると、href属性の抽出は
<a>要素が親要素になる様に指定しなくては取り出す事は出来ません。

まずはprint(item)で一つずつ取り出しているitemがどういったものであるのかお確かめ下さい。

python

1for item in items: 2 print(item) 3 4>>> <h3 class="m-miM09_title"><a href="/article/DGXMZO62833220Q0A820C2AM1000/"><span class="m-miM09_titleL">手袋・マスク…海汚す「コロナごみ」 生態 系に影響も</span></a><span class="m-iconMember">[有料会員限定]</span></h3> 5>>> <h3 class="m-miM09_title"><a href="/article/DGXMZO62811650Z10C20A8000000/"><span class="m-miM09_titleL">バブルの情景再び、イオンがドライブインシ アター</span></a><span class="m-iconMember">[有料会員限定]</span></h3> 6>>> <h3 class="m-miM09_title"><a href="/article/DGXMZO62660640U0A810C2ENI000/"><span class="m-miM09_titleL">米中対立の激化と日本の対応</span></a><span class="m-iconMember">[有料会員限定]</span></h3>

以上の様に、取り出したものは確かに<a>要素も含まれてはいますが
<a>要素が親要素になる様に指定出来ているわけではありません。
これにitem.get('href') / item['href']をしようとしても
親要素である<h3>要素からhref属性を取り出そうとしてしまい

<h3>要素にはhref属性が設定されていない為Noneが返ってきてしまうのです。

つまり<a>要素が親要素になる様に指定するには、item.find('a')として
<a>要素を指定してあげる必要があるのです。

python

1for item in items: 2 a = item.find('a') 3 4>>> <a href="/article/DGXMZO62833220Q0A820C2AM1000/"><span class="m-miM09_titleL">手袋・マスク…海汚す「コロナごみ」 生態 系に影響も</span></a> 5>>> <a href="/article/DGXMZO62811650Z10C20A8000000/"><span class="m-miM09_titleL">バブルの情景再び、イオンがドライブインシ アター</span></a> 6>>> <a href="/article/DGXMZO62660640U0A810C2ENI000/"><span class="m-miM09_titleL">米中対立の激化と日本の対応</span></a>

python

1for item in items: 2 a = item.find('a') 3 href = a.get('href') # または a['href'] 4 5>>> /article/DGXMZO62833220Q0A820C2AM1000/ 6>>> /article/DGXMZO62811650Z10C20A8000000/ 7>>> /article/DGXMZO62660640U0A810C2ENI000/

投稿2020/08/20 03:17

nto

総合スコア1438

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

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

zerokara.code

2020/08/20 12:57

再度大変ご丁寧にありがとうございます。完全に腹落ちしました。 昨日からすでに教えていただいていたことではありましたが、 ・ .get('href') / ['href'] は親要素から抽出する仕組みであること ・ 故に子要素のhrefを抽出するためには、一旦item.find('a')として <a>要素を親要素になるように指定してあげる必要がある  ことがキモということですね。 既に教えていただいたことを理解できておらず、歯がゆく申し訳ない気持ちですが、また新しいことを学べて成長を実感できました。 またteratailでご質問させていただく機会があるかと思いますが、 その折はまたぜひどうぞよろしくお願いいします。
nto

2020/08/20 13:38

とんでもないです。少しでもご理解ご納得いただけたなら幸いです。 だいぶ大きな進展と伺えます。 私も教えられるよりも実践するほうが理解が早いタイプなので、実際にコードを色々試してみたりすることが理解に繋がりやすいかもしれません。
guest

0

こんにちは。ブログ等拝見させていただきました。
item.get('href') →None
item.find('a')['href'] →URL
この違いについてですが
まず抽出方法ですが.get('href')['href']は同じ動作で、違いはありません。
(コードを書いた時に.get()で書いてしまった私のせいで混乱させてしまったかもしれません。)

ではなぜ挙動が違うのか?
以下のコードで試しましょう。

python

1from bs4 import BeautifulSoup 2 3html = ''' 4<html> 5 <body> 6 <div class="oya"> 7 <a class="ko" href="http://hogehoge.com/">hogeについて</a> 8 </div> 9 </body> 10<html> 11''' 12 13soup = BeautifulSoup(html, 'html.parser') 14 15div = soup.find('div', class_='oya') 16a = soup.find('a', class_='ko') 17 18print(a.get('href')) # 正常に取得出来る 19print(div.get('href')) # エラーになる

なぜエラーが生じるのかを煮詰めていきましょう。
.get('href') / ['href']というのは、指定された要素のhref属性を抽出するというメソッドです。

python

1print(a) 2>>> <a class="ko" href="http://hogehoge.com/">hogeについて</a> 3 4 5print(div) 6>>> <div class="oya"> 7>>> <a class="ko" href="http://hogehoge.com/">hogeについて</a> 8>>> </div>

変数aでは、直接href属性が設定された<a>要素が格納されている為href属性を抽出する事ができております。

一方で変数divの場合、下階層にhref属性が設定された要素はありますが、この場合.get('href') / ['href']を使用した場合の挙動としては親要素からhref属性を抽出するという挙動となり、<div>要素にはhref属性は設定されていない為Noneが返されます。
これが本筋のNoneが返ってくる理由です。

それでは試しに<div>にもhref属性を与えてみましょう。(本来その様なhtmコードはありませんが)

python

1from bs4 import BeautifulSoup 2 3html = ''' 4<html> 5 <body> 6 <div class="oya" href="hoge"> 7 <a class="ko" href="http://hogehoge.com/">hogeについて</a> 8 </div> 9 </body> 10<html> 11''' 12 13soup = BeautifulSoup(html, 'html.parser') 14 15div = soup.find('div', class_='oya') 16 17print(div.get('href')) 18>>> hoge

そうすると、子要素にもhref属性をもった<a>要素は存在しますが、親要素である<div>要素のhref属性が返ってきます。
これで.get('href') / ['href']がどういった挙動をもっているのか確認が出来たと思います。

本筋に戻りなぜ.find('a')['href']では抽出出来たのかですが

python

1div = soup.find('div', class_='oya') 2link = div.find('a').get('href')

とするのも

python

1div = soup.find('div', class_='oya') 2 3a = div.find('a') 4link = a.get('href')

とするのもどちらも同じで、正しくhref属性が設定された<a>要素を取り出したあとに抽出をしている為正常にURLが返ってくるのです。

投稿2020/08/19 04:50

nto

総合スコア1438

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

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

zerokara.code

2020/08/19 13:34

大変丁寧なご解説ありがとうございます。 実は、ご指摘のツイート、 「item.get('href') →None  item.find('a')['href'] →URL」 ツイートでは説明が不足しておりましたが、 (お陰様で完成した)Yahooニュースのスクレイピングのコードを応用して、 日経のサイトでも試そうとしたときに、出てきたエラーなのです。 以下はworkしたコードなのですが、 ==== import datetime import csv import requests from bs4 import BeautifulSoup d_today = datetime.date.today() url = 'https://www.nikkei.com/news/category/' with open (str(d_today) +'_nikkei_topics.csv', 'w') as f: writer = csv.writer(f) for i in range(5): html = requests.get(url).text soup = BeautifulSoup(html, 'html.parser') url = 'https://www.nikkei.com/news/category/?bn=' + str(i*30+1) items = soup.find_all('h3', class_='m-miM09_title') + soup.find_all(class_='m-miM32_itemTitleText') for item in items: title = item.find('a').text href = item.find('a')['href'] writer.writerow([title, href, ]) ==== この下から2行目を、 href = item.get('href') とすると、Noneになってしまいます。 なお、試しにprint(item)をすると、 以下のような文字列が出力されます。 ==== <span class="m-miM32_itemTitleText"> <a href="/article/DGXMZO62807440Z10C20A8QM8000/">7月のリゾート会員権、前月比3.3%高</a></span>     ==== このitemから、 item.find('a')['href'] → "/article/DGXMZ~~~" item.get('href') →None となる違いがどうしても分からずスタックしていました。 この答えは、すでに教えて頂いた内容に含まれているのかもしれませんが、 理解が追いつかず恐縮です。 この点だけ教えていただけますと大変幸いです。
zerokara.code

2020/08/19 13:39

(↑コメント欄でコードが上手く表現できていませんでした) urlは https://www.nikkei.com/news/category/ です。 また、writer以下、html以下、title以下はそれぞれ1階層ずつインデントが追加していっています。
nto

2020/08/20 03:17

コメント欄ではコードのインデントをつけてあげる事が出来ない為 改めて回答を記載しました!
guest

0

ベストアンサー

16行目の指定の仕方だと、ページ内の広告も拾ってしまう為
以下のコードの指定に変更してお試しください。

python

1 items = soup.find_all(class_='newsFeed_item_link') 2 for item in items: 3 title = item.find(class_='newsFeed_item_title').text 4 href = item.get('href') 5 writer.writerow([title, href, ])

投稿2020/08/17 13:02

編集2020/08/17 13:06
nto

総合スコア1438

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

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

zerokara.code

2020/08/17 21:16

ありがとうございます。1日スタックしていたところが解決しました! 元々がなぜ駄目で、これがworkするのか調べて見たいと思います
nto

2020/08/18 03:36

ブラウザでAddblockなど使用していませんか? 利用している場合には一旦オフにして掲題のURLのページを再確認して下さい。 ページ内にはyahooJAPAN広告が何個か配置されていると思います。 そして広告のソースにもclass属性にnewsFeed_itemが設定されており、他の記事とはソースの中身がやや異なります。 広告のソースを確かめるとnewsFeed_itemが設定された<li>の直下には<a>はなく、<div id="ad*">が存在し、更にその内側(ひ孫要素)に<a>が配置されています。 掲題のコードでは16行目で[newsFeed_itemクラス]が設定された<li>要素をリスト化し、for文内19行目でそれぞれ<li>内の子要素にある<a>タグのhref属性を取得しようとしているわけですが、広告の<li>タグ内の子要素には<a>タグが存在しない(実際にはひ孫要素である)為エラーが吐き出されてしまう次第です。
zerokara.code

2020/08/18 14:28

なるほど、adblockですね。確かに使用していました。 一旦オフにしたところ、仰るとおり、広告にもnewsFeed_itemが設定されており、その中身を見てみると、実際の記事の中身とは異なる階層構造であることがよく分かりました。 一方で、<li>タグの子要素の<a>タグは取得でき、ひ孫要素の<a>タグが取得できない(確かにitemを出力すると、広告部分は<li class="newsFeed_item"><div id="ad4"></div></li>しか取得できていませんでした)のか、まだ私は理解できていませんので、もう少しHTMLの階層構造や、findメソッドの原理も含めて勉強したいと思います。 adblockをオンにしたまま解析しようとするとこういう落とし穴があるのですね、非常に勉強になりました。ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問