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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Beautiful Soup

Beautiful Soupは、Pythonのライブラリの一つ。スクレイピングに特化しています。HTMLデータの構文の解析を行うために、HTMLタグ/CSSのセレクタで抽出する部分を指定することが可能です。

Python 3.x

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

Q&A

解決済

2回答

1406閲覧

Beautifulsoupで抽出したデータを辞書型として追加したい

makamaka

総合スコア21

Beautiful Soup

Beautiful Soupは、Pythonのライブラリの一つ。スクレイピングに特化しています。HTMLデータの構文の解析を行うために、HTMLタグ/CSSのセレクタで抽出する部分を指定することが可能です。

Python 3.x

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

0グッド

0クリップ

投稿2021/10/09 14:06

編集2021/10/09 16:27

前提・実現したいこと

次のようなコードを辞書型として次のようなCSVファイルで出力したいと思っております。
下記のようなコードを作成したのですが、printで表示するとうまく対象全部が抽出されるのですが、いざ辞書型としていれると、最後の一つ(ここでいうキモリ)しか抽出できません
対象すべてを辞書型として格納し、CSVファイルで抽出する方法についてご教授ください。

|ポケモン|ランク||
|:--:|:--:||
|アチャモ|なし||
|ミズゴロウ|A||
|キモリ|B||
||||

該当のソースコード

python

1soup = BeautifulSoup(page_source,"lxml") 2detail = {} 3for pokemon in soup.find_all("div", attrs={"class": "ttl-result-area"}): 4 name = pokemon.find("span",attrs={"class": "horizontal-rhythm"}).text.strip() 5 ranks = pokemon.find_all("span",attrs={"class": "lbl-ipo-decline"}) 6 if len(ranks)>0: 7 rank = ranks[0].text 8 else: 9 rank = "なし" 10 print(f"ランク={rank},ポケモン={name}") 11 detail['ポケモン'] = name 12  detail['ランク'] = rank 13 14#printの結果 15ランク=なし,ポケモン=アチャモ 16ランク=A,ポケモン=ミズゴロウ 17ランク=B,ポケモン=キモリ 18 19#detailの結果 20{'ポケモン': 'キモリ', 'ランク': 'B'}

追加のソースコード

python

1for pokemon in soup.find_all("div", attrs={"class": "ttl-result-area"}): 2 detail = {} 3 name = pokemon.find("span",attrs={"class": "horizontal-rhythm"}).text.strip() 4 ranks = pokemon.find_all("span",attrs={"class": "lbl-ipo-decline"}) 5 detail = {} 6 if len(ranks)>0: 7 rank = ranks[0].text 8 else: 9 rank = "" 10 print(f"rank={rank},name={name}") 11 detail['ポケモン'] = name 12 detail['ランク'] = rank 13#detailの結果 14{'ポケモン': 'キモリ', 'ランク': 'B'}

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

わかりづらいところばかりですが、質問いただければできるだけ早めに返答いたします。

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

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

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

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

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

guest

回答2

0

ベストアンサー

ひとつのポケモンデータがひとつの辞書に対応しているようですので、
すべてのポケモンのデータを入れるリストを用意して、そこにポケモンデータを追加していきます。

python

1import csv 2 3pokemons = [] # すべてのポケモンのデータを入れるリスト 4for pokemon in soup.find_all(...): 5 ... 6 detail = {} # これは for ループの中に入れる 7 detail['ポケモン'] = name 8 detail['ランク'] = rank 9 pokemons.append(detail) 10 11with open('pokemons.csv', 'w') as f: 12 writer = csv.DictWriter(f, ['ポケモン', 'ランク']) 13 writer.writeheader() 14 writer.writerows([pokemon for pokemon in pokemons])

python

