Skip to content

Commit ca021cb

Browse files
authored
Add geo location support (#17)
1 parent de99151 commit ca021cb

File tree

5 files changed

+87
-4
lines changed

5 files changed

+87
-4
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ npm install @lightfeed/browser-agent
2626

2727
### Serverless
2828

29-
Perfect for AWS Lambda and other serverless environments. Uses [@sparticuz/chromium](https://github.com/Sparticuz/chromium) to run Chrome in serverless environments with minimal cold start times and memory usage.
29+
Perfect for AWS Lambda and other serverless environments. Uses [@sparticuz/chromium](https://github.com/Sparticuz/chromium) to run Chrome in serverless environments with minimal cold start times and memory usage. Supports proxy configuration for geo-tracking and unblocking.
3030

3131
> [!IMPORTANT]
3232
> This project uses Playwright, which ships with a specific version of Chromium. You need to install the matching version of `@sparticuz/chromium`. For example, we are using [Playwright 1.52](https://playwright.dev/docs/release-notes#version-152) (which supports to Chromium 136), you should install `@sparticuz/chromium@136`.

scripts/test-geolocation-local.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { BrowserAgent } from "../src/agent";
2+
import dotenv from "dotenv";
3+
4+
dotenv.config();
5+
6+
const agent = new BrowserAgent({
7+
browserProvider: "Local",
8+
debug: true,
9+
localConfig: {
10+
proxy: {
11+
host: process.env.PROXY_HOST,
12+
port: parseInt(process.env.PROXY_PORT!),
13+
auth: {
14+
username: process.env.PROXY_USERNAME,
15+
password: process.env.PROXY_PASSWORD,
16+
},
17+
},
18+
},
19+
});
20+
21+
(async () => {
22+
const page = await agent.newPage();
23+
await page.goto(
24+
"https://geo.brdtest.com/welcome.txt?product=resi&method=native"
25+
);
26+
try {
27+
await page.waitForLoadState("networkidle", { timeout: 10000 });
28+
} catch {
29+
console.log("Network idle timeout, continuing...");
30+
}
31+
page.ai("Discard warning and click the button to continue", { maxSteps: 2 });
32+
})();

scripts/test-geolocation-remote.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { BrowserAgent } from "../src/agent";
2+
import dotenv from "dotenv";
3+
4+
dotenv.config();
5+
6+
const agent = new BrowserAgent({
7+
browserProvider: "Remote",
8+
remoteConfig: {
9+
wsEndpoint: process.env.REMOTE_BROWSER_WS_ENDPOINT,
10+
},
11+
debug: true,
12+
});
13+
14+
(async () => {
15+
const page = await agent.newPage();
16+
// White Rock, BC
17+
const lat = 49.019917;
18+
const lon = -122.802612;
19+
const client = await page.context().newCDPSession(page);
20+
await client.send("Proxy.setLocation", {
21+
lat,
22+
lon,
23+
distance: 50,
24+
strict: true,
25+
});
26+
await page.goto(
27+
"https://geo.brdtest.com/welcome.txt?product=resi&method=native"
28+
);
29+
try {
30+
await page.waitForLoadState("networkidle", { timeout: 10000 });
31+
} catch {
32+
console.log("Network idle timeout, continuing...");
33+
}
34+
page.ai("Discard warning and click the button to continue", { maxSteps: 2 });
35+
})();

src/agent/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class BrowserAgent<T extends BrowserProviders = "Local"> {
8181
? new ServerlessBrowserProvider(params.serverlessConfig!)
8282
: this.browserProviderType === "Remote"
8383
? new RemoteBrowserProvider(params.remoteConfig!)
84-
: new LocalBrowserProvider(params.localConfig)
84+
: new LocalBrowserProvider(params.localConfig ?? {})
8585
) as T extends "Serverless"
8686
? ServerlessBrowserProvider
8787
: T extends "Remote"

src/browser-providers/local.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
import { chromium, Browser, LaunchOptions } from "playwright";
22
import BrowserProvider from "@/types/browser-providers/types";
3+
import { AxiosProxyConfig } from "axios";
34

45
export class LocalBrowserProvider extends BrowserProvider<Browser> {
56
options: Omit<Omit<LaunchOptions, "headless">, "channel"> | undefined;
67
session: Browser | undefined;
7-
constructor(options?: Omit<Omit<LaunchOptions, "headless">, "channel">) {
8+
proxy: AxiosProxyConfig | null;
9+
10+
constructor(params: {
11+
options?: Omit<Omit<LaunchOptions, "headless">, "channel">;
12+
proxy?: AxiosProxyConfig;
13+
}) {
814
super();
9-
this.options = options;
15+
this.options = params.options;
16+
this.proxy = params.proxy ?? null;
1017
}
1118
async start(): Promise<Browser> {
1219
const launchArgs = this.options?.args ?? [];
@@ -15,6 +22,15 @@ export class LocalBrowserProvider extends BrowserProvider<Browser> {
1522
channel: "chrome",
1623
headless: false,
1724
args: ["--disable-blink-features=AutomationControlled", ...launchArgs],
25+
...(this.proxy == null
26+
? {}
27+
: {
28+
proxy: {
29+
server: `http://${this.proxy.host}:${this.proxy.port}`,
30+
username: this.proxy.auth?.username,
31+
password: this.proxy.auth?.password,
32+
},
33+
}),
1834
});
1935
this.session = browser;
2036
return this.session;

0 commit comments

Comments
 (0)