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

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

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

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

Python

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

5回答

1952閲覧

pythonのスクレイピングで情報要素がない場合の知得方法について

python_heroku

総合スコア23

スクレイピング

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

Python

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

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

1クリップ

投稿2020/09/21 12:31

編集2020/09/22 08:07

pythonでHTMLのスクレイピングをしています。
HTMLから以下のような形のpandasデータとして情報を取得したいのですが、うまく出来ずお知恵を拝借したく。

やりたい事

name, msg text, date 太郎, こんにちわ, 2020-09-01 次郎, ,,2020-01-01 三郎, お久しぶりです, 2020-06-01

という二次元配列データとして以下のHTMLから取り出したい。

HTML

<div class="msglist"> <div class="msgbox"> <div class="name">太郎</div> <span class="msg text">こんにちわ</span> <div class="date">2020-09-01</div> </div> <div class="msgbox"> <div class="name">次郎</div> <div class="date">2020-01-01</div> </div> <div class="msgbox"> <div class="name">三郎</div> <span class="msg text">お久しぶりです</span> <div class="date">2020-06-01</div> </div> </div>

次郎のところには

<span class="msg text">hogehoge</span>

自体が存在しないため、例えば

elems1 = html.findAll('div', class_='name') elems2 = html.findAll('span', class_='msg text') elems3 = html.findAll('div', class_='date')

という形で抜き出してしまうと次郎のところでmsg textがNULLである、ということが知得出来ず困っています。
どのようにすればやりたいことが出来るのが教えて頂きたく。

親divで全体を取得した際に例えば太郎のmsg textを取得、のような記載方法が理解できていないです。

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

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

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

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

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

attakei

2020/09/21 12:44

掲載されているHTMLは掲載どおり、DOMとしてフラットな並びになっているものですか? (例えば、それぞれのdiv,span,divがセットで上位のdivで囲まれてたりはしませんか?)
toast-uz

2020/09/21 13:02

要素が無いことよりも、1つ1つの(name, msg text, date)の固まりがフラットで構造が入っていないことに、課題がありますね
python_heroku

2020/09/21 13:39

コメントありがとうございます。 失礼しました。HTMLの構造について省略して記載してしまいましたが、それだと回答できませんね。 おっしゃる通りで上位のdivで囲まれているので修正します。
toast-uz

2020/09/21 13:51

修正後を確認しました。この位置ではなく、もう1つ下層の<div>が必要です。msglist全体ではなく、個々のmsgごとに。無いでしょうか?
python_heroku

2020/09/22 08:07

ご指摘通り、もう1つ下層に<div>がありました。失礼しました。
guest

回答5

0

python

1import pandas as pd 2from bs4 import BeautifulSoup 3 4body = """ 5<div class="msglist"> 6 <div class="msg"> 7 <div class="name">太郎</div> 8 <span class="msg text">こんにちわ</span> 9 <div class="date">2020-09-01</div> 10 </div> 11 12 <div class="msg"> 13 <div class="name">次郎</div> 14 <div class="date">2020-01-01</div> 15 </div> 16 17 <div class="msg"> 18 <div class="name">三郎</div> 19 <span class="msg text">お久しぶりです</span> 20 <div class="date">2020-06-01</div> 21 </div> 22</div> 23""" 24 25soup = BeautifulSoup(body, "html.parser") 26 27result = [] 28 29for tag in soup.find_all("div", class_="msg"): 30 31 data = {" ".join(i.get("class", ["unknown"])) : i.get_text() for i in tag.find_all()} 32 33 result.append(data) 34 35df = pd.DataFrame(result)

投稿2020/09/24 08:05

編集2020/09/24 08:14
barobaro

総合スコア1286

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

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

0

追記依頼等々で出ている通り、div,span,divを1セットとしたdivという階層構造でないと、パースが面倒になります。

パースする側で期待したいHTMLの構造:

<div class="msglist"><!- メッセージの塊をまとめたdiv -> <div> <!- メッセージをひとまとめにしたdiv -> <div class="name"> <span class="msg text"> <div class="date">

