BeautifulPathでxpathを使用できますか?


105

BeautifulSoupを使用してURLをスクレイピングしており、次のコードがありました

import urllib
import urllib2
from BeautifulSoup import BeautifulSoup

url =  "http://www.example.com/servlet/av/ResultTemplate=AVResult.html"
req = urllib2.Request(url)
response = urllib2.urlopen(req)
the_page = response.read()
soup = BeautifulSoup(the_page)
soup.findAll('td',attrs={'class':'empformbody'})

上記のコードではfindAll、タグとそれに関連する情報を取得するために使用できますが、xpathを使用したいと思います。BeautifulSoupでxpathを使用することは可能ですか?可能であれば、より役立つように、コードの例を誰かに教えてもらえますか?

回答:


168

いいえ、BeautifulSoup自体は、XPath式をサポートしていません。

代替ライブラリー、lxmlのはありませんサポートのXPath 1.0を。それは、Soupと同じように壊れたHTMLを解析しようとするBeautifulSoup互換モードを備えています。ただし、デフォルトのlxml HTMLパーサーは、壊れたHTMLを解析するのと同じようにうまく機能します。

ドキュメントをlxmlツリーに解析したら、この.xpath()メソッドを使用して要素を検索できます。

try:
    # Python 2
    from urllib2 import urlopen
except ImportError:
    from urllib.request import urlopen
from lxml import etree

url =  "http://www.example.com/servlet/av/ResultTemplate=AVResult.html"
response = urlopen(url)
htmlparser = etree.HTMLParser()
tree = etree.parse(response, htmlparser)
tree.xpath(xpathselector)

追加機能を備えた専用lxml.html()モジュールもあります。

上記の例では、responseオブジェクトをに直接渡したことに注意してください。lxmlストリームから直接パーサーを読み取る方が、最初に大きな文字列に応答を読み取るよりも効率的です。requestsライブラリで同じことを行うには、透過的なトランスポート解凍を有効にstream=Trueしたresponse.rawオブジェクトを設定して渡します:

import lxml.html
import requests

url =  "http://www.example.com/servlet/av/ResultTemplate=AVResult.html"
response = requests.get(url, stream=True)
response.raw.decode_content = True
tree = lxml.html.parse(response.raw)

CSSセレクターのサポートに興味があるかもしれません。このCSSSelectorクラスはCSSステートメントをXPath式に変換し、td.empformbodyその検索をはるかに簡単にします。

from lxml.cssselect import CSSSelector

td_empformbody = CSSSelector('td.empformbody')
for elem in td_empformbody(tree):
    # Do something with these table cells.

完全な円を来:BeautifulSoup自体がない非常に完全持つCSSセレクタのサポートを

for cell in soup.select('table#foobar td.empformbody'):
    # Do something with these table cells.

2
Pietersに感謝します。urコードから2つの情報を取得しました。1。BS 2.xpathを使用できないことの明確化。lxmlの使用方法の良い例。「明確な形でBSを使用してxpathを実装することはできません」という特定のドキュメントでそれを確認できますか。
Shiva Krishna Bavandla 2012

8
ネガティブを証明するのは難しいです。BeautifulSoup 4ドキュメントは、検索機能を持っていると「のXPath」にはヒットはありません。
Martijn Pieters

122

Beautiful Soup内にXPathサポートがないことを確認できます。


76
注:Leonard RichardsonはBeautiful Soupの作者です。彼のユーザープロファイルにクリックすると表示されます。
戦神

23
BeautifulSoup内でXPATHを使用できると非常に便利です
DarthOpto

4
では、代替案は何ですか?
static_rtti 2017年

40

他の人が言ったように、BeautifulSoupはxpathをサポートしていません。Seleniumの使用を含め、xpathから何かを取得する方法はおそらくいくつかあります。ただし、Python 2または3で機能するソリューションは次のとおりです。

from lxml import html
import requests

page = requests.get('http://econpy.pythonanywhere.com/ex/001.html')
tree = html.fromstring(page.content)
#This will create a list of buyers:
buyers = tree.xpath('//div[@title="buyer-name"]/text()')
#This will create a list of prices
prices = tree.xpath('//span[@class="item-price"]/text()')

print('Buyers: ', buyers)
print('Prices: ', prices)

参考にさせいただきました。


