Web Scraping With Ghost Cursor in 2025

Make your web scraper less detectable using Ghost Cursor with Puppeteer. Discover how to set it up and use it for more human-like scraping.
8 min read
Web Scraping with Ghost Cursor

In this guide, you’ll learn about Ghost Cursor. By the time you’ve finished the tutorial, you’ll be able to answer the following questions.

  • What is Ghost Cursor?
  • What tools is Ghost Cursor compatible with?
  • How is Ghost Cursor used?
  • Is it compatible with unblocking tools (proxies, remote browsers etc.)?
  • When is Ghost Cursor the right choice?

What is Ghost Cursor and Why Should You Care?

Ghost Cursor is a plug-in for Puppeteer. It allows you to control the mouse cursor and make page interactions using more human-like movements.

When you click a button with Puppeteer, the cursor moves to the button instantly and clicks on it. This is very robotic. Yes, it’s fast and efficient, but it’s one of the many things that often gets you blocked when scraping the web.

How The Cursor Moves in Puppeteer

With Ghost Cursor, we create an infinite amount of points on a curve between two points on the page. The cursor then follows this curve from its current location to your target element. This curve looks far more human.

How The Cursor Moves With Ghost Cursor

Getting Started

Before we start coding, we assume you’ve got a basic familiarity with JavaScript. First, we need to create a new project folder and initialize a JavaScript project.

The command below creates a new folder and hops into our project directory.

mkdir your-ghost-cursor-project
cd your-ghost-cursor-project

You can use npm init to convert this new folder into a NodeJS project. We use the --y flag because this is a tutorial. We don’t need a prompted setup with licensing decisions.

npm init --y

Now, we install our dependencies. Pretty simple — we’ve only got two.

npm install ghost-cursor puppeteer

Basic Ghost Cursor Usage

In the script below, we demonstrate basic usage of Ghost Cursor. First, we launch a headful browser — this is not a requirement, but you will be able to watch Ghost Cursor in action.

After creating a new page, we pass it into installMouseHelper(). This allows us to view the cursor moving inside the rendered page. We then create a cursor object. The cursor gets the location of the book and then clicks on it.

Finally, we take a screenshot of the product page before closing our browser and exiting the program.

const puppeteer = require('puppeteer');
const { createCursor, installMouseHelper } = require('ghost-cursor');

//this function holds our runtime
async function run() {

    //launch a headed browser to watch ghost cursor in action
    const browser = await puppeteer.launch({
        headless: false,
    });

    const page = await browser.newPage();

    //this allows you to watch the cursor as it moves around
    await installMouseHelper(page);

    //create the actual cursor object
    const cursor = createCursor(page);

    await page.goto("https://books.toscrape.com");

    //find the link inside the product pod
    const book = await cursor.getElement("article[class='product_pod'] a");

    //get its location (x,y) coordinates
    const location = cursor.getLocation(book);

    //move to the coordinates
    await cursor.moveTo(location);

    //click on the book
    await cursor.click(book);

    //take a screenshot of the product page
    await page.screenshot({path: "book.png"});

    await browser.close();
}

run().catch(console.error);

Available Methods in Ghost Cursor

Now that you’ve seen Ghost Cursor in action, let’s take a look at what its individual methods do. Each one of these is a new piece for your scraping toolbox.

installMouseHelper()

We used installMouseHelper() to track the mouse movement within the rendered browser. This method allows you to watch the cursor in action. This piece is not required for cursor usage and it needs a headful browser in order to run. This is more of a novel debugging piece than a useful tool.

//launch a headed browser to watch ghost cursor in action
const browser = await puppeteer.launch({
    headless: false,
});

const page = await browser.newPage();

//this allows you to watch the cursor as it moves around
await installMouseHelper(page);

getElement()

getElement() works pretty much the same way as page.$() does in Puppeteer. In fact, we even passed regular Puppeteer elements into other methods and the cursor was buggy, but it did continue to work. When finding an element on the page, it’s best to use cursor.getElement().

//find the link inside the product pod
const book = await cursor.getElement("article[class='product_pod'] a");

getLocation()

Once you’ve found an element, you often need to retrieve its location. This method allows you to retrieve the (x,y) coordinates of any page element with minimal code.

//get its location (x,y) coordinates
const location = cursor.getLocation(book);

move()

move() is a very convenient method. You can pass a selector object or page element straight into move() and the cursor will move to it — naturally, like a human.

//move straight to the book
await cursor.move(book);

//click on the book
await cursor.click(book);

moveTo()

