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

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

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

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

Python

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

Q&A

解決済

2回答

4750閲覧

bs4を用いて特定の文字列を含むタグを全て取得したいです

tyobit

総合スコア17

Python 3.x

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

Python

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

0グッド

0クリップ

投稿2018/09/19 20:39

編集2018/09/19 22:03

HTMLファイルから特定の文字列を含むタグ・class・idなどを全て取得したいと思っています。

python

1soup.find_all(text=re.compile("!"))

とすることで上記の場合は!が含まれるテキストは取得できますがタグの情報は取得できません。

また、

python

1soup.find(text=re.compile("!")).name

とするとNoneが返ってきます。

このとき、

python

1soup.find("div",text=re.compile("!")).name

とすればdivタグが返ってくるのですが、これだと全てのタグに関してfindメソッドをする必要が出てきてしまうように感じます。

なにか良い方法は無いでしょうか
アドバイス等よろしくお願い申し上げます

追記

python

1for text in soup.find_all(text=re.compile("!")): 2 print(text.name)

とした場合は!を含む文字列の数分Noneと出力されるだけでした

追記 解決

python

1soup.find("title",text=re.compile("!")).name

としたところ、titleに!が含まれていた場合はtitleと返ってくるのですが、含まれていない場合

AttributeError: 'NoneType' object has no attribute 'name'

となりました

python

1found = soup.find("title",text=re.compile("!")) 2if found is not None: 3 print(found.name)

とすることでエラーを回避できました

追記
最終的にはどのようなHTMLファイルに対応できるようにしたいですが現状は以下のようなHTMLファイルを扱っています。

Html

1<!DOCTYPE html> 2<html> 3 <head> 4 <meta charset="utf-8"> 5 <title>太字テスト</title> 6 <link rel="stylesheet" href="stylesheet.css"> 7 </head> 8 <body> 9 <div class="normal">これは普通の文章です。</div> 10 <div class="ep">この文章は強調の文章です!</div> 11  <div class="normal">これは<span class="ep2">この部分が!</span>強調の文章です。</div> 12 </body> 13</html>

この場合、取得したいものは<div class="ep"><span class="ep2">になります。

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

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

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

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

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

jun68ykt

2018/09/19 21:16

質問に以下の2点を追記されるとよいかもしれません。(1)対象とするHTMLファイルは何か決まった構造があるのでしょうか?あればそのHTMLのサンプルを載せて頂くと分かりやすいです。(2) 該当するテキストノードの直接の親であるノードの情報が取得できればよい、ということでしょうか? たとえば <div><h1>あいうえお!</h1></div>であれば、 <h1>だけが取得できて <div>は検索結果には含まれない、ということでよいのでしょうか?
tyobit

2018/09/19 21:24

アドバイスありがとうございます。指摘部分は質問の方に追記として書かせていただきました。よろしくお願い申し上げます。
guest

回答2

0

この場合、取得したいものは<div class="ep"><span class="ep2">になります。

タグを抽出したいのであれば

python

1from bs4 import BeautifulSoup 2import re 3 4html = ''' 5<!DOCTYPE html> 6<html> 7 <head> 8 <meta charset="utf-8"> 9 <title>太字テスト</title> 10 <link rel="stylesheet" href="stylesheet.css"> 11 </head> 12 <body> 13 <div class="normal">これは普通の文章です。</div> 14 <div class="ep">この文章は強調の文章です!</div> 15  <div class="normal">これは<span class="ep2">この部分が!</span>強調の文章です。</div> 16 </body> 17</html> 18''' 19 20soup = BeautifulSoup(html, 'html.parser') 21 22for i in soup.find_all(string=re.compile('!')): 23 # print(i.parent.name, i.parent.attrs, i) 24 25 result = re.match('<.*?>', str(i.parent)) 26 if result: 27 print(result.group())

soup.find_all(string=re.compile('!'))だと
「この文章は強調の文章です!」のテキストだけになってしまうので

i.parentでひとつ上の親タグに戻ると

<div class="ep">この文章は強調の文章です!</div> が取得できます

str(i.parent)で文字に変換したあと、正規表現で最初のタグのみ抽出しています。

投稿2018/10/09 15:24

barobaro

総合スコア1286

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

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

0

ベストアンサー

こんにちは。

再帰で全ノードをたどり、指定の文字列を含むテキストノードがみつかったら、それの親の情報とともに表示するという関数を作成してみました。

$ python -V

Python 3.7.0
$ cat q147524.py

python

1from bs4 import BeautifulSoup 2from bs4.element import NavigableString 3 4html = ''' 5<!DOCTYPE html> 6<html> 7 <head> 8 <meta charset="utf-8"> 9 <title>太字テスト</title> 10 <link rel="stylesheet" href="stylesheet.css"> 11 </head> 12 <body> 13 <div class="normal">これは普通の文章です。</div> 14 <div class="ep">この文章は強調の文章です!</div> 15  <div class="normal">これは<span class="ep2">この部分が!</span>強調の文章です。</div> 16 </body> 17</html> 18''' 19 20 21def search_nodes_including_text(node, text): 22 if node.name is not None: 23 for child in node.children: 24 if isinstance(child, NavigableString) and text in str(child): 25 print(child.parent.name + "/" + str(child.parent.attrs) + "/" + str(child)) 26 27 search_nodes_including_text(child, text) 28 29 30search_nodes_including_text(BeautifulSoup(html, 'lxml'), '!') 31

$ python q147524.py

div/{'class': ['ep']}/この文章は強調の文章です!
span/{'class': ['ep2']}/この部分が!
$

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


追記

蛇足ですが、lxml のHTMLパーサー lxml.htmlを使うと、XPath でテキストノードを持ってこれるので、以下のように手短に書けます。

python

1import lxml.html 2 3src = ''' 4<!DOCTYPE html> 5<html> 6 <head> 7 <meta charset="utf-8"> 8 <title>太字テスト</title> 9 <link rel="stylesheet" href="stylesheet.css"> 10 </head> 11 <body> 12 <div class="normal">これは普通の文章です。</div> 13 <div class="ep">この文章は強調の文章です!</div> 14  <div class="normal">これは<span class="ep2">この部分が!</span>強調の文章です。</div> 15 </body> 16</html> 17''' 18 19 20root = lxml.html.fromstring(src) 21 22for text in root.xpath('//text()'): 23 if '!' in text: 24 elm = text.getparent() 25 print(elm.tag + "/" + str(elm.attrib) + "/" + text)

上記を実行すると以下が表示されます。

div/{'class': 'ep'}/この文章は強調の文章です!

span/{'class': 'ep2'}/この部分が!

投稿2018/09/19 22:51

編集2018/09/19 23:20
jun68ykt

総合スコア9058

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

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

tyobit

2018/09/19 23:07

ありがとうございました。 無事動かすことができました。
jun68ykt

2018/09/19 23:21

> 無事動かすことができました。 とのことでよかったです。 lxml を使ったコードも追記しました。ご参考まで。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問