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

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

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

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

Python

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

Q&A

解決済

2回答

3841閲覧

空白行を削除し、指定する値を取得したい

退会済みユーザー

退会済みユーザー

総合スコア0

スクレイピング

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

Python

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

0グッド

0クリップ

投稿2020/02/09 05:23

前提・実現したいこと

空白行を削除し、指定するidの子要素であるpタグをすべて取得したいです。

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

スクレイピングしたいサイトのHTMLが下記のようになっています。

html

1 ...省略... 2 <div id="hogehoge"> 3 <p>hoge1</p> 4 <p>hoge2</p> 5 <p>hoge3</p> 6 <!--comment--> 7 <!--comment--> 8 <p>hoge4</p> 9 <p>hoge5</p> 10 </div>

id="hogehoge"のpタグをすべて取得したいのですが、コメントがあるためhoge1~hoge3までのpタグしか取れませんでした。

そこで、コメントを削除するプログラムを追加したところ、コメントはなくなったのですが下記のように空白行ができてしまいました。
空白行があるため、取得できるタグはhoge1~hoge3までと上記と変わりませんでした。

html

1 ...省略... 2 <div id="hogehoge"> 3 <p>hoge1</p> 4 <p>hoge2</p> 5 <p>hoge3</p> 6 7 8 <p>hoge4</p> 9 <p>hoge5</p> 10 </div>

error

1AttributeError: 'str' object has no attribute 'select'

該当のソースコード

Python3

1import requests 2from bs4 import BeautifulSoup, Comment 3 4 5response = requests.get('https://hogehoge.com/index.html') 6response.encoding = response.apparent_encoding # 日本語の文字化けを解消 7soup = BeautifulSoup(response.content, 'html.parser') # BeautifulSoupの初期化 8 9print(soup.title.text) #タイトルを表示 10 11#コメントの除去 12comments = soup.findAll(text=lambda text:isinstance(text, Comment)) 13[comment.extract() for comment in comments] 14 15# scriptタグとstyleタグを取り除く 16for script in soup(["script", "style"]): 17 script.decompose() 18 19# 空白行を取り除いて、値を取得する 20test = soup.prettify().select('hogehoge') #ここがダメっぽい。

当方、プログラム初心者のため優しくご教示いただきたく存じます。

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

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

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

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

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

jun68ykt

2020/02/09 05:46

質問の回答ではないのでこちらに書きますが、ご質問の発端である > コメントがあるためhoge1~hoge3までのpタグしか取れませんでした。 というのが疑問です。コメントがあっても取れると思います。 以下はその確認のためのサンプルです。 https://repl.it/@jun68ykt/Q240331 画面上部の [run]ボタンをクリックすると実行されます。
退会済みユーザー

退会済みユーザー

2020/02/09 07:03

試していただきありがとうございます。 本当ですね... 似たようなサイトでスクレイピングしてみたら、普通に取得できました。 質問は修正せずに、もう少し回答を待ってみようと思います。
guest

回答2

0

こんにちは

質問への追記、修正依頼からも確認させて頂きましたが、基本的には

python

1soup.select("#hogehoge > p")

で、コメント行の下の <p> も取得できるはずですが、取ってこれない理由として考えられるケースがあるので、それを回答します。
どういう状況を推測できるかというと、

html

1 <div id="hogehoge"> 2 <p>hoge1</p> 3 <p>hoge2</p> 4 <p>hoge3</p> 5 <!--comment--> 6 <!--comment--> 7 <p>hoge4</p> 8 <p>hoge5</p> 9 </div>

の例でいえば、5つのの <p> のうち、はじめの3つはサーバーサイドで作られており、コメント行の下にある2つは、フロントエンド側からAJAXなどで取得しているのでは?ということです。

もしそういうことになっていると、そのサイトをブラウザで見た限りでは、確かに5つの<p>に囲まれている5個の各テキストが表示されているのを目視で確認でき、ブラウザの開発ツールで確認しても、5つの<p> が存在しているものと思います。しかし、BeautifulSoup でスクレイピングする対象のHTMLには、ブラウザで表示させたときのAJAXで追加された2個は含まれておらず、そのため、ブラウザに展開されたHTMLと比較して、コメントより下の <p> が取得できないという解釈になるものと思います。

もし、上記のようにフロントエンドのJavaScriptによって、追加の要素が付加されている可能性があり、かつフロントエンドのJSの実行結果も含めてブラウザに展開されるHTMLをスクレイピングの対象にしたいのであれば、Selenium を使ってみることを検討するとよいかもしれません。

Qiitaにも いくつか、「Seleniumでスクレイピングしてみました」という趣旨の記事がいくつか投稿されています。

以上、参考になれば幸いです。

投稿2020/02/09 09:02

jun68ykt

総合スコア9058

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

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

退会済みユーザー

退会済みユーザー

2020/02/09 09:42

回答ありがとうございます。 なるほど、そういう場合もあるのですね... 今後、スクレイピングをする際に参考にさせていただきます。
jun68ykt

2020/02/09 09:50

どういたしまして。参考になれば幸いです。
guest

0

ベストアンサー

id が hogehoge である要素の子要素の p タグを取得する CSS セレクタは、#hogehoge > p ではないでしょうか。
子孫要素も含む場合は #hogehoge p です。

コメントやスクリプトタグを削除するコードがありますが、CSS セレクタでほしい要素だけ選択すればいいので、その処理はなくていいと思います。

スタイルシートリファレンス(目的別)

python

1html = """ 2<div id="hogehoge"> 3 <p>hoge1</p> 4 <p>hoge2</p> 5 <p>hoge3</p> 6 7 8 <p>hoge4</p> 9 <p>hoge5</p> 10 </div> 11""" 12 13import requests 14from bs4 import BeautifulSoup 15 16soup = BeautifulSoup(html) 17p_tags = soup.select("#hogehoge > p") 18print(p_tags)
[<p>hoge1</p>, <p>hoge2</p>, <p>hoge3</p>, <p>hoge4</p>, <p>hoge5</p>]

追記

python

1from pprint import pprint 2 3import requests 4from bs4 import BeautifulSoup 5 6url = "https://www.itmedia.co.jp/keywords/security.html" 7 8res = requests.get(url) 9soup = BeautifulSoup(res.content) 10 11data = [] 12for tag in soup.select("#kwdArticleBox p"): 13 a_tag = tag.select_one("a:has(> strong)") 14 15 if not a_tag.text: 16 # PR 記事の場合、構造が違うので場合分け 17 a_tag = tag.select("a")[2] 18 19 data.append({"title": a_tag.text, "url": a_tag["href"]}) 20 21pprint(data)

投稿2020/02/09 08:22

編集2020/02/09 09:39
tiitoi

総合スコア21956

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

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

退会済みユーザー

退会済みユーザー

2020/02/09 08:37

回答ありがとうございます。 回答いただいた記述をしてみたのですが、やはりコメントの下にある行のpタグは取得できませんでした。 似たようなサイトでは指定したタグを取得できたので、とても不思議です...
tiitoi

2020/02/09 08:42

URL を記載していただけますでしょうか?
tiitoi

2020/02/09 09:07

#kwdArticleBox p.newsart ですべて取得できましたが、このような意図であっていますか?
tiitoi

2020/02/09 09:16

ニュース記事以外の PR 記事等もあるみたいなので、それらも含めたい場合は #kwdArticleBox p でとれると思います。
退会済みユーザー

退会済みユーザー

2020/02/09 09:48

まさしくやりたいことです! 親切にお答えいただき、本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問