func (s Scraper) GetPart(URL string) (*Part, error) {
	if !MatchProductURL(URL) {
		return nil, errors.New("invalid part URL")
	}

	var images []string

	s.Collector.OnHTML(".single_image_gallery_box", func(image *colly.HTMLElement) {
		images = append(images, linkURL("https:", image.ChildAttr("a img", "src")))
	})

	if len(images) < 1 {
		s.Collector.OnHTML("script", func(script *colly.HTMLElement) {

			for _, match := range regexp2FindAllString(scriptImageCheck, script.Text) {
				if strings.HasPrefix(match, "//") {
					match = "https:" + match
				}
				images = append(images, match)
			}
		})
	}

	rating := Rating{}
	var name string

	s.Collector.OnHTML(".wrapper__pageTitle section.xs-col-11", func(ratingContainer *colly.HTMLElement) {
		var stars uint
		ratingContainer.ForEach(".product--rating li", func(i int, _ *colly.HTMLElement) {
			stars += 1
		})

		rating.Stars = stars
		name = ratingContainer.ChildText(".pageTitle")

		splitParts := strings.Split(strings.TrimSpace(strings.ReplaceAll(strings.ReplaceAll(ratingContainer.Text, name, ""), ratingContainer.ChildText(".breadcrumb"), "")), ",")

		if len(splitParts) < 2 {
			return
		}

		countParse, _ := strconv.Atoi(strings.Trim(strings.ReplaceAll(splitParts[0], "Ratings", ""), "( "))
		rating.Count = uint(countParse)

		averageParse, _ := strconv.ParseFloat(strings.Trim(strings.ReplaceAll(splitParts[1], "Average", ""), ") "), 64)
		rating.Average = float64(averageParse)
	})

	var vendors []Vendor

	s.Collector.OnHTML("#prices table tbody tr", func(vendor *colly.HTMLElement) {
		if vendor.Attr("class") != "" {
			return
		}

		price := Price{}

		for k, v := range partClassMappings {
			stringPrice := vendor.ChildText(v)
			val, curr, _ := StringPriceToFloat(stringPrice)

			switch k {
			case "Base":
				price.Base = val
			case "Shipping":
				price.Shipping = val
			case "Tax":
				price.Tax = val
			case "Discounts":
				price.Discounts = val
			case "Total":
				price.Total = val
				price.Currency = curr
				price.TotalString = stringPrice
			}
		}

		vendors = append(vendors, Vendor{
			Name:    vendor.ChildAttr(".td__logo a img", "alt"),
			Image:   linkURL("https:", vendor.ChildAttr(".td__logo a img", "src")),
			InStock: vendor.ChildText(".td__availability") == "In stock",
			URL:     linkURL("https://", vendor.Request.URL.Host, vendor.ChildAttr(".td__finalPrice a", "href")),
			Price:   price,
		})
	})

	var specs []Spec

	s.Collector.OnHTML(".specs", func(specsContainer *colly.HTMLElement) {
		if len(specs) > 0 {
			return
		}
		specsContainer.ForEach(".group", func(i int, spec *colly.HTMLElement) {
			var values []string

			spec.ForEach(".group__content li", func(i int, specValue *colly.HTMLElement) {
				values = append(values, specValue.Text)
			})

			if len(values) == 0 {
				values = []string{spec.ChildText(".group__content")}
			}

			specs = append(specs, Spec{
				Name:   spec.ChildText(".group__title"),
				Values: values,
			})
		})

	})

	err := s.Collector.Visit(URL)
	s.Collector.Wait()

	if err != nil {
		return nil, err
	}

	return &Part{
		Name:    name,
		Rating:  rating,
		Specs:   specs,
		Vendors: vendors,
		Images:  images,
	}, nil
}
// Fetches a PCPartPicker product via a URL.
Fetches a PCPartPicker product via a URL.
// Fetches a PCPartPicker product via a URL .
func (s Scraper) GetPart(URL string) (*Part, error) {
	if !MatchProductURL(URL) {
		return nil, errors.New("invalid part URL")
	}
	var images []string
	s.Collector.OnHTML(".single_image_gallery_box", func(image *colly.HTMLElement) {
		images = append(images, linkURL("https:", image.ChildAttr("a img", "src")))
	})
	if len(images) < 1 {
		s.Collector.OnHTML("script", func(script *colly.HTMLElement) {
			for _, match := range regexp2FindAllString(scriptImageCheck, script.Text) {
				if strings.HasPrefix(match, "//") {
					match = "https:" + match
				}
				images = append(images, match)
			}
		})
	}
	rating := Rating{}
	var name string
	s.Collector.OnHTML(".wrapper__pageTitle section.xs-col-11", func(ratingContainer *colly.HTMLElement) {
		var stars uint
		ratingContainer.ForEach(".product--rating li", func(i int, _ *colly.HTMLElement) {
			stars += 1
		})
		rating.Stars = stars
		name = ratingContainer.ChildText(".pageTitle")
		splitParts := strings.Split(strings.TrimSpace(strings.ReplaceAll(strings.ReplaceAll(ratingContainer.Text, name, ""), ratingContainer.ChildText(".breadcrumb"), "")), ",")
		if len(splitParts) < 2 {
			return
		}
		countParse, _ := strconv.Atoi(strings.Trim(strings.ReplaceAll(splitParts[0], "Ratings", ""), "( "))
		rating.Count = uint(countParse)
		averageParse, _ := strconv.ParseFloat(strings.Trim(strings.ReplaceAll(splitParts[1], "Average", ""), ") "), 64)
		rating.Average = float64(averageParse)
	})
	var vendors []Vendor
	s.Collector.OnHTML("#prices table tbody tr", func(vendor *colly.HTMLElement) {
		if vendor.Attr("class") != "" {
			return
		}
		price := Price{}
		for k, v := range partClassMappings {
			stringPrice := vendor.ChildText(v)
			val, curr, _ := StringPriceToFloat(stringPrice)
			switch k {
			case "Base":
				price.Base = val
			case "Shipping":
				price.Shipping = val
			case "Tax":
				price.Tax = val
			case "Discounts":
				price.Discounts = val
			case "Total":
				price.Total = val
				price.Currency = curr
				price.TotalString = stringPrice
			}
		}
		vendors = append(vendors, Vendor{
			Name:    vendor.ChildAttr(".td__logo a img", "alt"),
			Image:   linkURL("https:", vendor.ChildAttr(".td__logo a img", "src")),
			InStock: vendor.ChildText(".td__availability") == "In stock",
			URL:     linkURL("https://", vendor.Request.URL.Host, vendor.ChildAttr(".td__finalPrice a", "href")),
			Price:   price,
		})
	})
	var specs []Spec
	s.Collector.OnHTML(".specs", func(specsContainer *colly.HTMLElement) {
		if len(specs) > 0 {
			return
		}
		specsContainer.ForEach(".group", func(i int, spec *colly.HTMLElement) {
			var values []string
			spec.ForEach(".group__content li", func(i int, specValue *colly.HTMLElement) {
				values = append(values, specValue.Text)
			})
			if len(values) == 0 {
				values = []string{spec.ChildText(".group__content")}
			}
			specs = append(specs, Spec{
				Name:   spec.ChildText(".group__title"),
				Values: values,
			})
		})
	})
	err := s.Collector.Visit(URL)
	s.Collector.Wait()
	if err != nil {
		return nil, err
	}
	return &Part{
		Name:    name,
		Rating:  rating,
		Specs:   specs,
		Vendors: vendors,
		Images:  images,
	}, nil
}
func ( s Scraper ) GetPart ( URL string ) ( * Part , error ) { if ! MatchProductURL ( URL ) { return nil , errors . New ( " " ) } var images [ ] string s . Collector . OnHTML ( " " , func ( image * colly . HTMLElement ) { images = append ( images , linkURL ( " " , image . ChildAttr ( " " , " " ) ) ) } ) if len ( images ) < 1 { s . Collector . OnHTML ( " " , func ( script * colly . HTMLElement ) { for _ , match := range regexp2FindAllString ( scriptImageCheck , script . Text ) { if strings . HasPrefix ( match , " " ) { match = " " + match } images = append ( images , match ) } } ) } rating := Rating { } var name string s . Collector . OnHTML ( " " , func ( ratingContainer * colly . HTMLElement ) { var stars uint ratingContainer . ForEach ( " " , func ( i int , _ * colly . HTMLElement ) { stars += 1 } ) rating . Stars = stars name = ratingContainer . ChildText ( " " ) splitParts := strings . Split ( strings . TrimSpace ( strings . ReplaceAll ( strings . ReplaceAll ( ratingContainer . Text , name , " " ) , ratingContainer . ChildText ( " " ) , " " ) ) , " " ) if len ( splitParts ) < 2 { return } countParse , _ := strconv . Atoi ( strings . Trim ( strings . ReplaceAll ( splitParts [ 0 ] , " " , " " ) , " " ) ) rating . Count = uint ( countParse ) averageParse , _ := strconv . ParseFloat ( strings . Trim ( strings . ReplaceAll ( splitParts [ 1 ] , " " , " " ) , " " ) , 64 ) rating . Average = float64 ( averageParse ) } ) var vendors [ ] Vendor s . Collector . OnHTML ( " " , func ( vendor * colly . HTMLElement ) { if vendor . Attr ( " " ) != " " { return } price := Price { } for k , v := range partClassMappings { stringPrice := vendor . ChildText ( v ) val , curr , _ := StringPriceToFloat ( stringPrice ) switch k { case " " : price . Base = val case " " : price . Shipping = val case " " : price . Tax = val case " " : price . Discounts = val case " " : price . Total = val price . Currency = curr price . TotalString = stringPrice } } vendors = append ( vendors , Vendor { Name : vendor . ChildAttr ( " " , " " ) , Image : linkURL ( " " , vendor . ChildAttr ( " " , " " ) ) , InStock : vendor . ChildText ( " " ) == " " , URL : linkURL ( " " , vendor . Request . URL . Host , vendor . ChildAttr ( " " , " " ) ) , Price : price , } ) } ) var specs [ ] Spec s . Collector . OnHTML ( " " , func ( specsContainer * colly . HTMLElement ) { if len ( specs ) > 0 { return } specsContainer . ForEach ( " " , func ( i int , spec * colly . HTMLElement ) { var values [ ] string spec . ForEach ( " " , func ( i int , specValue * colly . HTMLElement ) { values = append ( values , specValue . Text ) } ) if len ( values ) == 0 { values = [ ] string { spec . ChildText ( " " ) } } specs = append ( specs , Spec { Name : spec . ChildText ( " " ) , Values : values , } ) } ) } ) err := s . Collector . Visit ( URL ) s . Collector . Wait ( ) if err != nil { return nil , err } return & Part { Name : name , Rating : rating , Specs : specs , Vendors : vendors , Images : images , } , nil }
Fetches a PCPartPicker product via a URL.
Fetches a PCPartPicker product via a URL .