PHP DOMDocument loadHTML không mã hóa UTF-8 chính xác


194

Tôi đang cố phân tích một số HTML bằng DOMDocument, nhưng khi tôi làm vậy, tôi đột nhiên mất mã hóa (ít nhất đó là cách nó xuất hiện với tôi).

$profile = "<div><p>various japanese characters</p></div>";
$dom = new DOMDocument();
$dom->loadHTML($profile); 

$divs = $dom->getElementsByTagName('div');

foreach ($divs as $div) {
    echo $dom->saveHTML($div);
}

Kết quả của mã này là tôi nhận được một loạt các ký tự không phải là tiếng Nhật. Tuy nhiên, nếu tôi làm:

echo $profile;

Nó hiển thị chính xác. Tôi đã thử lưuHTML và saveXML và không hiển thị chính xác. Tôi đang sử dụng PHP 5.3.

Những gì tôi thấy:

ã¤ãªãã¤å·ã·ã«ã´ã«ã¦ãã¢ã¤ã«ã©ã³ãç³»ã®å®¶åº­ã«ã9人åå¼ã®5çªç®ã¨ãã¦çã¾ãããå½¼ãå«ãã¦4人ã俳åªã«ãªã£ããç¶è¦ªã¯æ¨æã®ã»ã¼ã«ã¹ãã³ã§ãæ¯è¦ªã¯éµä¾¿å±ã®å®¢å®¤ä¿ã ã£ããé«æ ¡æ代ã¯ã­ã£ãã£ã®ã¢ã«ãã¤ãã«å¤ãã¿ãæè²è³éãåããªããã«ããªãã¯ç³»ã®é«æ ¡ã¸é²å­¦ã

Những gì cần được hiển thị:

イリノイ州シカゴにて、アイルランド系の家庭に、9人兄弟の5番目として生まれる。彼を含めて4人が俳優になった。父親は木材のセールスマンで、母親は郵便局の客室係だった。高校時代はキャディのアルバイトに勤しみ、教育資金を受けながらカトリック系の高校へ進学

EDIT: Tôi đã đơn giản hóa mã thành năm dòng để bạn có thể tự kiểm tra nó.

$profile = "<div lang=ja><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>";
$dom = new DOMDocument();
$dom->loadHTML($profile);
echo $dom->saveHTML();
echo $profile;

Đây là html được trả về:

<div lang="ja"><p>イリノイ州シカゴã«ã¦ã€ã‚¢ã‚¤ãƒ«ãƒ©ãƒ³ãƒ‰ç³»ã®å®¶åº­ã«ã€</p></div>
<div lang="ja"><p>イリノイ州シカゴにて、アイルランド系の家庭に、</p></div>

Điều này có thể giúp bạn. stackoverflow.com/questions/1580543/ từ
bực bội

Cảm ơn. Tôi đã kiểm tra tất cả những thứ đó và không có gì giúp được. Tôi không nhận được ????, nhưng một số văn bản lạ khác. Tôi sẽ cố gắng dán nó ở đây, nhưng không biết trang web sẽ hiển thị nó như thế nào.
Hơi A.

Hãy thử sử dụng utf8_encode
Webnet

Đã thử không thành công. Trả lại các nhân vật giống như trước đây.
Hơi A.

Câu trả lời:


512

DOMDocument::loadHTMLsẽ coi chuỗi của bạn là trong ISO-8859-1 trừ khi bạn nói khác. Điều này dẫn đến các chuỗi UTF-8 bị hiểu sai.

Nếu chuỗi của bạn không chứa khai báo mã hóa XML, bạn có thể thêm một chuỗi để khiến chuỗi được coi là UTF-8:

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();

Nếu bạn không thể biết liệu chuỗi có chứa một khai báo như vậy chưa, thì có một cách giải quyết trong SmartDOMDocument sẽ giúp bạn:

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));
echo $dom->saveHTML();