なお、フラットに並んでいるHTMLを頑張ってパースしようとすると、
例えばこうなります(あくまで例です

from pprint import pprint from bs4 import BeautifulSoup from bs4.element import Tag src = """ <div class="msglist"> <div class="name">太郎</div> <span class="msg text">こんにちわ</span> <div class="date">2020-09-01</div> <div class="name">次郎</div> <div class="date">2020-01-01</div> <div class="name">三郎</div> <span class="msg text">お久しぶりです</span> <div class="date">2020-06-01</div> </div> """ soup = BeautifulSoup(src, "html.parser") msglist = soup.find("div", class_="msglist") items = [] item = None # msglist配下をループして探索する for elm in msglist.children: if not isinstance(elm, Tag): continue # 探索したタグが条件に合うなら変数処理をしていく # (あくまでnameが最初に来ることのみを想定) if elm.name == "div" and "name" in elm.attrs["class"]: if item is not None: # 次のnameが来たら直前まで保持してたデータをリストに格納 item.setdefault("name", None) item.setdefault("date", None) item.setdefault("text", None) items.append(item) item = {} item["name"] = elm.string if elm.name == "span" and "text" in elm.attrs["class"]: item["text"] = elm.string if elm.name == "div" and "date" in elm.attrs["class"]: item["date"] = elm.string # 最後の保持データも忘れずに格納 if item is not None: item.setdefault("name", None) item.setdefault("date", None) item.setdefault("text", None) items.append(item) pprint(items)

実行結果

[{'date': '2020-09-01', 'name': '太郎', 'text': 'こんにちわ'}, {'date': '2020-01-01', 'name': '次郎', 'text': None}, {'date': '2020-06-01', 'name': '三郎', 'text': 'お久しぶりです'}]

投稿2020/09/21 15:05

attakei

総合スコア2740

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

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

python_heroku

2020/09/22 08:09

こちらありがとうございます。ご指摘通りメッセージ単位のデータが存在しました。 また、そうでない単位の情報処理方法までわざわざ詳細にありがとうございます!
guest

0

ベストアンサー

他の方も記載していますように、個々のメッセージ単位での固まりになっている必要があります。
それを前提とした回答を記載します。HTML bodyの形を確認ください。

Python

1import pandas as pd 2from bs4 import BeautifulSoup 3 4body = """ 5<div class="msglist"> 6 <div class="msg"> 7 <div class="name">太郎</div> 8 <span class="msg text">こんにちわ</span> 9 <div class="date">2020-09-01</div> 10 </div> 11 12 <div class="msg"> 13 <div class="name">次郎</div> 14 <div class="date">2020-01-01</div> 15 </div> 16 17 <div class="msg"> 18 <div class="name">三郎</div> 19 <span class="msg text">お久しぶりです</span> 20 <div class="date">2020-06-01</div> 21 </div> 22</div> 23""" 24 25soup = BeautifulSoup(body, 'html.parser') 26msgs = soup.find('div', class_='msglist').findAll('div', class_='msg') 27 28df = pd.DataFrame(index=[], columns=['name', 'msg text', 'date']) 29for msg in msgs: 30 elems1 = msg.find('div', class_='name') 31 elems2 = msg.find('span', class_='msg text') 32 elems3 = msg.find('div', class_='date') 33 text1 = elems1.get_text() if elems1 is not None else None 34 text2 = elems2.get_text() if elems2 is not None else None 35 text3 = elems3.get_text() if elems3 is not None else None 36 df = df.append(pd.Series([text1, text2, text3], index=df.columns), ignore_index=True) 37 38print(df)

投稿2020/09/21 14:24

toast-uz

総合スコア3266

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

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

python_heroku

2020/09/22 08:11

回答ありがとうございます! ご指摘の通りで、個々のmsg単位でdivが存在し、その前提での動作コードありがとうございます! こちらで希望通りの取得ができました!
guest

0

それぞれのかたまりを囲むdivとかが無いので有れば、全要素を取り出して、タグ名やクラス名を見ながら処理するしか無いと思います。
プレーンなテキストファイル処理をするみたいに。

投稿2020/09/21 13:49

otn

総合スコア85901

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

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

0

find_allだと希望の動作はできないと思います。
HTMLは以下のような構造になっていないですか?
私だったら、まず親のDIVを取得して、さらにその中で一つずつ
一つ目のdiv、2つ目のspanといった感じで取得していきます。
取得できなかったらNULLを入れます。

<div> <div class="name">次郎</div> <span></span> <div class="date">2020-01-01</div> </div>

投稿2020/09/21 13:15

Kaiser

総合スコア295

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問