1$ python3 -q 2>>> import csv 3>>> pokemons = [] 4>>> detail = {} 5>>> detail['ポケモン'] = 'アチャモ' 6>>> detail['ランク'] = 'なし' 7>>> pokemons.append(detail) 8>>> detail = {} 9>>> detail['ポケモン'] = 'ミズゴロウ' 10>>> detail['ランク'] = 'A' 11>>> pokemons.append(detail) 12>>> detail = {} 13>>> detail['ポケモン'] = 'キモリ' 14>>> detail['ランク'] = 'B' 15>>> pokemons.append(detail) 16>>> from pprint import pprint 17>>> pprint(pokemons) 18[{'ポケモン': 'アチャモ', 'ランク': 'なし'}, 19 {'ポケモン': 'ミズゴロウ', 'ランク': 'A'}, 20 {'ポケモン': 'キモリ', 'ランク': 'B'}] 21>>> with open('pokemons.csv', 'w') as f: 22... writer = csv.DictWriter(f, ['ポケモン', 'ランク']) 23... writer.writeheader() 24... writer.writerows([pokemon for pokemon in pokemons]) 25... 2610 27>>> exit() 28$ cat pokemons.csv 29ポケモン,ランク 30アチャモ,なし 31ミズゴロウ,A 32キモリ,B

  • ただの辞書

ポケモン名を辞書のキーとし、ランクを値としています。データにはポケモン名でアクセスします。

python

1{'アチャモ': 'なし', 'キモリ': 'B', 'ミズゴロウ': 'A'} 2 3>>> pokemons['キモリ'] 4'B'
  • 辞書を値とする辞書

ポケモン名を辞書のキーとし、ポケモンデータを入れた辞書を値としています。ポケモンデータがランクひとつだけならただの辞書でもいいですが、データの種類が増えてくるとこのように辞書の値をさらに辞書とする必要が出てきます。
ポケモン名でポケモンを特定した後に、データ名で各データにアクセスします。

python

1{'アチャモ': {'ランク': 'なし', '戦闘力': 0}, 2 'キモリ': {'ランク': 'B', '戦闘力': 50}, 3 'ミズゴロウ': {'ランク': 'A', '戦闘力': 100}} 4 5>>> pokemons['キモリ'] 6{'ランク': 'B', '戦闘力': 50} 7>>> pokemons['キモリ']['ランク'] 8'B'
  • 辞書を要素とするリスト

ポケモンデータを入れた辞書を要素とするリストです。辞書と違ってキーがないので、ポケモン名もデータの一部となりました。
ポケモンの特定には位置(インデックス)を用い、各データにはデータ名でアクセスします。

python

1[{'ポケモン': 'アチャモ', 'ランク': 'なし'}, 2 {'ポケモン': 'ミズゴロウ', 'ランク': 'A'}, 3 {'ポケモン': 'キモリ', 'ランク': 'B'}] 4 5>>> pokemons[2] 6{'ポケモン': 'キモリ', 'ランク': 'B'} 7>>> pokemons[2]['ランク'] 8'B'
  • リストを要素とするリスト

ポケモンデータを入れたリストを要素とするリストです。
キーが一切ないので、位置(インデックス)のみでデータにアクセスします。

python

1[['アチャモ', 'なし'], 2 ['ミズゴロウ', 'A'], 3 ['キモリ', 'B']] 4 5>>> pokemons[2] 6['キモリ', 'B'] 7>>> pokemons[2][1] 8'B'
  • リストを値とする辞書

ポケモン名を辞書のキーとし、ポケモンデータを入れたリストを値としています。
ポケモン名でポケモンを特定した後に、位置(インデックス)で各データにアクセスします。

python

1{'アチャモ': ['なし', 0], 2 'キモリ': ['B', 50], 3 'ミズゴロウ': ['A', 100]} 4 5>>> pokemons['キモリ'] 6['B', 50] 7>>> pokemons['キモリ'][0] 8'B'

投稿2021/10/09 14:35

編集2021/10/12 17:35
etherbeg

総合スコア1195

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

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

makamaka

2021/10/09 14:53

ご回答ありがとうございます。 大変初歩的な質問で申し訳ありませんが、detail = {} # これは for ループの中に入れる とはどういうことでしょうか。
etherbeg

2021/10/09 15:35 編集

私の理解では detail という辞書にはひとつのポケモンのデータが入る、そして for pokemon in soup.find_all() のループが1回まわるごとに新しくひとつのポケモンデータが抽出される、と理解していますので、ループが回るごとに新しい空の detail 辞書を作成して、そこにポケモンのデータを入れていく(そしてそれを pokemons リストに追加する)、ということです。 ループの外で最初に1回だけ作成した辞書をループの中で使い回すと、同じ辞書が上書きされていくので、最終的には同じ辞書への参照がいくつも作られるだけになってしまいます。 >>> pokemons = [] >>> detail = {} >>> detail['ポケモン'] = 'アチャモ' >>> detail['ランク'] = 'なし' >>> pokemons.append(detail) >>> detail['ポケモン'] = 'ミズゴロウ' >>> detail['ランク'] = 'A' >>> pokemons.append(detail) >>> detail['ポケモン'] = 'キモリ' >>> detail['ランク'] = 'B' >>> pokemons.append(detail) >>> pokemons [{'ポケモン': 'キモリ', 'ランク': 'B'}, {'ポケモン': 'キモリ', 'ランク': 'B'}, {'ポケモン': 'キモリ', 'ランク': 'B'}]
makamaka