moveTo allows you to pass coordinates directly into the cursor. Instead of moving to the book, we use getLocation() and pass its output into the cursor with moveTo().

//move to the coordinates
await cursor.moveTo(location);

//click on the book
await cursor.click(book);

scrollIntoView()

Sometimes, you don’t want to move the cursor directly over an object. You might just need to scroll the page. scrollIntoView() allows you to pass a selector or page element and scroll until your element shows up inside the viewport.

//scroll until the book is in the viewport
await cursor.scrollIntoView(book);

//click on the book
await cursor.click(book);

scrollTo()

scrollTo() is another really convenient one. It allows you to pass in top, bottom, left and right. Control your scrolling motions with natural language, no AI required!

//scroll to the top of the page
await cursor.scrollTo('top');

//click on the book
await cursor.click(book);

scroll()

scroll() is perhaps the most basic of our scrolling actions. With scroll, you pass (x,y) coordinates — remember getLocation()? Then the cursor scrolls until they’re within the viewport.

//move to the coordinates
await cursor.scroll(location);

//click on the book
await cursor.click(book);

Common Limitations of Ghost Cursor

  • CAPTCHAs: Ghost Cursor can make you appear more human, but at the end of the day, you’re still the one controlling the scraper. Take a look at the Top 10 CAPTCHA Solvers to learn more.
  • Puppeteer: Ghost cursor only officially supports Puppeteer. Compared to other headless browsers like Selenium and Playwright, Puppeteer is quite limited and lacking in features and maintenance.
  • Local Only: Ghost Cursor needs to run locally. This severely limits unblocking options. With Scraping Browser, you can run remote headless browsers with automated proxy management and CAPTCHA solving. Because it doesn’t support remote browsing, Ghost Cursor can’t take full advantage of the best tools available.

Proxy Integration

While it doesn’t support Scraping Browser, Puppeteer does integrate with local proxies. Puppeteer follows the standard proxy authentication with your basic proxy URL, username and password.

The code below gives you a basic script you can tweak for any Puppeteer/Ghost Cursor runtime. Make sure to replace the username, zone name and password with your own credentials!

const puppeteer = require('puppeteer');
const { createCursor } = require('ghost-cursor');

async function run() {
    const browser = await puppeteer.launch({
        headless: false,

        args: [
            '--proxy-server=http://brd.superproxy.io:33335',
            '--ignore-certificate-errors'
        ]

    });

    const page = await browser.newPage();

    const cursor = createCursor(page);

    await page.authenticate({
        username: "brd-customer-<your-username>-zone-<your-zone-name>",
        password: "<your-password>"
    });

    await page.goto("https://ipinfo.io");

    await cursor.click("a[href='/what-is-my-ip']");


    await page.screenshot({path: "ipinfo.png"});


    await browser.close();
}

run().catch(console.error);

As mentioned, with Puppeteer and Ghost Cursor, you can’t take full advantage of Scraping Browser itself. The breakdown below shows the cost of a single run using the scraper above. The cost is a real eye-opener.

Cost Breakdown of Ghost Cursor with Proxies

When managing your proxies manually, it’s best to use a rotation. As you can see with Web Unlocker, this scrape costs $0.23 to perform. With a residential connection, it was $0.05 and with a datacenter connection, it only costs $0.01.

When managing proxies with Ghost Cursor and Puppeteer, use datacenter proxies when available. Residential Proxies should be used only when your Datacenter connection gets blocked by the target site.

Conclusion

Ghost Cursor is a simple and effective way to make your scraper appear more human. When your scrolls and clicks are more natural, you’re less likely to get blocked from your target site. This tooling works with general proxy integration, but it’s not compatible with remote browsers.

For basic scraping tasks, Ghost Cursor can add some humanity to your code without much boilerplate. Take a look at the scraping products mentioned in this article to level up your scraping today.

  • Web Unlocker: Automated CAPTCHA solving and proxy management. Just hook it up and continue coding.
  • Scraping Browser: Remote browsing with managed proxies and a CAPTCHA solver.
  • Residential Proxies: Route your traffic through real home internet connections on consumer devices. Blend in with the best of traffic.
  • Datacenter Proxies: High end hardware with lightning fast connections. Scrape intermediate sites at low cost.

Sign up for a free trial and get started today!

Jake Nulty

Technical Writer

6 years experience

Jacob Nulty is a Detroit-based software developer and technical writer exploring AI and human philosophy, with experience in Python, Rust, and blockchain.

Expertise
Data Structures Python Rust