Đây không phải là một cách giải quyết tuyệt vời, nhưng vì không phải tất cả các ký tự đều có thể được thể hiện trong ISO-8859-1 (như những thanh katana này), đây là cách thay thế an toàn nhất.


1
Vâng, điều đó đã làm nó. Cảm ơn sự giúp đỡ của bạn. Tôi đã thử saveHTML, saveXML, không nghĩ rằng vấn đề có thể xảy ra trong quá trình tải.
Hơi A.

4
Cuộc gọi mb_convert_encoding hoạt động với tôi, trong khi việc khai báo khai báo mã hóa thì không. Có khả năng vì tài liệu đã có một tuyên bố mâu thuẫn. Rất cám ơn - đã tiết kiệm cho tôi rất nhiều thời gian để theo đuổi điều này.
Peter Bagnall

1
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $content);đã sửa nó cho tôi trong PHP7 (vì vậy nó vẫn là một vấn đề) - đây là một vấn đề thực sự khó chịu, vì tôi đã định nghĩa utf8 trong tài liệu HTML ( <meta charset="UTF-8" />nhưng) không có tác dụng, dường như nó cần phần <? xml, mà là hoàn toàn không trực quan.
Muỗi

11
Vẫn trong năm 2017 câu trả lời này có liên quan và làm việc cho tôi quá. Tôi đã có cơ sở dữ liệu, đa thẻ, thẻ meta html và mã hóa DOM tất cả được đặt thành utf8 và vẫn có mã hóa xấu khi nhập nút từ DOC này sang DOC khác. php.net/manual/en/feft.mb-convert-encoding.php là bản sửa lỗi.
Louis Loudog Trottier

6
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));làm việc tuyệt vời Cảm ơn bạn,
vee

66

Vấn đề là với saveHTML()saveXML(), cả hai đều không hoạt động chính xác trong Unix. Chúng không lưu chính xác các ký tự UTF-8 khi được sử dụng trong Unix, nhưng chúng hoạt động trong Windows.

Cách giải quyết rất đơn giản:

Nếu bạn thử mặc định, bạn sẽ nhận được lỗi bạn mô tả

$str = $dom->saveHTML(); // saves incorrectly

Tất cả bạn phải làm là lưu như sau:

$str = $dom->saveHTML($dom->documentElement); // saves correctly

Dòng mã này sẽ giúp các ký tự UTF-8 của bạn được lưu chính xác. Sử dụng cách giải quyết tương tự nếu bạn đang sử dụng saveXML().


Cập nhật

Theo đề xuất của " Jack M " trong phần bình luận bên dưới và được xác minh bởi " Pamela " và " Marco Aurélio Deleu ", biến thể sau có thể hoạt động trong trường hợp của bạn:

$str = utf8_decode($dom->saveHTML($dom->documentElement));

Ghi chú

  1. Các ký tự tiếng Anh không gây ra bất kỳ vấn đề nào khi bạn sử dụng saveHTML()mà không có tham số (vì các ký tự tiếng Anh được lưu dưới dạng các ký tự byte đơn trong UTF-8)

  2. Vấn đề xảy ra khi bạn có các ký tự nhiều byte (như tiếng Trung, tiếng Nga, tiếng Ả Rập, tiếng Do Thái, ... vv.)

Tôi khuyên bạn nên đọc bài viết này: http://coding.smashingmagazine.com/2012/06/06/all-about-unicode-utf8-character-sets/ . Bạn sẽ hiểu UTF-8 hoạt động như thế nào và tại sao bạn gặp vấn đề này. Nó sẽ đưa bạn khoảng 30 phút, nhưng đó là thời gian chi tiêu tốt.


5
Tôi đã phải utf8_decode trong khi sử dụng giải pháp này. Cảm ơn!
Jack M.

9
Điều này đã phải trở thành utf8_decode ($ dom-> saveHTML (dom-> documentEuity)) để giữ các ký tự đặc biệt của tôi. Nếu không, họ chỉ trở thành một cái gì đó khác. Chỉ cần đề cập đến nó trong trường hợp nó giúp người khác.
Jack M.

