chúng ta có thể sử dụng xpath với BeautifulSoup không?


105

Tôi đang sử dụng BeautifulSoup để quét url và tôi có mã sau

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

Bây giờ trong đoạn mã trên, chúng ta có thể sử dụng findAllđể lấy các thẻ và thông tin liên quan đến chúng, nhưng tôi muốn sử dụng xpath. Có thể sử dụng xpath với BeautifulSoup không? Nếu có thể, bất cứ ai có thể vui lòng cung cấp cho tôi một mã ví dụ để nó sẽ hữu ích hơn?

Câu trả lời:


168

Không, bản thân BeautifulSoup không hỗ trợ các biểu thức XPath.

Một thư viện thay thế, lxml , không hỗ trợ XPath 1.0. Nó có chế độ tương thích với BeautifulSoup , nơi nó sẽ thử và phân tích cú pháp HTML bị hỏng theo cách của Soup. Tuy nhiên, trình phân tích cú pháp HTML lxml mặc định thực hiện tốt công việc phân tích cú pháp HTML bị hỏng và tôi tin rằng nó nhanh hơn.

Khi bạn đã phân tích cú pháp tài liệu của mình thành cây lxml, bạn có thể sử dụng .xpath()phương pháp này để tìm kiếm các phần tử.

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)

Ngoài ra còn có một mô-đun chuyên dụnglxml.html() với chức năng bổ sung.

Lưu ý rằng trong ví dụ trên, tôi đã truyền responsetrực tiếp đối tượng đến lxml, vì để trình phân tích cú pháp đọc trực tiếp từ luồng sẽ hiệu quả hơn đọc phản hồi thành một chuỗi lớn trước. Để thực hiện tương tự với requeststhư viện, bạn muốn đặt stream=Truevà chuyển response.rawđối tượng vào sau khi bật giải nén truyền tải minh bạch :

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)

Bạn có thể quan tâm đến CSS Selector ; các CSSSelectorlớp dịch báo cáo CSS vào các biểu thức XPath, làm cho tìm kiếm của bạn cho td.empformbodyrằng dễ dàng hơn nhiều:

from lxml.cssselect import CSSSelector

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

Đến vòng tròn đầy đủ: BeautifulSoup bản thân không có rất đầy đủ hỗ trợ CSS selector :

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

2
Cảm ơn Pieters rất nhiều, tôi đã nhận được hai thông tin từ mã của bạn, 1. Giải thích rõ rằng chúng ta không thể sử dụng xpath với BS 2. Một ví dụ hay về cách sử dụng lxml. Chúng ta có thể thấy nó trên một tài liệu cụ thể rằng "chúng ta không thể triển khai xpath bằng BS ở dạng văn bản", bởi vì chúng ta nên hiển thị một số bằng chứng cho một người nào đó yêu cầu làm rõ đúng không?
Shiva Krishna Bavandla

8
Thật khó để chứng minh một tiêu cực; các tài liệu BeautifulSoup 4 có chức năng tìm kiếm và không có hit cho 'xpath'.
Martijn Pieters

122

Tôi có thể xác nhận rằng không có hỗ trợ XPath trong Beautiful Soup.


76
Lưu ý: Leonard Richardson là tác giả của Beautiful Soup, như bạn sẽ thấy nếu bạn nhấp vào hồ sơ người dùng của anh ấy.
senshin

23
Nó sẽ là rất tốt đẹp để có thể sử dụng XPath trong BeautifulSoup
DarthOpto

4
Vậy thay thế bằng cái gì?
static_rtti

40

Như những người khác đã nói, BeautifulSoup không có hỗ trợ xpath. Có thể có một số cách để lấy thứ gì đó từ xpath, bao gồm cả sử dụng Selenium. Tuy nhiên, đây là một giải pháp hoạt động trên Python 2 hoặc 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)

Tôi đã sử dụng điều này như một tài liệu tham khảo.


