เราใช้ xpath กับ BeautifulSoup ได้ไหม


109

ฉันใช้ 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 เป็นไปได้ไหมที่จะใช้ xpath กับ BeautifulSoup? ถ้าเป็นไปได้ใครช่วยกรุณาให้รหัสตัวอย่างเพื่อที่จะเป็นประโยชน์มากขึ้น?

คำตอบ:


169

ไม่ BeautifulSoup โดยตัวมันเองไม่รองรับนิพจน์ XPath

ห้องสมุดทางเลือกlxml , ไม่สนับสนุน XPath 1.0 มีโหมดที่เข้ากันได้กับ BeautifulSoupซึ่งจะพยายามแยกวิเคราะห์ HTML ที่ใช้งานไม่ได้เหมือนที่ Soup ทำ อย่างไรก็ตามโปรแกรมแยกวิเคราะห์ HTML lxml เริ่มต้นทำงานได้ดีในการแยกวิเคราะห์ 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 Selector ; 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 ฉันได้รับสองข้อมูลจากรหัสของคุณ 1 คำชี้แจงที่เราไม่สามารถใช้ xpath กับ BS 2 ได้เป็นตัวอย่างที่ดีในการใช้ lxml เราสามารถดูได้จากเอกสารที่ระบุว่า "เราไม่สามารถใช้ xpath โดยใช้ BS ในรูปแบบลายลักษณ์อักษร" ได้เพราะเราควรแสดงหลักฐานบางอย่างให้กับผู้ที่ขอคำชี้แจงใช่ไหม
Shiva Krishna Bavandla

8
เป็นการยากที่จะพิสูจน์ว่าเป็นลบ เอกสาร BeautifulSoup 4มีฟังก์ชั่นการค้นหาและมีเพลงฮิตสำหรับ 'XPath' ไม่
Martijn Pieters

127

ฉันสามารถยืนยันได้ว่าไม่มีการรองรับ XPath ใน Beautiful Soup


78
หมายเหตุ: Leonard Richardson เป็นผู้เขียน Beautiful Soup เนื่องจากคุณจะเห็นว่าคุณคลิกผ่านไปยังโปรไฟล์ผู้ใช้ของเขาหรือไม่
senshin

23
คงจะดีมากที่สามารถใช้ XPATH ภายใน BeautifulSoup
DarthOpto

4
แล้วทางเลือกคืออะไร?
static_rtti

42

อย่างที่คนอื่น ๆ บอกว่า BeautifulSoup ไม่มีการรองรับ xpath อาจมีหลายวิธีในการรับบางสิ่งจาก xpath รวมถึงการใช้ Selenium อย่างไรก็ตามนี่คือวิธีแก้ปัญหาที่ใช้ได้กับ 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)

ฉันใช้สิ่งนี้เป็นข้อมูลอ้างอิง


คำเตือนอย่างหนึ่ง: ฉันสังเกตเห็นว่ามีบางอย่างอยู่นอกรูท (เช่น \ n นอกแท็ก <html> ด้านนอก) การอ้างอิง xpaths โดย root จะไม่ทำงานคุณต้องใช้ xpaths สัมพัทธ์ lxml.de/xpathxslt.html
Wordsforthewise

รหัสของ Martijn ทำงานไม่ถูกต้องอีกต่อไป (ตอนนี้มีอายุ 4 ปีขึ้นไป ... ) บรรทัด etree.parse () จะพิมพ์ไปยังคอนโซลและไม่กำหนดค่าให้กับตัวแปรทรี นั่นเป็นการอ้างสิทธิ์ แน่นอนฉันไม่สามารถทำซ้ำได้และมันจะไม่ทำให้รู้สึกใดคุณแน่ใจว่าคุณกำลังใช้งูหลาม 2 เพื่อทดสอบรหัสของฉันด้วยหรือมีการแปลurllib2การใช้ห้องสมุดหลาม 3 urllib.request?
Martijn Pieters

ใช่นั่นอาจเป็นกรณีที่ฉันใช้ Python3 เมื่อเขียนสิ่งนั้นและมันไม่ได้ผลตามที่คาดไว้ เพิ่งผ่านการทดสอบและการทำงานร่วมกับคุณ Python2 แต่ Python3 เป็นที่ต้องการมากที่สุดเท่าที่ 2 จะถูกพระอาทิตย์ตก (สนับสนุนอีกต่อไปอย่างเป็นทางการ) ในปี 2020
wordsforthewise

อย่างเห็นด้วย แต่คำถามที่นี่ใช้งูหลาม 2
Martijn Pieters

17

BeautifulSoup มีฟังก์ชั่นที่ชื่อfindNextจากองค์ประกอบปัจจุบันที่กำกับ childern ดังนั้น:

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

โค้ดด้านบนสามารถเลียนแบบ xpath ต่อไปนี้:

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

2
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 object กับ lxml และสามารถแยกค่าโดยใช้ xpath


นี่เป็นทางออกที่ดีที่สุดในหน้านี้ .... มันใช้ BeautifulSoup เพื่อแยกวิเคราะห์โดเมน HTML ซึ่งดีกว่าตัวแยกวิเคราะห์ html lxml มาก ..... โดยทั่วไปใช้ดีที่สุดของทั้งสองไลบรารี .... @Shiva Krishna Bavandla ได้ไหม เปลี่ยนเป็นวิธีแก้ปัญหาที่ยอมรับ ....
Vivek

1

ฉันค้นหาเอกสารของพวกเขาและดูเหมือนว่าไม่มีตัวเลือก xpath นอกจากนี้ในฐานะที่คุณสามารถดูที่นี่ในคำถามที่คล้ายกันในดังนั้น, OP จะขอสำหรับการแปลจาก XPath เพื่อ BeautifulSoup ดังนั้นข้อสรุปของฉันจะเป็น - ไม่ไม่มี XPath แยกใช้ได้


ใช่จริง ๆ แล้วจนถึงตอนนี้ฉันใช้ scrapy ซึ่งใช้ xpath เพื่อดึงข้อมูลภายในแท็กมันสะดวกและง่ายต่อการดึงข้อมูล แต่ฉันจำเป็นต้องทำเช่นเดียวกันกับ beautifulsoup ดังนั้นตั้งหน้าตั้งตารอ
Shiva Krishna Bavandla

1

เมื่อคุณใช้ lxml ง่าย ๆ ทั้งหมด:

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

แต่เมื่อใช้ BeautifulSoup BS4 ก็เรียบง่ายเช่นกัน:

  • ก่อนอื่นให้ลบ "//" และ "@"
  • วินาที - เพิ่มดาวก่อน "="

ลองใช้เวทมนตร์นี้:

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

0

นี่เป็นกระทู้เก่าที่สวย แต่ตอนนี้มีวิธีแก้ปัญหาซึ่งอาจไม่ได้อยู่ใน BeautifulSoup ในเวลานั้น

นี่คือตัวอย่างของสิ่งที่ฉันทำ ฉันใช้โมดูล "คำขอ" เพื่ออ่านฟีด 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 เป็นอีกอย่าง?
raffaem
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.