Exemplo n.º 1
0
 public static function parse($html, $url)
 {
     $recipe = RecipeParser_Parser_MicrodataDataVocabulary::parse($html, $url);
     libxml_use_internal_errors(true);
     $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
     $doc = new DOMDocument();
     $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
     $xpath = new DOMXPath($doc);
     //
     // Some of the ingredient lines in on The Daily Meal do not adhere to
     // the usual microdata formatting.  Here we fall back to looking for a
     // regular list within a higher-level ingredients div.
     //
     if (!empty($recipe->ingredients)) {
         $nodes = $xpath->query("//div[@class='content']/div[@class='ingredient']/ul/li");
         foreach ($nodes as $node) {
             $value = RecipeParser_Text::formatAsOneLine($node->nodeValue);
             if (empty($value)) {
                 continue;
             }
             if (RecipeParser_Text::matchSectionName($value)) {
                 $value = RecipeParser_Text::formatSectionName($value);
                 $recipe->addIngredientsSection($value);
             } else {
                 $recipe->appendIngredient($value);
             }
         }
     }
     //
     // The Daily Meal provides servings details via Edamam's plugin.
     //
     if (!$recipe->yield) {
         $nodes = $xpath->query("//table[@class='edamam-data']/tr[2]/td[2]");
         if ($nodes->length) {
             $recipe->yield = RecipeParser_Text::formatYield($nodes->item(0)->nodeValue);
         }
     }
     return $recipe;
 }
