BeautifulSoup ile xpath kullanabilir miyiz?


107

Bir url'yi kazımak için BeautifulSoup kullanıyorum ve aşağıdaki koda sahiptim

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'})

Şimdi yukarıdaki kodda findAllbunlarla ilgili etiketleri ve bilgileri almak için kullanabiliriz, ancak ben xpath kullanmak istiyorum. BeautifulSoup ile xpath kullanmak mümkün mü? Mümkünse, herhangi biri bana daha yararlı olması için bir örnek kod sağlayabilir mi?

Yanıtlar:


169

Hayır, BeautifulSoup tek başına XPath ifadelerini desteklemez.

Alternatif bir kütüphane, Lxml , yaptığı destek XPath 1.0. Bu bir var BeautifulSoup uyumlu modu o deneyeceğim ve HTML Çorbası yaptığı şekilde kırılmış ayrıştırmak. Ancak, varsayılan lxml HTML ayrıştırıcısı , bozuk HTML'yi ayrıştırmak kadar başarılıdır ve bence daha hızlıdır.

Belgenizi bir lxml ağacına ayrıştırdıktan sonra, .xpath()öğeleri aramak için yöntemi kullanabilirsiniz .

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)

Ek işlevselliğe sahip özel bir lxml.html()modül de vardır .

Yukarıdaki örnekte , ayrıştırıcının doğrudan akıştan okunmasını sağlamak, önce yanıtı büyük bir dizge halinde okumaktan daha verimli olduğundan , responsenesneyi doğrudan ilettiğime dikkat edin lxml. requestsKütüphane ile aynı şeyi yapmak için , şeffaf aktarım açmayı etkinleştirdikten sonra nesneyi ayarlamak stream=Trueve aktarmak istiyorsunuz :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)

İlginizi çekebilecek olası şey CSS Seçici desteğidir ; CSSSelectorsınıf için arama yaparak, XPath ifadeleri içine CSS ifadeleri çevirir td.empformbodyçok daha kolay:

from lxml.cssselect import CSSSelector

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

Tam daire geliyor: BeautifulSoup kendisi vermez çok tam sahip CSS seçici destek :

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

2
Çok teşekkürler Pieters, ur kodumdan iki bilgi aldım, 1. BS 2. ile xpath kullanamayacağımızın açıklaması lxml'nin nasıl kullanıldığına dair güzel bir örnek. "Xpath'i yazılı biçimde BS kullanarak uygulayamayacağımızı" belirli bir belgede görebilir miyiz, çünkü açıklama isteyenlere bazı kanıtlar göstermeliyiz, değil mi?
Shiva Krishna Bavandla

8
Negatif olduğunu kanıtlamak zor; BeautifulSoup 4 dokümantasyon bir arama işlevi vardır ve 'xpath' için hiçbir isabet bulunmamaktadır.
Martijn Pieters

123

Beautiful Soup içinde XPath desteği olmadığını doğrulayabilirim.


76
Not: Leonard Richardson, Beautiful Soup'un yazarıdır, çünkü onun kullanıcı profiline tıklarsanız göreceksiniz.
senshin

23
BeautifulSoup içinde XPATH kullanabilmek çok güzel olurdu
DarthOpto

4
Yani alternatif nedir?
static_rtti

41

Diğerlerinin de söylediği gibi, BeautifulSoup'un xpath desteği yoktur. Selenium kullanmak da dahil olmak üzere bir xpath'tan bir şeyler elde etmenin muhtemelen birkaç yolu vardır. Ancak, işte Python 2 veya 3'te çalışan bir çözüm:

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)

Kullandığım bu bir referans olarak kullanılabilir.


Bir uyarı: Kökün dışında bir şey varsa (dış <html> etiketlerinin dışında \ n gibi), o zaman xpath'lere kökten referans vermenin işe yaramayacağını, göreli xpath'leri kullanmanız gerektiğini fark ettim. lxml.de/xpathxslt.html
wordsforthewise

Martijn'in kodu artık düzgün çalışmıyor (şimdiye kadar 4+ yaşında ...), etree.parse () satırı konsola yazdırılıyor ve değeri ağaç değişkenine atamıyor. Bu oldukça büyük bir iddia. Kesinlikle bunu yeniden üretemem ve bu hiç mantıklı olmaz . Kodumu test etmek için Python 2 kullandığınızdan emin misiniz veya urllib2kitaplık kullanımını Python 3'e çevirdiniz urllib.requestmi?
Martijn Pieters

Evet, bunu yazarken Python3 kullandığım ve beklendiği gibi çalışmadığı için durum bu olabilir. Az önce test edildi ve sizinki Python2 ile çalışıyor, ancak 2 2020'de gün batımı olduğu için (artık resmi olarak desteklenmiyor) Python3 daha çok tercih ediliyor.
wordforthewise

kesinlikle katılıyorum, ancak buradaki soru Python 2'yi kullanıyor .
Martijn Pieters

17

BeautifulSoup, childern tarafından yönlendirilen mevcut elemandan findNext adında bir işleve sahiptir , yani:

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

Yukarıdaki kod aşağıdaki xpath'i taklit edebilir:

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

1

Dokümanlarını araştırdım ve xpath seçeneği yok gibi görünüyor. Ayrıca, burada SO ile ilgili benzer bir soruda da görebileceğiniz gibi , OP xpath'ten BeautifulSoup'a bir çeviri istiyor, bu yüzden benim sonucum - hayır, xpath ayrıştırması yok.


evet aslında şimdiye kadar etiketlerin içindeki verileri almak için xpath kullanan scrapy kullandım. Verileri almak çok kullanışlı ve kolay, ancak aynısını güzel grupla yapmam gerekiyor, bu yüzden dört gözle bekliyorum.
Shiva Krishna Bavandla

1

lxml kullandığınızda çok basit:

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

ancak BeautifulSoup BS4'ü kullandığınızda hepsi basit:

  • önce "//" ve "@" yi kaldırın
  • saniye - "=" öncesine yıldız ekleyin

bu sihri dene:

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

Gördüğünüz gibi, bu alt etiketi desteklemiyor, bu yüzden "/ @ href" kısmını kaldırıyorum


select()CSS seçiciler içindir, XPath değildir. Gördüğünüz gibi, bu alt etiketi desteklemiyor O zamanlar bunun doğru olup olmadığından emin olmasam da, kesinlikle şimdi değil.
AMC

1

Belki aşağıdakileri XPath olmadan deneyebilirsiniz

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()')

Yukarıda, Soup nesnesinin lxml ile kombinasyonunu kullandı ve biri xpath kullanarak değeri çıkarabilir


0

Bu oldukça eski bir iş parçacığı, ancak şu anda BeautifulSoup'ta bulunmayan geçici bir çözüm var.

İşte yaptığım şeyin bir örneği. Bir RSS beslemesini okumak ve metin içeriğini "rss_text" adlı bir değişkende almak için "requestler" modülünü kullanıyorum. Bununla, onu BeautifulSoup aracılığıyla çalıştırıyorum, xpath / rss / channel / title için arıyorum ve içeriğini alıyorum. Tüm ihtişamıyla tam olarak XPath değil (joker karakterler, çoklu yollar, vb.), Ancak bulmak istediğiniz basit bir yolunuz varsa, bu işe yarar.

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

Bunun sadece alt unsurları bulduğuna inanıyorum. XPath başka bir şey mi?
raffaem
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.