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

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

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

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

Q&A

解決済

1回答

2253閲覧

BeautifulSoupで入れ子タグの分割取得

kaneuchi

総合スコア12

Python 3.x

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

0グッド

0クリップ

投稿2019/04/29 14:01

前提・実現したいこと

PythonでWebスクレイピングによる情報収集するプログラムを作成しています。
途中で情報がうまく収集できないため、HTMLのソースを見たところ、情報に
よってHTMLが変化していました。

取得したいサイト情報の変化点

HTML

1<li class="item"> 2 <a href="/detail/?id=1"> 3 <div class="image"> 4 <img src="/images/1.jpg" alt="1"> 5 </div> 6 <p class="desc"><span class="set">SET</span>アイテム1</p> <--ココ 7 </a> 8 <div class="info"> 9 <div class="no"><span class="bold">1</span></div> 10 </div> 11 <a href="id=A"> 12 <p class="name">A</p> 13 </a> 14</li> 15 16<li class="item"> 17 <a href="/detail/?id=2"> 18 <div class="image"> 19 <img src="/images/2.jpg" alt="2"> 20 </div> 21 <p class="desc">アイテム2</p> <--ココ 22 </a> 23 <div class="info"> 24 <div class="no"><span class="bold">2</span></div> 25 </div> 26 <a href="id=B"> 27 <p class="name">B</p> 28 </a> 29</li> 30

class="desc" のテキストを .string を使い取り出そうとしていますが、内容により<span class="set">が入りうまくテキストを抽出することができません。

取り出したいイメージは次の通りです。

1つ目の desc : [SET]アイテム1 2つ目の desc : アイテム2

試したこと

以前、PerlによるWebスクレイピングをしていたので、1行ずつ処理していくイメージがあり、Pythonのコード書きましたが、結果は得られませんでした。

該当のソースコード

Python3

1for i in bs.select('item'): 2 if i.find(class_="desc"): 3 if i.find(class_="set"): 4 title = SET + i.find(class_="desc").string 5 else 6 title = i.find(class_="desc").striing

どの様にコーディングをすれば、目的のイメージの様な出力結果が得られますでしょうか。
よろしくお願い致します。

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

利用環境:
OSX 10.13.6
python 3.7.0
beautifulsoup4 4.7.1

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

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

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

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

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

guest

回答1

0

ベストアンサー

こんにちは。

以下、回答となるプログラムを実行してみたところです。

$ date

2019年 4月29日 月曜日 23時40分32秒 JST
$ python -V
Python 3.7.3
$ pip list | grep beautifulsoup4
beautifulsoup4 4.7.1
$ cat test.py

python3

1from bs4 import BeautifulSoup 2 3html = open('input.html', 'r').read() 4 5soup = BeautifulSoup(html, 'lxml') 6 7for p in soup.select('p.desc'): 8 span = p.find('span') 9 if span: 10 print("[%s]%s" % (span.text, span.next_sibling)) 11 else: 12 print(p.text)

$ cat input.html

html

1<!DOCTYPE html> 2<html> 3<head> 4 <meta charset="UTF-8"> 5</head> 6<body> 7 <ul> 8 <li class="item"> 9 <a href="/detail/?id=1"> 10 <div class="image"> 11 <img src="/images/1.jpg" alt="1"> 12 </div> 13 <p class="desc"><span class="set">SET</span>アイテム1</p> 14 </a> 15 <div class="info"> 16 <div class="no"><span class="bold">1</span></div> 17 </div> 18 <a href="id=A"> 19 <p class="name">A</p> 20 </a> 21 </li> 22 <li class="item"> 23 <a href="/detail/?id=2"> 24 <div class="image"> 25 <img src="/images/2.jpg" alt="2"> 26 </div> 27 <p class="desc">アイテム2</p> 28 </a> 29 <div class="info"> 30 <div class="no"><span class="bold">2</span></div> 31 </div> 32 <a href="id=B"> 33 <p class="name">B</p> 34 </a> 35 </li> 36 </ul> 37</body> 38</html>

$ python test.py

[SET]アイテム1
アイテム2
$

参考になれば幸いです。

追記

コメントから頂きました、以下

最終的には class=item のブロック毎に class="bold",class="name" を抜き出したいのですが、頂いたコードをマージしたら、desc の内容(50件)が毎回が出力されてしまいました。item単位で出力する方法はありますでしょうか?

を満たすコードを検討しました。質問者様の要件を過不足無く満たしているかは確信を持てませんが、以下ではどうでしょうか?

python3

1from bs4 import BeautifulSoup 2 3html = open('input.html', 'r').read() 4 5soup = BeautifulSoup(html, 'lxml') 6 7for i, item in enumerate(soup.select('.item')): 8 9 print("-- %d 番目の item: --" % (i+1)) 10 11 for clazz in ['desc', 'bold', 'name']: 12 elm = item.select_one('.' + clazz) 13 set_span = elm.select_one('.set') 14 15 if set_span: 16 print("%s: [%s]%s" % (clazz, set_span.text, set_span.next_sibling)) 17 else: 18 print("%s: %s" % (clazz, elm.text))

input.html は先の回答に挙げたもののままで、上記を実行すると、以下のように出力されます。

$ python test.py

-- 1 番目の item: --
desc: [SET]アイテム1
bold: 1
name: A
-- 2 番目の item: --
desc: アイテム2
bold: 2
name: B
$

投稿2019/04/29 14:44

編集2019/04/30 03:29
jun68ykt

総合スコア9058

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

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

kaneuchi

2019/04/29 15:46

早速のご回答ありがとうございます。 頂いたコードで desc の内容取得はできました。私の質問が悪かったのですが、最終的には class=item の ブロック毎に class="bold",class="name" を抜き出したいのですが、頂いたコードをマージしたら、desc の内容(50件)が毎回が出力されてしまいました。item単位で出力する方法はありますでしょうか?
jun68ykt

2019/04/30 03:30

> item単位で出力する方法 について、回答のほうに追記しましたので、ご確認ください。
kaneuchi

2019/04/30 09:46

ありがとうございました。希望する出力内容が得られました。 頂いたコードをアレンジして色々と試してみます。
jun68ykt

2019/04/30 10:21

どういたしまして。 > 希望する出力内容が得られました。 とのことでよかったです ????
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問