Skip to content

Commit a90b462

Browse files
authored
fix: 673 scrape url validation (#22)
* fix: improve crawler type description and options * feat: add url validation in scrape-single-url action * feat: implement url validation method and add tests for scrape-single-url action * chore: update version of scrape-single-url action to 0.0.4 * chore: remove unecesary test file * chore: lowered scrape-single-url version
1 parent 5214fe3 commit a90b462

2 files changed

Lines changed: 36 additions & 14 deletions

File tree

components/apify/actions/scrape-single-url/scrape-single-url.mjs

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
import apify from "../../apify.app.mjs";
22
import { WCC_ACTOR_ID } from "../../common/constants.mjs";
33
import { ACTOR_JOB_STATUSES } from "@apify/consts";
4+
import { ConfigurationError } from "@pipedream/platform";
45

56
export default {
67
key: "apify-scrape-single-url",
78
name: "Scrape Single URL",
89
description: "Executes a scraper on a specific website and returns its content as HTML. This action is perfect for extracting content from a single page. [See the documentation](https://docs.apify.com/sdk/js/docs/examples/crawl-single-url)",
9-
version: "0.1.3",
10+
version: "0.1.4",
1011
annotations: {
1112
destructiveHint: false,
1213
openWorldHint: true,
@@ -24,29 +25,50 @@ export default {
2425
crawlerType: {
2526
type: "string",
2627
label: "Crawler Type",
27-
description: "Select the crawling engine:\n- **Headless web browser** - Useful for modern websites with anti-scraping protections and JavaScript rendering. It recognizes common blocking patterns like CAPTCHAs and automatically retries blocked requests through new sessions. However, running web browsers is more expensive as it requires more computing resources and is slower. It is recommended to use at least 8 GB of RAM.\n- **Stealthy web browser** (default) - Another headless web browser with anti-blocking measures enabled. Try this if you encounter bot protection while scraping. For best performance, use with Apify Proxy residential IPs. \n- **Raw HTTP client** - High-performance crawling mode that uses raw HTTP requests to fetch the pages. It is faster and cheaper, but it might not work on all websites.",
28+
description: "Select the crawling engine:\n- **Adaptive** - Automatically switches between raw HTTP for static pages and a headless browser for dynamic pages to get the maximum performance wherever possible.\n- **Firefox (Headless Browser)** (default) - Headless Firefox with Playwright and anti-blocking measures enabled. Reliable, renders JavaScript content, and best at avoiding blocking, but might be slow. For best performance, use with Apify Proxy residential IPs.\n- **Cheerio (Raw HTTP)** - High-performance crawling mode that uses raw HTTP requests to fetch the pages. Fastest and cheapest, but doesn't render JavaScript content.",
2829
options: [
2930
{
30-
label: "Headless browser (stealthy Firefox+Playwright) - Very reliable, best in avoiding blocking, but might be slow",
31-
value: "playwright:firefox",
31+
label: "Adaptive",
32+
value: "playwright:adaptive",
3233
},
3334
{
34-
label: "Headless browser (Chrome+Playwright) - Reliable, but might be slow",
35-
value: "playwright:chrome",
35+
label: "Firefox (Headless Browser)",
36+
value: "playwright:firefox",
3637
},
3738
{
38-
label: "Raw HTTP client (Cheerio) - Extremely fast, but cannot handle dynamic content",
39+
label: "Cheerio (Raw HTTP)",
3940
value: "cheerio",
4041
},
41-
{
42-
label: "The crawler automatically switches between raw HTTP for static pages and Chrome browser (via Playwright) for dynamic pages, to get the maximum performance wherever possible.",
43-
value: "playwright:adaptive",
44-
},
4542
],
4643
default: "playwright:firefox",
4744
},
4845
},
46+
methods: {
47+
// new URL() accepts hosts with empty labels (e.g. "google..com"), so check explicitly
48+
validateUrl(url) {
49+
let parsedUrl;
50+
try {
51+
parsedUrl = new URL(url);
52+
} catch {
53+
throw new ConfigurationError(`Invalid URL "${url}": could not be parsed. Use a valid absolute URL like https://example.com.`);
54+
}
55+
56+
if (![
57+
"http:",
58+
"https:",
59+
].includes(parsedUrl.protocol)) {
60+
throw new ConfigurationError(`Invalid URL "${url}": only http and https protocols are supported. Use a valid absolute URL like https://example.com.`);
61+
}
62+
63+
if (parsedUrl.hostname.split(".").some((label) => label.length === 0)) {
64+
throw new ConfigurationError(`Invalid URL "${url}": host contains an empty label. Use a valid absolute URL like https://example.com.`);
65+
}
66+
},
67+
},
4968
async run({ $ }) {
69+
const url = this.url?.trim();
70+
this.validateUrl(url);
71+
5072
const {
5173
status,
5274
defaultDatasetId,
@@ -60,7 +82,7 @@ export default {
6082
maxResults: 1,
6183
startUrls: [
6284
{
63-
url: this.url,
85+
url,
6486
},
6587
],
6688
},
@@ -74,7 +96,7 @@ export default {
7496
datasetId: defaultDatasetId,
7597
});
7698

77-
$.export("$summary", "Run of Web Content Crawler finished successfully.");
99+
$.export("$summary", "Scraped the URL successfully.");
78100
return items[0];
79101
},
80102
};

components/apify_oauth/actions/scrape-single-url/scrape-single-url.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const props = adjustPropDefinitions(others.props, app);
1111
export default {
1212
...others,
1313
key: "apify_oauth-scrape-single-url",
14-
version: "0.0.2",
14+
version: "0.0.3",
1515
name,
1616
description,
1717
type,

0 commit comments

Comments
 (0)