Tôi đang sử dụng cURL để kéo trang web từ máy chủ. Tôi chuyển nó vào Tidy và ném đầu ra vào DOMDocument. Sau đó, rắc rối bắt đầu.Làm cách nào để cải thiện hiệu suất lặp lại DOMDocument?
Trang web chứa khoảng ba nghìn thẻ bảng (yikes) và tôi đang lấy dữ liệu từ chúng. Có hai loại bảng, trong đó một hoặc nhiều loại B theo một loại A.
Tôi đã lược tả tập lệnh của mình bằng cách sử dụng các cuộc gọi microtome(true)
. Tôi đã đặt các cuộc gọi trước và sau mỗi giai đoạn của kịch bản của tôi và trừ đi thời gian của nhau. Vì vậy, nếu bạn sẽ theo tôi thông qua mã của tôi, tôi sẽ giải thích nó, chia sẻ kết quả hồ sơ, và chỉ ra nơi mà vấn đề là. Có lẽ bạn thậm chí có thể giúp tôi giải quyết vấn đề. Ở đây chúng tôi đi:
Trước tiên, tôi bao gồm hai tệp. Một xử lý một số phân tích cú pháp, và định nghĩa khác hai lớp "cấu trúc dữ liệu".
// Imports
include('./course.php');
include('./utils.php');
Bao gồm không quan trọng theo như tôi biết và vì vậy hãy tiếp tục quá trình nhập cURL.
// Execute cURL
$response = curl_exec($curl_handle);
Tôi đã định cấu hình cURL để không hết thời gian và đăng một số dữ liệu tiêu đề cần thiết để nhận phản hồi có ý nghĩa. Tiếp theo, tôi dọn sạch dữ liệu để chuẩn bị cho DOMDocument.
// Run about 25 str_replace calls here, to clean up
// then run tidy.
$html = $response;
//
// Prepare some config for tidy
//
$config = array(
'indent' => true,
'output-xhtml' => true,
'wrap' => 200);
//
// Tidy up the HTML
//
$tidy = new tidy;
$tidy->parseString($html, $config, 'utf8');
$tidy->cleanRepair();
$html = $tidy;
Cho đến bây giờ, mã đã mất khoảng 9 giây. Xem xét việc này là một công việc cron, chạy thường xuyên, tôi tốt với điều đó. Tuy nhiên, phần tiếp theo của mã thực sự là barfs. Đây là nơi tôi lấy những gì tôi muốn từ HTML và đưa nó vào các lớp tùy chỉnh của tôi. (. Tôi có kế hoạch cụ này vào một cơ sở dữ liệu MySQL cũng vậy, nhưng đây là một bước đầu tiên)
// Get all of the tables in the page
$tables = $dom->getElementsByTagName('table');
// Create a buffer for the courses
$courses = array();
// Iterate
$numberOfTables = $tables->length;
for ($i=1; $i <$numberOfTables ; $i++) {
$sectionTable = $tables->item($i);
$courseTable = $tables->item($i-1);
// We've found a course table, parse it.
if (elementIsACourseSectionTable($sectionTable)) {
$course = courseFromTable($courseTable);
$course = addSectionsToCourseUsingTable($course, $sectionTable);
$courses[] = $course;
}
}
Để tham khảo, đây là chức năng tiện ích mà tôi gọi là:
//
// Tell us if a given element is
// a course section table.
//
function elementIsACourseSectionTable(DOMElement $element){
$tableHasClass = $element->hasAttribute('class');
$tableIsCourseTable = $element->getAttribute("class") == "coursetable";
return $tableHasClass && $tableIsCourseTable;
}
//
// Takes a table and parses it into an
// instance of the Course class.
//
function courseFromTable(DOMElement $table){
$secondRow = $table->getElementsByTagName('tr')->item(1);
$cells = $secondRow->getElementsByTagName('td');
$course = new Course;
$course->startDate = valueForElementInList(0, $cells);
$course->endDate = valueForElementInList(1, $cells);
$course->name = valueForElementInList(2, $cells);
$course->description = valueForElementInList(3, $cells);
$course->credits = valueForElementInList(4, $cells);
$course->hours = valueForElementInList(5, $cells);
$course->division = valueForElementInList(6, $cells);
$course->subject = valueForElementInList(7, $cells);
return $course;
}
//
// Takes a table and parses it into an
// instance of the Section class.
//
function sectionFromRow(DOMElement $row){
$cells = $row->getElementsByTagName('td');
//
// Skip any row with a single cell
//
if ($cells->length == 1) {
# code...
return NULL;
}
//
// Skip header rows
//
if (valueForElementInList(0, $cells) == "Section" || valueForElementInList(0, $cells) == "") {
return NULL;
}
$section = new Section;
$section->section = valueForElementInList(0, $cells);
$section->code = valueForElementInList(1, $cells);
$section->openSeats = valueForElementInList(2, $cells);
$section->dayAndTime = valueForElementInList(3, $cells);
$section->instructor = valueForElementInList(4, $cells);
$section->buildingAndRoom = valueForElementInList(5, $cells);
$section->isOnline = valueForElementInList(6, $cells);
return $section;
}
//
// Take a table containing course sections
// and parse it put the results into a
// give course object.
//
function addSectionsToCourseUsingTable(Course $course, DOMElement $table){
$rows = $table->getElementsByTagName('tr');
$numRows = $rows->length;
for ($i=0; $i < $numRows; $i++) {
$section = sectionFromRow($rows->item($i));
// Make sure we have an array to put sections into
if (is_null($course->sections)) {
$course->sections = array();
}
// Skip "meta" rows, since they're not really sections
if (is_null($section)) {
continue;
}
$course->addSection($section);
}
return $course;
}
//
// Returns the text from a cell
// with a
//
function valueForElementInList($index, $list){
$value = $list->item($index)->nodeValue;
$value = trim($value);
return $value;
}
Mã này mất 63 giây . Đó là hơn một phút cho một kịch bản PHP để kéo dữ liệu từ một trang web. Sheesh!
Tôi đã được khuyên nên phân chia khối lượng công việc của vòng lặp công việc chính của mình, nhưng xem xét tính chất đồng nhất của dữ liệu của tôi, tôi không hoàn toàn chắc chắn như thế nào. Bất kỳ đề xuất nào về việc cải thiện mã này đều được đánh giá cao.
Tôi có thể làm gì để cải thiện thời gian thực thi mã của mình?
Có thể sử dụng 'foreach ($ tables as $ table) nhanh hơn vì bạn đang gọi' $ tables-> item ($ i) 'trong vòng lặp đó. Tôi không chắc chắn, nhưng nó * có thể * đang làm một traversal tuyến tính của một danh sách liên kết để tìm chỉ mục mỗi lần. 'foreach' chắc chắn chỉ liệt kê danh sách theo thứ tự. –
Vấn đề rất có thể là @mootinator nói ... các ghi chú trên trang này có một số thông tin liên quan đến nó http://php.net/manual/en/domnodelist.item.php – Eliezer
Vì vậy, 'foreach' thực sự cải thiện việc xử lý thời gian, nhưng sử dụng một vòng lặp while và loại bỏ các lệnh '-> item()' thậm chí còn nhanh hơn. Xem câu trả lời của tôi. – Moshe