Tôi đang cố phân tích cú pháp các tệp XML/nội dung DMOZ thành MySQL, nhưng tất cả các tập lệnh hiện có để làm điều này rất cũ và không hoạt động tốt. Làm cách nào tôi có thể mở một tệp XML lớn (+ 1GB) trong PHP để phân tích cú pháp?Phân tích cú pháp các tệp XML lớn trong PHP
Trả lời
Chỉ có hai API php thực sự phù hợp để xử lý các tệp lớn. Đầu tiên là số expat api cũ và số thứ hai là các hàm XMLreader mới hơn. Các apis đọc luồng liên tục thay vì tải toàn bộ cây vào bộ nhớ (đó là những gì simplexml và DOM làm).
Đối với một ví dụ, bạn có thể muốn xem xét phân tích cú pháp phần này của DMOZ-catalogue:
<?php
class SimpleDMOZParser
{
protected $_stack = array();
protected $_file = "";
protected $_parser = null;
protected $_currentId = "";
protected $_current = "";
public function __construct($file)
{
$this->_file = $file;
$this->_parser = xml_parser_create("UTF-8");
xml_set_object($this->_parser, $this);
xml_set_element_handler($this->_parser, "startTag", "endTag");
}
public function startTag($parser, $name, $attribs)
{
array_push($this->_stack, $this->_current);
if ($name == "TOPIC" && count($attribs)) {
$this->_currentId = $attribs["R:ID"];
}
if ($name == "LINK" && strpos($this->_currentId, "Top/Home/Consumer_Information/Electronics/") === 0) {
echo $attribs["R:RESOURCE"] . "\n";
}
$this->_current = $name;
}
public function endTag($parser, $name)
{
$this->_current = array_pop($this->_stack);
}
public function parse()
{
$fh = fopen($this->_file, "r");
if (!$fh) {
die("Epic fail!\n");
}
while (!feof($fh)) {
$data = fread($fh, 4096);
xml_parse($this->_parser, $data, feof($fh));
}
}
}
$parser = new SimpleDMOZParser("content.rdf.u8");
$parser->parse();
chắc chắn Hầu hết các câu trả lời tốt nhất – Evert
Đây là một câu trả lời tuyệt vời, nhưng nó đã cho tôi một thời gian dài để tìm ra rằng bạn cần phải sử dụng [xml_set_default_handler()] (http://php.net/manual/en/function.xml-set-default-handler.php) để truy cập dữ liệu nút XML , với mã ở trên, bạn chỉ có thể thấy tên của các nút và thuộc tính của chúng. – DirtyBirdNJ
tôi sẽ đề nghị sử dụng một SAX dựa phân tích cú pháp chứ không phải là phân tích cú pháp DOM dựa.
Thông tin về việc sử dụng SAX trong PHP: http://www.brainbell.com/tutorials/php/Parsing_XML_With_SAX.htm
Đây không phải là một giải pháp tuyệt vời, nhưng chỉ để ném một lựa chọn khác ngoài kia:
Bạn có thể phá vỡ nhiều file XML lớn lên thành khối, đặc biệt là những đó thực sự chỉ là danh sách các phần tử tương tự (như tôi nghi ngờ tệp bạn đang làm việc với).
ví dụ, nếu doc của bạn trông giống như:
<dmoz>
<listing>....</listing>
<listing>....</listing>
<listing>....</listing>
<listing>....</listing>
<listing>....</listing>
<listing>....</listing>
...
</dmoz>
Bạn có thể đọc nó trong một hoặc hai meg tại một thời điểm, quấn nhân tạo vài hoàn <listing>
thẻ bạn nạp trong một thẻ cấp cơ, và sau đó tải chúng thông qua simplexml/domxml (tôi đã sử dụng domxml, khi thực hiện phương pháp này).
Thành thật mà nói, tôi thích cách tiếp cận này nếu bạn đang sử dụng PHP < 5.1.2. Với 5.1.2 và cao hơn, XMLReader có sẵn, đây có lẽ là lựa chọn tốt nhất, nhưng trước đó, bạn bị mắc kẹt với chiến lược chunking ở trên, hoặc lib SAX/expat cũ. Và tôi không biết về phần còn lại của bạn, nhưng tôi HATE viết/duy trì SAX/phân tích cú pháp người nước ngoài. Tuy nhiên, hãy lưu ý rằng cách tiếp cận này KHÔNG thực sự thực tế khi tài liệu của bạn không bao gồm nhiều phần tử cấp dưới giống hệt nhau (ví dụ: nó hoạt động tốt cho bất kỳ loại danh sách tệp hoặc URL nào, v.v., v.v., v.v. , nhưng sẽ không có ý nghĩa khi phân tích cú pháp tài liệu HTML lớn)
Gần đây tôi đã phân tích một số tài liệu XML khá lớn và cần một phương thức để đọc từng phần tử.
Nếu bạn có các tập tin sau đây complex-test.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<Complex>
<Object>
<Title>Title 1</Title>
<Name>It's name goes here</Name>
<ObjectData>
<Info1></Info1>
<Info2></Info2>
<Info3></Info3>
<Info4></Info4>
</ObjectData>
<Date></Date>
</Object>
<Object></Object>
<Object>
<AnotherObject></AnotherObject>
<Data></Data>
</Object>
<Object></Object>
<Object></Object>
</Complex>
Và muốn trả lại <Object/>
s
PHP:
require_once('class.chunk.php');
$file = new Chunk('complex-test.xml', array('element' => 'Object'));
while ($xml = $file->read()) {
$obj = simplexml_load_string($xml);
// do some parsing, insert to DB whatever
}
###########
Class File
###########
<?php
/**
* Chunk
*
* Reads a large file in as chunks for easier parsing.
*
* The chunks returned are whole <$this->options['element']/>s found within file.
*
* Each call to read() returns the whole element including start and end tags.
*
* Tested with a 1.8MB file, extracted 500 elements in 0.11s
* (with no work done, just extracting the elements)
*
* Usage:
* <code>
* // initialize the object
* $file = new Chunk('chunk-test.xml', array('element' => 'Chunk'));
*
* // loop through the file until all lines are read
* while ($xml = $file->read()) {
* // do whatever you want with the string
* $o = simplexml_load_string($xml);
* }
* </code>
*
* @package default
* @author Dom Hastings
*/
class Chunk {
/**
* options
*
* @var array Contains all major options
* @access public
*/
public $options = array(
'path' => './', // string The path to check for $file in
'element' => '', // string The XML element to return
'chunkSize' => 512 // integer The amount of bytes to retrieve in each chunk
);
/**
* file
*
* @var string The filename being read
* @access public
*/
public $file = '';
/**
* pointer
*
* @var integer The current position the file is being read from
* @access public
*/
public $pointer = 0;
/**
* handle
*
* @var resource The fopen() resource
* @access private
*/
private $handle = null;
/**
* reading
*
* @var boolean Whether the script is currently reading the file
* @access private
*/
private $reading = false;
/**
* readBuffer
*
* @var string Used to make sure start tags aren't missed
* @access private
*/
private $readBuffer = '';
/**
* __construct
*
* Builds the Chunk object
*
* @param string $file The filename to work with
* @param array $options The options with which to parse the file
* @author Dom Hastings
* @access public
*/
public function __construct($file, $options = array()) {
// merge the options together
$this->options = array_merge($this->options, (is_array($options) ? $options : array()));
// check that the path ends with a/
if (substr($this->options['path'], -1) != '/') {
$this->options['path'] .= '/';
}
// normalize the filename
$file = basename($file);
// make sure chunkSize is an int
$this->options['chunkSize'] = intval($this->options['chunkSize']);
// check it's valid
if ($this->options['chunkSize'] < 64) {
$this->options['chunkSize'] = 512;
}
// set the filename
$this->file = realpath($this->options['path'].$file);
// check the file exists
if (!file_exists($this->file)) {
throw new Exception('Cannot load file: '.$this->file);
}
// open the file
$this->handle = fopen($this->file, 'r');
// check the file opened successfully
if (!$this->handle) {
throw new Exception('Error opening file for reading');
}
}
/**
* __destruct
*
* Cleans up
*
* @return void
* @author Dom Hastings
* @access public
*/
public function __destruct() {
// close the file resource
fclose($this->handle);
}
/**
* read
*
* Reads the first available occurence of the XML element $this->options['element']
*
* @return string The XML string from $this->file
* @author Dom Hastings
* @access public
*/
public function read() {
// check we have an element specified
if (!empty($this->options['element'])) {
// trim it
$element = trim($this->options['element']);
} else {
$element = '';
}
// initialize the buffer
$buffer = false;
// if the element is empty
if (empty($element)) {
// let the script know we're reading
$this->reading = true;
// read in the whole doc, cos we don't know what's wanted
while ($this->reading) {
$buffer .= fread($this->handle, $this->options['chunkSize']);
$this->reading = (!feof($this->handle));
}
// return it all
return $buffer;
// we must be looking for a specific element
} else {
// set up the strings to find
$open = '<'.$element.'>';
$close = '</'.$element.'>';
// let the script know we're reading
$this->reading = true;
// reset the global buffer
$this->readBuffer = '';
// this is used to ensure all data is read, and to make sure we don't send the start data again by mistake
$store = false;
// seek to the position we need in the file
fseek($this->handle, $this->pointer);
// start reading
while ($this->reading && !feof($this->handle)) {
// store the chunk in a temporary variable
$tmp = fread($this->handle, $this->options['chunkSize']);
// update the global buffer
$this->readBuffer .= $tmp;
// check for the open string
$checkOpen = strpos($tmp, $open);
// if it wasn't in the new buffer
if (!$checkOpen && !($store)) {
// check the full buffer (in case it was only half in this buffer)
$checkOpen = strpos($this->readBuffer, $open);
// if it was in there
if ($checkOpen) {
// set it to the remainder
$checkOpen = $checkOpen % $this->options['chunkSize'];
}
}
// check for the close string
$checkClose = strpos($tmp, $close);
// if it wasn't in the new buffer
if (!$checkClose && ($store)) {
// check the full buffer (in case it was only half in this buffer)
$checkClose = strpos($this->readBuffer, $close);
// if it was in there
if ($checkClose) {
// set it to the remainder plus the length of the close string itself
$checkClose = ($checkClose + strlen($close)) % $this->options['chunkSize'];
}
// if it was
} elseif ($checkClose) {
// add the length of the close string itself
$checkClose += strlen($close);
}
// if we've found the opening string and we're not already reading another element
if ($checkOpen !== false && !($store)) {
// if we're found the end element too
if ($checkClose !== false) {
// append the string only between the start and end element
$buffer .= substr($tmp, $checkOpen, ($checkClose - $checkOpen));
// update the pointer
$this->pointer += $checkClose;
// let the script know we're done
$this->reading = false;
} else {
// append the data we know to be part of this element
$buffer .= substr($tmp, $checkOpen);
// update the pointer
$this->pointer += $this->options['chunkSize'];
// let the script know we're gonna be storing all the data until we find the close element
$store = true;
}
// if we've found the closing element
} elseif ($checkClose !== false) {
// update the buffer with the data upto and including the close tag
$buffer .= substr($tmp, 0, $checkClose);
// update the pointer
$this->pointer += $checkClose;
// let the script know we're done
$this->reading = false;
// if we've found the closing element, but half in the previous chunk
} elseif ($store) {
// update the buffer
$buffer .= $tmp;
// and the pointer
$this->pointer += $this->options['chunkSize'];
}
}
}
// return the element (or the whole file if we're not looking for elements)
return $buffer;
}
}
Cảm ơn. Điều này thực sự hữu ích. –
Đây là một câu hỏi rất giống với Best way to process large XML in PHP nhưng với một câu trả lời cụ thể rất tốt upvoted giải quyết vấn đề cụ thể của phân tích danh mục DMOZ. Tuy nhiên, vì đây là một Google tốt nhấn cho XMLs lớn nói chung, tôi sẽ repost câu trả lời của tôi từ các câu hỏi khác nữa:
Đưa tôi về nó:
https://github.com/prewk/XmlStreamer
Một lớp đơn giản mà sẽ trích xuất tất cả các phần tử con thành phần tử gốc XML trong khi truyền trực tuyến tệp. Đã thử nghiệm trên tệp XML 108 MB từ pubmed.com.
class SimpleXmlStreamer extends XmlStreamer {
public function processNode($xmlString, $elementName, $nodeIndex) {
$xml = simplexml_load_string($xmlString);
// Do something with your SimpleXML object
return true;
}
}
$streamer = new SimpleXmlStreamer("myLargeXmlFile.xml");
$streamer->parse();
Điều này thật tuyệt! Cảm ơn. một câu hỏi: làm thế nào để có được thuộc tính của nút gốc bằng cách sử dụng này? –
@gyaani_guy Tôi không nghĩ rằng nó hiện đang có thể không may. – oskarth
Thao tác này chỉ tải toàn bộ tệp vào bộ nhớ! –
- 1. Phân tích cú pháp XML PHP
- 2. xml: phân tích cú pháp trong PHP
- 3. Phân tích cú pháp các tệp XML cực lớn trong php
- 4. php chuỗi phân tích cú pháp xml
- 5. C# Phân tích cú pháp tệp XML
- 6. Phân tích cú pháp các tệp giả xml lớn trong python
- 7. JAXB có thể phân tích cú pháp các tệp XML lớn theo các khối
- 8. Trình phân tích cú pháp PHP cho các tệp cachegrind?
- 9. Phân tích cú pháp XML trong Cocoa
- 10. Phân tích cú pháp XML CDATA bằng PHP
- 11. Phân tích cú pháp tệp JSON lớn trong Nodejs
- 12. Phân tích cú pháp các tệp XML rất lớn và marshalling đối tượng Java
- 13. Phân tích cú pháp tệp XML cục bộ trong Android
- 14. Phân tích cú pháp XML trong VBNet
- 15. Cách phân tích cú pháp tệp XML trong RapidXML
- 16. Phân tích cú pháp XML trong iPhone
- 17. Perl, cách phân tích cú pháp tệp XML, xpath
- 18. Trình phân tích cú pháp XML tốt nhất cho PHP
- 19. Phân tích cú pháp XML của Python
- 20. Phân tích cú pháp XML bằng Python
- 21. Phân tích cú pháp khối CDATA XML
- 22. Sử dụng JavaScript để phân tích cú pháp tệp XML
- 23. FileNotFoundException khi đọc tệp .xml để phân tích cú pháp
- 24. xml.parsers.expat.ExpatError khi phân tích cú pháp XML
- 25. Phân tích cú pháp XML Libxmljs (Node.js)
- 26. F # Phân tích cú pháp XML
- 27. Phân tích cú pháp xml bằng powershell
- 28. phân tích cú pháp tệp xml lớn bằng lỗi Python - etree.parse
- 29. Haskell phân tích cú pháp tệp xml lớn với bộ nhớ thấp
- 30. Rails Phân tích cú pháp XML
http://amolnpujari.wordpress.com/2012/03/31/reading_huge_xml-rb/ của nó đơn giản như vậy để đối phó với xml lớn trong ruby –