疑似BUG:SGMLParser處理html標籤中的javascript時特定情況下失當
庫:Python2.4/2.5的sgmllib庫
牽連庫:Beautiful Soup version 3.0.5以及3.0.3版本
舉例:
html代碼如下定義: sExceptionHtml = '''<span>出錯的html標籤:</span><div id='error'>
<img src="http://www.onejoo.com/daylife_media/images/articlesid/1.jpg" border="0"
onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7;
this.style.cursor='hand';this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}"
onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7;
this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}"
/>寒!<br /></div>'''
這個img標籤有兩個屬性:onload和onmouseover,裡面都寫的是javascript代碼,並且出現了“>”判斷符號。當讓SGMLParser處理這種html代碼時,它錯誤地解析了。
對於上面的html代碼,會得到如下處理過後的img:
<img src="http://www.onejoo.com/daylife_media/images/articlesid/1.jpg" border="0" onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7;
this.style.cursor='hand';this.alt='Click here to open new window
CTRL+Mouse wheel to zoom in/out';}" />screen.width*0.7) {this.resized=true; this.width=screen.width*0.7;
this.style.cursor='hand';this.alt='Click here to open new window
CTRL+Mouse wheel to zoom in/out';}"
onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7;
this.alt='Click here to open new window
CTRL+Mouse wheel to zoom in/out';}"
/>
顯然,onmouseover的東西亂掉了。很有可能就是javascript中的“this.width>screen.width*0.7”中“>”被誤當作html標籤的結束符處理了。
如果確實是這樣,倒也可以理解,只是咱們就受累些,之前提前清除onload屬性和onmouseover屬性吧,省得裡面的javascript幹擾。
page_content = re.sub('onload=\"\s*[^\"]*\"','',page_content)
page_content = re.sub('onmouseover=\"\s*[^\"]*\"','',page_content)
牽連影響:並因此影響到了Beautiful Soup對html的解析。
你可以測試如下代碼,可以重現此問題:#coding=utf-8
import sys, os, urllib, re
from sgmllib import SGMLParser
from BeautifulSoup import BeautifulSoup
def replaceHTMLTag(content):
htmlextractor = html2txt()
# 調用定義在 SGMLParser 中的 feed 方法,將 HTML 內容放入分析器中。
htmlextractor.feed(content)
# 應該 close 您的分析器對象,但出於不同的原因。feed 方法不保證對傳給它的全部 HTML 進行處理,
# 它可能會對其進行緩衝處理,等待接收更多的內容。一旦沒有更多的內容,應調用 close 來重新整理緩衝區,並且強制所有內容被完全處理。
htmlextractor.close()
# 一旦分析器被 close,分析過程也就結束了。htmlextractor.urls 中包含了在 HTML 文檔中所有的連結 URL。
return htmlextractor.text
# 為了從 HTML 文檔中提取資料,將 SGMLParser 類進行子類化,然後對想要捕捉的標記或實體定義方法。
class html2txt(SGMLParser):
def __init__(self):
SGMLParser.__init__(self)
self._result = []
self._data_stack = []
'''
reset 由 SGMLParser 的 __init__ 方法來調用,也可以在建立一個分析器執行個體時手工來調用。
所以如果您需要做初始化,在 reset 中去做,而不要在 __init__ 中做。
這樣當某人重用一個分析器執行個體時,會正確地重新初始化。
'''
def reset(self):
self.text = ''
self.inbody = True
SGMLParser.reset(self)
def handle_data(self,text):
if self.inbody:
self.text += text
def _write(self, d):
if len(self._data_stack) < 2:
target = self._result
else:
target = self._data_stack[-1]
if type(d) in (list, tuple):
target += d
else:
target.append(str(d))
def start_head(self,text):
self.inbody = False
def end_head(self):
self.inbody = True
def _get_result(self):
return "".join(self._result).strip()
result = property(_get_result)
# 應用入口
if __name__ == '__main__':
sExceptionHtml = '''<span>出錯的html標籤:</span><div id='error'>
<img src="http://www.onejoo.com/daylife_media/images/articlesid/1.jpg" border="0"
onmouseover="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7;
this.style.cursor='hand';this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}"
onload="if(this.width>screen.width*0.7) {this.resized=true; this.width=screen.width*0.7;
this.alt='Click here to open new window\nCTRL+Mouse wheel to zoom in/out';}"
/>寒!<br /></div>'''
soup = BeautifulSoup(sExceptionHtml,fromEncoding='gbk')
body_content = soup.findAll('div',attrs={'id' : re.compile("^error")})
print '----------------------'
print body_content[0]
print '----------------------'
sExceptionHtml = replaceHTMLTag(sExceptionHtml).strip()
print '----------------------'
print sExceptionHtml
print '-----------------------'
結論:不是什麼嚴重問題。只是當html代碼中在標籤的屬性中寫javascript時,需要注意到此種特性,如果出現“>”符號,就會導致SGMLParser以及使用SGMLParser的其他庫解析失當。zhengyun 20080115