diff --git a/connection.go b/connection.go index 51d4b72..fb26b15 100644 --- a/connection.go +++ b/connection.go @@ -8,10 +8,11 @@ import ( type Connection struct { client http.Client - - queryCount int } +// Opens a new connection to the reichelt-Server +// this will try to connect and consequently +// throw an error on connection failure func NewConnection() (c *Connection, err error) { jar, err := cookiejar.New(nil) @@ -25,7 +26,7 @@ func NewConnection() (c *Connection, err error) { }, } - // get reichelt SID cookie set + // set reichelt SID cookie resp, err := c.client.Get(apiurl) if err != nil { return nil, err diff --git a/picture.go b/image.go similarity index 92% rename from picture.go rename to image.go index 1a2f440..c7fa28d 100644 --- a/picture.go +++ b/image.go @@ -7,6 +7,7 @@ import ( // gets the product image of a reichelt article using the internal Part number // the reader will return a image/jpg file +// NOTE: the Reader must be closed func (c *Connection) GetImage(p Part, w, h uint) (io.ReadCloser, error) { resp, err := c.client.Get("https://www.reichelt.de/artimage/resize_" + strconv.Itoa(int(w)) + "x" + strconv.Itoa(int(h)) + "/" + strconv.Itoa(p.Number)) diff --git a/meta.go b/meta.go index d128b92..30c805d 100644 --- a/meta.go +++ b/meta.go @@ -15,6 +15,10 @@ var ( metaSelector = cascadia.MustCompile(".av_propview") metaItemNameSelector = cascadia.MustCompile(".av_propname") metaItemValueSelector = cascadia.MustCompile(".av_propvalue") + mpnSelector = cascadia.MustCompile("#av_articlemanufacturer > .av_fontnormal") + manufacturerSelector = cascadia.MustCompile("#av_articlemanufacturer > [itemprop=\"manufacturer\"]") + + datasheetSelector = cascadia.MustCompile(".av_datasheet_description a") ) // Get Metadata connected to specified part @@ -65,5 +69,47 @@ func (c *Connection) GetMeta(p Part) (Meta, error) { result[headline] = data } + // insert datasheets + nodes = datasheetSelector.MatchAll(doc) + temp := make(map[string]string) + + for _, node := range nodes { + + if node.FirstChild == nil || node.FirstChild.Type != html.TextNode { + continue + } + + name := node.FirstChild.Data + link := "" + + // find href of link + for _, k := range node.Attr { + if k.Key == "href" { + link = k.Val + } + } + + if link == "" { + // no link found + continue + } + + temp[name] = link + } + + result["datasheets"] = temp + + // get MPN + node := mpnSelector.MatchFirst(doc) + if node != nil && node.FirstChild != nil { + temp["mpn"] = node.FirstChild.Data + } + + // get Manufacturer + node = manufacturerSelector.MatchFirst(doc) + if node != nil && node.FirstChild != nil { + temp["manufacturer"] = node.FirstChild.Data + } + return result, nil } diff --git a/part.go b/part.go index e090686..23a3bb4 100644 --- a/part.go +++ b/part.go @@ -14,33 +14,42 @@ import ( ) type Part struct { - Number int `json:"article_artid"` + Number int + Description string +} +// used for the json Decoder +// The fields are then copied to Part +// (This was done to remove the structure tags). +type partInternal struct { + Number int `json:"article_artid"` Description string `json:"article_lang_besch"` } +// Where to dispatch api queries to const apiurl = "https://www.reichelt.de/index.html" -type ResponseField struct { +// One Response-field from the autocomplete +// endpoint (a small excerpt from it) +type responseField struct { NumFound int `json:"numFound"` MaxScore float32 `json:"maxScore"` - Docs []Part `json:"docs"` + Docs []partInternal `json:"docs"` } -type SearchResponse struct { - Response ResponseField `json:"response"` +// The Full response +// notice: There are more fields that the server response contains +type searchResponse struct { + Response responseField `json:"response"` } -var ( - priceSelector = cascadia.MustCompile("#av_price") -) +var priceSelector = cascadia.MustCompile("#av_price") // Search for a part like using the sites search engine // can be used to resolv partnumbers to internal ones -func (c *Connection) FindPart(query string) ([]Part, error) { +func (c *Connection) FindPart(query string) (result []Part, err error) { resp, err := c.client.Get(apiurl + "?ACTION=514&id=8&term=" + url.PathEscape(query)) - c.queryCount++ if err != nil { return nil, err @@ -52,12 +61,21 @@ func (c *Connection) FindPart(query string) ([]Part, error) { } reader := json.NewDecoder(resp.Body) - response := SearchResponse{} + response := searchResponse{} if err = reader.Decode(&response); err != nil { return nil, err } - return response.Response.Docs, nil + result = make([]Part, len(response.Response.Docs)) + + for i, j := range response.Response.Docs { + result[i] = Part{ + Number: j.Number, + Description: j.Description, + } + } + + return result, nil } // Returns the Price of the Part @@ -102,8 +120,8 @@ func (c *Connection) GetPrice(p Part) float32 { return 0 } - // need to convert german decimals (using ,) to american decimals - // using . + // need to convert german float (using ,) to american decimals + // using . for ParseFloat (since ParseFloat is not localized) str := strings.Replace(price[:i], ",", ".", 1) ret, _ := strconv.ParseFloat(str, 32) return float32(ret)