警告の1つ:ルートの外側に何かがある場合(外側の<html>タグの外側の\ nなど)は、ルートでxpathを参照しても機能しません。相対xpathを使用する必要があります。lxml.de/xpathxslt.html
wordsforthewise

Martijnのコードは適切に機能しなくなり(現在では4年以上経過しています...)、etree.parse()行がコンソールに出力され、tree変数に値が割り当てられません。それはかなりの主張です。確かにそれを再現することはできませんし、意味がありません。Python 2を使用してコードをテストしていますか、それともurllib2ライブラリの使用をPython 3に変換しましたurllib.requestか?
Martijn Pieters

ええ、それは私がそれを書いているときに私がPython3を使用していて、それが期待通りに機能しなかった場合かもしれません。ただ、2020年には(もはや公式にサポートされている)日没されテストされ、あなたはPython2で動作しますが、のpython3はずっと2として好ましい
wordsforthewise

完全に同意しますが、ここでの質問はPython 2を使用しています。
Martijn Pieters

17

BeautifulSoupには、現在の要素向けのchildernからfindNextという関数があります。

father.findNext('div',{'class':'class_value'}).findNext('div',{'id':'id_value'}).findAll('a') 

上記のコードは、次のxpathを模倣できます。

div[class=class_value]/div[id=id_value]

1

私はそれらのドキュメントを検索しましたが、xpathオプションがないようです。また、あなたが見ることができるように、ここで SO上で同様の質問には、OPは、XPathからBeautifulSoupへの翻訳を求めているので、私の結論は次のようになります-いいえ、XPathの利用可能パースはありません。


はい、実際にはこれまで、xpathを使用してタグ内のデータをフェッチするscrapyを使用していました。データをフェッチするのは非常に便利で簡単ですが、beautifulsoupを使用して同じことを行う必要があるので、楽しみにしています。
Shiva Krishna Bavandla 2012

1

lxmlをすべて単純に使用する場合:

tree = lxml.html.fromstring(html)
i_need_element = tree.xpath('//a[@class="shared-components"]/@href')

ただし、BeautifulSoup BS4を使用する場合も、すべてシンプルです。

  • 最初に「//」と「@」を削除します
  • 2番目-"="の前にスターを追加

この魔法を試してください:

soup = BeautifulSoup(html, "lxml")
i_need_element = soup.select ('a[class*="shared-components"]')

ご覧のとおり、これはサブタグをサポートしていないため、「/ @ href」の部分を削除します


select()CSSセレクター用であり、XPathではありません。ご覧のとおり、これはサブタグをサポートしていません。その時点でそれが真実であったかどうかはわかりませんが、確かに今はそうではありません。
AMC

1

多分あなたはXPathなしで以下を試すことができます

from simplified_scrapy.simplified_doc import SimplifiedDoc 
html = '''
<html>
<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.</p>
    <p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
'''
# What XPath can do, so can it
doc = SimplifiedDoc(html)
# The result is the same as doc.getElementByTag('body').getElementByTag('div').getElementByTag('h1').text
print (doc.body.div.h1.text)
print (doc.div.h1.text)
print (doc.h1.text) # Shorter paths will be faster
print (doc.div.getChildren())
print (doc.div.getChildren('p'))

1
from lxml import etree
from bs4 import BeautifulSoup
soup = BeautifulSoup(open('path of your localfile.html'),'html.parser')
dom = etree.HTML(str(soup))
print dom.xpath('//*[@id="BGINP01_S1"]/section/div/font/text()')

上記は、Soupオブジェクトとlxmlの組み合わせを使用しており、xpathを使用して値を抽出できます。


0

これはかなり古いスレッドですが、現時点ではBeautifulSoupになかった可能性がある回避策のソリューションがあります。

これが私がしたことの例です。「requests」モジュールを使用してRSSフィードを読み取り、そのテキストコンテンツを「rss_text」という変数に取得します。それで、BeautifulSoupで実行し、xpath / rss / channel / titleを検索して、その内容を取得します。これは、すべての栄光(ワイルドカード、複数のパスなど)が正確にXPathであるとは限りませんが、特定したい基本的なパスだけがある場合、これは機能します。

from bs4 import BeautifulSoup
rss_obj = BeautifulSoup(rss_text, 'xml')
cls.title = rss_obj.rss.channel.title.get_text()

これは子要素のみを見つけると思います。XPathは別のものですか?
ラファエム
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.