Một cảnh báo: Tôi đã nhận thấy nếu có thứ gì đó bên ngoài thư mục gốc (như \ n bên ngoài các thẻ <html> bên ngoài), thì việc tham chiếu đến xpath bởi thư mục gốc sẽ không hoạt động, bạn phải sử dụng xpath tương đối. lxml.de/xpathxslt.html
wordsforthewise

Mã của Martijn không còn hoạt động bình thường (tính đến nay đã hơn 4 năm tuổi ...), dòng etree.parse () in ra bảng điều khiển và không gán giá trị cho biến cây. Đó là một tuyên bố khá. Tôi chắc chắn không thể tái tạo điều đó, và nó sẽ không có ý nghĩa gì . Bạn có chắc mình đang sử dụng Python 2 để kiểm tra mã của tôi hoặc đã dịch việc urllib2sử dụng thư viện sang Python 3 urllib.requestkhông?
Martijn Pieters

Vâng, đó có thể là trường hợp mà tôi đã sử dụng Python3 khi viết điều đó và nó không hoạt động như mong đợi. Chỉ cần thử nghiệm và bạn làm việc với python2, nhưng Python3 được nhiều ưu đãi như 2 đang được hoàng hôn (không còn hỗ trợ chính thức) trong năm 2020.
wordsforthewise

hoàn toàn đồng ý, nhưng câu hỏi ở đây sử dụng Python 2 .
Martijn Pieters

17

BeautifulSoup có một hàm có tên findNext từ phần tử con hiện tại được hướng dẫn, vì vậy:

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

Đoạn mã trên có thể bắt chước xpath sau:

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

1

Tôi đã tìm kiếm qua tài liệu của họ và có vẻ như không có tùy chọn xpath. Ngoài ra, như bạn có thể thấy ở đây về một câu hỏi tương tự trên SO, OP đang yêu cầu bản dịch từ xpath sang BeautifulSoup, vì vậy kết luận của tôi sẽ là - không, không có phân tích cú pháp xpath nào.


Vâng, thực sự cho đến bây giờ tôi đã sử dụng scrapy sử dụng xpath để tìm nạp dữ liệu bên trong các thẻ. Nó rất tiện dụng và dễ dàng để tìm nạp dữ liệu, nhưng tôi cần phải làm điều tương tự với beautifulsoup nên rất mong chờ nó.
Shiva Krishna Bavandla

1

khi bạn sử dụng lxml, tất cả đều đơn giản:

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

nhưng khi sử dụng BeautifulSoup BS4, tất cả đều đơn giản:

  • trước tiên hãy xóa "//" và "@"
  • thứ hai - thêm dấu sao trước "="

thử phép thuật này:

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

như bạn thấy, phần này không hỗ trợ thẻ phụ, vì vậy tôi xóa phần "/ @ href"


select()là dành cho bộ chọn CSS, nó hoàn toàn không phải XPath. như bạn thấy, điều này không hỗ trợ thẻ phụ Mặc dù tôi không chắc điều đó có đúng vào thời điểm đó hay không, nhưng chắc chắn bây giờ không phải vậy.
AMC

1

Có lẽ bạn có thể thử cách sau mà không có 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()')

Ở trên đã sử dụng kết hợp đối tượng Soup với lxml và một người có thể trích xuất giá trị bằng cách sử dụng xpath


0

Đây là một chủ đề khá cũ, nhưng hiện đã có một giải pháp khắc phục được, có thể chưa có trong BeautifulSoup vào thời điểm đó.

Đây là một ví dụ về những gì tôi đã làm. Tôi sử dụng mô-đun "yêu cầu" để đọc nguồn cấp RSS và lấy nội dung văn bản của nó trong một biến có tên "rss_text". Sau đó, tôi chạy nó qua BeautifulSoup, tìm kiếm xpath / rss / channel / title và truy xuất nội dung của nó. Nó không hẳn là XPath trong mọi vinh quang của nó (ký tự đại diện, nhiều đường dẫn, v.v.), nhưng nếu bạn chỉ có một đường dẫn cơ bản mà bạn muốn xác định, thì điều này sẽ hoạt động.

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

Tôi tin rằng điều này chỉ tìm thấy các phần tử con. XPath là một thứ khác?
raffaem
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.