✨
lxmlで子要素の後ろ側にある文字列を取得する方法
問題
例えば、下記のようなHTMLがある場合で、すべての文字列を取得したい場合。
<p>
これは
<b style="font-weight:bold;">
テスト
</b>
です。
</p>
Pythonのlxml
を利用すると下記のような手順で取得する事ができる。
これは
を取得する
lxmlで解析して、xptahという指定文でテキストを取得できる
from lxml import etree
html_data="""
<p>
これは
<b style="font-weight:bold;">
テスト
</b>
です。
</p>
"""
et = etree.fromstring(html_data, parser=etree.HTMLParser())
o = et.xpath("//p")
print(o.text)
実行すると下記のような結果が取得できます。
# python main.py
これは
テスト
を取得する
テストはp
の子要素の一番上ですので、下記のように指定すれば取得できます。
html_string="""
<p>
これは
<b style="font-weight:bold;">
テスト
</b>
です。
</p>
"""
# print(html_string)
et = etree.fromstring(html_string, parser=etree.HTMLParser())
o = et.xpath("//p")[0]
print(o[0].text)
実行結果
# python main.py
テスト
です
をどのように取得するか?
下記の記事にそれっぽい回答がありましたので、書き出します。
lxml.etree, element.text doesn't return the entire text from an element
こちらのファンクションを使えばいいのではないかというお話でした。
element.xpath("string()")
html_string="""
<p>
これは
<b style="font-weight:bold;">
テスト
</b>
です。
</p>
"""
# print(html_string)
et = etree.fromstring(html_string, parser=etree.HTMLParser())
o = et.xpath("//p")[0]
print(o.xpath("string()"))
結果としては、子要素も含めて文字列化して出力されました。
# python main.py
これは
テスト
です。
こちらの方法もあるようです。
lxml.etree.tostring(element, method="text")
import lxml
html_string="""
<p>
これは
<b style="font-weight:bold;">
テスト
</b>
です。
</p>
"""
# print(html_string)
et = etree.fromstring(html_string, parser=etree.HTMLParser())
o = et.xpath("//p")[0]
print(lxml.etree.tostring(o, method="text"))
結果としてはasciiコードしか対応していないのか、エラーが発生しました。
# python main.py
Traceback (most recent call last):
File "/work/main.py", line 183, in <module>
print(lxml.etree.tostring(o, method="text"))
NameError: name 'lxml' is not defined
root@456a44e4258b:/work# python main.py
Traceback (most recent call last):
File "/work/main.py", line 184, in <module>
print(lxml.etree.tostring(o, method="text"))
File "src/lxml/etree.pyx", line 3454, in lxml.etree.tostring
File "src/lxml/serializer.pxi", line 102, in lxml.etree._tostring
File "src/lxml/serializer.pxi", line 74, in lxml.etree._textToString
UnicodeEncodeError: 'ascii' codec can't encode characters in position 5-7: ordinal not in range(128)
同スレッドの下記の関数を利用すれば指定して取得する事もできそうです。
ただ、工夫しないと複数ある場合は対応が難しそうですね…
Discussion