2013-03-16 18 views
6

Hãy xem xét các phiên python sau:find() sau khi replaceWith() không hoạt động (sử dụng BeautifulSoup)

>>> from BeautifulSoup import BeautifulSoup 
>>> s = BeautifulSoup("<p>This <i>is</i> a <i>test</i>.</p>"); myi = s.find("i") 
>>> myi.replaceWith(BeautifulSoup("was")) 
>>> s.find("i") 
>>> s = BeautifulSoup("<p>This <i>is</i> a <i>test</i>.</p>"); myi = s.find("i") 
>>> myi.replaceWith("was") 
>>> s.find("i") 
<i>test</i> 

Xin lưu ý đầu ra thiếu của s.find ("i") sau khi dòng 4!

Lý do cho việc này là gì? Có cách giải quyết nào không?

EDIT: Trên thực tế, ví dụ không chứng minh usecase, đó là:

myi.replaceWith(BeautifulSoup("wa<b>s</b>")) 

Bất cứ khi nào phần chèn chứa bản thân mã html nontrivial, tôi không thấy làm thế nào bạn có thể thay thế cú pháp này với một cái gì đó khác. Chỉ cần có

myi.replaceWith("wa<b>s</b>") 

sẽ thay thế ký tự đặc biệt html theo thực thể.

+0

Tại sao bạn cần thay thế bằng 'sometag.renderContents()' thay vì chỉ thay thế bằng 'someTag'? – BrenBarn

+0

Được rồi, hãy cụ thể hơn bằng cách thêm một ví dụ khác ... (xem ở trên, tôi đã chỉnh sửa lại) – thomas

Trả lời

5

Câu trả lời đơn giản: sau khi gọi tới replaceWith, tạo lại và làm sạch s bằng cách gọi s = BeautifulSoup(s.renderContents()). Sau đó, bạn có thể find một lần nữa.

3

Vấn đề có vẻ là đối tượng BeautifulSoup được coi là toàn bộ tài liệu. find lặp qua tài liệu yêu cầu mỗi phần tử cho phần tử tiếp theo sau nó. Nhưng khi nó đến được BeautifulSoup("was"), đối tượng đó nghĩ rằng đó là toàn bộ tài liệu, vì vậy nó nói không có gì sau đó. Điều này hủy bỏ quá trình tìm kiếm quá sớm.

Tôi không nghĩ rằng BeautifulSoup được thiết kế để có các đối tượng BeautifulSoup bên trong các đối tượng BeautifulSoup khác. Cách giải quyết là không làm điều đó. Tại sao bạn cảm thấy bạn cần sử dụng biểu mẫu đầu tiên thay vì biểu mẫu thứ hai, đã hoạt động? Nếu bạn muốn thay thế một phần tử bằng một chút HTML, hãy sử dụng một số Tag để thay thế của bạn, không phải là đối tượng BeautifulSoup.

+0

Tôi thừa nhận rằng ví dụ của tôi không rõ ràng, tại sao tôi cần xây dựng kỳ lạ này, tôi đã giải thích thêm ở trên. – thomas

+0

Tuy nhiên, lời giải thích của bạn là hoàn toàn chính xác, cảm ơn! Nó sẽ là tuyệt vời có một workaround mặc dù. (Vì vậy, đừng giận tôi vì tôi không đánh dấu câu trả lời của bạn như một giải pháp.) – thomas

+0

@thomas: Có một báo cáo lỗi về một vấn đề tương tự [ở đây] (https://bugs.launchpad.net/beautifulsoup/+ bug/1105148). Một nhận xét nói rằng nó đã được giải quyết nhưng nó vẫn có vẻ bị phá vỡ với tôi và tôi không thể hiểu được lời giải thích ở đó. Bạn có thể muốn bình luận về lỗi đó và hiển thị ví dụ của bạn và xem những gì họ nói. – BrenBarn

2

Tôi nghĩ, tôi đã tìm được giải pháp khắc phục sự cố cho tôi. Tôi lặp lại toàn bộ mã một lần nữa dưới dạng tập lệnh Python để đưa ra một ví dụ hoàn chỉnh:

from BeautifulSoup import BeautifulSoup 
s = BeautifulSoup("<p>This <i>is</i> a <i>test</i>.</p>") 
myi = s.find("i") 
s2 = BeautifulSoup("wa<b>s</b>") 
myi_id = myi.parent.contents.index(myi) 
for c in reversed(s2.contents): 
    myi.parent.insert(myi_id + 1, c) 
myi.extract() 

Xin lưu ý rằng điều này sẽ không hoạt động nếu không có reversed(). Nếu bạn bỏ qua nó, bạn không chỉ thay đổi thứ tự của các phần tử. Nếu bạn thực sự muốn để có thể được thay đổi, bạn sẽ phải viết như sau:

for c in list(s2.contents): 
    myi.parent.insert(myi_id + 1, c) 

ai đó có thể vui lòng giải thích, tại sao bỏ qua list() sẽ bỏ qua <b>s</b>? (Vui lòng trả lời trong một bình luận, vì đây không phải là câu hỏi chính ở đây.)

+0

Lý do bạn cần 'danh sách' là vì những gì nó nói [ở đây] (http: //www.crummy.com/software/BeautifulSoup/bs3/documentation.html # Adding% 20a% 20Brand% 20New% 20Element): một phần tử chỉ có thể xảy ra ở một nơi trong tài liệu. Khi bạn thực hiện 'insert', nó sẽ loại bỏ phần tử đầu tiên khỏi' s2.contents' để chèn nó vào nơi khác. Vì vậy, bạn đang sửa đổi 's2' trong khi bạn lặp lại nó. – BrenBarn