Làm thế nào để bạn phân tích và xử lý HTML / XML trong PHP?


Câu trả lời:


1897

Phần mở rộng XML gốc

Tôi thích sử dụng một trong các phần mở rộng XML nguyên gốc vì chúng đi kèm với PHP, thường nhanh hơn tất cả các lib của bên thứ 3 và cung cấp cho tôi tất cả quyền kiểm soát tôi cần khi đánh dấu.

DOM

Tiện ích mở rộng DOM cho phép bạn vận hành trên các tài liệu XML thông qua API DOM với PHP 5. Đây là một triển khai của Mô hình đối tượng tài liệu cấp 3 của W3C, giao diện trung lập về ngôn ngữ và nền tảng cho phép các chương trình và tập lệnh tự động truy cập và cập nhật nội dung, cấu trúc và phong cách của tài liệu.

DOM có khả năng phân tích cú pháp và sửa đổi HTML thế giới thực (bị hỏng) và nó có thể thực hiện các truy vấn XPath . Nó dựa trên libxml .

Phải mất một thời gian để làm việc hiệu quả với DOM, nhưng thời gian đó rất đáng để IMO. Vì DOM là giao diện không biết ngôn ngữ, bạn sẽ tìm thấy các triển khai bằng nhiều ngôn ngữ, vì vậy nếu bạn cần thay đổi ngôn ngữ lập trình của mình, rất có thể bạn sẽ biết cách sử dụng API DOM của ngôn ngữ đó.

Có thể tìm thấy một ví dụ sử dụng cơ bản trong Lấy thuộc tính href của phần tử A và tổng quan về khái niệm chung có thể được tìm thấy tại DOMDocument trong php

Cách sử dụng tiện ích mở rộng DOM đã được trình bày rộng rãi trên StackOverflow , vì vậy nếu bạn chọn sử dụng nó, bạn có thể chắc chắn rằng hầu hết các vấn đề bạn gặp phải có thể được giải quyết bằng cách tìm kiếm / duyệt Stack Overflow.

XMLReader

Phần mở rộng XMLReader là một trình phân tích cú pháp kéo XML. Trình đọc hoạt động như một con trỏ đi về phía trước trên luồng tài liệu và dừng lại ở mỗi nút trên đường đi.

XMLReader, như DOM, dựa trên libxml. Tôi không biết làm thế nào để kích hoạt Mô-đun phân tích cú pháp HTML, do đó, rất có thể việc sử dụng XMLReader để phân tích cú pháp HTML bị hỏng có thể kém mạnh mẽ hơn so với sử dụng DOM nơi bạn có thể nói rõ ràng để sử dụng Mô-đun phân tích HTML của libxml.

Một ví dụ sử dụng cơ bản có thể được tìm thấy khi nhận tất cả các giá trị từ các thẻ h1 bằng cách sử dụng php

Trình phân tích cú pháp XML

Tiện ích mở rộng này cho phép bạn tạo các trình phân tích cú pháp XML và sau đó xác định các trình xử lý cho các sự kiện XML khác nhau. Mỗi trình phân tích cú pháp XML cũng có một vài tham số bạn có thể điều chỉnh.

Thư viện XML Parser cũng dựa trên libxml và triển khai trình phân tích cú pháp đẩy XML kiểu SAX . Nó có thể là một lựa chọn tốt hơn để quản lý bộ nhớ so với DOM hoặc SimpleXML, nhưng sẽ khó làm việc hơn so với trình phân tích cú pháp kéo do XMLReader triển khai.

SimpleXml

Phần mở rộng SimpleXML cung cấp một bộ công cụ rất đơn giản và dễ sử dụng để chuyển đổi XML thành một đối tượng có thể được xử lý bằng các bộ chọn thuộc tính thông thường và các trình vòng lặp mảng.

SimpleXML là một tùy chọn khi bạn biết HTML là XHTML hợp lệ. Nếu bạn cần phân tích HTML bị hỏng, thậm chí đừng xem xét SimpleXml vì nó sẽ bị sặc.

Một ví dụ sử dụng cơ bản có thể được tìm thấy tại Một chương trình đơn giản cho nút CRUD và các giá trị nút của tệp xml và có rất nhiều ví dụ bổ sung trong Hướng dẫn PHP .


Thư viện của bên thứ 3 (dựa trên libxml)

Nếu bạn thích sử dụng lib của bên thứ 3, tôi khuyên bạn nên sử dụng lib thực sự sử dụng DOM / libxml bên dưới thay vì phân tích chuỗi.

FluentDom - Repo

FluentDOM cung cấp giao diện XML thông thạo giống như jQuery cho DOMDocument trong PHP. Các bộ chọn được viết bằng XPath hoặc CSS (sử dụng trình chuyển đổi CSS sang XPath). Các phiên bản hiện tại mở rộng DOM thực hiện các giao diện tiêu chuẩn và thêm các tính năng từ DOM Living Standard. FluentDOM có thể tải các định dạng như JSON, CSV, JsonML, RabbitFish và các định dạng khác. Có thể được cài đặt thông qua Trình soạn thảo.

HtmlPageDom

