2010-04-21 10 views
10

Tôi cần tải xuống và phân tích trang web bằng lxml và tạo đầu ra xml UTF-8. Tôi nghĩ rằng giản đồ trong mã giả là minh họa hơn:Mã hóa bằng python với lxml - giải pháp phức tạp

from lxml import etree 

webfile = urllib2.urlopen(url) 
root = etree.parse(webfile.read(), parser=etree.HTMLParser(recover=True)) 

txt = my_process_text(etree.tostring(root.xpath('/html/body'), encoding=utf8)) 


output = etree.Element("out") 
output.text = txt 

outputfile.write(etree.tostring(output, encoding=utf8)) 

Vì vậy, tệp web có thể có bất kỳ mã hóa nào (lxml nên xử lý điều này). Outputfile phải nằm trong utf-8. Tôi không chắc chắn nơi để sử dụng mã hóa/mã hóa. Lược đồ này có được không? (Tôi không thể tìm thấy hướng dẫn tốt về lxml và mã hóa, nhưng tôi có thể tìm thấy nhiều vấn đề với điều này ...) Tôi cần giải pháp mạnh mẽ.

Edit:

Vì vậy, để gửi utf-8 để lxml tôi sử dụng

 converted = UnicodeDammit(webfile, isHTML=True) 
     if not converted.unicode: 
      print "ERR. UnicodeDammit failed to detect encoding, tried [%s]", \ 
       ', '.join(converted.triedEncodings) 
      continue 
     webfile = converted.unicode.encode('utf-8') 

Trả lời

18

lxml có thể là một chút rung rinh về mã hóa đầu vào. Tốt nhất là gửi UTF8 vào và nhận UTF8.

Bạn có thể muốn sử dụng mô-đun chardet hoặc UnicodeDammit để giải mã dữ liệu thực tế.

Bạn muốn làm điều gì đó mơ hồ như:

import chardet 
from lxml import html 
content = urllib2.urlopen(url).read() 
encoding = chardet.detect(content)['encoding'] 
if encoding != 'utf-8': 
    content = content.decode(encoding, 'replace').encode('utf-8') 
doc = html.fromstring(content, base_url=url) 

Tôi không chắc chắn lý do tại sao bạn đang di chuyển giữa lxml và etree, trừ khi bạn đang tương tác với một thư viện mà đã sử dụng etree?

+0

Unicode Dường như có vẻ tốt. Và về etree bạn là đúng, tôi đã loại bỏ nó từ mã. –

+2

Tại sao không truyền trực tiếp chuỗi đã giải mã (đối tượng unicode) sang html.fromstring(), thay vì mã hóa lại nó thành utf-8 ?? – lajarre

+1

Tôi không thể nhớ được động lực cụ thể cách đây hai năm rưỡi, nhưng tôi nhớ rằng lxml không thích đầu vào Unicode trong một số trường hợp. Có một cơ hội rất tốt mà bất cứ vấn đề gì đã được sửa chữa, vì vậy có lẽ tốt hơn nên bỏ qua phần đó ngay bây giờ. libxml2 (mà quyền hạn lxml) không giống như đầu vào UTF-8, vì vậy nếu bạn có hiệu suất rất nhạy cảm, bạn có thể muốn tránh giải mã mã hóa nói riêng. –

2

phát hiện mã hóa lxml là weak.

Tuy nhiên, lưu ý rằng vấn đề phổ biến nhất với trang web là thiếu các khai báo mã hóa (hoặc sự tồn tại không chính xác). Đó là do đó thường đủ để chỉ sử dụng tính năng phát hiện mã hóa của BeautifulSoup, được gọi là UnicodeDammit và để phần còn lại cho trình phân tích cú pháp HTML của riêng lxml là , nhanh hơn vài lần.

tôi khuyên bạn nên để phát hiện mã hóa sử dụng UnicodeDammit và phân tích sử dụng lxml. Ngoài ra, bạn có thể sử dụng tiêu đề http Loại nội dung (bạn cần trích xuất charset = ENCODING_NAME) để phát hiện mã hóa chính xác hơn.

Ví dụ này tôi đang sử dụng BeautifulSoup4 (cũng có thể bạn phải cài đặt chardet cho dò tốt hơn, vì UnicodeDammit sử dụng chardet nội):

from bs4 import UnicodeDammit 

if http_charset == "": 
    ud = UnicodeDammit(content, is_html=True) 
else: 
    ud = UnicodeDammit(content, override_encodings=[http_charset], is_html=True) 
root = lxml.html.fromstring(ud.unicode_markup) 

OR, để làm cho câu trả lời trước nhiều hoàn tất, bạn có thể sửa đổi nó thành:

if ud.original_encoding != 'utf-8': 
    content = content.decode(ud.original_encoding, 'replace').encode('utf-8') 

Tại sao điều này tốt hơn đơn giản bằng cách sử dụng tài liệu?

  1. Bạn đừng bỏ qua Content-Type tiêu đề HTTP

    Content-Type: text/html; charset = utf-8

  2. Bạn không bỏ qua http-equiv thẻ meta. Ví dụ:

    ... http-equiv = "Content-Type" content = "text/html; charset = UTF-8" ...

  3. Ngày đầu này, bạn đang sử dụng sức mạnh của chardet, cjkcodecsiconvcodec codec và many more.