2012-12-18 33 views
11

मैं सर्वर से वेबपृष्ठ खींचने के लिए curl का उपयोग कर रहा हूं। मैं इसे टिडी में पास करता हूं और आउटपुट को एक DOMDocument में फेंक देता हूं। फिर मुसीबत शुरू होती है।एक DOMDocument पुनरावृत्ति प्रदर्शन को बेहतर बनाने के लिए कैसे?

वेबपृष्ठ में लगभग तीन हजार (याइक) टेबल टैग हैं, और मैं उनसे डेटा स्क्रैप कर रहा हूं। दो प्रकार की टेबल हैं, जहां एक या अधिक प्रकार बी एक प्रकार का पालन करता है ए

मैंने microtome(true) कॉल का उपयोग करके अपनी स्क्रिप्ट प्रोफाइल की है। मैंने अपनी स्क्रिप्ट के प्रत्येक चरण से पहले और बाद में कॉल रखी है और एक दूसरे से समय घटा दिया है। इसलिए, यदि आप मेरे कोड के माध्यम से मेरे पीछे आ जाएंगे, तो मैं इसे समझाऊंगा, प्रोफाइल परिणाम साझा करूंगा, और बताऊंगा कि समस्या कहां है। शायद आप समस्या को हल करने में भी मेरी मदद कर सकते हैं। यहां हम जाते हैं:

सबसे पहले, मुझे दो फाइलें शामिल हैं। एक कुछ पार्सिंग को संभालता है, और दूसरा दो "डेटा संरचना" कक्षाओं को परिभाषित करता है।

// Imports 
include('./course.php'); 
include('./utils.php'); 

जहां तक ​​मुझे पता है, अपरिहार्य है, और इसलिए चलो कर्ल आयात पर आगे बढ़ें।

// 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; 

अब तक, कोड में लगभग नौ सेकंड लग गए हैं। यह एक क्रॉन नौकरी होने के लिए, अक्सर चल रहा है, मैं इसके साथ ठीक हूँ। हालांकि, कोड का अगला भाग वास्तव में barfs। यहां वह जगह है जहां मैं HTML से जो चाहता हूं उसे लेता हूं और इसे अपने कस्टम वर्गों में फेंक देता हूं।

// 
// 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 सेकंड लेता है: (। मैं एक 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; 
    } 
} 

संदर्भ के लिए, उपयोगिता कार्यों है कि मैं फोन है । एक वेब स्क्रिप्ट से डेटा खींचने के लिए एक PHP स्क्रिप्ट के लिए यह एक मिनट से अधिक है। शीश!

मुझे सलाह दी गई है कि मेरे मुख्य कार्य पाश के वर्कलोड को विभाजित किया जाए, लेकिन मेरे डेटा की समरूप प्रकृति पर विचार करने के लिए, मुझे पूरा यकीन नहीं है कि कैसे। इस कोड को बेहतर बनाने के लिए किसी भी सुझाव की सराहना की जाती है।

मैं अपना कोड निष्पादन समय सुधारने के लिए क्या कर सकता हूं?

+2

'foreach ($ table के रूप में $ टेबल) का उपयोग करना तेज़ हो सकता है क्योंकि आप उस लूप में' $ table-> आइटम ($ i) 'को कॉल कर रहे हैं। मुझे यकीन नहीं है, लेकिन यह हर बार इंडेक्स को खोजने के लिए एक लिंक्ड सूची का रैखिक ट्रैवर्सल कर सकता है। 'foreach' निश्चित रूप से क्रम में सूची की गणना करना चाहिए। –

+2

समस्या यह है कि @mootinator कहता है ... इस पृष्ठ के नोट्स के बारे में कुछ जानकारी है http://php.net/manual/en/domnodelist.item.php – Eliezer

+0

तो 'foreach' वास्तव में प्रसंस्करण में सुधार करता है बार, लेकिन थोड़ी देर लूप का उपयोग करके और '-> आइटम()' कॉल को समाप्त करना भी तेज़ है। मेरा जवाब देखें – Moshe

उत्तर

10

यह पता चला है कि मेरा पाश बहुत अक्षम है।

foreach का उपयोग आधे से लगभग 31 सेकंड में कटौती का समय। लेकिन वह पर्याप्त तेज़ नहीं था। इसलिए मैंने कुछ स्प्लिंस को दोहराया और कुछ आधा प्रोग्रामर के साथ कुछ दिमाग में काम किया जो मुझे पता है कि ऑनलाइन कैसे पोक करना है। यहां हमने पाया है:

DOMNodeList के item() एक्सेसर का उपयोग रैखिक है, जो लूप में तेजी से धीमी प्रसंस्करण के समय का उत्पादन करता है। तो, प्रत्येक पुनरावृत्ति के बाद पहला तत्व हटाने से लूप तेजी से हो जाता है। अब, हम हमेशा सूची के पहले तत्व तक पहुंचते हैं। यह मुझे 8 सेकंड तक लाया।

कुछ और खेलने के बाद, मुझे एहसास हुआ कि ->lengthDOMNodeList की संपत्ति item() जितनी खराब है, क्योंकि यह रैखिक लागत भी लेती है।तो मैं इस के लिए पाश के लिए मेरे बदल दिया है:

$table = $tables->item(0); 

while ($table != NULL) { 

    $table = $tables->item(0); 

    if ($table === NULL) { 
     break; 
    } 

    // 
    // We've found a section table, parse it. 
    // 

    if (elementIsACourseSectionTable($table)) { 

     $course = addSectionsToCourseUsingTable($course, $table);   
    } 

    // 
    // Skip the last table if it's not a course section 
    // 

    else if(elementIsCourseHeaderTable($table)){ 
     $course = courseFromTable($table); 
     $courses[] = $course; 
    } 

    // 
    // Remove the first item from the list 
    // 

    $first = $tables->item(0); 
    $first->parentNode->removeChild($first); 

    // 
    // Get the next table to parse 
    // 

    $table = $tables->item(0); 
} 

ध्यान दें कि मैं डेटा मैं चाहता हूँ को निशाना बनाने के मामले में कुछ अन्य अनुकूलन किया है, लेकिन प्रासंगिक हिस्सा है कि कैसे मैं एक आइटम से अगले करने के लिए प्रगति संभाल।

+1

इस समाधान ने मेरी स्क्रिप्ट के रन टाइम को लगभग 1 दिन से लगभग 25 मिनट तक घटा दिया! –