我正在使用cURL从服务器中提取网页。我将它传递给Tidy并将输出抛出到DOMDocument中。然后麻烦就开始了。
该网页包含大约三千个(yikes)表标签,我正在从中抓取数据。有两种表,其中一种或多种类型B遵循类型A.
我使用microtome(true)
调用了我的脚本。我在脚本的每个阶段之前和之后都进行了调用,并相互减去了时间。所以,如果你跟着我完成我的代码,我会解释它,分享个人资料结果,并指出问题所在。也许你甚至可以帮我解决问题。我们走了:
首先,我包含两个文件。一个处理一些解析,另一个定义两个“数据结构”类。
// Imports
include('./course.php');
include('./utils.php');
据我所知,包含是无关紧要的,所以我们继续进行cURL导入。
// Execute cURL
$response = curl_exec($curl_handle);
我已将cURL配置为不超时,并发布一些标头数据,这是获得有意义的响应所必需的。接下来,我清理数据以准备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;
到目前为止,代码大约花了9秒钟。考虑到这是一个cron工作,不经常运行,我很好。但是,代码的下一部分确实是barfs。这是我从HTML中获取我想要的内容并将其推送到我的自定义类中。 (我计划将其填入MySQL数据库,但这是第一步。)
// 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;
}
}
作为参考,这里是我调用的实用程序函数:
//
// 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;
}
此代码需要63秒。 PHP脚本从网页中提取数据的时间超过一分钟。啧!
我被建议将主要工作循环的工作量分开,但考虑到我的数据的同质性,我不完全确定如何。任何有关改进此代码的建议都非常感谢。
我可以做些什么来改善代码执行时间?
答案 0 :(得分:12)
事实证明我的循环非常低效。
使用foreach
切割时间为一半到约31秒。但那还不够快。所以我对一些样条曲线进行了网络化,并与大约一半的程序员进行了一些头脑风暴,我知道如何在网上戳。这是我们发现的:
使用DOMNodeList的item()
访问器是线性的,在循环中产生指数级缓慢的处理时间。因此,在每次迭代后删除第一个元素会使循环更快。现在,我们总是访问列表的第一个元素。这让我降到了8秒。
在玩了一些之后,我意识到->length
的{{1}}属性与DOMNodeList
一样糟糕,因为它也会产生线性成本。所以我将for循环更改为:
item()
请注意,我在针对我想要的数据方面做了一些其他优化,但相关部分是我如何处理从一个项目到下一个项目的进展。