Exemplo n.º 2
0
 public static function parse($html, $url)
 {
     $recipe = RecipeParser_Parser_MicrodataSchema::parse($html, $url);
     libxml_use_internal_errors(true);
     $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
     $doc = new DOMDocument();
     $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
     $xpath = new DOMXPath($doc);
     // Yield
     $nodes = $xpath->query('//*[@class="prep_box"]');
     foreach ($nodes as $node) {
         $line = $node->nodeValue;
         if (preg_match("/Number of Servings: (\\d+)/", $line, $m)) {
             $recipe->yield = RecipeParser_Text::formatYield($m[1]);
         }
     }
     // Instructions
     $recipe->resetInstructions();
     $str = "";
     $nodes = $xpath->query('//*[@itemprop="recipeInstructions"]');
     if ($nodes->length) {
         $children = $nodes->item(0)->childNodes;
         // This is a piece of HTML that has <br> tags for breaks in each instruction.
         // Rather than just getting nodeValue, I want to preserve the <br> tags. So I'm
         // looking for them as nodes and appending them to the string. Any other nodes
         // (either #text or other, e.g. <a href="">) get passed along into the string as
         // nodeValue.
         foreach ($children as $child) {
             if ($child->nodeName == "br") {
                 $str .= "<br>";
             } else {
                 $line = trim($child->nodeValue);
                 if (!empty($line)) {
                     $str .= $line;
                 }
             }
         }
         $lines = explode("<br>", $str);
         foreach ($lines as $line) {
             if (empty($line)) {
                 continue;
             } else {
                 if (RecipeParser_Text::matchSectionName($line)) {
                     $line = RecipeParser_Text::formatSectionName($line);
                     $recipe->addInstructionsSection($line);
                 } else {
                     if (!empty($line)) {
                         $line = RecipeParser_Text::formatAsOneLine($line);
                         $line = RecipeParser_Text::stripLeadingNumbers($line);
                         if (stripos($line, "Recipe submitted by SparkPeople") === 0) {
                             continue;
                         }
                         if (stripos($line, "Number of Servings:") === 0) {
                             continue;
                         }
                         $recipe->appendInstruction($line);
                     }
                 }
             }
         }
     }
     return $recipe;
 }
 public static function parse($html, $url)
 {
     $recipe = new RecipeParser_Recipe();
     libxml_use_internal_errors(true);
     $doc = new DOMDocument();
     $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
     $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
     $xpath = new DOMXPath($doc);
     // Title
     $nodes = $xpath->query('//*[@property="v:name"]');
     if ($nodes->length) {
         $recipe->title = trim($nodes->item(0)->nodeValue);
     }
     // Summary
     $nodes = $xpath->query('//*[@property="v:summary"]');
     if ($nodes->length) {
         $value = trim($nodes->item(0)->nodeValue);
         $recipe->description = $value;
     }
     // Times
     $searches = array('v:prepTime' => 'prep', 'v:cookTime' => 'cook', 'v:totalTime' => 'total');
     foreach ($searches as $itemprop => $time_key) {
         $nodes = $xpath->query('//*[@property="' . $itemprop . '"]');
         if ($nodes->length) {
             if ($value = $nodes->item(0)->getAttribute('content')) {
                 $value = RecipeParser_Text::iso8601ToMinutes($value);
             } else {
                 $value = trim($nodes->item(0)->nodeValue);
                 $value = RecipeParser_Times::toMinutes($value);
             }
             if ($value) {
                 $recipe->time[$time_key] = $value;
             }
         }
     }
     // Yield
     $nodes = $xpath->query('//*[@property="v:yield"]');
     if ($nodes->length) {
         $line = trim($nodes->item(0)->nodeValue);
         $line = preg_replace('/\\s+/', ' ', $line);
         $recipe->yield = RecipeParser_Text::formatYield($line);
     }
     // Ingredients
     $nodes = null;
     // (data-vocabulary)
     $nodes = $xpath->query('//*[@rel="v:ingredient"]');
     foreach ($nodes as $node) {
         $value = $node->nodeValue;
         $value = RecipeParser_Text::formatAsOneLine($value);
         if (empty($value)) {
             continue;
         }
         if (RecipeParser_Text::matchSectionName($value)) {
             $value = RecipeParser_Text::formatSectionName($value);
             $recipe->addIngredientsSection($value);
         } else {
             $recipe->appendIngredient($value);
         }
     }
     // Instructions
     $found = false;
     // Some sites will use an "instruction" class for each line.
     if (!$found) {
         $nodes = $xpath->query('//*[@property="v:instructions"]//*[@property="v:instruction"]');
         if ($nodes->length) {
             RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
             $found = true;
         }
     }
     // Look for markup that uses <li>, <p> or other tags for each instruction.
     $search_sub_nodes = array("p", "li");
     while (!$found && ($tag = array_pop($search_sub_nodes))) {
         $nodes = $xpath->query('//*[@property="v:instructions"]//' . $tag);
         if ($nodes->length) {
             RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
             $found = true;
         }
     }
     // Either multiple instrutions nodes, or one node with a blob of text.
     if (!$found) {
         $nodes = $xpath->query('//*[@property="v:instructions"]');
         if ($nodes->length > 1) {
             // Multiple nodes
             RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
             $found = true;
         } else {
             if ($nodes->length == 1) {
                 // Blob
                 $str = $nodes->item(0)->nodeValue;
                 RecipeParser_Text::parseInstructionsFromBlob($str, $recipe);
                 $found = true;
             }
         }
     }
     // Photo
     $photo_url = "";
     $nodes = $xpath->query('//*[@rel="v:photo"]');
     if ($nodes->length) {
         $photo_url = $nodes->item(0)->getAttribute('src');
     }
     if (!$photo_url) {
         // for <img> as sub-node of rel="v:photo"
         $nodes = $xpath->query('//*[@rel="v:photo"]//img');
         if ($nodes->length) {
             $photo_url = $nodes->item(0)->getAttribute('src');
         }
     }
     if ($photo_url) {
         $recipe->photo_url = RecipeParser_Text::formatPhotoUrl($photo_url, $url);
     }
     // Credits
     $nodes = $xpath->query('//*[@property="v:author"]');
     if ($nodes->length) {
         $line = $nodes->item(0)->nodeValue;
         $recipe->credits = RecipeParser_Text::formatCredits($line);
     }
     return $recipe;
 }