2021/10/09 15:39

たびたびのご回答ありがとうございます。 私の解釈があっていれば アチャモ、ミズゴロウ、キモリでそれぞれ手動でキーと、バリューを設定するということでしょうか。 たとえば、今回は3つで数が少ないため簡単なのですが、対象が100以上など多数になった時も同じように自力でキーとバリューを設定することになるということでしょうか?
etherbeg

2021/10/09 15:46

「手動で」というのが意味が分かりません。バリューを抽出するのも、バリューをキーに設定するのも、プログラムに書いてあるので、手動ですることは何もないのでは。対象が幾つになろうと自動で行うために、for ループを使ったプログラムを書いているのではないですか。
makamaka

2021/10/09 16:09 編集

説明が足らず申し訳ありません。 私なりに解釈し、 pokemon = [] for pokemon in soup.find_all("div", attrs={"class": "ttl-result-area"}): name = pokemon.find("span",attrs={"class": "horizontal-rhythm"}).text.strip() ranks = pokemon.find_all("span",attrs={"class": "lbl-ipo-decline"}) if len(ranks)>0: rank = ranks[0].text else: rank = "" print(f"rank={rank},name={name}") detail = {} detail['ポケモン'] = name detail['ランク'] = rank  pokemon.append(detail) としましたが、うまくいきませんでした。 もし、原因がわかりましたら教えていただきたいです
etherbeg

2021/10/09 16:21 編集

もしかして対話モードでの実行を説明抜きに示したのが紛らわしかったのかと思い、 >>> が頭についているのは、Pythonの対話モードでのプログラムの実行を示しています。 これ自体はプログラムの一部ではありません。ソースファイルに書かれたプログラムを、サンプルデータを使うなどして実際に1行1行実行することによって、プログラムはエラーなく実行できるか、生成されたデータは期待したものと違っていないかを確認するために行っています。 これを示しているのはあくまで説明の一部です。実際にこのようにプログラムを1行1行手動で実行しなければいけないわけではありません。 …という説明を書いていたところですが、これ自体はいいですかね? 上記質問に関しては、うまくいかなかったというのがどううまくいかなかったのかを具体的に書いてもらわないと何が問題になっているのか分からないのと、コメント欄でコードを書くとインデントがなくなるのでPythonには致命的で正確な回答ができないので、コードを伴う質問は質問欄に追記してもらいたいです。
makamaka

2021/10/09 16:30

>>>については私なりに調べ、理解しました。 理解が遅く申し訳ありません。 コードについては質問の方に追記しました。 お時間ある際にお目通しいただけたらと思います。 うまくいかなかった点については detailで実行した結果が{'ポケモン': 'キモリ', 'ランク': 'B'}と最初と変わりありませんでした。 ほんらいなら[{'ポケモン': 'キモリ', 'ランク': 'B'}, {'ポケモン': 'キモリ', 'ランク': 'B'}, {'ポケモン': 'キモリ', 'ランク': 'B'}]となるかと思いますが、何か私の理解が足らないところがあるのでしょうか。
etherbeg

2021/10/10 07:20 編集