4
Cảm ơn @MrJack. Tôi cũng phải làm điều tương tự để làm cho nó hiển thị mà không có các ký tự lạ$str = utf8_decode($dom->saveHTML($dom->documentElement));
Pamela

1
utf8_decode($dom->saveHTML($dom->documentElement));đã làm nó hoàn hảo cho tôi.
Marco Aurélio Deleu

2
Bạn đã cứu cuộc đời tôi với điều này. Tôi đã tìm kiếm câu trả lời này MỌI NƠI! Cảm ơn bạn!
Paulo Hgo

15

Đảm bảo tệp nguồn thực được lưu dưới dạng UTF-8 (Bạn thậm chí có thể muốn dùng thử các ký tự BOM không được đề xuất với UTF-8 để đảm bảo).

Ngoài ra trong trường hợp HTML, hãy đảm bảo bạn đã khai báo mã hóa chính xác bằng metacác thẻ:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

Nếu đó là một CMS (như bạn đã gắn thẻ câu hỏi của mình với Joomla), bạn có thể cần phải định cấu hình các cài đặt phù hợp cho mã hóa.


Tôi hiểu những gì bạn đang nói, nhưng tôi không có vấn đề gì khi hiển thị các nhân vật. nếu tôi làm "echo $ profile;" nó hoạt động tốt. Đó là khi DomDocument nhận ra rằng nó bắt đầu thất bại.
Hơi A.

2
Meta của bạn ngăn saveHTML mã hóa mọi thứ trên ASCII thành các thực thể. Giải pháp tôi đang tìm kiếm :)
sod

2
Là một lưu ý phụ, <meta charset="UTF-8">thẻ mới hơn không hoạt động với DOMDocument.
Taylan

10

Bạn có thể tiền tố một dòng thực thi utf-8mã hóa, như thế này:

@$doc->loadHTML('<?xml version="1.0" encoding="UTF-8"?>' . "\n" . $profile);

Và sau đó bạn có thể tiếp tục với mã bạn đã có, như:

$doc->saveXML()

10

Điều này khiến tôi mất một lúc để tìm ra nhưng đây là câu trả lời của tôi.

Trước khi sử dụng DomDocument, tôi sẽ sử dụng file_get_contents để truy xuất các url và sau đó xử lý chúng bằng các hàm chuỗi. Có lẽ không phải là cách tốt nhất nhưng nhanh chóng. Sau khi bị thuyết phục, Dom cũng nhanh như vậy, lần đầu tiên tôi đã thử như sau:

$dom = new DomDocument('1.0', 'UTF-8');
if ($dom->loadHTMLFile($url) == false) { // read the url
    // error message
}
else {
    // process
}

Điều này đã thất bại một cách ngoạn mục trong việc bảo tồn mã hóa UTF-8 mặc dù các thẻ meta phù hợp, cài đặt php và tất cả các biện pháp khắc phục còn lại được cung cấp ở đây và các nơi khác. Đây là những gì hoạt động:

$dom = new DomDocument('1.0', 'UTF-8');
$str = file_get_contents($url);
if ($dom->loadHTML(mb_convert_encoding($str, 'HTML-ENTITIES', 'UTF-8')) == false) {
}

vv Bây giờ mọi thứ đều đúng với thế giới. Hi vọng điêu nay co ich.


Chỉ muốn thêm vào câu trả lời của tôi ở trên rằng một cách khác để giải quyết vấn đề này bằng cách sau, cũng được đề xuất ở nơi khác: if ($ dom-> loadHTML ('<? Xml mã hóa = "UTF-8">'. $ Str) = = sai). Sau khi đăng câu trả lời của tôi, tôi đã tìm thấy một dịp mà đề nghị đầu tiên của tôi thất bại nhưng lần thứ hai làm việc.
Sam

Làm việc cho tôi ngay cả khi không có params trong DomDocument('1.0', 'UTF-8'). Nhưng trong trường hợp của tôi chỉ tải một phần html.
JKB

