30, 'headers' => [ 'User-Agent' => $this->user_agent, 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language' => 'en-US,en;q=0.5', ], ]); if (is_wp_error($response)) { return ['error' => $response->get_error_message()]; } $body = wp_remote_retrieve_body($response); return $this->parse_search_results($body); } /** * Parse Amazon search results */ private function parse_search_results($html) { $books = []; // Match search result items with data-asin if (preg_match_all('/]*data-asin="([A-Z0-9]{10})"[^>]*class="[^"]*s-result-item[^"]*"[^>]*>(.*?)<\/div>\s*<\/div>\s*<\/div>/si', $html, $matches)) { foreach ($matches as $index => $match) { $asin = $match[1]; $item_html = $match[2]; $book = $this->parse_search_item($item_html, $asin); if ($book) { $books[] = $book; } } } // Alternative pattern if (empty($books)) { if (preg_match_all('/data-asin="([A-Z0-9]{10})"/si', $html, $asin_matches)) { foreach ($asin_matches[1] as $asin) { // Try to find context around this ASIN $pattern = '/data-asin="' . preg_quote($asin) . '"[^>]*>(.*?)(?=data-asin="|$)/si'; if (preg_match($pattern, $html, $context_match)) { $book = $this->parse_search_item($context_match[1], $asin); if ($book) { $books[] = $book; } } } } } return $books; } /** * Parse a single search result item */ private function parse_search_item($html, $asin) { $book = [ 'asin' => $asin, 'title' => '', 'author' => '', 'format' => '', 'rating' => 0, 'ratings_count' => 0, 'cover_image' => '', 'url' => 'https://www.amazon.com/dp/' . $asin, ]; // Extract title if (preg_match('/]*>.*?]*>(.*?)<\/span>/si', $html, $matches)) { $book['title'] = trim(strip_tags($matches[1])); } elseif (preg_match('/]*>.*?]*class="[^"]*a-size[^"]*"[^>]*>(.*?)<\/span>/si', $html, $matches)) { $book['title'] = trim(strip_tags($matches[1])); } // Extract author if (preg_match('/]*class="[^"]*a-size[^"]*"[^>]*>\s*by\s+]*>(.*?)<\/a>/si', $html, $matches)) { $book['author'] = trim(strip_tags($matches[1])); } // Extract format (Kindle, Paperback, Hardcover, Audio) if (preg_match('/(Kindle\s*Edition|Paperback|Hardcover|Audio\s*CD|Audible|MP3\s*CD)/si', $html, $matches)) { $book['format'] = trim($matches[1]); } // Extract rating if (preg_match('/(\d+\.?\d*)\s*out\s*of\s*5\s*stars?/si', $html, $matches)) { $book['rating'] = (float) $matches[1]; } // Extract ratings count if (preg_match('/([\d,]+)\s*ratings?/si', $html, $matches)) { $book['ratings_count'] = absint(str_replace(',', '', $matches[1])); } // Extract cover image if (preg_match('/]*src="(https:\/\/[^"]*\.jpg)"/si', $html, $matches)) { $book['cover_image'] = $matches[1]; } if (empty($book['title'])) { return null; } return $book; } /** * Look up book by ASIN and get full details including ISBN * * @param string $asin Amazon ASIN. * @return array Book details. */ public function lookup_by_asin($asin) { $url = 'https://www.amazon.com/dp/' . $asin; $response = wp_remote_get($url, [ 'timeout' => 30, 'headers' => [ 'User-Agent' => $this->user_agent, 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 'Accept-Language' => 'en-US,en;q=0.5', ], ]); if (is_wp_error($response)) { return ['error' => $response->get_error_message()]; } $body = wp_remote_retrieve_body($response); return $this->parse_product_page($body, $asin); } /** * Parse Amazon product page for full book details */ private function parse_product_page($html, $asin) { $book = [ 'asin' => $asin, 'title' => '', 'author' => '', 'isbn' => '', 'isbn13' => '', 'rating' => 0, 'ratings_count' => 0, 'cover_image' => '', 'description' => '', 'formats' => [], ]; // Extract title if (preg_match('/]*id="title"[^>]*>(.*?)<\/h1>/si', $html, $matches)) { $book['title'] = trim(strip_tags($matches[1])); } // Extract author if (preg_match('/by\s+]*>(.*?)<\/a>/si', $html, $matches)) { $book['author'] = trim(strip_tags($matches[1])); } // Extract rating if (preg_match('/(\d+\.?\d*)\s*out\s*of\s*5\s*stars?/si', $html, $matches)) { $book['rating'] = (float) $matches[1]; } // Extract ratings count if (preg_match('/([\d,]+)\s*ratings?/si', $html, $matches)) { $book['ratings_count'] = absint(str_replace(',', '', $matches[1])); } // Extract cover image if (preg_match('/]*id="landingImage"[^>]*src="([^"]+)"/si', $html, $matches)) { $book['cover_image'] = $matches[1]; } // Extract ISBN from product details if (preg_match('/ISBN-10:\s*(\d{10})/si', $html, $matches)) { $book['isbn'] = $matches[1]; } if (preg_match('/ISBN-13:\s*(\d{13})/si', $html, $matches)) { $book['isbn13'] = $matches[1]; } // Extract ASIN if (preg_match('/ASIN:\s*([A-Z0-9]{10})/si', $html, $matches)) { $book['asin'] = $matches[1]; } // Extract description if (preg_match('/]*id="bookDescription_feature_div"[^>]*>.*?]*>(.*?)<\/span>/si', $html, $matches)) { $book['description'] = trim(strip_tags($matches[1])); } // Look for other format links (Kindle, Paperback, Hardcover) if (preg_match_all('/]*>.*?]*href="\/dp\/([A-Z0-9]{10})"[^>]*>.*?]*>(.*?)<\/span>/si', $html, $matches)) { foreach ($matches[1] as $index => $format_asin) { $format_name = trim(strip_tags($matches[2][$index])); if ($format_asin !== $asin && !empty($format_name)) { $book['formats'][] = [ 'asin' => $format_asin, 'format' => $format_name, 'url' => 'https://www.amazon.com/dp/' . $format_asin, ]; } } } return $book; } /** * Get all format ASINs for a book by searching * * @param string $title Book title. * @param string $author Author name. * @return array Format => ASIN mapping. */ public function get_format_asins($title, $author = '') { $formats = [ 'kindle' => '', 'paperback' => '', 'hardcover' => '', 'audio' => '', ]; // Search for the book $results = $this->search_books($title, $author); if (isset($results['error']) || empty($results)) { return $formats; } // Match results to formats foreach ($results as $book) { $book_title = strtolower($book['title'] ?? ''); $book_format = strtolower($book['format'] ?? ''); // Check if this is the same book (title match) similar_text(strtolower($title), $book_title, $percent); if ($percent < 60) { continue; } if (strpos($book_format, 'kindle') !== false || strpos($book_format, 'ebook') !== false) { $formats['kindle'] = $book['asin']; } elseif (strpos($book_format, 'paperback') !== false) { $formats['paperback'] = $book['asin']; } elseif (strpos($book_format, 'hardcover') !== false) { $formats['hardcover'] = $book['asin']; } elseif (strpos($book_format, 'audio') !== false || strpos($book_format, 'audible') !== false) { $formats['audio'] = $book['asin']; } } return $formats; } }