[英]Python UTF-8 XML parsing (SUDS): Removing 'invalid token'
處理UTF-8時出現常見錯誤 - “無效令牌”
在我的例子中,它來自處理不尊重unicode字符的SOAP服務提供者,只是將值截斷為100字節而忽略了第100個字節可能在多字節字符的中間:例如:
<name xsi:type="xsd:string">浙江家庭教會五十人遭驅散及抓打 聖誕節聚會被斷電及搶走物品(圖、視頻\xef\xbc</name>
在截斷刀假定世界使用1字節字符之后,最后兩個字節是3字節unicode字符的剩余字節。 下一站,sax解析器和:
xml.sax._exceptions.SAXParseException: <unknown>:1:2392: not well-formed (invalid token)
我不再關心這個角色了。 它應該從文檔中刪除並允許sax解析器運行。
除了這些值之外,XML回復在其他方面都有效。
問題:如何在不解析整個文檔並重新發明UTF-8編碼來檢查每個字節的情況下如何刪除此字符?
使用:Python + SUDS
事實證明,SUDS將xml看作類型'string'(不是unicode),因此這些是編碼值。
1)過濾器:
badXML = "your bad utf-8 xml here" #(type <str>)
#Turn it into a python unicode string - ignore errors, kick out bad unicode
decoded = badXML.decode('utf-8', errors='ignore') #(type <unicode>)
#turn it back into a string, using utf-8 encoding.
goodXML = decoded.encode('utf-8') #(type <str>)
2)SUDS:請參閱https://fedorahosted.org/suds/wiki/Documentation#MessagePlugin
from suds.plugin import MessagePlugin
class UnicodeFilter(MessagePlugin):
def received(self, context):
decoded = context.reply.decode('utf-8', errors='ignore')
reencoded = decoded.encode('utf-8')
context.reply = reencoded
和
from suds.client import Client
client = Client(WSDL_url, plugins=[UnicodeFilter()])
希望這有助於某人。
注意:感謝John Machin !
請參閱: 為什么python decode會替換編碼字符串中的無效字節?
關於errors='ignore'
Python issue8271可能會妨礙你。 如果沒有在python中修復此錯誤,'ignore'將使用接下來的幾個字節來滿足長度
在解碼無效的UTF-8字節序列期間,只有
起始字節和連續字節現在被認為是無效的,而不是起始字節指定的字節數
問題修復於:
Python 2.6.6 rc1
Python 2.7.1 rc1(以及2.7的所有未來版本)
Python 3.1.3 rc1(以及3.x的所有未來版本)
Python 2.5及更低版本將包含此問題。
在上面的例子中, "\\xef\\xbc</name".decode('utf-8', errors='ignore')
應該
返回"</name"
,但在'bugged'版本的python中它返回"/name"
。
前四位( 0xe
)描述了一個3字節的UTF字符,因此0xbc
了字節0xef
, 0xbc
,然后(錯誤地) 0x3c
( '<'
)。
0x3c
不是一個有效的連續字節,它首先創建無效的3字節UTF字符。
固定版本的python只刪除第一個字節,只刪除有效的連續字節,而不使用0x3c
@ FlipMcF是正確的答案 - 我只是為他的解決方案發布了我的過濾器,因為原來的那個沒有為我工作(我的XML中有一些表情符號字符,它們是用UTF-8正確編碼的,但它們是仍然崩潰的XML解析器):
class UnicodeFilter(MessagePlugin):
def received(self, context):
from lxml import etree
from StringIO import StringIO
parser = etree.XMLParser(recover=True) # recover=True is important here
doc = etree.parse(StringIO(context.reply), parser)
context.reply = etree.tostring(doc)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.