/// <summary> /// Parses and validates a single product's html, returning a /// Result containing error messages or the valid AmazonItem /// </summary> /// <param name="html">Product html to parse</param> /// <returns>List of AmazonItem Results</returns> private Result <AmazonItem> ParseAndValidateProductHtml(string html) { Result <AmazonItem> result = new Result <AmazonItem>(); // Parse each item's html and exit early if validation fails on any item. string name = Parser.GetProductName(html); if (name == null || name.Length == 0) { // Do not report a "missing product name" status message here. // Sometimes Amazon injects blurbs or information // sections in lieu of results (book results, for example). // This should not trigger an error. return(result); } if (!ItemValidator.ValidateItemName(_searchCriteria, name)) { result.StatusMessage = name + " doesn't contain all search criteria."; return(result); } // Scrape the review histogram to obtain the review distribution // and the review count (originally review count was being // obtained on the main page, but Amazon removes review // information from search results if they smell a bot). string reviewHistogramHtml = Parser.GetReviewHistogramHtml(html); if (reviewHistogramHtml == null || reviewHistogramHtml.Length == 0) { string msg = "Couldn't obtain review histogram data"; result.ErrorMessage = msg; } ScoreDistribution scoreDistribution = Parser.GetScoreDistribution(reviewHistogramHtml); if (!ItemValidator.ValidateReviewDistribution(_searchCriteria, scoreDistribution)) { result.StatusMessage = name + " doesn't fall within your review distribution."; return(result); } int reviewCount = Parser.GetReviewCount(reviewHistogramHtml); if (!ItemValidator.ValidateReviewCount(_searchCriteria, reviewCount)) { string message = name + " "; if (reviewCount == 0) { message += "doesn't have any reviews."; } else { message += "only has " + reviewCount.ToString() + " reviews."; } result.StatusMessage = message; return(result); } DoubleRange priceRange = Parser.GetPriceRange(html); if (!ItemValidator.ValidatePriceRange(_searchCriteria, priceRange)) { result.StatusMessage = name + " doesn't fit in your price range."; return(result); } // Grab the item's URL so the user can go directly to the product page Uri url = Parser.GetURL(html); // Note: Right now there's no UI capability of validating average rating double rating = Parser.GetRating(reviewHistogramHtml); // TODO: implement a "prime-only" checkbox in the UI bool primeEligibility; if (_searchCriteria.StrictPrimeEligibility) { primeEligibility = Parser.GetStrictPrimeEligibility(url); } else { primeEligibility = Parser.GetFuzzyPrimeEligibility(html); } // Leave the image load for last since it takes longer and if the // item doesn't pass validation we don't waste time downloading BitmapImage image = Parser.GetImageThumbnail(html); // We have everything we need, build the AmazonItem to be returned result.Value = new AmazonItem(name, reviewCount, priceRange, scoreDistribution, url, rating, primeEligibility, image); return(result); }