package auction import ( "bytes" "encoding/gob" "encoding/json" "errors" "fmt" "net/url" "strings" "time" ) type Car struct { Bid string Details map[string]string Images []*Image Title string Watched bool TS time.Time } type CarOption func(*Car) func NewCar(options ...CarOption) *Car { c := &Car{ Details: map[string]string{}, Images: []*Image{}, } c.parseTS(nil) for _, opt := range options { opt(c) } return c } func (c *Car) MarshalJSON() ([]byte, error) { m := map[string]interface{}{} m["bid"] = c.Bid m["details"] = c.Details m["images"] = c.Images m["title"] = c.Title m["TS"] = c.TS.Format("2006-01-02") return json.Marshal(m) } func (c *Car) Encode() ([]byte, error) { buff := bytes.NewBuffer(nil) enc := gob.NewEncoder(buff) err := enc.Encode(c) return buff.Bytes(), err } func (c *Car) Decode(b []byte) error { buff := bytes.NewBuffer(b) enc := gob.NewDecoder(buff) return enc.Decode(c) } func (c *Car) Copy() *Car { d := *c return &d } func (c *Car) Equals(d *Car) bool { if c == d { return true } if d == nil && c != nil { return false } if c == nil && d != nil { return false } for k, v := range c.Details { if w, ok := d.Details[k]; ok && w != v { return false } } for k, v := range c.Details { if w, ok := c.Details[k]; ok && w != v { return false } } if c.Title != d.Title { return false } return true } func (c *Car) String() string { return fmt.Sprintf( "[bid:%v title:%v watched:%v images:%v when:%v]", c.Bid, c.Title, c.Watched, len(c.Images), c.TS, ) } func (c *Car) Parse(wd WebDriver) error { we, err := wd.FindElement("tag name", "body") if err != nil { return err } do := []func(WebElement) error{ c.parseTitle, c.parseBid, c.parseWatched, c.parseImages, c.parseDetails, c.parseTS, } for _, d := range do { if err := d(we); err != nil { return err } } return nil } func (c *Car) parseTS(WebElement) error { c.TS = time.Now() return nil } func (c *Car) parseBid(wd WebElement) error { elem := waitForXPath(wd, "//bidding-dialer-refactor") elem, err := elem.FindElement("tag name", "text") if err != nil { return err } text, err := elem.Text() if err != nil { return err } if strings.Contains(text, "$") { c.Bid = text } return nil } func (c *Car) parseDetails(wd WebElement) error { details := make(map[string]string) elems, err := wd.FindElements("tag name", "perfect-scrollbar") if err != nil { return err } if len(elems) > 2 { elems = elems[:2] } for _, elem := range elems { labels, err := elem.FindElements("tag name", "label") if err != nil { return err } for _, label := range labels { ltext, err := label.Text() if err != nil { return err } if ltext == "" { continue } value, err := label.FindElement("xpath", "../div[1]") if err != nil { return err } vtext, err := value.Text() if err != nil { return err } details[ltext] = vtext } } if len(details) > 0 { c.Details = details } return nil } func (c *Car) parseImages(wd WebElement) error { elem := waitForXPath(wd, "//ngx-carousel") elems, err := elem.FindElements("tag name", "ngx-item") if err != nil { return err } if len(elems) == 0 { return err } images := make([]*Image, 0) for _, elem := range elems { elems, err := elem.FindElements("tag name", "img") if err != nil { return err } for _, img := range elems { attr, err := img.GetAttribute("src") if err != nil { return err } url, err := url.Parse(attr) if err != nil { return err } images = append(images, NewImage(url)) } } c.Images = images return nil } func (c *Car) parseTitle(wd WebElement) error { elem := waitForXPath(wd, "//lot-header") elem = followTags(elem, "section", "div", "div") if elem == nil { return errors.New("not found") } text, err := elem.Text() if err != nil { return err } c.Title = text return nil } func (c *Car) parseWatched(wd WebElement) error { elem := waitForXPath(wd, "//widget-header-sale") elems, err := elem.FindElements("tag name", ".//div[@class=\"watchlist\"]") if err != nil { return err } c.Watched = len(elems) > 0 return nil }