Exemplo n.º 4
0
 public static function parse($html, $url)
 {
     $recipe = RecipeParser_Parser_Microformat::parse($html, $url);
     // Turn off libxml errors to prevent mismatched tag warnings.
     libxml_use_internal_errors(true);
     $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
     $doc = new DOMDocument();
     $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
     $xpath = new DOMXPath($doc);
     // Description
     $description = "";
     $nodes = $xpath->query('//div[@id="recipe"]/p/i');
     foreach ($nodes as $node) {
         $line = trim($node->nodeValue);
         if (strpos($line, "Adapted from") === false) {
             $description .= $line . "\n\n";
         }
     }
     $description = trim($description);
     $recipe->description = $description;
     // Ingredients
     $recipe->resetIngredients();
     $lines = array();
     // Add ingredients to blob
     $nodes = $xpath->query('//div[@id="recipe"]/blockquote/p');
     foreach ($nodes as $node) {
         foreach ($node->childNodes as $child) {
             $line = trim($child->nodeValue);
             switch ($child->nodeName) {
                 case "strong":
                 case "b":
                     if (strpos($line, ":") === false) {
                         $line .= ":";
                     }
                     $lines[] = $line;
                     break;
                 case "#text":
                 case "div":
                 case "p":
                     $lines[] = $line;
                     break;
             }
         }
     }
     foreach ($lines as $line) {
         if (RecipeParser_Text::matchSectionName($line)) {
             $recipe->addIngredientsSection(RecipeParser_Text::formatSectionName($line));
         } else {
             $line = RecipeParser_Text::formatAsOneLine($line);
             $recipe->appendIngredient($line);
         }
     }
     // Instructions
     $recipe->resetInstructions();
     $lines = array();
     $nodes = $xpath->query('//div[@id="recipe"]/*');
     $passed_ingredients = false;
     foreach ($nodes as $node) {
         if ($node->nodeName == "blockquote") {
             $passed_ingredients = true;
             continue;
         }
         if ($node->nodeName == "p") {
             if ($passed_ingredients) {
                 $line = trim($node->nodeValue);
                 // Finished with ingredients once we hit "Adapted" notes or any <p>
                 // with a class attribute.
                 if (stripos($line, "Adapted from") !== false) {
                     break;
                 } else {
                     if ($node->getAttribute("class")) {
                         break;
                     }
                 }
                 // Servings?
                 if (stripos($line, "Serves ") === 0) {
                     $recipe->yield = RecipeParser_Text::formatYield($line);
                     continue;
                 }
                 $recipe->appendInstruction(RecipeParser_Text::formatAsOneLine($node->nodeValue));
             }
         }
     }
     return $recipe;
 }
 public function test_match_section_name_wrapped_equals()
 {
     $this->assertTrue(RecipeParser_Text::matchSectionName("==That=="));
 }
