Ultimate Screenshot
Try for free
No credit card required
View all Actors
Ultimate Screenshot
dz_omar/ultimate-screenshot
Try for free
No credit card required
Ultimate Screenshot allows you to extract data in formats like JPEG, PNG, PDF, GIF, and MP4. It supports device emulation, including iPhones, Android phones, tablets, and desktops, or uses a default resolution of 1920x1080 for accurate, versatile screenshots and videos.
.actor/Dockerfile
1# Specify the base Docker image
2FROM apify/actor-node-puppeteer-chrome:20
3
4# Switch to root user for installing packages
5USER root
6
7# Install required dependencies for running Puppeteer and FFmpeg
8RUN apt-get update && apt-get install -y \
9 wget \
10 gnupg2 \
11 libx11-xcb1 \
12 libxcomposite1 \
13 libxcursor1 \
14 libxdamage1 \
15 libxrandr2 \
16 libgbm-dev \
17 libasound2 \
18 fonts-liberation \
19 libxss1 \
20 libatk-bridge2.0-0 \
21 libgtk-3-0 \
22 libnss3 \
23 libxshmfence1 \
24 ffmpeg \
25 --no-install-recommends \
26 && rm -rf /var/lib/apt/lists/*
27
28# Check preinstalled packages
29RUN npm ls crawlee apify puppeteer playwright
30
31# Copy just package.json and package-lock.json
32COPY package*.json ./
33
34# Install NPM packages, skip optional and development dependencies to keep the image small
35RUN npm --quiet set progress=false \
36 && npm install --omit=dev --omit=optional \
37 && echo "Installed NPM packages:" \
38 && (npm list --omit=dev --all || true) \
39 && echo "Node.js version:" \
40 && node --version \
41 && echo "NPM version:" \
42 && npm --version \
43 && rm -r ~/.npm
44
45# Copy the remaining files and directories with the source code
46COPY . ./
47
48# Set the environment variable for Puppeteer to use without sandboxing
49ENV PUPPETEER_SKIP_DOWNLOAD=true
50
51# Switch back to non-root user
52USER myuser
53
54# Command to run your main application
55CMD ["node", "src/main.js"]
.actor/actor.json
1{
2 "actorSpecification": 1,
3 "name": "screenshot",
4 "title": "screenshot",
5 "description": "Screenshot selenium",
6 "version": "1.0",
7 "meta": {
8 "templateId": "Screenshot"
9 },
10 "input": "./input_schema.json",
11 "dockerfile": "./Dockerfile",
12 "buildTag": "latest",
13 "minMemoryMbytes": 4096,
14 "readme": "./README.md",
15 "storages": {
16 "dataset": {
17 "actorSpecification": 1,
18 "title": "Results",
19 "views": {
20 "results": {
21 "title": "results to scan",
22 "transformation": {
23 "fields": ["screenshot_image", "content_Type", "linkUrl", "screenshot_url"]
24 },
25 "display": {
26 "component": "table",
27 "properties": {
28 "screenshot_image": {
29 "label": "Screenshot Url",
30 "format": "image"
31 },
32 "content_Type": {
33 "label": "Content Type",
34 "format": "text"
35 },
36 "linkUrl": {
37 "label": "link Url",
38 "format": "link"
39 },
40 "screenshot_url": {
41 "label": "Screenshot IF Video",
42 "format": "link"
43 }
44 }
45 }
46 }
47 }
48 }
49 }
50}
.actor/input_schema.json
1{
2 "$schema": "http://json-schema.org/draft-07/schema#",
3 "title": "Input Schema",
4 "type": "object",
5 "schemaVersion": 1,
6 "properties": {
7 "fullPage": {
8 "title": "Full Page Screenshot --- If you Select the Output Format MP4 or GIF you might want to check out (MP4 or GIF Options Section)",
9 "type": "boolean",
10 "description": "Should the screenshot capture the full length of the page?",
11 "default": false
12 },
13 "linkUrls": {
14 "title": "Link URLs",
15 "type": "array",
16 "description": "The URLs to be processed (e.g., websites to take screenshots from).",
17 "default": ["https://apify.com"],
18 "editor": "stringList"
19 },
20 "outputFormat": {
21 "title": "Output Format",
22 "type": "string",
23 "description": "The format of the screenshot output (JPEG, PNG, GIF, MP4, or PDF).",
24 "enum": ["jpeg", "png", "pdf", "gif", "mp4"],
25 "enumTitles": ["JPEG", "PNG", "PDF", "GIF", "MP4"],
26 "default": "jpeg"
27 },
28 "waitUntil": {
29 "title": "Navigation Wait Condition",
30 "type": "string",
31 "description": "Specify when the navigation should be considered finished. Options are 'load' for when the load event is fired, or 'domcontentloaded' for when the DOM has been loaded.",
32 "editor": "select",
33 "default": "networkidle0",
34 "enum": ["load", "domcontentloaded", "networkidle0", "networkidle2"],
35 "enumTitles": ["Load (all resources loaded)", "DOM Content Loaded (HTML parsed)", "Network Idle (no network connections)", "Network Idle (minimal network connections)"]
36 },
37 "timeouT": {
38 "title": "Timeout",
39 "type": "integer",
40 "description": "Time in milliseconds to wait for the webpage to load.",
41 "default": 15,
42 "unit": "s"
43 },
44 "maxRetries": {
45 "title": "Maximum Retries",
46 "type": "integer",
47 "description": "The number of times to retry in case of a failure.",
48 "default": 3
49 },
50 "delayBeforeScreenshot": {
51 "title": "Delay Before Screenshot",
52 "type": "integer",
53 "description": "Time in seconds to wait before taking the screenshot.",
54 "default": 1500,
55 "unit": "ms"
56 },
57 "infiniteScroll": {
58 "sectionCaption": "MP4 or GIF Options",
59 "sectionDescription": "This section configures the output settings, specifically for MP4 or GIF formats, These options will be applied when MP4 or GIF is selected in the Output Format.",
60 "title": "Infinite Scroll - This process will not stop if the page has Infinite Scrolling",
61 "type": "boolean",
62 "description": "Determines if the page should scroll indefinitely. If true, the scrolling continues indefinitely; if false, the scrolling will stop after a specified timeout (10 seconds).",
63 "default": false
64 },
65 "timefullPagE": {
66 "title": "Time for Full Page Capture",
67 "type": "integer",
68 "description": "Time in seconds to allow for full-page capture before stopping. If infiniteScroll is true, this value is ignored.",
69 "default": 10,
70 "unit": "s"
71 },
72 "frameCounT": {
73 "title": "Frame Count",
74 "type": "integer",
75 "description": "The total number of frames to capture for the output video or GIF.",
76 "default": 10,
77 "unit": "frames"
78 },
79 "frameIntervaL": {
80 "title": "Frame Interval",
81 "type": "integer",
82 "description": "The interval in milliseconds between each captured frame.",
83 "default": 10,
84 "unit": "ms"
85 },
86 "frame": {
87 "title": "Frames per Second",
88 "type": "integer",
89 "description": "The number of frames to capture per second for the output.",
90 "default": 10,
91 "unit": "fps"
92 },
93 "scrollSteP": {
94 "title": "Scroll Step Distance This feature will only be applied if the 'Full Page Screenshot' option is selected",
95 "type": "integer",
96 "description": "Specifies the number of pixels to scroll down during full-page screenshots. This feature will only be applied if the 'Full Page Screenshot' option is selected; otherwise, it will not affect the scrolling behavior. A smaller scroll step allows for smoother scrolling and better image quality by ensuring that each frame is captured with minimal jumps. The default value is 300 pixels, but it can be adjusted for finer control over scrolling.",
97 "default": 300,
98 "unit": "px"
99
100 },
101 "printBackground": {
102 "sectionCaption": "PDF Options",
103 "sectionDescription": "This section configures the output settings, specifically for PDF format. These options will be applied when PDF is selected in the Output Format.",
104 "title": "Print Background",
105 "type": "boolean",
106 "description": "Configure whether to include the background graphics when generating a PDF.",
107 "default": true
108 },
109 "formaT": {
110 "title": "PDF Paper Format",
111 "type": "string",
112 "description": "Select the paper size to be used for generating the PDF.",
113 "editor": "select",
114 "default": "A4",
115 "enum": ["A4", "LETTER", "LEGAL", "TABLOID", "LEDGER", "A0", "A1", "A2", "A3", "A5", "A6"],
116 "enumTitles": ["A4", "Letter", "Legal", "Tabloid", "Ledger", "A0", "A1", "A2", "A3", "A5", "A6"]
117 },
118 "toP": {
119 "title": "Top Margin",
120 "type": "integer",
121 "description": "Specify the top margin for the PDF in millimeters.",
122 "default": 0,
123 "unit": "mm"
124
125
126 },
127 "righT": {
128 "title": "Right Margin",
129 "type": "integer",
130 "description": "Specify the right margin for the PDF in millimeters.",
131 "default": 0,
132 "unit": "mm"
133
134 },
135 "bottoM": {
136 "title": "Bottom Margin",
137 "type": "integer",
138 "description": "Specify the bottom margin for the PDF in millimeters.",
139 "default": 0,
140 "unit": "mm"
141
142 },
143 "lefT": {
144 "title": "Left Margin",
145 "type": "integer",
146 "description": "Specify the left margin for the PDF in millimeters.",
147 "default": 0,
148 "unit": "mm"
149
150 },
151 "device": {
152 "sectionCaption": "Output Results: Device Emulation or Custom Window Dimensions",
153 "sectionDescription":"Configure the output display by selecting a device for emulation or manually specifying the window width and height. If no device is chosen, the default dimensions of 1920x1080 will be applied.",
154 "title": "Device",
155 "type": "string",
156 "description": "Choose a device to emulate specific dimensions and settings. If no device is selected, the default resolution of 1920x1080 will be used.",
157 "editor": "select",
158 "enum": [
159 "Blackberry PlayBook", "Blackberry PlayBook landscape",
160 "BlackBerry Z30", "BlackBerry Z30 landscape",
161 "Galaxy Note 3", "Galaxy Note 3 landscape",
162 "Galaxy Note II", "Galaxy Note II landscape",
163 "Galaxy S III", "Galaxy S III landscape",
164 "Galaxy S5", "Galaxy S5 landscape",
165 "Galaxy S8", "Galaxy S8 landscape",
166 "Galaxy S9+", "Galaxy S9+ landscape",
167 "Galaxy Tab S4", "Galaxy Tab S4 landscape",
168 "iPad", "iPad landscape",
169 "iPad (gen 6)", "iPad (gen 6) landscape",
170 "iPad (gen 7)", "iPad (gen 7) landscape",
171 "iPad Mini", "iPad Mini landscape",
172 "iPad Pro", "iPad Pro landscape",
173 "iPad Pro 11", "iPad Pro 11 landscape",
174 "iPhone 4", "iPhone 4 landscape",
175 "iPhone 5", "iPhone 5 landscape",
176 "iPhone 6", "iPhone 6 landscape",
177 "iPhone 6 Plus", "iPhone 6 Plus landscape",
178 "iPhone 7", "iPhone 7 landscape",
179 "iPhone 7 Plus", "iPhone 7 Plus landscape",
180 "iPhone 8", "iPhone 8 landscape",
181 "iPhone 8 Plus", "iPhone 8 Plus landscape",
182 "iPhone SE", "iPhone SE landscape",
183 "iPhone X", "iPhone X landscape",
184 "iPhone XR", "iPhone XR landscape",
185 "iPhone 11", "iPhone 11 landscape",
186 "iPhone 11 Pro", "iPhone 11 Pro landscape",
187 "iPhone 11 Pro Max", "iPhone 11 Pro Max landscape",
188 "iPhone 12", "iPhone 12 landscape",
189 "iPhone 12 Pro", "iPhone 12 Pro landscape",
190 "iPhone 12 Pro Max", "iPhone 12 Pro Max landscape",
191 "iPhone 12 Mini", "iPhone 12 Mini landscape",
192 "iPhone 13", "iPhone 13 landscape",
193 "iPhone 13 Pro", "iPhone 13 Pro landscape",
194 "iPhone 13 Pro Max", "iPhone 13 Pro Max landscape",
195 "iPhone 13 Mini", "iPhone 13 Mini landscape",
196 "iPhone 14", "iPhone 14 landscape",
197 "iPhone 14 Plus", "iPhone 14 Plus landscape",
198 "iPhone 14 Pro", "iPhone 14 Pro landscape",
199 "iPhone 14 Pro Max", "iPhone 14 Pro Max landscape",
200 "iPhone 15", "iPhone 15 landscape",
201 "iPhone 15 Plus", "iPhone 15 Plus landscape",
202 "iPhone 15 Pro", "iPhone 15 Pro landscape",
203 "iPhone 15 Pro Max", "iPhone 15 Pro Max landscape",
204 "JioPhone 2", "JioPhone 2 landscape",
205 "Kindle Fire HDX", "Kindle Fire HDX landscape",
206 "LG Optimus L70", "LG Optimus L70 landscape",
207 "Microsoft Lumia 550",
208 "Microsoft Lumia 950", "Microsoft Lumia 950 landscape",
209 "Nexus 10", "Nexus 10 landscape",
210 "Nexus 4", "Nexus 4 landscape",
211 "Nexus 5", "Nexus 5 landscape",
212 "Nexus 5X", "Nexus 5X landscape",
213 "Nexus 6", "Nexus 6 landscape",
214 "Nexus 6P", "Nexus 6P landscape",
215 "Nexus 7", "Nexus 7 landscape",
216 "Nokia Lumia 520", "Nokia Lumia 520 landscape",
217 "Nokia N9", "Nokia N9 landscape",
218 "Pixel 2", "Pixel 2 landscape",
219 "Pixel 2 XL", "Pixel 2 XL landscape",
220 "Pixel 3", "Pixel 3 landscape",
221 "Pixel 4", "Pixel 4 landscape",
222 "Pixel 4a (5G)", "Pixel 4a (5G) landscape",
223 "Pixel 5", "Pixel 5 landscape",
224 "Moto G4", "Moto G4 landscape"
225 ],
226 "enumTitles": [
227 "Blackberry PlayBook", "Blackberry PlayBook (Landscape)",
228 "BlackBerry Z30", "BlackBerry Z30 (Landscape)",
229 "Galaxy Note 3", "Galaxy Note 3 (Landscape)",
230 "Galaxy Note II", "Galaxy Note II (Landscape)",
231 "Galaxy S III", "Galaxy S III (Landscape)",
232 "Galaxy S5", "Galaxy S5 (Landscape)",
233 "Galaxy S8", "Galaxy S8 (Landscape)",
234 "Galaxy S9+", "Galaxy S9+ (Landscape)",
235 "Galaxy Tab S4", "Galaxy Tab S4 (Landscape)",
236 "iPad", "iPad (Landscape)",
237 "iPad (Gen 6)", "iPad (Gen 6) (Landscape)",
238 "iPad (Gen 7)", "iPad (Gen 7) (Landscape)",
239 "iPad Mini", "iPad Mini (Landscape)",
240 "iPad Pro", "iPad Pro (Landscape)",
241 "iPad Pro 11", "iPad Pro 11 (Landscape)",
242 "iPhone 4", "iPhone 4 (Landscape)",
243 "iPhone 5", "iPhone 5 (Landscape)",
244 "iPhone 6", "iPhone 6 (Landscape)",
245 "iPhone 6 Plus", "iPhone 6 Plus (Landscape)",
246 "iPhone 7", "iPhone 7 (Landscape)",
247 "iPhone 7 Plus", "iPhone 7 Plus (Landscape)",
248 "iPhone 8", "iPhone 8 (Landscape)",
249 "iPhone 8 Plus", "iPhone 8 Plus (Landscape)",
250 "iPhone SE", "iPhone SE (Landscape)",
251 "iPhone X", "iPhone X (Landscape)",
252 "iPhone XR", "iPhone XR (Landscape)",
253 "iPhone 11", "iPhone 11 (Landscape)",
254 "iPhone 11 Pro", "iPhone 11 Pro (Landscape)",
255 "iPhone 11 Pro Max", "iPhone 11 Pro Max (Landscape)",
256 "iPhone 12", "iPhone 12 (Landscape)",
257 "iPhone 12 Pro", "iPhone 12 Pro (Landscape)",
258 "iPhone 12 Pro Max", "iPhone 12 Pro Max (Landscape)",
259 "iPhone 12 Mini", "iPhone 12 Mini (Landscape)",
260 "iPhone 13", "iPhone 13 (Landscape)",
261 "iPhone 13 Pro", "iPhone 13 Pro (Landscape)",
262 "iPhone 13 Pro Max", "iPhone 13 Pro Max (Landscape)",
263 "iPhone 13 Mini", "iPhone 13 Mini (Landscape)",
264 "iPhone 14", "iPhone 14 (Landscape)",
265 "iPhone 14 Plus", "iPhone 14 Plus (Landscape)",
266 "iPhone 14 Pro", "iPhone 14 Pro (Landscape)",
267 "iPhone 14 Pro Max", "iPhone 14 Pro Max (Landscape)",
268 "iPhone 15", "iPhone 15 (Landscape)",
269 "iPhone 15 Plus", "iPhone 15 Plus (Landscape)",
270 "iPhone 15 Pro", "iPhone 15 Pro (Landscape)",
271 "iPhone 15 Pro Max", "iPhone 15 Pro Max (Landscape)",
272 "JioPhone 2", "JioPhone 2 (Landscape)",
273 "Kindle Fire HDX", "Kindle Fire HDX (Landscape)",
274 "LG Optimus L70", "LG Optimus L70 (Landscape)",
275 "Microsoft Lumia 550",
276 "Microsoft Lumia 950", "Microsoft Lumia 950 (Landscape)",
277 "Nexus 10", "Nexus 10 (Landscape)",
278 "Nexus 4", "Nexus 4 (Landscape)",
279 "Nexus 5", "Nexus 5 (Landscape)",
280 "Nexus 5X", "Nexus 5X (Landscape)",
281 "Nexus 6", "Nexus 6 (Landscape)",
282 "Nexus 6P", "Nexus 6P (Landscape)",
283 "Nexus 7", "Nexus 7 (Landscape)",
284 "Nokia Lumia 520", "Nokia Lumia 520 (Landscape)",
285 "Nokia N9", "Nokia N9 (Landscape)",
286 "Pixel 2", "Pixel 2 (Landscape)",
287 "Pixel 2 XL", "Pixel 2 XL (Landscape)",
288 "Pixel 3", "Pixel 3 (Landscape)",
289 "Pixel 4", "Pixel 4 (Landscape)",
290 "Pixel 4a (5G)", "Pixel 4a (5G) (Landscape)",
291 "Pixel 5", "Pixel 5 (Landscape)",
292 "Moto G4", "Moto G4 (Landscape)"
293 ]
294 },
295 "window_Width": {
296 "title": "Window Width",
297 "type": "integer",
298 "description": "The width of the browser window in pixels.",
299 "default": 1920,
300 "unit": "px"
301 },
302 "window_Height": {
303 "title": "Window Height",
304 "type": "integer",
305 "description": "The height of the browser window in pixels.",
306 "default": 1080,
307 "unit": "px"
308 },
309 "scrollToBottom": {
310 "sectionCaption": "Scrolling Option",
311 "title": "Scroll to Bottom",
312 "type": "boolean",
313 "description": "Should the browser scroll to the bottom of the page before taking a screenshot?",
314 "default": false
315 },
316 "delayAfterScrolling": {
317 "title": "Delay After Scrolling",
318 "type": "integer",
319 "description": "Specify the delay (in milliseconds) after scrolling to the bottom before taking the screenshot. Only used if 'Wait for Network Idle After Scrolling' is not enabled.",
320 "default": 300,
321 "unit": "ms"
322
323 },
324 "cookies": {
325 "sectionCaption": "Cookies",
326 "sectionDescription": "You can use cookie editors such as [Cookie Editor](https://cookie-editor.com/) or [Copy Cookies](https://chromewebstore.google.com/detail/copy-cookies/jcbpglbplpblnagieibnemmkiamekcdg) to format cookies.",
327 "title": "Cookies",
328 "type": "array",
329 "default": [],
330 "description": "Cookies to be used for the browsing session, formatted as JSON objects.",
331 "editor": "json"
332 }
333 },
334 "required": ["linkUrls"],
335 "additionalProperties": true
336}
src/main.js
1import puppeteer from 'puppeteer';
2import path from 'path';
3import { Actor, Dataset } from 'apify';
4import fs from 'fs/promises';
5import { KnownDevices } from 'puppeteer';
6import { exec } from 'child_process';
7
8const wait = (milliseconds) => {
9 return new Promise(resolve => setTimeout(resolve, milliseconds));
10};
11
12class ScreenHot {
13 constructor(actorInput) {
14 this.linkUrls = actorInput.linkUrls;
15 this.fullPage = actorInput.fullPage;
16 this.waitUntil = actorInput.waitUntil;
17 this.device = actorInput.device;
18 this.timeouT = actorInput.timeouT;
19 this.maxRetries = actorInput.maxRetries;
20 this.window_Width = actorInput.window_Width;
21 this.window_Height = actorInput.window_Height;
22 this.delayBeforeScreenshot = actorInput.delayBeforeScreenshot;
23 this.outputFormat = actorInput.outputFormat;
24 this.scrollToBottom = actorInput.scrollToBottom;
25 this.delayAfterScrolling = actorInput.delayAfterScrolling;
26 this.cookies = actorInput.cookies;
27 this.formaT = actorInput.formaT;
28 this.printBackground = actorInput.printBackground;
29 this.toP = actorInput.toP;
30 this.righT = actorInput.righT;
31 this.bottoM = actorInput.bottoM;
32 this.lefT = actorInput.lefT;
33 this.frameCounT = actorInput.frameCounT;
34 this.frameIntervaL = actorInput.frameIntervaL;
35 this.infiniteScroll = actorInput.infiniteScroll; // Default to false if not provided
36 this.scrollStep = actorInput.scrollSteP;
37 this.timefullPagE = actorInput.timefullPagE;
38 this.timeout = actorInput.timeouT;
39 this.proxyConfig = actorInput.proxyConfig; // Accept proxy config in the constructor
40 this.userAgent = actorInput.userAgent || ''; // User-Agent passed by the user, if any
41
42 }
43
44 async loadUserAgentsFromFile(filePath) {
45 try {
46 const data = await fs.readFile(filePath, 'utf-8');
47 const userAgents = data.split('\n').map(line => line.trim()).filter(Boolean);
48 return userAgents;
49 } catch (error) {
50 console.error(`Error reading User-Agent file: ${error.message}`);
51 return [];
52 }
53 }
54
55 async selectRandomUserAgent() {
56 const userAgents = await this.loadUserAgentsFromFile('user-agents.txt');
57 if (userAgents.length > 0) {
58 const randomIndex = Math.floor(Math.random() * userAgents.length);
59 return userAgents[randomIndex];
60 } else {
61 return '';
62 }
63 }
64
65 async navigatePage(page, linkUrl) {
66 let timeout = this.timeouT * 1000;
67 for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
68 try {
69 await page.goto(linkUrl, { waitUntil: this.waitUntil, timeout: timeout });
70 return; // Exit if the page loads successfully
71 } catch (error) {
72 console.error(`Error navigating to ${linkUrl}: ${error}. Attempt ${attempt} of ${this.maxRetries}.`);
73 if (attempt < this.maxRetries) {
74 console.log(`Retrying ${linkUrl}...`);
75 } else {
76 throw new Error(`Failed to load ${linkUrl} after ${this.maxRetries} attempts.`);
77 }
78 }
79 }
80 }
81
82 async run() {
83
84 let content_Type;
85 let Buffer;
86 let fileName;
87 let filePath;
88
89 await fs.mkdir('Output_Files', { recursive: true });
90
91 // Initialize Apify Proxy if provided in input
92 let proxyUrl;
93 try {
94 if (this.proxyConfig) {
95 const proxyConfiguration = await Actor.createProxyConfiguration(this.proxyConfig);
96 const proxyInfo = await proxyConfiguration.newProxyInfo();
97 proxyUrl = proxyInfo.url; // Get the proxy URL
98 console.log(`Using proxy: ${proxyUrl}`);
99 }
100 } catch (error) {
101 console.error('Error setting up proxy:', error.message);
102 proxyUrl = null; // Proceed without proxy if there's an error
103 }
104
105 // Set browser arguments with or without proxy
106 const browserArgs = [
107 `--window-size=${this.window_Width},${this.window_Height}`,
108 '--no-sandbox',
109 '--disable-setuid-sandbox',
110 '--ignore-certificate-errors' // Ignore certificate errors for SSL bypass
111 ];
112 if (proxyUrl) {
113 browserArgs.push(`--proxy-server=${proxyUrl}`);
114 }
115
116 // Select a random User-Agent if not provided
117 if (!this.userAgent) {
118 this.userAgent = await this.selectRandomUserAgent();
119 }
120
121 let browser;
122 try {
123 // Launch the browser with proxy, SSL bypass, and User-Agent settings
124 browser = await puppeteer.launch({
125 headless: true,
126 args: browserArgs,
127 ignoreHTTPSErrors: true // This option allows bypassing SSL certificate errors
128 });
129 console.log('Browser launched successfully with SSL bypass.');
130 } catch (error) {
131 console.error('Failed to launch the browser:', error.message);
132 throw new Error('Browser launch failed due to proxy, SSL issues, or other problems.');
133 }
134
135 try {
136 const page = await browser.newPage();
137
138 // Set the User-Agent
139 if (this.userAgent) {
140 await page.setUserAgent(this.userAgent);
141 console.log(`User-Agent set: ${this.userAgent}`);
142 }
143
144
145 // If a device is provided, emulate it
146 if (this.device) {
147 if (KnownDevices[this.device]) {
148 console.log(`Emulating device: ${this.device}`);
149 await page.emulate(KnownDevices[this.device]);
150 } else {
151 console.log(`Setting custom viewport for ${this.device}`);
152 await page.setViewport({ width: this.window_Width, height: this.window_Height });
153 }
154 } else {
155 await page.setViewport({ width: this.window_Width, height: this.window_Height });
156 }
157
158 // Set cookies before navigation
159 if (this.cookies.length > 0) {
160 console.log(`Setting cookies: ${this.cookies.length}`);
161 await page.setCookie(...this.cookies);
162 } else {
163 console.log(`Continuing without cookies`);
164 }
165
166 for (const linkUrl of this.linkUrls) {
167 console.log(`Processing URL: ${linkUrl}`);
168 try {
169 await this.navigatePage(page, linkUrl);
170
171 // Scrolling feature
172 if (this.scrollToBottom) {
173 await this.scrollPageToBottom(page, this.delayAfterScrolling);
174 }
175 // Delay before capturing output
176 if (this.delayBeforeScreenshot > 0) {
177 console.log(`Waiting ${this.delayBeforeScreenshot} milliseconds before capturing output.`);
178 await wait(this.delayBeforeScreenshot);
179 }
180 // Handle different output formats
181 if (this.outputFormat === 'mp4' || this.outputFormat === 'gif') {
182 content_Type = this.outputFormat === 'gif' ? "image/gif" : "video/mp4";
183 const frameDir = path.join('Output_Files', 'frames');
184 await fs.mkdir(frameDir, { recursive: true });
185 const frameCount = this.frameCounT; // Number of frames to capture
186 const frameInterval = this.frameIntervaL; // Capture frame every second
187
188 // Capture frames based on the fullPage flag
189 if (this.fullPage === true) {
190 // Capture frames and scroll down
191 await this.fullPageVideo(page, frameDir, this.scrollStep);
192 } else {
193 for (let i = 0; i < frameCount; i++) {
194 const framePath = path.join(frameDir, `frame${i.toString().padStart(3, '0')}.png`);
195 await page.screenshot({ path: framePath });
196 console.log(`Captured frame: ${framePath}`);
197 await wait(frameInterval / 1000); // Wait before capturing next frame
198 }
199 }
200
201 // Check if frames are saved
202 const savedFrames = await fs.readdir(frameDir);
203 console.log(`Frames saved: ${savedFrames.length}`);
204 if (savedFrames.length === 0) {
205 throw new Error("No frames captured, cannot create GIF or video.");
206 }
207
208 // Create the video or GIF
209 const scaleWidth = this.window_Width || -1; // Get the scale width from user input (or default to -1)
210 const scaleHeight = this.window_Height || -1; // Get the scale height from user input (or default to -1)
211
212 // Ensure width and height are even numbers
213 const adjustedWidth = scaleWidth === -1 ? -1 : (scaleWidth % 2 === 0 ? scaleWidth : scaleWidth - 1);
214 const adjustedHeight = scaleHeight === -1 ? -1 : (scaleHeight % 2 === 0 ? scaleHeight : scaleHeight - 1);
215
216 const scaleOption = `scale=${adjustedWidth}:${adjustedHeight}`; // Scaling option for ffmpeg
217
218 fileName = `${Array.from({ length: 16 }, () => Math.random().toString(36)[2]).join('')}.${this.outputFormat}`;
219 filePath = path.join('Output_Files', fileName);
220
221 const ffmpegCommand = this.outputFormat === 'gif'
222 ? `ffmpeg -f image2 -i ${frameDir}/frame%03d.png -vf "fps=10,${scaleOption}:flags=lanczos" ${filePath}`
223 : `ffmpeg -framerate 1 -i ${frameDir}/frame%03d.png -vf "${scaleOption},format=yuv420p" -c:v libx264 -r 30 -pix_fmt yuv420p ${filePath}`;
224
225 await new Promise((resolve, reject) => {
226 exec(ffmpegCommand, (error, stdout, stderr) => {
227 if (error) {
228 console.error(`Error creating ${this.outputFormat}: ${error.message}`);
229 console.error(`FFmpeg stderr: ${stderr}`);
230 console.error(`FFmpeg stdout: ${stdout}`);
231 reject(new Error(`FFmpeg command failed with error: ${error.message}`));
232 return;
233 }
234 console.log(`Created ${this.outputFormat} at: ${filePath}`);
235 resolve();
236 });
237 });
238
239 // Clean up frames after creating the video or GIF
240 await fs.rm(frameDir, { recursive: true });
241 Buffer = await fs.readFile(filePath);
242 }
243 else if (this.outputFormat === 'pdf') {
244 if (this.delayBeforeScreenshot > 0) {await wait(this.delayBeforeScreenshot);}
245
246 // Generate PDF
247 content_Type = "application/pdf";
248 fileName = `${Array.from({ length: 16 }, () => Math.random().toString(36)[2]).join('')}.${this.outputFormat}`;
249 filePath = path.join('Output_Files', fileName);
250 Buffer = await page.pdf({
251 path: filePath,
252 format: this.formaT,
253 printBackground: this.printBackground,
254 margin: {
255 top: this.toP,
256 right: this.righT,
257 bottom: this.bottoM,
258 left: this.lefT,
259 },
260 });
261 console.log(`PDF saved as: ${filePath}`);
262
263 }
264 else if (this.outputFormat === 'jpeg' || this.outputFormat === 'png') {
265 // Capture screenshot
266 content_Type = this.outputFormat === 'jpeg' ? "image/jpeg" : "image/png";
267 fileName = `${Array.from({ length: 16 }, () => Math.random().toString(36)[2]).join('')}.${this.outputFormat}`;
268 filePath = path.join('Output_Files', fileName);
269 Buffer = await page.screenshot({ path: filePath, type: this.outputFormat, fullPage: this.fullPage });
270 }
271 // Upload the buffer to Apify Key-Value Store
272 const Apify = await Actor.openKeyValueStore();
273 await Apify.setValue(fileName, Buffer, { contentType: content_Type });
274 const storeId = process.env.APIFY_DEFAULT_KEY_VALUE_STORE_ID;
275
276 // Generate URL
277 const screenshot_url = `https://api.apify.com/v2/key-value-stores/${storeId}/records/${fileName}`;
278 console.log(`- ${screenshot_url}`);
279 const screenshot_image = screenshot_url
280 await Dataset.pushData({ screenshot_image, content_Type, linkUrl, screenshot_url });
281
282 } catch (error) {
283 console.error(`Error processing ${linkUrl}: ${error}`);
284 }
285 }
286
287 } finally {
288 await browser.close();
289 }
290 }
291
292 async scrollPageToBottom(page, delayAfterScrolling) {
293 console.log('Scrolling to the bottom of the page...');
294
295 const startTime = Date.now(); // Record the start time
296 const timefullPage = this.timefullPagE*1000; // Set the time limit to 40s (40,000 milliseconds)
297 console.log(`Time for Full Page Capture limit: ${this.timefullPagE} seconds`);
298
299
300 await page.evaluate(async (startTime, timefullPage, infiniteScroll) => {
301 await new Promise((resolve) => {
302 const distance = 100;
303 const timer = setInterval(() => {
304 const now = Date.now();
305 const scrollHeight = document.body.scrollHeight;
306 window.scrollBy(0, distance);
307
308 // Stop if reaching the bottom or if timefullPage occurs (when infiniteScroll is false)
309 if (window.innerHeight + window.scrollY >= scrollHeight || (!infiniteScroll && (now - startTime) >= timefullPage)) {
310 clearInterval(timer);
311 resolve();
312 }
313 }, 100);
314 });
315 }, startTime, timefullPage, this.infiniteScroll); // Pass infiniteScroll option to page context
316
317 if (delayAfterScrolling > 0) {
318 console.log(`Waiting ${delayAfterScrolling} milliseconds after scrolling.`);
319 await wait(delayAfterScrolling);
320 }
321 }
322
323 async fullPageVideo(page, frameDir, scrollStep) {
324 let frameCount = 0;
325 const frameInterval = this.frameIntervaL; // Interval in milliseconds between screenshots
326 let previousHeight;
327 let currentHeight = 0;
328
329 const startTime = Date.now(); // Record the start time
330 const timefullPage = this.timefullPagE*1000; // Set the time limit to 40s (40,000 milliseconds)
331
332 // Scroll down, take a screenshot at each step, and stop when reaching the bottom or timefullPage
333 do {
334 const now = Date.now();
335
336 // Check timefullPage only if infiniteScroll is false
337 if (!this.infiniteScroll && (now - startTime) >= timefullPage) {
338 console.log(`Stopping scrolling in ${this.timefullPagE} seconds due to (Time for Full Page Capture) Section.`);
339 break;
340 }
341
342 const fileName = path.join(frameDir, `frame${frameCount.toString().padStart(3, '0')}.png`);
343 await page.screenshot({ path: fileName });
344 console.log(`Captured frame: ${fileName}`);
345 frameCount++;
346 await wait(frameInterval / 1000); // Wait before capturing the next frame
347
348 // Scroll down by a smaller step for smoother scrolling
349 previousHeight = await page.evaluate(() => window.scrollY);
350 await page.evaluate((step) => window.scrollBy(0, step), scrollStep); // Scroll by scrollStep pixels
351
352
353 // Check if the page height has changed
354 currentHeight = await page.evaluate(() => window.scrollY);
355
356 } while (currentHeight > previousHeight); // Continue scrolling until no new content is loaded (bottom reached)
357
358 }
359}
360
361(async () => {
362 await Actor.init(); // Initialize the Actor
363 const actorInput = await Actor.getInput() || {};
364 const screenHot = new ScreenHot(actorInput);
365 await screenHot.run();
366 await Actor.exit(); // Exit the Actor
367})();
.editorconfig
1root = true
2
3[*]
4indent_style = space
5indent_size = 4
6charset = utf-8
7trim_trailing_whitespace = true
8insert_final_newline = true
9end_of_line = lf
.eslintrc
1{
2 "extends": "@apify",
3 "root": true
4}
package.json
1{
2 "name": "crawlee-puppeteer-javascript",
3 "version": "0.0.1",
4 "type": "module",
5 "description": "This is an example of an Apify actor.",
6 "dependencies": {
7 "apify": "^3.1.10",
8 "crawlee": "^3.5.4",
9 "puppeteer": "^23.6.0"
10 },
11 "devDependencies": {
12 "@apify/eslint-config": "^0.4.0",
13 "eslint": "^8.50.0"
14 },
15 "scripts": {
16 "start": "node src/main.js",
17 "test": "echo \"Error: oops, the actor has no tests yet, sad!\" && exit 1"
18 },
19 "author": "It's not you it's me",
20 "license": "ISC"
21}
user-agents.txt
1Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.0.0 Safari/537.36
2Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0
3Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:112.0) Gecko/20100101 Firefox/112.0
4Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/113.0.0.0 Safari/537.36
5Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36
6Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
7Mozilla/5.0 (Macintosh; Intel Mac OS X 13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
8Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:118.0) Gecko/20100101 Firefox/118.0
9Mozilla/5.0 (Macintosh; Intel Mac OS X 13_4; rv:118.0) Gecko/20100101 Firefox/118.0
10Mozilla/5.0 (Macintosh; Intel Mac OS X 13_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15
11Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36 Edg/117.0.2045.55
12Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36 OPR/101.0.4843.43
13Mozilla/5.0 (Macintosh; Intel Mac OS X 13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36 Edg/117.0.2045.55
14Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36 Vivaldi/5.7.2921.66
15Mozilla/5.0 (Windows NT 11.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36
16Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36 Brave/1.58.113
17Mozilla/5.0 (Macintosh; Intel Mac OS X 13_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.5938.132 Safari/537.36 Brave/1.58.113
18Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.110 Safari/537.36
19Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15
20Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:114.0) Gecko/20100101 Firefox/114.0
21Mozilla/5.0 (Macintosh; Intel Mac OS X 12_5_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.102 Safari/537.36
22Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/116.0.1938.69
23Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15
24Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36
25Mozilla/5.0 (Macintosh; Intel Mac OS X 12_4_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15
26Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.171 Safari/537.36
27Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36
28Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4 Safari/605.1.15
29Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/116.0.1938.81 Safari/537.36
30Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15
31Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:116.0) Gecko/20100101 Firefox/116.0
32Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6_8) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.8 Safari/605.1.15
33Mozilla/5.0 (Macintosh; Intel Mac OS X 13_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.4.1 Safari/605.1.15
34Mozilla/5.0 (Macintosh; Intel Mac OS X 13_2) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605.1.15
35Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.110 Safari/537.36
36Mozilla/5.0 (Macintosh; Intel Mac OS X 13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.6 Safari/605.1.15
37Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:115.0) Gecko/20100101 Firefox/115.0
38Mozilla/5.0 (Macintosh; Intel Mac OS X 13_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.5 Safari/605.1.15
39Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/117.0.2045.47 Safari/537.36
40Mozilla/5.0 (Macintosh; Intel Mac OS X 13_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15
41Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.199 Safari/537.36
42Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.1 Safari/605.1.15
43Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Edge/114.0.1823.43 Safari/537.36
44Mozilla/5.0 (Macintosh; Intel Mac OS X 12_5_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.1 Safari/605.1.15
45Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36
46Mozilla/5.0 (Macintosh; Intel Mac OS X 12_6_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.6.6 Safari/605.1.15
47Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:113.0) Gecko/20100101 Firefox/113.0
48Mozilla/5.0 (Macintosh; Intel Mac OS X 13_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.3 Safari/605
Developer
Maintained by Community
Actor metrics
- 9 monthly users
- 3 stars
- 91.7% runs succeeded
- Created in Oct 2024
- Modified 4 days ago
Categories