Frontend Development 9 min read

Headless Chrome Automation: API Overview, Coding Tips, and Example Scripts

This article introduces the Chrome DevTools Protocol API, provides practical coding tips for using the chrome-remote-interface library, and demonstrates complete Node.js examples for gathering performance metrics, automating Baidu searches, and extracting first‑page results with headless Chrome.

Qunar Tech Salon
Qunar Tech Salon
Qunar Tech Salon
Headless Chrome Automation: API Overview, Coding Tips, and Example Scripts

1 API Overview & Coding Tips

1.1 Documentation

GitHub Chrome DevTools Protocol repository – the protocol source; issues can be filed here.

GitHub debugger-protocol-viewer – repository for the API documentation viewer.

API documentation site (https://chromedevtools.github.io/devtools-protocol/) – frequently used for browsing the API.

1.2 Common APIs

Network – network requests, cookies, cache, certificates, etc.

Page – page loading, resources, dialogs, screenshots, printing, etc.

DOM – DOM retrieval, modification, deletion, queries, etc.

Runtime – JavaScript execution; this is where most custom logic lives.

1.3 Coding Tips

Instead of using raw WebSocket commands, employ the chrome-remote-interface library, which offers a Promise‑based wrapper.

Each functional area is represented as a separate domain (e.g., Network, Page, DOM).

Most domains provide an enable method that must be called before using other methods.

Domain method parameters are passed as a single object (or Map); you do not need to worry about positional arguments.

Domain method return values are also objects; retrieve the needed fields by key.

Parameters and return values often contain meta‑information such as IDs rather than full object content.

2 Coding Examples

First, create a simple wrapper to prepare the API execution environment (see the previous article for the utility library).

const chromeLauncher = require('chrome-launcher');
const chromeRemoteInterface = require('chrome-remote-interface');

const prepareAPI = (config = {}) => {
    const {host = 'localhost', port = 9222, autoSelectChrome = true, headless = true} = config;
    const wrapperEntry = chromeLauncher.launch({
        host,
        port,
        autoSelectChrome,
        additionalFlags: [
            '--disable-gpu',
            headless ? '--headless' : ''
        ]
    }).then(chromeInstance => {
        const remoteInterface = chromeRemoteInterface(config).then(chromeAPI => chromeAPI).catch(err => { throw err; });
        return Promise.all([chromeInstance, remoteInterface]);
    }).catch(err => { throw err; });
    return wrapperEntry;
};

Collect Page Performance Data (Navigation Timing)

const wrapper = require('the-wrapper-module');

const performanceParser = (performanceTiming) => {
    let timingGather = {};
    performanceTiming = performanceTiming || {};
    timingGather.redirect = performanceTiming.redirectEnd - performanceTiming.redirectStart;
    timingGather.dns = performanceTiming.domainLookupEnd - performanceTiming.domainLookupStart;
    timingGather.tcp = performanceTiming.connectEnd - performanceTiming.connectStart;
    timingGather.request = performanceTiming.responseStart - performanceTiming.requestStart;
    timingGather.response = performanceTiming.responseEnd - performanceTiming.responseStart;
    timingGather.domReady = performanceTiming.domContentLoadedEventStart - performanceTiming.navigationStart;
    timingGather.load = performanceTiming.loadEventStart - performanceTiming.navigationStart;
    return timingGather;
};

const showPerformanceInfo = (performanceInfo) => {
    performanceInfo = performanceInfo || {};
    console.log(`Redirect time: ${performanceInfo.redirect}`);
    console.log(`DNS lookup time: ${performanceInfo.dns}`);
    console.log(`TCP connection time: ${performanceInfo.tcp}`);
    console.log(`Request send time: ${performanceInfo.request}`);
    console.log(`Response receive time: ${performanceInfo.response}`);
    console.log(`DOM ready time: ${performanceInfo.domReady}`);
    console.log(`Page load time: ${performanceInfo.load}`);
};

wrapper.prepareAPI().then(([chromeInstance, remoteInterface]) => {
    const {Runtime, Page} = remoteInterface;
    Page.loadEventFired(() => {
        Runtime.evaluate({
            expression: 'window.performance.timing.toJSON()',
            returnByValue: true
        }).then(resultObj => {
            const {result, exceptionDetails} = resultObj;
            if (!exceptionDetails) {
                showPerformanceInfo(performanceParser(result.value));
            } else {
                throw exceptionDetails;
            }
        });
    });
    Page.enable().then(() => {
        Page.navigate({url: 'http://www.baidu.com'});
    });
});

Search Baidu and Scrape First‑Page Results

const wrapper = require('the-wrapper-module');
// Note: arrow functions lose their own `this` binding; use regular functions when needed.
const buttonClick = function () { this.click(); };

const setInputValue = () => {
    var input = document.getElementById('kw');
    input.value = 'Web automation headless chrome';
};

const parseSearchResult = () => {
    let resultList = [];
    const linkBlocks = document.querySelectorAll('div.result.c-container');
    for (let block of Array.from(linkBlocks)) {
        let targetObj = block.querySelector('h3');
        resultList.push({
            title: targetObj.textContent,
            link: targetObj.querySelector('a').getAttribute('href')
        });
    }
    return resultList;
};

wrapper.prepareAPI({/* headless: false // uncomment to see the browser */}).then(([chromeInstance, remoteInterface]) => {
    const {Runtime, DOM, Page, Network} = remoteInterface;
    let framePointer;
    Promise.all([Page.enable(), Network.enable(), DOM.enable(), Page.setAutoAttachToCreatedPages({autoAttach: true})])
        .then(() => {
            Page.domContentEventFired(() => {
                console.log('Page.domContentEventFired');
                Runtime.evaluate({expression: `window.location.href`, returnByValue: true}).then(result => console.log(result));
            });
            Page.frameNavigated(() => console.log('Page.frameNavigated'));
            Page.loadEventFired(() => {
                console.log('Page.loadEventFired');
                Runtime.evaluate({expression: `window.location.href`, returnByValue: true}).then(result => console.log(result));
                DOM.getDocument().then(({root}) => {
                    DOM.querySelector({nodeId: root.nodeId, selector: '#form'}).then(({nodeId}) => {
                        Promise.all([
                            // Fill the search box
                            DOM.querySelector({nodeId, selector: '#kw'}).then(inputNode => {
                                Runtime.evaluate({expression: `(${setInputValue})()`, returnByValue: true});
                            }),
                            // Get the submit button
                            DOM.querySelector({nodeId, selector: '#su'})
                        ]).then(([inputNode, buttonNode]) => {
                            // Click the button
                            DOM.resolveNode({nodeId: buttonNode.nodeId}).then(({object}) => {
                                const {objectId} = object;
                                return Runtime.callFunctionOn({objectId, functionDeclaration: `(${buttonClick})`});
                            }).then(() => {
                                setTimeout(() => {
                                    Runtime.evaluate({expression: `(${parseSearchResult})()`, returnByValue: true})
                                        .then(({result}) => console.log(result.value));
                                }, 3000);
                            });
                        });
                    });
                });
            });
            Page.navigate({url: 'http://www.baidu.com'}).then(frameObj => { framePointer = frameObj; });
        });
});

The article concludes with screenshots illustrating the automation results.

JavaScriptNode.jsperformance testingheadless-chromeChrome DevTools ProtocolWeb Automation
Qunar Tech Salon
Written by

Qunar Tech Salon

Qunar Tech Salon is a learning and exchange platform for Qunar engineers and industry peers. We share cutting-edge technology trends and topics, providing a free platform for mid-to-senior technical professionals to exchange and learn.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.