Using chromedp , how can i check if an element is present on the page?

i used to do that with python and selenium
user = browser.find_element_by_css_selector(user_input)
this gives me a pointer to the element or nil value , which i can check to see if the element is there
but how can i do that with chromedp

I think https://chromedevtools.github.io/devtools-protocol/tot/DOM/#method-querySelector is what you are looking for.

1 Like

can you respond with an example ,
i find the package docs very confusing and telling very little

If you don’t actually need Chrome, you might try https://github.com/headzoo/surf instead.

When I tried to use github.com/knq/chromedp a few weeks ago I couldn’t get it to work. I did get github.com/mafredri/cdp working though.

Starting from what I was doing with it (using Chrome to generate PDFs), I hacked together the following example that prints all the link hrefs from https://golang.org:

package main

import (
	"context"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"sync"
	"time"

	"github.com/mafredri/cdp"
	"github.com/mafredri/cdp/devtool"
	"github.com/mafredri/cdp/protocol/dom"
	"github.com/mafredri/cdp/protocol/page"
	"github.com/mafredri/cdp/rpcc"
)

func main() {
	log.SetFlags(log.Lshortfile)

	log.Println("starting")

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	wg := &sync.WaitGroup{}

	const debuggingPort = "9222"

	wg.Add(1)
	go chrome(ctx, wg, debuggingPort)

	done := make(chan struct{})
	wg.Add(1)
	go queryBrowser(ctx, wg, debuggingPort, done)

	signals := make(chan os.Signal)
	signal.Notify(signals, os.Interrupt)

	select {
	case <-signals:
		log.Println("canceled")
	case <-done:
		log.Println("queryBrowser done")
	}

	cancel()
	wg.Wait()
	log.Println("done")
}

func chrome(ctx context.Context, wg *sync.WaitGroup, port string) {
	defer wg.Done()

	log.Println("starting chrome")

	// chrome will be killed when the context is cancelled

	// https://developers.google.com/web/updates/2017/04/headless-chrome
	cmd := exec.CommandContext(ctx, "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
		"--headless",
		"--disable-gpu", // needed for now, hopefully will go away
		"--remote-debugging-port="+port,
	)

	err := cmd.Run()
	if err != nil {
		log.Println("chrome:", err)
	}

	log.Println("chrome is shutdown")
}

func queryBrowser(ctx context.Context, wg *sync.WaitGroup, debuggingPort string, done chan struct{}) {
	defer wg.Done()
	defer func() {
		done <- struct{}{}
	}()

	time.Sleep(5 * time.Second) // wait for chrome to start
	log.Println("devtool")

	devt := devtool.New("http://127.0.0.1:" + debuggingPort)
	pt, err := devt.Get(ctx, devtool.Page)
	if err != nil {
		pt, err = devt.Create(ctx)
		if err != nil {
			log.Println("Error:", err)
			return
		}
	}

	log.Println("rpcc")

	conn, err := rpcc.DialContext(ctx, pt.WebSocketDebuggerURL)
	if err != nil {
		log.Println("Error:", err)
		return
	}
	defer conn.Close()

	c := cdp.NewClient(conn)

	// Open a DOMContentEventFired client to buffer this event.
	domContent, err := c.Page.DOMContentEventFired(ctx)
	if err != nil {
		log.Println("Error:", err)
		return
	}
	defer domContent.Close()

	// Enable events on the Page domain, it's often preferrable to create
	// event clients before enabling events so that we don't miss any.
	err = c.Page.Enable(ctx)
	if err != nil {
		log.Println("Error:", err)
		return
	}

	const url = "https://golang.org"

	_, err = c.Page.Navigate(ctx, page.NewNavigateArgs(url))
	if err != nil {
		log.Println("Error:", err)
		return
	}

	// Wait until we have a DOMContentEventFired event.
	// this should mean the page is loaded
	_, err = domContent.Recv()
	if err != nil {
		log.Println("Error:", err)
		return
	}

	err = c.DOM.Enable(ctx)
	if err != nil {
		log.Println("Error:", err)
		return
	}

	getDocumentReply, err := c.DOM.GetDocument(ctx, dom.NewGetDocumentArgs().SetDepth(-1))
	if err != nil {
		log.Println("Error:", err)
		return
	}

	queryReply, err := c.DOM.QuerySelectorAll(ctx, dom.NewQuerySelectorAllArgs(getDocumentReply.Root.NodeID, "a"))
	if err != nil {
		log.Println("Error:", err)
		return
	}

	// log.Println(queryReply.NodeIDs)

	nodes := getChildrenWithNodeID(&getDocumentReply.Root, func(id dom.NodeID) bool {
		// log.Println("shouldReturn", id)
		for _, nodeID := range queryReply.NodeIDs {
			if id == nodeID {
				return true
			}
		}
		return false
	})

	for _, node := range nodes {
		hrefIndex := -1
		for i, attr := range node.Attributes {
			if attr == "href" {
				hrefIndex = i
				break
			}
		}
		if hrefIndex == -1 {
			// log.Println("href not found")
			continue
		}

		log.Println("links to", node.Attributes[hrefIndex+1])
	}
}

func getChildrenWithNodeID(root *dom.Node, shouldReturn func(id dom.NodeID) bool) []*dom.Node {
	// log.Println("getChildrenwithNodeID")
	var nodes []*dom.Node

	for i := range root.Children {
		node := &root.Children[i]
		if shouldReturn(node.NodeID) {
			nodes = append(nodes, node)
		}

		nodes = append(nodes, getChildrenWithNodeID(node, shouldReturn)...)
	}

	return nodes
}
go run main.go
main.go:22: starting
main.go:56: starting chrome
main.go:82: devtool
main.go:94: rpcc
main.go:180: links to /
main.go:180: links to /
main.go:180: links to #
main.go:180: links to /doc/
main.go:180: links to /pkg/
main.go:180: links to /project/
main.go:180: links to /help/
main.go:180: links to /blog/
main.go:180: links to http://play.golang.org/
main.go:180: links to #
main.go:180: links to #
main.go:180: links to //tour.golang.org/
main.go:180: links to https://golang.org/dl/
main.go:180: links to //blog.golang.org/
main.go:180: links to https://developers.google.com/site-policies#restrictions
main.go:180: links to /LICENSE
main.go:180: links to /doc/tos.html
main.go:180: links to http://www.google.com/intl/en/policies/privacy/
main.go:45: queryBrowser done
main.go:69: chrome: signal: killed
main.go:72: chrome is shutdown
main.go:50: done

Good luck.

i found that mafredri is much confusing than chromedp
all i need is a snippet of code which checks if an element is present on he page or not

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.