Exemplo n.º 6
0
 public static function parse($html, $url)
 {
     $recipe = new RecipeParser_Recipe();
     libxml_use_internal_errors(true);
     $doc = new DOMDocument();
     $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
     $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
     $xpath = new DOMXPath($doc);
     $microdata = null;
     $nodes = $xpath->query('//*[contains(@itemtype, "//schema.org/Recipe") or contains(@itemtype, "//schema.org/recipe")]');
     if ($nodes->length) {
         $microdata = $nodes->item(0);
     }
     // Parse elements
     if ($microdata) {
         // Title
         $nodes = $xpath->query('.//*[@itemprop="name"]', $microdata);
         if ($nodes->length) {
             $value = trim($nodes->item(0)->nodeValue);
             $recipe->title = RecipeParser_Text::formatTitle($value);
         }
         // Summary
         $nodes = $xpath->query('.//*[@itemprop="description"]', $microdata);
         if ($nodes->length) {
             $value = $nodes->item(0)->nodeValue;
             $value = RecipeParser_Text::formatAsParagraphs($value);
             $recipe->description = $value;
         }
         // Times
         $searches = array('prepTime' => 'prep', 'cookTime' => 'cook', 'totalTime' => 'total');
         foreach ($searches as $itemprop => $time_key) {
             $nodes = $xpath->query('.//*[@itemprop="' . $itemprop . '"]', $microdata);
             if ($nodes->length) {
                 if ($value = $nodes->item(0)->getAttribute('content')) {
                     $value = RecipeParser_Text::iso8601ToMinutes($value);
                 } else {
                     if ($value = $nodes->item(0)->getAttribute('datetime')) {
                         $value = RecipeParser_Text::iso8601ToMinutes($value);
                     } else {
                         $value = trim($nodes->item(0)->nodeValue);
                         $value = RecipeParser_Times::toMinutes($value);
                     }
                 }
                 if ($value) {
                     $recipe->time[$time_key] = $value;
                 }
             }
         }
         // Yield
         $nodes = $xpath->query('.//*[@itemprop="recipeYield"]', $microdata);
         if (!$nodes->length) {
             $nodes = $xpath->query('.//*[@itemprop="recipeyield"]', $microdata);
         }
         if ($nodes->length) {
             if ($nodes->item(0)->hasAttribute('content')) {
                 $line = $nodes->item(0)->getAttribute('content');
             } else {
                 $line = $nodes->item(0)->nodeValue;
             }
             $recipe->yield = RecipeParser_Text::formatYield($line);
         }
         // Ingredients
         $nodes = $xpath->query('//*[@itemprop="ingredients"]');
         foreach ($nodes as $node) {
             $value = $node->nodeValue;
             $value = RecipeParser_Text::formatAsOneLine($value);
             if (empty($value)) {
                 continue;
             }
             if (strlen($value) > 150) {
                 // probably a mistake, like a run-on of existing ingredients?
                 continue;
             }
             if (RecipeParser_Text::matchSectionName($value)) {
                 $value = RecipeParser_Text::formatSectionName($value);
                 $recipe->addIngredientsSection($value);
             } else {
                 $recipe->appendIngredient($value);
             }
         }
         // Instructions
         $found = false;
         // Look for markup that uses <li> tags for each instruction.
         if (!$found) {
             $nodes = $xpath->query('//*[@itemprop="recipeInstructions"]//li');
             if ($nodes->length) {
                 RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
                 $found = true;
             }
         }
         // Look for instructions as direct descendents of "recipeInstructions".
         if (!$found) {
             $nodes = $xpath->query('//*[@itemprop="recipeInstructions"]/*');
             if ($nodes->length) {
                 RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
                 $found = true;
             }
         }
         // Some sites will use an "instruction" class for each line.
         if (!$found) {
             $nodes = $xpath->query('.//*[@itemprop="recipeInstructions"]//*[contains(concat(" ", normalize-space(@class), " "), " instruction ")]');
             if ($nodes->length) {
                 RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
                 $found = true;
             }
         }
         // Either multiple recipeInstructions nodes, or one node with a blob of text.
         if (!$found) {
             $nodes = $xpath->query('.//*[@itemprop="recipeInstructions"]');
             if ($nodes->length > 1) {
                 // Multiple nodes
                 RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
                 $found = true;
             } else {
                 if ($nodes->length == 1) {
                     // Blob
                     $str = $nodes->item(0)->nodeValue;
                     RecipeParser_Text::parseInstructionsFromBlob($str, $recipe);
                     $found = true;
                 }
             }
         }
         // Photo
         $photo_url = "";
         if (!$photo_url) {
             // try to find open graph url
             $nodes = $xpath->query('//meta[@property="og:image"]');
             if ($nodes->length) {
                 $photo_url = $nodes->item(0)->getAttribute('content');
             }
         }
         if (!$photo_url) {
             $nodes = $xpath->query('.//*[@itemprop="image"]', $microdata);
             if ($nodes->length) {
                 $photo_url = $nodes->item(0)->getAttribute('src');
             }
         }
         if (!$photo_url) {
             // for <img> as sub-node of class="photo"
             $nodes = $xpath->query('.//*[@itemprop="image"]//img', $microdata);
             if ($nodes->length) {
                 $photo_url = $nodes->item(0)->getAttribute('src');
             }
         }
         if ($photo_url) {
             $recipe->photo_url = RecipeParser_Text::formatPhotoUrl($photo_url, $url);
         }
         // Credits
         $line = "";
         $nodes = $xpath->query('.//*[@itemprop="author"]', $microdata);
         if ($nodes->length) {
             $line = $nodes->item(0)->nodeValue;
         }
         $nodes = $xpath->query('.//*[@itemprop="publisher"]', $microdata);
         if ($nodes->length) {
             $line = $nodes->item(0)->nodeValue;
         }
         $recipe->credits = RecipeParser_Text::formatCredits($line);
     }
     return $recipe;
 }
 public static function parse($html, $url)
 {
     $recipe = new RecipeParser_Recipe();
     libxml_use_internal_errors(true);
     $doc = new DOMDocument();
     $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
     $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
     $xpath = new DOMXPath($doc);
     // Find the top-level node for Recipe microdata
     $microdata = null;
     $nodes = $xpath->query('//*[@itemtype="http://data-vocabulary.org/Recipe"]');
     if ($nodes->length) {
         $microdata = $nodes->item(0);
     }
     // Parse elements
     if ($microdata) {
         // Title
         $nodes = $xpath->query('.//*[@itemprop="name"]', $microdata);
         if ($nodes->length) {
             $value = $nodes->item(0)->nodeValue;
             $value = RecipeParser_Text::formatTitle($value);
             $recipe->title = $value;
         }
         // Summary
         $nodes = $xpath->query('.//*[@itemprop="summary"]', $microdata);
         if ($nodes->length) {
             $value = trim($nodes->item(0)->nodeValue);
             $recipe->description = $value;
         }
         // Times
         $searches = array('prepTime' => 'prep', 'cookTime' => 'cook', 'totalTime' => 'total');
         foreach ($searches as $itemprop => $time_key) {
             $nodes = $xpath->query('.//*[@itemprop="' . $itemprop . '"]', $microdata);
             if ($nodes->length) {
                 if ($value = $nodes->item(0)->getAttribute('datetime')) {
                     $value = RecipeParser_Text::iso8601ToMinutes($value);
                 } else {
                     if ($value = $nodes->item(0)->getAttribute('content')) {
                         $value = RecipeParser_Text::iso8601ToMinutes($value);
                     } else {
                         $value = trim($nodes->item(0)->nodeValue);
                         $value = RecipeParser_Times::toMinutes($value);
                     }
                 }
                 if ($value) {
                     $recipe->time[$time_key] = $value;
                 }
             }
         }
         // Yield
         $line = "";
         $nodes = $xpath->query('.//*[@itemprop="yield"]', $microdata);
         if ($nodes->length) {
             $line = trim($nodes->item(0)->nodeValue);
         } else {
             $nodes = $xpath->query('.//*[@itemprop="servingSize"]', $microdata);
             if ($nodes->length) {
                 $line = trim($nodes->item(0)->nodeValue);
             }
         }
         if ($line) {
             $line = preg_replace('/\\s+/', ' ', $line);
             $recipe->yield = RecipeParser_Text::formatYield($line);
         }
         // Ingredients
         $nodes = null;
         // (data-vocabulary)
         if (!$nodes || !$nodes->length) {
             $nodes = $xpath->query('.//*[@itemprop="ingredient"]', $microdata);
         }
         if (!$nodes || !$nodes->length) {
             // non-standard
             $nodes = $xpath->query('.//*[@id="ingredients"]//li', $microdata);
         }
         if (!$nodes || !$nodes->length) {
             // non-standard
             $nodes = $xpath->query('.//*[@class="ingredients"]//li', $microdata);
         }
         foreach ($nodes as $node) {
             $value = $node->nodeValue;
             $value = RecipeParser_Text::formatAsOneLine($value);
             if (empty($value)) {
                 continue;
             }
             if (RecipeParser_Text::matchSectionName($value)) {
                 $value = RecipeParser_Text::formatSectionName($value);
                 $recipe->addIngredientsSection($value);
             } else {
                 $recipe->appendIngredient($value);
             }
         }
         // Instructions
         $found = false;
         // Look for markup that uses <li> tags for each instruction.
         if (!$found) {
             $nodes = $xpath->query('.//*[@itemprop="instructions"]//li', $microdata);
             if ($nodes->length) {
                 RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
                 $found = true;
             }
         }
         // Some sites will use an "instruction" class for each line.
         if (!$found) {
             $nodes = $xpath->query('.//*[@itemprop="instruction"]//*[contains(concat(" ", normalize-space(@class), " "), " instruction ")]', $microdata);
             if ($nodes->length) {
                 RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
                 $found = true;
             }
         }
         // Either multiple instrutions nodes, or one node with a blob of text.
         if (!$found) {
             $nodes = $xpath->query('.//*[@itemprop="instructions"]', $microdata);
             if ($nodes->length > 1) {
                 // Multiple nodes
                 RecipeParser_Text::parseInstructionsFromNodes($nodes, $recipe);
                 $found = true;
             } else {
                 if ($nodes->length == 1) {
                     // Blob
                     $str = $nodes->item(0)->nodeValue;
                     RecipeParser_Text::parseInstructionsFromBlob($str, $recipe);
                     $found = true;
                 }
             }
         }
         // Photo
         $photo_url = "";
         if (!$photo_url) {
             // try to find open graph url
             $nodes = $xpath->query('//meta[@property="og:image"]');
             if ($nodes->length) {
                 $photo_url = $nodes->item(0)->getAttribute('content');
             }
         }
         if (!$photo_url) {
             $nodes = $xpath->query('.//*[@itemprop="photo"]', $microdata);
             if ($nodes->length) {
                 if ($nodes->item(0)->hasAttribute('src')) {
                     $photo_url = $nodes->item(0)->getAttribute('src');
                 } else {
                     if ($nodes->item(0)->hasAttribute('content')) {
                         $photo_url = $nodes->item(0)->getAttribute('content');
                     }
                 }
             }
         }
         if (!$photo_url) {
             // for <img> as sub-node of class="photo"
             $nodes = $xpath->query('.//*[@itemprop="photo"]//img', $microdata);
             if ($nodes->length) {
                 $photo_url = $nodes->item(0)->getAttribute('src');
             }
         }
         if ($photo_url) {
             $recipe->photo_url = RecipeParser_Text::relativeToAbsolute($photo_url, $url);
         }
         // Credits
         $nodes = $xpath->query('.//*[@itemprop="author"]', $microdata);
         if ($nodes->length) {
             $line = $nodes->item(0)->nodeValue;
             $recipe->credits = RecipeParser_Text::formatCredits($line);
         }
     }
     return $recipe;
 }
