bazaar/pkg/shop/amazon/amazon.go

108 lines
2.5 KiB
Go

package amazon
import (
"encoding/json"
"fmt"
"log"
"net/url"
"strconv"
"strings"
"github.com/PuerkitoBio/goquery"
"github.com/fmartingr/bazaar/pkg/models"
"github.com/fmartingr/bazaar/pkg/utils"
"github.com/goodsign/monday"
)
var Domains = []string{"www.amazon.es", "www.amazon.com"}
type AmazonShop struct {
models.ShopOptions
domains []string
}
var priceSelectors = []string{
"#buybox span.a-color-price",
"#tp_price_block_total_price_ww",
"#price",
".priceToPay .a-offscreen",
}
var releaseDateLayoutByDomain = map[string]string{
Domains[0]: "2 January 2006",
Domains[1]: "January 2, 2006",
}
func (s *AmazonShop) Get(u *url.URL) (*models.Product, error) {
body, err := s.ShopOptions.Client.Get(u)
if err != nil {
return nil, fmt.Errorf("error during request: %s", err)
}
doc, err := goquery.NewDocumentFromReader(body)
if err != nil {
return nil, fmt.Errorf("error parsing body: %s", err)
}
product := models.Product{
URL: u.String(),
}
var tentativePrice string
for _, selector := range priceSelectors {
tentativePrice = strings.TrimSpace(doc.Find(selector).First().Text())
if tentativePrice != "" {
priceNum, err := strconv.ParseFloat(utils.ExtractPrice(tentativePrice), 64)
if err != nil {
log.Println(err)
} else {
product.PriceText = tentativePrice
product.Price = priceNum
break
}
}
}
product.Name = strings.TrimSpace(doc.Find("#productTitle").Text())
imagesJSON, exists := doc.Find("#main-image-container img").Attr("data-a-dynamic-image")
if !exists {
log.Printf("Can't find image for %s", u.String())
}
var images map[string]interface{}
if err := json.Unmarshal([]byte(imagesJSON), &images); err != nil {
log.Printf("error unmarshalling: %s", err)
}
var lastImage string
for key := range images {
lastImage = key
}
product.ImageURL = lastImage
releaseDateElement := doc.Find(".book_details-publication_date")
if len(releaseDateElement.Nodes) > 0 {
releaseDateRaw := releaseDateElement.Parent().Parent().Find(".rpi-attribute-value").Text()
releaseDate, err := utils.ParseReleaseDate(releaseDateLayoutByDomain[u.Host], strings.TrimSpace(releaseDateRaw), monday.LocaleEsES)
if err != nil {
log.Println(err)
} else {
product.ReleaseDate = releaseDate
}
}
product.InStock = !strings.Contains(doc.Find("#availability").Text(), "No disponible")
return &product, nil
}
func NewAmazonShopFactory() models.ShopFactory {
return func(shopOptions models.ShopOptions) models.Shop {
shop := AmazonShop{
ShopOptions: shopOptions,
domains: Domains,
}
return &shop
}
}