Pythonにおける辞書型とリスト型の違い、また、それらを入れ子にして組み合わせた「辞書を値とする辞書」「辞書を要素とするリスト」「リストを値とする辞書」「リストを要素とするリスト」の違いは分かりますか? ひとつのポケモンのデータを保存するだけなら単なる辞書型でいいのですが、複数のポケモンのデータを保存しようとしたら、「辞書を値とする辞書」か、「辞書を要素とするリスト」にする必要があります。そのことは分かりますか? 質問文を読むと、質問者さんは「辞書を値とする辞書」を想定しているようにも読めます。もしくは、単に辞書というデータ型についての理解が足りていないようにも思えます。 私の回答は、「最終的に質問文にあるようなCSVに出力することが目的なら、『辞書を要素とするリスト』にするのが良いと思うので、そのやり方を説明します」というものです。これは回答を読んだらわかると思って、回答に明記しなかったので、申し訳なかったのですが。 今回の事例に即して言えば、「辞書を要素とするリスト」とは、「個々のポケモンデータが順番に並んだリスト」です。個々のポケモンデータを作成するための辞書が detail で、複数の detail が順番に納められて並んでいるものが pokemons リストです。 pokemons リストの中では、個々のポケモンデータはリストの中での位置で識別されます。detail という名称は個々のポケモンデータを作成するための作業用のものですので、detail という名称では pokemons リストの中のデータにはアクセスできません。個々のデータには、pokemons[0], pokemons[1], pokemons[2] ... というようにアクセスします。 質問文やコメントを読んでいると、質問者さんは、detail という辞書にすべてのポケモンデータを収めると思っている節があります。 もしどうしてもそうしたいのなら、「辞書を値とする辞書」にする必要があります。すべてのポケモンを入れる辞書を detail という名前にしたいのなら、個々のポケモンデータを入れる辞書も、それとは別に作らなければなりません。 そして個々のポケモンデータを識別するキーを決めなければなりません。ポケモンの名前とか、連番とか、一意に決まるものをキーにする必要があります。 キーに対応する値として、個々のポケモンデータの辞書を登録するのが、「辞書を値とする辞書」を作る作業になります。 「追加のソースコード」がうまくいかないとのことですが、detail という辞書は for ループが回るごとに新しく作られて書き換えられていくので、for ループが終了してループを抜けた後に detail の中身を表示すると、一番最後のループで作られた detail のデータのみが表示されます。 ループを回るごとに、detail を別のリストなり辞書なりに保存しておかないと、それはループを回るごとに消えていきます。「追加のソースコード」では、「detail を別のリストなり辞書なりに保存しておく」作業が行われていません。 print(f"rank={rank},name={name}") の表示はループごとに行われているので、そのループの回での detail の中身の表示しか行っていません。先に表示した文字が消えないので、結果的に最後にはすべてのポケモンデータの表示がされていますが、それは先に表示されたポケモンのデータがどこかに保存されていることを意味しません。
etherbeg

2021/10/10 07:19

あと「追加のソースコード」には detail = {} が2箇所にありますが、どちらかひとつでいいです。
makamaka

2021/10/11 23:21

ご回答ありがとうございます。 返信が遅くなり、大変申し訳ありません。 ご指摘いただいた箇所については私の知らないことばかりでしたので、帰宅後、再度勉強し、実行させていただきます。
etherbeg

2021/10/12 09:18

すみません、私がちょっと勘違いしていたかもしれません。 detail[name] = rank だと あちゃも  ミズゴロウ   キモリ  なし     A      B と出力されるというのは、CSVにそのように出力されるということでしょうか。だとしたら上で述べたようなややこしいことは言わずとも、CSVへの書き込み方を変えればそれで良かったのでは、ということに今になって気がつきました。 最初に自分で思いついた回答に引きずられることなく、もっと簡単な回答である detail[name] = rank では駄目な理由をもっとちゃんと掘り下げて聞くべきだったかもしれません。 もし私が回答を書き込まなければ、先に書き込んでいた回答者の方とコメント欄でそのような話になっていたかもしれません。申し訳ありません。
etherbeg

2021/10/12 17:26

これだけだと何なので、データ型の違いを回答に追記しました。
guest

0

辞書型は、ひとつのキーに対して、ひとつの値しか持てません。

python

1 detail['ポケモン'] = name 2  detail['ランク'] = rank

を、

python

1 detail[name] = rank

とすれば、全部の結果が入るでしょう。

投稿2021/10/09 14:25

ppaul

総合スコア24666

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

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

makamaka

2021/10/09 14:30 編集

ご回答ありがとうございます。 教えていただいた方法についてはすでに施行したのですが、 detail[name] = rank ですと あちゃも  ミズゴロウ   キモリ  なし     A      B というに出力されます。 実際はこれ以上長いので教えていただいたやり方ですと多少の不都合があるため ポケモン    ランク アチャモ     なし ミズゴロウ    A キモリ      B としたいと思っています。 もしやり方とうわかればご教授願います
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問