Exemplo n.º 8
0
 public static function parse($html, $url)
 {
     // Turn off libxml errors to prevent mismatched tag warnings.
     libxml_use_internal_errors(true);
     $html = mb_convert_encoding($html, 'HTML-ENTITIES', "UTF-8");
     $doc = new DOMDocument();
     $doc->loadHTML('<?xml encoding="UTF-8">' . $html);
     $xpath = new DOMXPath($doc);
     $recipe = RecipeParser_Parser_MicrodataSchema::parse($html, $url);
     // OVERRIDES FOR ABOUT.COM
     // Title
     $nodes = $xpath->query('//*[@itemprop="headline name"]');
     if ($nodes->length) {
         $value = trim($nodes->item(0)->nodeValue);
         $recipe->title = RecipeParser_Text::formatTitle($value);
     }
     // Credits
     $nodes = $xpath->query('//*[@itemprop="author"]//*[@itemprop="name"]');
     if ($nodes->length) {
         $line = $nodes->item(0)->nodeValue;
         $recipe->credits = RecipeParser_Text::formatCredits($line . ", About.com");
     }
     // Ingredients
     $recipe->resetIngredients();
     $nodes = $xpath->query('//*[@itemprop="ingredients"]');
     foreach ($nodes as $node) {
         $value = $node->nodeValue;
         $value = RecipeParser_Text::formatAsOneLine($value);
         if (RecipeParser_Text::matchSectionName($value) || $node->childNodes->item(0)->nodeName == "strong" || $node->childNodes->item(0)->nodeName == "b") {
             $value = RecipeParser_Text::formatSectionName($value);
             $recipe->addIngredientsSection($value);
         } else {
             $recipe->appendIngredient($value);
         }
     }
     // Instructions
     $recipe->resetInstructions();
     $nodes = $xpath->query('//div[@itemprop="recipeInstructions"]');
     foreach ($nodes as $node) {
         $text = trim($node->nodeValue);
         $lines = preg_split("/[\n\r]+/", $text);
         for ($i = count($lines) - 1; $i >= 0; $i--) {
             $lines[$i] = trim($lines[$i]);
             // Remove ends of lines that have the word "recipes" squashed up against
             // another word, which seems to happen with long lists of related
             // recipe links.
             // Remove lines that have the phrase "Xxxxx Recipes and More".
             // Remove lines that have the phrase "Xxxxx Recipes | Xxxxx".
             // Remove mentions of newsletters.
             $lines[$i] = preg_replace("/(.*)recipes\\w/i", "\$1", $lines[$i]);
             $lines[$i] = preg_replace("/(.*)More .* Recipes.*/", "\$1", $lines[$i]);
             $lines[$i] = preg_replace("/(.*)Recipes and More.*/", "\$1", $lines[$i]);
             $lines[$i] = preg_replace("/(.*)Recipes \\| .*/", "\$1", $lines[$i]);
             $lines[$i] = preg_replace("/(.*)Recipe Newsletter.*/", "\$1", $lines[$i]);
             // Look for a line in the instructions that looks like a yield.
             if (strpos($lines[$i], "Makes ") === 0) {
                 $recipe->yield = substr($lines[$i], 6);
                 $lines[$i] = '';
                 continue;
             }
         }
         foreach ($lines as $line) {
             $line = trim($line);
             if (empty($line)) {
                 continue;
             }
             if (strtolower($line) == "preparation") {
                 continue;
             }
             // Match section names that read something like "---For the cake: Raise the oven temperature..."
             if (preg_match("/^(?:-{2,})?For the (.+)\\: (.*)\$/i", $line, $m)) {
                 $section = $m[1];
                 $section = RecipeParser_Text::formatSectionName($section);
                 $recipe->addInstructionsSection($section);
                 // Reset the value of $line, without the section name.
                 $line = ucfirst($m[2]);
             }
             $recipe->appendInstruction($line);
         }
     }
     return $recipe;
 }