Wa72 \ HtmlPageDom` là một thư viện PHP để dễ dàng thao tác các tài liệu HTML bằng cách sử dụng Nó yêu cầu DomCrawler từ các thành phần Symfony2 để duyệt qua cây DOM và mở rộng nó bằng cách thêm các phương thức để xử lý cây DOM của tài liệu HTML.

phpQuery (không được cập nhật trong nhiều năm)

phpQuery là API Mô hình đối tượng tài liệu (DOM) được điều khiển bởi bộ chọn CSS3 phía máy chủ, dựa trên thư viện jQuery JavaScript được viết bằng PHP5 và cung cấp thêm Giao diện dòng lệnh (CLI).

Xem thêm: https://github.com/electrolinux/phpquery

Zend_Dom

Zend_Dom cung cấp các công cụ để làm việc với các tài liệu và cấu trúc DOM. Hiện tại, chúng tôi cung cấp Zend_Dom_Query, cung cấp giao diện hợp nhất để truy vấn các tài liệu DOM sử dụng cả bộ chọn XPath và CSS.

QueryPath

QueryPath là một thư viện PHP để thao tác XML và HTML. Nó được thiết kế để hoạt động không chỉ với các tệp cục bộ mà còn với các dịch vụ web và tài nguyên cơ sở dữ liệu. Nó thực hiện phần lớn giao diện jQuery (bao gồm các bộ chọn kiểu CSS), nhưng nó được điều chỉnh nhiều để sử dụng phía máy chủ. Có thể được cài đặt thông qua Trình soạn thảo.

fDOMDocument

fDOMDocument mở rộng DOM tiêu chuẩn để sử dụng các ngoại lệ tại mọi trường hợp xảy ra lỗi thay vì cảnh báo hoặc thông báo PHP. Họ cũng thêm các phương thức và lối tắt tùy chỉnh khác nhau để thuận tiện và để đơn giản hóa việc sử dụng DOM.

kẻ phá hoại / xml

saber / xml là một thư viện bao bọc và mở rộng các lớp XMLReader và XMLWriter để tạo ra một hệ thống ánh xạ và mẫu thiết kế "xml to object / Array" đơn giản. Viết và đọc XML là một lần và do đó có thể nhanh và yêu cầu bộ nhớ thấp trên các tệp xml lớn.

FluidXML

FluidXML là một thư viện PHP để thao tác XML với API ngắn gọn và trôi chảy. Nó thúc đẩy XPath và mô hình lập trình trôi chảy trở nên thú vị và hiệu quả.


Bên thứ 3 (không dựa trên libxml)

Lợi ích của việc xây dựng dựa trên DOM / libxml là bạn có được hiệu suất tốt do bạn dựa trên tiện ích mở rộng riêng. Tuy nhiên, không phải tất cả các lib của bên thứ 3 đều đi theo tuyến đường này. Một số trong số họ được liệt kê dưới đây

Trình phân tích cú pháp DOM HTML đơn giản

  • Trình phân tích cú pháp DOM DOM được viết bằng PHP5 + cho phép bạn thao tác HTML một cách rất dễ dàng!
  • Yêu cầu PHP 5+.
  • Hỗ trợ HTML không hợp lệ.
  • Tìm thẻ trên trang HTML với các bộ chọn giống như jQuery.
  • Trích xuất nội dung từ HTML trong một dòng duy nhất.

Tôi thường không đề xuất trình phân tích cú pháp này. Codebase là khủng khiếp và bản thân trình phân tích cú pháp khá chậm và đói bộ nhớ. Không phải tất cả các Bộ chọn jQuery (như bộ chọn con ) đều có thể. Bất kỳ thư viện dựa trên libxml nên dễ dàng vượt qua điều này.

Trình phân tích cú pháp PHP

PHPHtmlParser là một trình phân tích cú pháp html đơn giản, linh hoạt, cho phép bạn chọn các thẻ bằng bất kỳ bộ chọn css nào, như jQuery. Mục tiêu là hỗ trợ phát triển các công cụ đòi hỏi một cách nhanh chóng, dễ dàng để loại bỏ html, cho dù nó có hợp lệ hay không! Dự án này ban đầu được hỗ trợ bởi sunra / php-simple-html-dom-Parser nhưng sự hỗ trợ dường như đã dừng lại vì vậy dự án này là sự điều chỉnh của tôi cho công việc trước đây của anh ấy.

Một lần nữa, tôi sẽ không đề xuất trình phân tích cú pháp này. Nó khá chậm với việc sử dụng CPU cao. Cũng không có chức năng xóa bộ nhớ của các đối tượng DOM đã tạo. Những vấn đề quy mô đặc biệt với các vòng lặp lồng nhau. Bản thân tài liệu này không chính xác và sai chính tả, không có phản hồi cho các bản sửa lỗi kể từ ngày 14 tháng 4.

Ganon

  • Trình mã thông báo phổ biến và Trình phân tích cú pháp HTML / XML / RSS DOM
    • Khả năng thao tác các yếu tố và thuộc tính của chúng
    • Hỗ trợ HTML và UTF8 không hợp lệ
  • Có thể thực hiện các truy vấn giống CSS3 nâng cao trên các phần tử (như jQuery - không gian tên được hỗ trợ)
  • Trình làm đẹp HTML (như HTML Tidy)
    • Giảm thiểu CSS và Javascript
    • Sắp xếp các thuộc tính, thay đổi trường hợp ký tự, thụt lề chính xác, v.v.
  • Mở rộng
    • Phân tích tài liệu bằng cách sử dụng các cuộc gọi lại dựa trên ký tự / mã thông báo hiện tại
    • Các hoạt động được phân tách trong các chức năng nhỏ hơn để dễ dàng ghi đè
  • Nhanh và dễ

Không bao giờ sử dụng nó. Không thể biết nó có tốt không.


HTML 5

Bạn có thể sử dụng cách trên để phân tích cú pháp HTML5, nhưng có thể có những điều kỳ quặc do đánh dấu mà HTML5 cho phép. Vì vậy, đối với HTML5, bạn muốn xem xét sử dụng trình phân tích cú pháp chuyên dụng, như

html5lib

Một triển khai Python và PHP của trình phân tích cú pháp HTML dựa trên đặc tả HTML5 của WHATWG để tương thích tối đa với các trình duyệt web trên máy tính để bàn chính.

Chúng ta có thể thấy các trình phân tích cú pháp chuyên dụng hơn sau khi HTML5 được hoàn thành. Ngoài ra còn có một blogpost bởi W3 có tiêu đề Cách phân tích cú pháp html 5 đáng để kiểm tra.


Dịch vụ web

Nếu bạn không thích lập trình PHP, bạn cũng có thể sử dụng các dịch vụ Web. Nói chung, tôi tìm thấy rất ít tiện ích cho những thứ này, nhưng đó chỉ là tôi và các trường hợp sử dụng của tôi.

ScripWiki .

Giao diện bên ngoài của ScraperWiki cho phép bạn trích xuất dữ liệu ở dạng bạn muốn sử dụng trên web hoặc trong các ứng dụng của riêng bạn. Bạn cũng có thể trích xuất thông tin về trạng thái của bất kỳ dụng cụ cạo nào.


Biểu thức chính quy

Cuối cùng và ít nhất được đề xuất , bạn có thể trích xuất dữ liệu từ HTML bằng các biểu thức thông thường . Nói chung, việc sử dụng Biểu thức chính quy trên HTML không được khuyến khích.

Hầu hết các đoạn bạn sẽ tìm thấy trên web để khớp với đánh dấu là dễ vỡ. Trong hầu hết các trường hợp, họ chỉ làm việc cho một đoạn HTML rất đặc biệt. Thay đổi đánh dấu nhỏ, như thêm khoảng trắng ở đâu đó hoặc thêm hoặc thay đổi thuộc tính trong thẻ, có thể khiến RegEx không thành công khi không được viết đúng. Bạn nên biết những gì bạn đang làm trước khi sử dụng RegEx trên HTML.

Các trình phân tích cú pháp HTML đã biết các quy tắc cú pháp của HTML. Các biểu thức thông thường phải được dạy cho mỗi RegEx mới mà bạn viết. RegEx vẫn ổn trong một số trường hợp, nhưng nó thực sự phụ thuộc vào trường hợp sử dụng của bạn.

Bạn có thể viết các trình phân tích cú pháp đáng tin cậy hơn , nhưng viết một trình phân tích cú pháp tùy chỉnh đầy đủ và đáng tin cậy với các biểu thức thông thường là một sự lãng phí thời gian khi các thư viện nói trên đã tồn tại và thực hiện công việc này tốt hơn nhiều.

Cũng xem Phân tích cú pháp Html Cách Cthulhu


Sách

Nếu bạn muốn chi tiêu một số tiền, hãy xem

Tôi không liên kết với Kiến trúc sư PHP hoặc các tác giả.


10
@Naveed mà phụ thuộc vào nhu cầu của bạn. Tôi không có nhu cầu truy vấn CSS Selector, đó là lý do tại sao tôi chỉ sử dụng DOM với XPath. phpQuery nhằm mục đích trở thành một cổng jQuery. Zend_Dom rất nhẹ. Bạn thực sự phải kiểm tra chúng để xem bạn thích cái nào nhất.
Gordon

2
@ Ms2ger Chủ yếu, nhưng không hoàn toàn. Giống như đã chỉ ra ở trên, bạn có thể sử dụng các trình phân tích cú pháp dựa trên libxml nhưng có những trường hợp đặc biệt trong đó chúng sẽ bị sặc. Nếu bạn cần khả năng tương thích tối đa, bạn nên sử dụng một trình phân tích cú pháp chuyên dụng. Tôi thích giữ sự khác biệt.
Gordon

9
Quan điểm của bạn về việc không sử dụng PHP Simple HTML DOM Parser có vẻ như chưa được.
Petah

3
Kể từ ngày 29 tháng 3 năm 2012, DOM không hỗ trợ html5, XMLReader không hỗ trợ HTML và lần cam kết cuối cùng trên html5lib cho PHP là vào tháng 9 năm 2009. Sử dụng gì để phân tích HTML5, HTML4 và XHTML?
Shiplu Mokaddim

4
@Nasha Tôi đã cố tình loại trừ Zalgo khét tiếng ra khỏi danh sách trên vì nó không quá hữu ích cho chính nó và dẫn đến một số giáo phái hàng hóa kể từ khi nó được viết. Mọi người đã bị tát với liên kết đó cho dù regex có thích hợp như thế nào đi chăng nữa. Để có ý kiến ​​cân bằng hơn, vui lòng xem liên kết tôi đã đưa vào thay vào đó và xem qua các nhận xét tại stackoverflow.com/questions/4245008/ chủ
Gordon

322

Hãy thử Trình phân tích cú pháp DOM HTML đơn giản

  • Trình phân tích cú pháp DOM DOM được viết bằng PHP 5+ cho phép bạn thao tác HTML theo cách rất dễ dàng!
  • Yêu cầu PHP 5+.
  • Hỗ trợ HTML không hợp lệ.
  • Tìm thẻ trên trang HTML với các bộ chọn giống như jQuery.
  • Trích xuất nội dung từ HTML trong một dòng duy nhất.
  • Tải xuống


Ví dụ:

Cách nhận các phần tử HTML:

// Create DOM from URL or file
$html = file_get_html('http://www.example.com/');

// Find all images
foreach($html->find('img') as $element)
       echo $element->src . '<br>';

// Find all links
foreach($html->find('a') as $element)
       echo $element->href . '<br>';


Cách sửa đổi các thành phần HTML:

// Create DOM from string
$html = str_get_html('<div id="hello">Hello</div><div id="world">World</div>');

$html->find('div', 1)->class = 'bar';

$html->find('div[id=hello]', 0)->innertext = 'foo';

echo $html;


Trích xuất nội dung từ HTML:

// Dump contents (without tags) from HTML
echo file_get_html('http://www.google.com/')->plaintext;


Cạo Slashdot:

// Create DOM from URL
$html = file_get_html('http://slashdot.org/');

// Find all article blocks
foreach($html->find('div.article') as $article) {
    $item['title']     = $article->find('div.title', 0)->plaintext;
    $item['intro']    = $article->find('div.intro', 0)->plaintext;
    $item['details'] = $article->find('div.details', 0)->plaintext;
    $articles[] = $item;
}

print_r($articles);

8
Đầu tiên, có những thứ tôi cần chuẩn bị như DOM xấu, mã Invlid, cũng là js phân tích dựa trên công cụ DNSBL, điều này cũng sẽ được sử dụng để tìm kiếm các trang web / nội dung độc hại, cũng như tôi đã xây dựng trang web của mình xung quanh một khung i đã xây dựng nó cần phải sạch sẽ, dễ đọc và có cấu trúc tốt. SimpleDim rất tuyệt nhưng mã hơi lộn xộn
RobertPitt

9
@Robert bạn cũng có thể muốn kiểm tra htmlpurifier.org để biết những điều liên quan đến bảo mật.
Gordon

3
Anh ta có một điểm hợp lệ: SimpleHTMLDOM rất khó để mở rộng, trừ khi bạn sử dụng mẫu trang trí mà tôi thấy khó sử dụng. Tôi thấy mình rùng mình khi thực hiện các thay đổi cho (các) lớp bên dưới.
Erik

1
Những gì tôi đã làm là chạy html thông qua gọn gàng trước khi gửi nó tới SimpleDOM.
MB34

1
Hiện tại tôi đang sử dụng nó, chạy nó như một phần của dự án để xử lý vài trăm url. Nó trở nên rất chậm và thời gian chờ đều đặn. Đó là một kịch bản mới bắt đầu tuyệt vời và trực quan đơn giản để học, nhưng chỉ quá cơ bản cho các dự án nâng cao hơn.
luke_mclachlan

236

Chỉ cần sử dụng DOMDocument-> loadHTML () và được thực hiện với nó. Thuật toán phân tích HTML của libxml khá tốt và nhanh, và trái với niềm tin phổ biến, không gây nghẹt thở cho HTML không đúng định dạng.


19
Thật. Và nó hoạt động với các lớp XPath và XSLTProcessor tích hợp sẵn của PHP, rất tốt cho việc trích xuất nội dung.
Kornel

8
Đối với HTML thực sự sai lệch, bạn luôn có thể chạy nó qua htmltidy trước khi chuyển nó sang DOM. Bất cứ khi nào tôi cần cạo dữ liệu từ HTML, tôi luôn sử dụng DOM hoặc ít nhất là đơn giản.
Nông dân Frank

9
Một điều nữa với việc tải HTML không đúng định dạng i có thể là khôn ngoan khi gọi libxml_use_iternal_errors (true) để ngăn các cảnh báo sẽ ngừng phân tích cú pháp.
Husky

6
Tôi đã sử dụng DOMDocument để phân tích khoảng 1000 nguồn html (bằng nhiều ngôn ngữ được mã hóa với các bộ ký tự khác nhau) mà không gặp vấn đề gì. Bạn có thể gặp phải vấn đề mã hóa với điều này, nhưng chúng không thể vượt qua. Bạn cần biết 3 điều: 1) loadHTML sử dụng bộ ký tự của thẻ meta để xác định mã hóa 2) # 2 có thể dẫn đến phát hiện mã hóa không chính xác nếu nội dung html không bao gồm thông tin này 3) các ký tự UTF-8 xấu có thể ngắt trình phân tích cú pháp. Trong các trường hợp như vậy, hãy sử dụng kết hợp mb_detect_encoding () và mã hóa / chuyển đổi / tước mã ký tự UTF-8 của Simplepie RSS để giải quyết.
Không

1
DOM thực sự hỗ trợ XPath, hãy xem DOMXPath .
Ryan McCue

147

Tại sao bạn không nên và khi nào bạn nên sử dụng biểu thức thông thường?

Trước hết, một cách hiểu sai phổ biến: Regexps không dành cho " phân tích cú pháp " HTML. Regexes tuy nhiên có thể " trích xuất " dữ liệu. Trích xuất là những gì họ làm cho. Hạn chế chính của việc trích xuất HTML regex so với các bộ công cụ SGML hoặc trình phân tích cú pháp XML cơ bản là nỗ lực cú pháp của chúng và độ tin cậy khác nhau.

Hãy xem xét việc tạo một biểu thức trích xuất HTML có phần đáng tin cậy:

<a\s+class="?playbutton\d?[^>]+id="(\d+)".+?    <a\s+class="[\w\s]*title
[\w\s]*"[^>]+href="(http://[^">]+)"[^>]*>([^<>]+)</a>.+?

là cách dễ đọc hơn so với tương đương phpQuery hoặc QueryPath đơn giản:

$div->find(".stationcool a")->attr("title");

Tuy nhiên, có những trường hợp sử dụng cụ thể mà họ có thể giúp đỡ.

  • Nhiều giao diện truyền tải DOM không tiết lộ các nhận xét HTML <!--, đôi khi là các neo hữu ích hơn để trích xuất. Trong các biến thể giả HTML cụ thể <$var>hoặc dư lượng SGML rất dễ thuần hóa bằng biểu thức chính quy.
  • Thông thường các biểu thức thông thường có thể lưu xử lý hậu kỳ. Tuy nhiên, các thực thể HTML thường yêu cầu chăm sóc thủ công.
  • Và cuối cùng, đối với các tác vụ cực kỳ đơn giản như trích xuất <img src = url, thực tế chúng là một công cụ có thể xảy ra. Lợi thế về tốc độ so với các trình phân tích cú pháp SGML / XML chủ yếu chỉ dùng để chơi các quy trình trích xuất rất cơ bản này.

Đôi khi, thậm chí nên trích xuất trước một đoạn mã HTML bằng cách sử dụng các biểu thức thông thường /<!--CONTENT-->(.+?)<!--END-->/và xử lý phần còn lại bằng cách sử dụng trình phân tích cú pháp HTML đơn giản hơn.

Lưu ý: Tôi thực sự có ứng dụng này , nơi tôi sử dụng phân tích cú pháp XML và các biểu thức thông thường thay thế. Mới tuần trước, phân tích cú pháp PyQuery đã bị hỏng và regex vẫn hoạt động. Vâng, và tôi không thể tự giải thích nó. Nhưng nó đã xảy ra.
Vì vậy, vui lòng không bỏ phiếu cân nhắc trong thế giới thực, chỉ vì nó không phù hợp với regex = meme ác. Nhưng chúng ta cũng đừng bỏ phiếu này quá nhiều. Nó chỉ là một sidenote cho chủ đề này.


20
DOMCommentcó thể đọc bình luận, vì vậy không có lý do để sử dụng Regex cho điều đó.
Gordon

4
Cả bộ công cụ SGML hoặc trình phân tích cú pháp XML đều phù hợp để phân tích cú pháp HTML trong thế giới thực. Vì thế, chỉ có một trình phân tích cú pháp HTML chuyên dụng là phù hợp.
Alohci

12
@Alohci DOMsử dụng libxmllibxml có một mô-đun trình phân tích cú pháp HTML riêng biệt sẽ được sử dụng khi tải HTML loadHTML()để có thể tải HTML "thế giới thực" (đọc bị hỏng) rất nhiều.
Gordon

6
Chà, chỉ là một nhận xét về quan điểm "cân nhắc trong thế giới thực" của bạn. Chắc chắn, có những tình huống hữu ích cho Regex khi phân tích cú pháp HTML. Và cũng có những tình huống hữu ích cho việc sử dụng GOTO. Và có những tình huống hữu ích cho các biến-biến. Vì vậy, không có triển khai cụ thể nào là mã xác thực để sử dụng nó. Nhưng đó là một dấu hiệu cảnh báo RẤT mạnh mẽ. Và nhà phát triển trung bình dường như không đủ sắc thái để nói lên sự khác biệt. Vì vậy, như một quy luật chung, Regex GOTO và Biến-Biến đều là ác. Có những cách sử dụng không xấu, nhưng đó là những trường hợp ngoại lệ (và hiếm ở đó) ... (IMHO)
ircmaxell

11
@mario: Trên thực tế, HTML có thể được phân tích cú pháp 'chính xác' bằng cách sử dụng biểu thức chính quy, mặc dù thông thường phải mất một vài trong số chúng để thực hiện công việc một cách công bằng. Đó chỉ là một nỗi đau của hoàng gia trong trường hợp chung. Trong các trường hợp cụ thể với đầu vào được xác định rõ, nó sẽ thay đổi tầm thường. Đó là những trường hợp mà mọi người nên sử dụng regexes trên. Các trình phân tích cú pháp lớn đói cũ thực sự là những gì bạn cần cho các trường hợp chung, mặc dù điều đó không phải lúc nào cũng rõ ràng đối với người dùng thông thường nơi vẽ đường đó. Mã nào đơn giản và dễ dàng hơn, sẽ thắng.
tchrist

131

phpQueryQueryPath cực kỳ giống nhau trong việc sao chép API jQuery trôi chảy. Đó cũng là lý do tại sao chúng là hai trong những cách tiếp cận dễ nhất để phân tích HTML đúng trong PHP.

Ví dụ cho QueryPath

Về cơ bản, trước tiên bạn tạo một cây DOM có thể truy vấn từ một chuỗi HTML:

 $qp = qp("<html><body><h1>title</h1>..."); // or give filename or URL

Đối tượng kết quả chứa một biểu diễn cây hoàn chỉnh của tài liệu HTML. Nó có thể được duyệt qua các phương thức DOM. Nhưng cách tiếp cận phổ biến là sử dụng các bộ chọn CSS như trong jQuery:

 $qp->find("div.classname")->children()->...;

 foreach ($qp->find("p img") as $img) {
     print qp($img)->attr("src");
 }

Chủ yếu là bạn muốn sử dụng đơn giản #id.classhoặc DIVbộ chọn thẻ cho ->find(). Nhưng bạn cũng có thể sử dụng các câu lệnh XPath , đôi khi nhanh hơn. Ngoài ra phương pháp jQuery điển hình như ->children()->text()và đặc biệt là ->attr()đơn giản hóa việc giải nén các đoạn mã HTML ngay. (Và đã giải mã các thực thể SGML của chúng.)

 $qp->xpath("//div/p[1]");  // get first paragraph in a div

QueryPath cũng cho phép chèn các thẻ mới vào luồng ( ->append) và sau đó xuất ra và sắp xếp lại một tài liệu cập nhật ( ->writeHTML). Nó không chỉ có thể phân tích cú pháp HTML không đúng định dạng mà còn cả các phương ngữ XML khác nhau (có không gian tên) và thậm chí trích xuất dữ liệu từ các vi định dạng HTML (XFN, vCard).

 $qp->find("a[target=_blank]")->toggleClass("usability-blunder");

.

phpQuery hay QueryPath?

Nói chung QueryPath phù hợp hơn cho việc thao tác với các tài liệu. Trong khi phpQuery cũng triển khai một số phương thức AJAX giả (chỉ yêu cầu HTTP) để gần giống với jQuery hơn. Người ta nói rằng phpQuery thường nhanh hơn QueryPath (vì ít tính năng tổng thể hơn).

Để biết thêm thông tin về sự khác biệt, hãy xem so sánh này trên máy wayback từ tagbyte.org . (Nguồn gốc bị mất, vì vậy đây là một liên kết lưu trữ internet. Có, bạn vẫn có thể xác định vị trí các trang bị thiếu, mọi người.)

Và đây là phần giới thiệu QueryPath toàn diện .

Ưu điểm

  • Đơn giản và đáng tin cậy
  • Đơn giản để sử dụng thay thế ->find("a img, a object, div a")
  • Dữ liệu không phù hợp (so với biểu thức chính quy)

88

DOM HTML đơn giản là một trình phân tích cú pháp nguồn mở tuyệt vời:

Simplehtmldom.sourceforge

Nó xử lý các phần tử DOM theo cách hướng đối tượng và phép lặp mới có nhiều phạm vi bảo hiểm cho mã không tuân thủ. Ngoài ra còn có một số chức năng tuyệt vời như bạn thấy trong JavaScript, chẳng hạn như chức năng "tìm", sẽ trả về tất cả các phiên bản của các thành phần của tên thẻ đó.

Tôi đã sử dụng công cụ này trong một số công cụ, thử nghiệm nó trên nhiều loại trang web khác nhau và tôi nghĩ rằng nó hoạt động rất tốt.


61

Một cách tiếp cận chung mà tôi chưa thấy được đề cập ở đây là chạy HTML thông qua Tidy , có thể được thiết lập để nhổ XHTML có hiệu lực được bảo đảm. Sau đó, bạn có thể sử dụng bất kỳ thư viện XML cũ nào trên đó.

Nhưng đối với vấn đề cụ thể của bạn, bạn nên xem dự án này: http://fivefilters.org/content-only/ - đây là phiên bản sửa đổi của thuật toán Readability , được thiết kế để chỉ trích xuất nội dung văn bản (không phải tiêu đề và chân trang) từ một trang.


56

Trong 1a và 2: Tôi sẽ bỏ phiếu cho lớp Symfony WIFIet mới DOMCrawler ( DomCrawler ). Lớp này cho phép các truy vấn tương tự như Bộ chọn CSS. Hãy xem bài trình bày này để biết các ví dụ trong thế giới thực: news-of-the-symfony2-world .

Thành phần này được thiết kế để hoạt động độc lập và có thể được sử dụng mà không cần Symfony.

Hạn chế duy nhất là nó sẽ chỉ hoạt động với PHP 5.3 hoặc mới hơn.


Các truy vấn css giống như jquery cũng được nói, bởi vì có một số điều còn thiếu trong tài liệu w3c, nhưng hiện diện như các tính năng bổ sung trong jquery.
Nikola Petkanski

53

Nhân tiện, điều này thường được gọi là cào màn hình . Thư viện tôi đã sử dụng cho việc này là Simple HTML Dom Parser .


8
Không hoàn toàn đúng ( en.wikipedia.org/wiki/Screen_scraping#Screen_scraping ). Manh mối nằm trong "màn hình"; trong trường hợp được mô tả, không có màn hình liên quan. Mặc dù, thừa nhận, thuật ngữ này đã phải chịu rất nhiều sự lạm dụng gần đây.
Bobby Jack

4
Tôi không quét màn hình, nội dung sẽ được phân tích cú pháp sẽ được nhà cung cấp nội dung cho phép theo thỏa thuận của tôi.
RobertPitt

41

Chúng tôi đã tạo ra một vài trình thu thập thông tin cho nhu cầu của chúng tôi trước đây. Vào cuối ngày, thường là các biểu thức chính quy đơn giản làm điều tốt nhất. Mặc dù các thư viện được liệt kê ở trên tốt cho lý do chúng được tạo, nhưng nếu bạn biết bạn đang tìm kiếm gì, các biểu thức chính quy là cách an toàn hơn, vì bạn có thể xử lý cả các cấu trúc HTML / XHTML không hợp lệ , nếu bị tải thông qua hầu hết các trình phân tích cú pháp.


38

Tôi khuyên dùng PHP Pars HTML DOM Parser .

Nó thực sự có các tính năng tốt, như:

foreach($html->find('img') as $element)
       echo $element->src . '<br>';

36

Điều này nghe có vẻ như là một mô tả nhiệm vụ tốt của công nghệ W3C XPath . Thật dễ dàng để thể hiện các truy vấn như "trả lại tất cả các hrefthuộc tính trong imgcác thẻ được lồng vào <foo><bar><baz> elements." Không phải là một người dùng PHP, tôi không thể nói cho bạn biết XPath ở dạng nào có thể có sẵn. Nếu bạn có thể gọi một chương trình bên ngoài để xử lý tệp HTML, bạn sẽ có thể sử dụng phiên bản dòng lệnh của XPath. Để có phần giới thiệu nhanh, hãy xem http://en.wikipedia.org/wiki/XPath .


29

Các lựa chọn thay thế của bên thứ ba cho SimpleHtmlDom sử dụng DOM thay vì Phân tích chuỗi: phpQuery , Zend_Dom , QueryPathFluentDom .


3
Nếu bạn đã sao chép nhận xét của tôi, ít nhất là liên kết chúng đúng cách;) Đó là: Các lựa chọn thay thế của bên thứ ba được đề xuất cho SimpleHtmlDom thực sự sử dụng DOM thay vì String Parsing: phpQuery , Zend_Dom , QueryPathFluentDom .
Gordon

1
Câu trả lời tốt là một nguồn tuyệt vời. stackoverflow.com/questions/3606792/
hy

24

Có, bạn có thể sử dụng Simple_html_dom cho mục đích này. Tuy nhiên, tôi đã làm việc khá nhiều với Simple_html_dom, đặc biệt đối với việc loại bỏ web và nhận thấy nó quá dễ bị tấn công. Nó làm công việc cơ bản nhưng tôi sẽ không đề xuất dù sao đi nữa.

Tôi chưa bao giờ sử dụng curl cho mục đích nhưng những gì tôi đã học được là curl có thể thực hiện công việc hiệu quả hơn và vững chắc hơn nhiều.

Vui lòng kiểm tra liên kết này: scraping-website-with-curl


2
curl có thể lấy tệp, nhưng nó sẽ không phân tích HTML cho bạn. Đó là phần khó khăn.
cHao

23

QueryPath là tốt, nhưng hãy cẩn thận với "trạng thái theo dõi" nếu bạn không nhận ra ý nghĩa của nó, điều đó có nghĩa là bạn lãng phí rất nhiều thời gian gỡ lỗi để cố gắng tìm hiểu điều gì đã xảy ra và tại sao mã không hoạt động.

Điều đó có nghĩa là mỗi cuộc gọi trên tập kết quả sẽ sửa đổi tập kết quả trong đối tượng, nó không thể kết nối được như trong jquery trong đó mỗi liên kết là một tập hợp mới, bạn có một tập hợp là kết quả từ truy vấn của bạn và mỗi lệnh gọi hàm sẽ sửa đổi bộ đơn đó.

để có được hành vi giống như jquery, bạn cần phân nhánh trước khi thực hiện bộ lọc / sửa đổi như thao tác, điều đó có nghĩa là nó sẽ phản ánh những gì xảy ra trong jquery chặt chẽ hơn nhiều.

$results = qp("div p");
$forename = $results->find("input[name='forename']");

$resultsbây giờ chứa kết quả được đặt cho input[name='forename']KHÔNG phải truy vấn ban đầu, "div p"điều này làm tôi vấp ngã rất nhiều, điều tôi tìm thấy là QueryPath theo dõi các bộ lọc và tìm và mọi thứ điều chỉnh kết quả của bạn và lưu trữ chúng trong đối tượng. bạn cần phải làm điều này thay vào đó

$forename = $results->branch()->find("input[name='forname']")

sau đó $resultssẽ không được sửa đổi và bạn có thể sử dụng lại kết quả được đặt lại, có lẽ ai đó có nhiều kiến ​​thức hơn có thể làm rõ điều này một chút, nhưng về cơ bản nó giống như thế này từ những gì tôi đã tìm thấy.


20

Advanced Html Dom là một thay thế DOM HTML đơn giản cung cấp cùng một giao diện, nhưng nó dựa trên DOM, điều đó có nghĩa là không có vấn đề bộ nhớ liên quan nào xảy ra.

Nó cũng có hỗ trợ CSS đầy đủ, bao gồm các phần mở rộng jQuery .


Tôi đã nhận được kết quả tốt từ Advanced Html Dom và tôi nghĩ rằng nó nên có trong danh sách trong câu trả lời được chấp nhận. Một điều quan trọng cần biết mặc dù đối với bất kỳ ai dựa vào "Mục tiêu của dự án này là trở thành một thay thế thả xuống dựa trên DOM cho thư viện dom html đơn giản của PHP ... Nếu bạn sử dụng tệp / str_get_html thì bạn không cần phải thay đổi bất cứ điều gì. " archive.is/QtSuj#selection-933.34-933.100 là bạn có thể cần phải thay đổi mã của mình để phù hợp với một số điểm không tương thích. Tôi đã lưu ý bốn điều được biết đến với tôi trong các vấn đề về github của dự án. github.com/monkeysuffrage/advified_html_dom/issues
ChrisJJ

Đã làm việc ! Thankss
Faisal Shani

18

Đối với HTML5 , lib html5 đã bị bỏ rơi trong nhiều năm nay. Thư viện HTML5 duy nhất tôi có thể tìm thấy với một bản ghi cập nhật và bảo trì gần đây là html5-php vừa được đưa lên bản beta 1.0 cách đây một tuần.


17

Tôi đã viết một trình phân tích cú pháp XML có mục đích chung có thể dễ dàng xử lý các tệp GB. Nó dựa trên XMLReader và nó rất dễ sử dụng:

$source = new XmlExtractor("path/to/tag", "/path/to/file.xml");
foreach ($source as $tag) {
    echo $tag->field1;
    echo $tag->field2->subfield1;
}

Đây là repo github: XmlExtractor


17

Tôi đã tạo một thư viện có tên PHPPowertools / DOM-Query , cho phép bạn thu thập dữ liệu các tài liệu HTML5 và XML giống như bạn làm với jQuery.

Trong phần mềm này, nó sử dụng symfony / DomCrawler để chuyển đổi các bộ chọn CSS sang bộ chọn XPath . Nó luôn sử dụng cùng một DomDocument, ngay cả khi truyền một đối tượng này sang đối tượng khác, để đảm bảo hiệu suất tốt.


Ví dụ sử dụng:

namespace PowerTools;

// Get file content
$htmlcode = file_get_contents('https://github.com');

// Define your DOMCrawler based on file string
$H = new DOM_Query($htmlcode);

// Define your DOMCrawler based on an existing DOM_Query instance
$H = new DOM_Query($H->select('body'));

// Passing a string (CSS selector)
$s = $H->select('div.foo');

// Passing an element object (DOM Element)
$s = $H->select($documentBody);

// Passing a DOM Query object
$s = $H->select( $H->select('p + p'));

// Select the body tag
$body = $H->select('body');

// Combine different classes as one selector to get all site blocks
$siteblocks = $body->select('.site-header, .masthead, .site-body, .site-footer');

// Nest your methods just like you would with jQuery
$siteblocks->select('button')->add('span')->addClass('icon icon-printer');

// Use a lambda function to set the text of all site blocks
$siteblocks->text(function( $i, $val) {
    return $i . " - " . $val->attr('class');
});

// Append the following HTML to all site blocks
$siteblocks->append('<div class="site-center"></div>');

// Use a descendant selector to select the site's footer
$sitefooter = $body->select('.site-footer > .site-center');

// Set some attributes for the site's footer
$sitefooter->attr(array('id' => 'aweeesome', 'data-val' => 'see'));

// Use a lambda function to set the attributes of all site blocks
$siteblocks->attr('data-val', function( $i, $val) {
    return $i . " - " . $val->attr('class') . " - photo by Kelly Clark";
});

// Select the parent of the site's footer
$sitefooterparent = $sitefooter->parent();

// Remove the class of all i-tags within the site's footer's parent
$sitefooterparent->select('i')->removeAttr('class');

// Wrap the site's footer within two nex selectors
$sitefooter->wrap('<section><div class="footer-wrapper"></div></section>');

[...]

Phương pháp được hỗ trợ:


  1. Đổi tên 'chọn', vì lý do rõ ràng
  2. Đổi tên thành 'void', vì 'trống' là một từ dành riêng trong PHP

GHI CHÚ :

Thư viện cũng bao gồm trình tải tự động cấu hình không của riêng nó cho các thư viện tương thích PSR-0. Ví dụ bao gồm sẽ hoạt động ngoài hộp mà không có bất kỳ cấu hình bổ sung. Ngoài ra, bạn có thể sử dụng nó với nhà soạn nhạc.


Có vẻ như công cụ phù hợp cho công việc nhưng không tải cho tôi trong PHP 5.6.23 trong Worpress. Bất kỳ hướng bổ sung về làm thế nào để bao gồm nó một cách chính xác?. Bao gồm nó với: định nghĩa ("BASE_PATH", dirname ( FILE )); định nghĩa ("LIBRARY_PATH", BASE_PATH. TRỰC TIẾP_SEPARATOR. 'lib / nhà cung cấp'); yêu cầu LIBRARY_PATH. GIÁM ĐỐC_SEPARATOR. 'Trình tải.php'; Trình tải :: init (mảng (LIBRARY_PATH, USER_PATH)); trong
hàm.php

15

Bạn có thể thử sử dụng một cái gì đó như HTML Tidy để dọn dẹp bất kỳ HTML "bị hỏng" nào và chuyển đổi HTML thành XHTML, sau đó bạn có thể phân tích cú pháp bằng trình phân tích cú pháp XML.


15

Một tùy chọn khác bạn có thể thử là QueryPath . Nó lấy cảm hứng từ jQuery, nhưng trên máy chủ trong PHP và được sử dụng trong Drupal .


12

XML_HTMLSaxlà khá ổn định - ngay cả khi nó không được duy trì nữa. Một tùy chọn khác có thể dẫn bạn HTML qua Html Tidy và sau đó phân tích nó bằng các công cụ XML tiêu chuẩn.


11

Các Symfony framework có bó mà có thể phân tích cú pháp HTML, và bạn có thể sử dụng phong cách CSS để chọn DOMs thay vì sử dụng XPath .


11

Có nhiều cách để xử lý HTML / XML DOM mà hầu hết đã được đề cập. Do đó, tôi sẽ không thực hiện bất kỳ nỗ lực nào để liệt kê những bản thân mình.

Tôi chỉ muốn thêm rằng cá nhân tôi thích sử dụng tiện ích mở rộng DOM và tại sao:

  • iit sử dụng tối ưu lợi thế hiệu suất của mã C cơ bản
  • đó là OO PHP (và cho phép tôi phân lớp nó)
  • mức độ khá thấp (cho phép tôi sử dụng nó như một nền tảng không phình to cho hành vi nâng cao hơn)
  • nó cung cấp quyền truy cập vào mọi phần của DOM (không giống như SimpleXml, bỏ qua một số tính năng XML ít được biết đến)
  • nó có một cú pháp được sử dụng để thu thập thông tin DOM tương tự như cú pháp được sử dụng trong Javascript gốc.

Và trong khi tôi bỏ lỡ khả năng sử dụng các bộ chọn CSS DOMDocument, thì có một cách khá đơn giản và thuận tiện để thêm tính năng này: phân lớp DOMDocumentvà thêm giống như JS querySelectorAllquerySelector phương thức vào lớp con của bạn.

Để phân tích cú pháp các bộ chọn, tôi khuyên bạn nên sử dụng thành phần CssSelector rất tối giản từ khung Symfony . Thành phần này chỉ dịch các bộ chọn CSS sang bộ chọn XPath, sau đó có thể được đưa vào mộtDOMXpath để lấy Nodelist tương ứng.

Sau đó, bạn có thể sử dụng lớp con này (vẫn ở mức rất thấp) làm nền tảng cho các lớp cấp cao hơn, dự định là ví dụ. phân tích các loại XML rất cụ thể hoặc thêm nhiều hành vi giống jQuery.

Mã dưới đây đi thẳng ra thư viện DOM-Query của tôi và sử dụng kỹ thuật tôi đã mô tả.

Để phân tích cú pháp HTML:

namespace PowerTools;

use \Symfony\Component\CssSelector\CssSelector as CssSelector;

class DOM_Document extends \DOMDocument {
    public function __construct($data = false, $doctype = 'html', $encoding = 'UTF-8', $version = '1.0') {
        parent::__construct($version, $encoding);
        if ($doctype && $doctype === 'html') {
            @$this->loadHTML($data);
        } else {
            @$this->loadXML($data);
        }
    }

    public function querySelectorAll($selector, $contextnode = null) {
        if (isset($this->doctype->name) && $this->doctype->name == 'html') {
            CssSelector::enableHtmlExtension();
        } else {
            CssSelector::disableHtmlExtension();
        }
        $xpath = new \DOMXpath($this);
        return $xpath->query(CssSelector::toXPath($selector, 'descendant::'), $contextnode);
    }

    [...]

    public function loadHTMLFile($filename, $options = 0) {
        $this->loadHTML(file_get_contents($filename), $options);
    }

    public function loadHTML($source, $options = 0) {
        if ($source && $source != '') {
            $data = trim($source);
            $html5 = new HTML5(array('targetDocument' => $this, 'disableHtmlNsInDom' => true));
            $data_start = mb_substr($data, 0, 10);
            if (strpos($data_start, '<!DOCTYPE ') === 0 || strpos($data_start, '<html>') === 0) {
                $html5->loadHTML($data);
            } else {
                @$this->loadHTML('<!DOCTYPE html><html><head><meta charset="' . $encoding . '" /></head><body></body></html>');
                $t = $html5->loadHTMLFragment($data);
                $docbody = $this->getElementsByTagName('body')->item(0);
                while ($t->hasChildNodes()) {
                    $docbody->appendChild($t->firstChild);
                }
            }
        }
    }

    [...]
}

Xem thêm Phân tích tài liệu XML bằng bộ chọn CSS của người sáng tạo Symfony Fabien Potencier về quyết định tạo thành phần CssSelector cho Symfony và cách sử dụng nó.


9

Với FluidXML, bạn có thể truy vấn và lặp lại XML bằng cách sử dụng XPathCSS Selector .

$doc = fluidxml('<html>...</html>');

$title = $doc->query('//head/title')[0]->nodeValue;

$doc->query('//body/p', 'div.active', '#bgId')
        ->each(function($i, $node) {
            // $node is a DOMNode.
            $tag   = $node->nodeName;
            $text  = $node->nodeValue;
            $class = $node->getAttribute('class');
        });

https://github.com/servo-php/fluidxml


7

JSON và mảng từ XML trong ba dòng:

$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);

Ta da!


7

Có một số lý do để không phân tích HTML bằng biểu thức chính quy. Nhưng, nếu bạn có toàn quyền kiểm soát HTML nào sẽ được tạo, thì bạn có thể thực hiện với biểu thức chính quy đơn giản.

Trên đây là một chức năng phân tích cú pháp HTML bằng biểu thức chính quy. Lưu ý rằng chức năng này rất nhạy cảm và yêu cầu HTML tuân theo các quy tắc nhất định, nhưng nó hoạt động rất tốt trong nhiều tình huống. Nếu bạn muốn một trình phân tích cú pháp đơn giản và không muốn cài đặt thư viện, hãy thử cách này:

function array_combine_($keys, $values) {
    $result = array();
    foreach ($keys as $i => $k) {
        $result[$k][] = $values[$i];
    }
    array_walk($result, create_function('&$v', '$v = (count($v) == 1)? array_pop($v): $v;'));

    return $result;
}

function extract_data($str) {
    return (is_array($str))
        ? array_map('extract_data', $str)
        : ((!preg_match_all('#<([A-Za-z0-9_]*)[^>]*>(.*?)</\1>#s', $str, $matches))
            ? $str
            : array_map(('extract_data'), array_combine_($matches[1], $matches[2])));
}

print_r(extract_data(file_get_contents("http://www.google.com/")));

2

Tôi đã tạo một thư viện có tên HTML5DOMDocument có sẵn miễn phí tại https://github.com/ivopetkov/html5-dom-document-php

Nó cũng hỗ trợ các bộ chọn truy vấn mà tôi nghĩ sẽ cực kỳ hữu ích trong trường hợp của bạn. Dưới đây là một số mã ví dụ:

$dom = new IvoPetkov\HTML5DOMDocument();
$dom->loadHTML('<!DOCTYPE html><html><body><h1>Hello</h1><div class="content">This is some text</div></body></html>');
echo $dom->querySelector('h1')->innerHTML;

0

Nếu bạn quen thuộc với bộ chọn jQuery, bạn có thể sử dụng ScarletsQuery cho PHP

<pre><?php
include "ScarletsQuery.php";

// Load the HTML content and parse it
$html = file_get_contents('https://www.lipsum.com');
$dom = Scarlets\Library\MarkupLanguage::parseText($html);

// Select meta tag on the HTML header
$description = $dom->selector('head meta[name="description"]')[0];

// Get 'content' attribute value from meta tag
print_r($description->attr('content'));

$description = $dom->selector('#Content p');

// Get element array
print_r($description->view);

Thư viện này thường mất ít hơn 1 giây để xử lý html ngoại tuyến.
Nó cũng chấp nhận HTML không hợp lệ hoặc thiếu trích dẫn về các thuộc tính thẻ.


0

Phương pháp tốt nhất cho phân tích xml:

$xml='http://www.example.com/rss.xml';
$rss = simplexml_load_string($xml);
$i = 0;
foreach ($rss->channel->item as $feedItem) {
  $i++;
  echo $title=$feedItem->title;
  echo '<br>';
  echo $link=$feedItem->link;
  echo '<br>';
  if($feedItem->description !='') {
    $des=$feedItem->description;
  } else {
    $des='';
  }
  echo $des;
  echo '<br>';
  if($i>5) break;
}
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.