5

Bạn phải cung cấp cho DOMDocument một phiên bản HTML của bạn với một tiêu đề có ý nghĩa. Giống như HTML5.

$profile ='<?xml version="1.0" encoding="'.$_encoding.'"?>'. $html;

có lẽ là một ý tưởng tốt để giữ cho html của bạn hợp lệ nhất có thể, vì vậy bạn không gặp vấn đề khi bạn bắt đầu truy vấn ... xung quanh :-) và tránh xa htmlentities!!!! Đó là một sự lãng phí cần thiết và lãng phí tài nguyên. giữ mã của bạn điên rồ !!!!


5

Tôi đang sử dụng php 7.3.8 trên manjaro và tôi đang làm việc với nội dung tiếng Ba Tư. Điều này đã giải quyết vấn đề của tôi:

$html = 'hi</b><p>سلام<div>の家庭に、9 ☆';
$doc = new DOMDocument('1.0', 'UTF-8');
$doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
print $doc->saveHTML($doc->documentElement) . PHP_EOL . PHP_EOL;

Lời khuyên chính xác này đã được Sam đưa ra trước đó trên cùng một trang. Xin vui lòng không đăng thông tin dư thừa.
mickmackusa

4

Công trình tìm cho tôi:

$dom = new \DOMDocument;
$dom->loadHTML(utf8_decode($html));
...
return  utf8_encode( $dom->saveHTML());

2
Hãy cẩn thận, utf8_decode có thể bị mất thông tin (được thay thế bằng a ?)
jwal

2

Sử dụng nó cho kết quả chính xác

$dom = new DOMDocument();
$dom->loadHTML('<meta http-equiv="Content-Type" content="text/html; charset=utf-8">' . $profile);
echo $dom->saveHTML();
echo $profile;

Hoạt động này

mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8');

Đó là cách xấu, bởi vì các biểu tượng đặc biệt như & lt; , & gt; có thể ở cấu hình $ và chúng sẽ không chuyển đổi hai lần sau mb_convert_encoding. Đó là lỗ hổng cho XSS và HTML không chính xác.


1

Điều duy nhất làm việc cho tôi là câu trả lời được chấp nhận của

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML('<?xml encoding="utf-8" ?>' . $profile);
echo $dom->saveHTML();

TUY NHIÊN

Điều này mang lại những vấn đề mới, về việc có <?xml encoding="utf-8" ?>đầu ra của tài liệu.

Giải pháp cho tôi là sau đó phải làm

foreach ($doc->childNodes as $xx) {
    if ($xx instanceof \DOMProcessingInstruction) {
        $xx->parentNode->removeChild($xx);
    }
}

Một số giải pháp cho tôi biết để xóa xmltiêu đề, tôi phải thực hiện

$dom->saveXML($dom->documentElement);

Điều này không làm việc cho tôi như đối với một tài liệu một phần (ví dụ: tài liệu có hai <p>thẻ), chỉ một trong những <p>thẻ được trả về.


0

Vấn đề là khi bạn thêm tham số vào hàm DOMDocument :: saveHTML (), bạn sẽ mất mã hóa. Trong một số trường hợp, bạn sẽ cần tránh sử dụng tham số và sử dụng hàm chuỗi cũ để tìm thứ bạn đang tìm kiếm.

Tôi nghĩ rằng câu trả lời trước có hiệu quả với bạn, nhưng vì cách giải quyết này không hiệu quả với tôi, tôi thêm câu trả lời đó để giúp đỡ người có thể trong trường hợp của tôi.


0

Cũng có thể mã hóa như dưới đây .... được thu thập từ https://davidwalsh.name/domdocument-utf8-probols

$profile = '<p>イリノイ州シカゴにて、アイルランド系の家庭に、9</p>';
$dom = new DOMDocument();
$dom->loadHTML(mb_convert_encoding($profile, 'HTML-ENTITIES', 'UTF-8'));
echo $dom->saveHTML();
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.