From 5866b9746bcf1a6136c9571719ca90268dac7a33 Mon Sep 17 00:00:00 2001 From: Arto Gahr Date: Fri, 29 May 2026 13:52:11 +0200 Subject: [PATCH 1/4] docs: clarify Gmail and Drive integrations work on Actors and tasks --- sources/platform/integrations/data-storage/drive.md | 7 +++---- .../integrations/workflows-and-notifications/gmail.md | 5 ++--- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/sources/platform/integrations/data-storage/drive.md b/sources/platform/integrations/data-storage/drive.md index 126103484a..44d1c83138 100644 --- a/sources/platform/integrations/data-storage/drive.md +++ b/sources/platform/integrations/data-storage/drive.md @@ -1,6 +1,6 @@ --- title: Google Drive integration -description: Automatically save Apify Actor run results to Google Drive. Set up the integration on your task to upload files to your Drive after each successful run. +description: Automatically save Apify Actor run results to Google Drive. Set up the integration on an Actor or saved task to upload files after each successful run. sidebar_label: Google Drive sidebar_position: 3 slug: /integrations/drive @@ -8,7 +8,7 @@ slug: /integrations/drive import ThirdPartyDisclaimer from '@site/sources/_partials/_third-party-integration.mdx'; -Save Apify Actor run results directly to Google Drive. Set up the integration on your task to automatically upload files after each successful run. +Save Apify Actor run results directly to Google Drive. Set up the integration on an Actor or saved task to automatically upload files after each successful run. @@ -18,11 +18,10 @@ To use the Apify integration for Google Drive, you will need: - An [Apify account](https://console.apify.com/). - A Google account -- A saved Actor Task ## Set up Google Drive integration -1. Head over to **Integrations** tab in your saved task and click on the **Upload results to GDrive** integration. +1. Head over to the **Integrations** tab of your Actor or saved task and click on the **Upload results to GDrive** integration. ![Google Drive integration](../images/google/google-integrations-add.png) diff --git a/sources/platform/integrations/workflows-and-notifications/gmail.md b/sources/platform/integrations/workflows-and-notifications/gmail.md index cb2fd4814a..357643504d 100644 --- a/sources/platform/integrations/workflows-and-notifications/gmail.md +++ b/sources/platform/integrations/workflows-and-notifications/gmail.md @@ -8,7 +8,7 @@ slug: /integrations/gmail import ThirdPartyDisclaimer from '@site/sources/_partials/_third-party-integration.mdx'; -Send automated email notifications with Actor run results to any Gmail address. Set up the integration on your task to receive emails after each successful run. +Send automated email notifications with Actor run results to any Gmail address. Set up the integration on an Actor or saved task to receive emails after each successful run. @@ -18,11 +18,10 @@ To use the Apify integration for Gmail, you will need: - An [Apify account](https://console.apify.com/). - A Google account -- A saved Actor Task ## Set up Gmail integration -1. Head over to **Integrations** tab in your task and click on the **Send results email via Gmail** integration. +1. Head over to the **Integrations** tab of your Actor or saved task and click on the **Send results email via Gmail** integration. ![Google Drive integration](../images/google/google-integrations-add.png) From 6c615e7e878fd621d1cb78032627009dcd7244b9 Mon Sep 17 00:00:00 2001 From: Arto Gahr Date: Fri, 29 May 2026 13:52:14 +0200 Subject: [PATCH 2/4] docs: fix inconsistencies in expert scraping course --- .../expert_scraping_with_apify/actors_webhooks.md | 12 +++++++++--- .../managing_source_code.md | 12 ++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/sources/academy/platform/expert_scraping_with_apify/actors_webhooks.md b/sources/academy/platform/expert_scraping_with_apify/actors_webhooks.md index c5c7d1d4a8..8f661f23cc 100644 --- a/sources/academy/platform/expert_scraping_with_apify/actors_webhooks.md +++ b/sources/academy/platform/expert_scraping_with_apify/actors_webhooks.md @@ -20,7 +20,7 @@ Thus far, you've run Actors on the platform and written an Actor of your own, wh ## Advanced Actor overview {#advanced-actors} -In this course, we'll be working out of the Amazon scraper project from the **Web scraping basics for JavaScript devs** course. If you haven't already built that project, you can do it in [three short lessons](../../webscraping/scraping_basics_legacy/challenge/index.md). We've made a few small modifications to the project with the Apify SDK, but 99% of the code is still the same. +In this course, we'll be working out of the Amazon scraper built in the [Web scraping basics for JavaScript devs](../../webscraping/scraping_basics_legacy/index.md) course. If you haven't gone through it yet, we recommend doing so - it covers the fundamentals this project is built on. If you'd rather skip straight to this course, a working implementation is available at [academy-amazon-scraper](https://github.com/apify-projects/academy-amazon-scraper). Take another look at the files within your Amazon scraper project. You'll notice that there is a **Dockerfile**. Every single Actor has a Dockerfile (the Actor's **Image**) which tells Docker how to spin up a container on the Apify platform which can successfully run the Actor's code. "Apify Actors" is a serverless platform that runs multiple Docker containers. For a deeper understanding of Actor Dockerfiles, refer to the [Apify Actor Dockerfile docs](/sdk/js/docs/guides/docker-images#example-dockerfile). @@ -46,9 +46,15 @@ Prior to moving forward, please read over these resources: ## Our task {#our-task} -In this task, we'll be building on top of what we already created in the [Web scraping basics for JavaScript devs](../../webscraping/scraping_basics_legacy/challenge/index.md) course's final challenge, so keep those files safe! +In this task, we'll be building on top of the [academy-amazon-scraper](https://github.com/apify-projects/academy-amazon-scraper). -Once our Amazon Actor has completed its run, we will, rather than sending an email to ourselves, call an Actor through a webhook. The Actor called will be a new Actor that we will create together, which will take the dataset ID as input, then subsequently filter through all of the results and return only the cheapest one for each product. All of the results of the Actor will be pushed to its default dataset. +Once our Amazon Actor has completed its run, we might want to send an email to ourselves, but instead of that let's call another Actor through a webhook. The Actor called will be a new Actor that you will create, which will take the dataset ID as input, then filter through all of the results and return only the cheapest result for each unique ASIN. All of the results of the Actor will be pushed to its default dataset. + +:::note Starter repo + +The [starter repo](https://github.com/apify-projects/academy-amazon-scraper) produces one result per product, so in practice the filtering Actor will pass every item through unchanged. That's fine - the goal here is to learn how to pass data between Actors using webhooks, not to do complex filtering. + +::: [**Solution**](./solutions/integrating_webhooks.md) diff --git a/sources/academy/platform/expert_scraping_with_apify/managing_source_code.md b/sources/academy/platform/expert_scraping_with_apify/managing_source_code.md index e9cf36d342..6aec1b39f0 100644 --- a/sources/academy/platform/expert_scraping_with_apify/managing_source_code.md +++ b/sources/academy/platform/expert_scraping_with_apify/managing_source_code.md @@ -40,19 +40,15 @@ First, let's create a repository. This can be done [in a number of ways](https:/ Then, we'll run the commands it tells us in our terminal (while within the **demo-actor** directory) to initialize the repository locally, and then push all of the files to the remote one. -After you've created your repo, navigate on the Apify platform to the Actor we called **demo-actor**. In the **Source** tab, click the dropdown menu under **Source code** and select **Git repository**. By default, this is set to **Web IDE**, which is what we've been using so far. +After you've created your repo, in the Apify platform navigate to your Amazon scraping Actor that we called **demo-actor**. In the **Source** tab, click the dropdown menu under **Source code** and select **Git repository**. By default, this is set to **Web IDE**, which is what we've been using so far. ![Select source code location](./images/select-source-location.png) Then, go ahead and paste the link to your repository into the **Git URL** text field and click **Save**. -The final step is to click on **API** in the top right corner of your Actor's page: +The final step is to check your **Build settings**. Under the Git URL field, there're two options: **Automatic builds** and **Manual builds**. Select **Automatic builds**. It tells Apify to rebuild your Actor whenever you push to GitHub, with no extra configuration needed. -![API button](./images/api-button.jpg) - -And scroll through all of the links until you find the **Build Actor** API endpoint. Copy this endpoint's URL, then head back over to your GitHub repository and navigate to **Settings > Webhooks > Add webhook**. The final thing to do is to paste the URL and save the webhook. - -![Adding a webhook to your GitHub repo](../../../platform/actors/development/deployment/images/ci-github-integration.png) +![Build settings with Automatic builds selected](./images/build-settings.webp) And you're done! 🎉 @@ -60,7 +56,7 @@ And you're done! 🎉 This was a bit of overhead, but the good news is that you don't ever have to configure this stuff again for this Actor. Now, every time the content of your **main**/**master** branch changes, the Actor on the Apify platform will rebuild based on the newest code. -Think of it as combining two steps into one! Normally, you'd have to do a `git push` from your terminal in order to get the newest code onto GitHub, then run `apify push` to push it to the platform. +Think of it as combining two steps into one. Normally, you'd have to do a `git push` from your terminal to get the newest code onto GitHub, then run `apify push` to push it to the platform. It's also important to know that GitHub/Gitlab repository integration is standard practice. As projects grow and the number of contributors and maintainers increases, it only makes sense to have a GitHub repository integrated with the project's Actor. For the remainder of this course, all Actors created will be integrated with a GitHub repository. From 8cde792d492990409d1b2dddbb3d0024d58acb91 Mon Sep 17 00:00:00 2001 From: Arto Gahr Date: Fri, 29 May 2026 13:52:17 +0200 Subject: [PATCH 3/4] docs: update scraping basics exercises for current website state --- .../03_devtools_extracting_data.md | 6 +- .../challenge/scraping_amazon.md | 70 +++++++------------ .../03_devtools_extracting_data.md | 6 +- 3 files changed, 33 insertions(+), 49 deletions(-) diff --git a/sources/academy/webscraping/scraping_basics_javascript/03_devtools_extracting_data.md b/sources/academy/webscraping/scraping_basics_javascript/03_devtools_extracting_data.md index e774b7e1d7..87cad8c97a 100644 --- a/sources/academy/webscraping/scraping_basics_javascript/03_devtools_extracting_data.md +++ b/sources/academy/webscraping/scraping_basics_javascript/03_devtools_extracting_data.md @@ -81,7 +81,7 @@ In the next lesson, we'll start with our Node.js project. First we'll be figurin ### Extract the price of IKEA's most expensive artificial plant -At IKEA's [Artificial plants & flowers listing](https://www.ikea.com/se/en/cat/artificial-plants-flowers-20492/), use CSS selectors and HTML elements manipulation in the **Console** to extract the price of the most expensive artificial plant (sold in Sweden, as you'll be browsing their Swedish offer). Before opening DevTools, use your judgment to adjust the page to make the task as straightforward as possible. Finally, use the [`parseInt()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) function to convert the price text into a number. +At IKEA's [Artificial plants & flowers listing](https://www.ikea.com/se/en/cat/artificial-plants-flowers-20492/), use CSS selectors and HTML elements manipulation in the **Console** to extract the price of the most expensive artificial plant (sold in Sweden, as you'll be browsing their Swedish offer). Before opening DevTools, use your judgment to adjust the page to make the task as straightforward as possible. Finally, use the [`parseInt()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) function to convert the price text into a number. You might also need [`replace()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) to handle spaces.
Solution @@ -93,8 +93,8 @@ At IKEA's [Artificial plants & flowers listing](https://www.ikea.com/se/en/cat/a 1. Notice that the price is structured into two elements, with the integer separated from the currency, under a class named `plp-price__integer`. This structure is convenient for extracting the value. 1. In the **Console**, execute `document.querySelector('.plp-price__integer')`. This returns the element representing the first price in the listing. Since `document.querySelector()` returns the first matching element, it directly selects the most expensive plant's price. 1. Save the element in a variable by executing `price = document.querySelector('.plp-price__integer')`. - 1. Convert the price text into a number by executing `parseInt(price.textContent)`. - 1. At the time of writing, this returns `699`, meaning [699 SEK](https://www.google.com/search?q=699%20sek). + 1. Convert the price text into a number by executing `parseInt(price.textContent.replace(' ', ''))`. The price text contains spaces as thousand separators, so `.replace(' ', '')` strips them before parsing. You'll explore this technique further in the [Extracting data](./07_extracting_data.md#removing-dollar-sign-and-commas) lesson. + 1. At the time of writing, this returns `1299`, meaning [1 299 SEK](https://www.google.com/search?q=1299%20sek).
diff --git a/sources/academy/webscraping/scraping_basics_legacy/challenge/scraping_amazon.md b/sources/academy/webscraping/scraping_basics_legacy/challenge/scraping_amazon.md index 94545b3e7d..3126d2983f 100644 --- a/sources/academy/webscraping/scraping_basics_legacy/challenge/scraping_amazon.md +++ b/sources/academy/webscraping/scraping_basics_legacy/challenge/scraping_amazon.md @@ -31,29 +31,17 @@ router.addHandler(labels.PRODUCT, async ({ $ }) => { ``` -Great! But wait, where do we go from here? We need to go to the offers page next and scrape each offer, but how can we do that? Let's take a small break from writing the scraper and open up Proxyman to analyze requests which we might be difficult to find in the network tab, then we'll click the button on the product page that loads up all of the product offers: +Great! But where do we go from here? We need to visit the product page and scrape the offer from there. -![View offers button](./images/view-offers-button.jpg) +:::note Why only one offer per product? -After clicking this button and checking back in Proxyman, we discovered this link: +Amazon product pages list a single featured offer in their static HTML. The full list of sellers is loaded separately by JavaScript after the page loads. CheerioCrawler can't see the list because it only fetches the raw HTML, without running JavaScript. -> You can find the request below in the network tab just fine, but with Proxyman, it is much easier and faster due to the extended filtering options. +If you need all seller offers, you have to use [PlaywrightCrawler](../../puppeteer_playwright/index.md), which runs a real browser and can wait for that content to load. For this course, scraping the featured offer is enough to cover the key concepts. -```text -https://www.amazon.com/gp/aod/ajax/ref=auto_load_aod?asin=B07ZPKBL9V&pc=dp -``` - -The `asin` [query parameter](https://www.branch.io/glossary/query-parameters/) matches up with our product's ASIN, which means we can use this for any product of which we have the ASIN. - -Here's what this page looks like: - -![View offers page](./images/offers-page.jpg) - -Wow, that's ugly. But for our scenario, this is really great. When we click the **View offers** button, we usually have to wait for the offers to load and render, which would mean we could have to switch our entire crawler to a **PuppeteerCrawler** or **PlaywrightCrawler**. The data on this page we've just found appears to be loaded statically, which means we can still use CheerioCrawler and keep the scraper as efficient as possible 😎 - -> It's totally possible to scrape the same data as this crawler using [Puppeteer or Playwright](../../puppeteer_playwright/index.md); however, with this offers link found in Postman, we can follow the same workflow much more quickly with static HTTP requests using CheerioCrawler. +::: -First, we'll create a request for each product's offers page: +We'll add a request to the product page for each product: ```js // routes.js @@ -63,23 +51,22 @@ First, we'll create a request for each product's offers page: router.addHandler(labels.PRODUCT, async ({ $, crawler, request }) => { const { data } = request.userData; - const element = $('div#productDescription'); + const description = $('div#productDescription').text().trim(); - // Add to the request queue await crawler.addRequests([{ - url: `${BASE_URL}/gp/aod/ajax/ref=auto_load_aod?asin=${data.asin}&pc=dp`, + url: `${BASE_URL}/dp/${data.asin}?th=1&psc=1`, label: labels.OFFERS, userData: { data: { ...data, - description: element.text().trim(), + description, }, }, }]); }); ``` -Finally, we can handle the offers in a separate handler: +Then we handle it in the OFFERS handler: ```js // routes.js @@ -87,16 +74,14 @@ Finally, we can handle the offers in a separate handler: router.addHandler(labels.OFFERS, async ({ $, request }) => { const { data } = request.userData; - for (const offer of $('#aod-offer')) { - const element = $(offer); - - await Dataset.pushData({ - ...data, - sellerName: element.find('div[id*="soldBy"] a[aria-label]').text().trim(), - offer: element.find('.a-price .a-offscreen').text().trim(), - }); + const price = $('.a-price .a-offscreen').first().text().trim(); + const sellerName = $('#sellerProfileTriggerId, #merchant-info a').first().text().trim(); - } + await Dataset.pushData({ + ...data, + sellerName, + offer: price, + }); }); ``` @@ -153,16 +138,16 @@ router.addHandler(labels.START, async ({ $, crawler, request }) => { router.addHandler(labels.PRODUCT, async ({ $, crawler, request }) => { const { data } = request.userData; - const element = $('div#productDescription'); + const description = $('div#productDescription').text().trim(); await crawler.addRequests([ { - url: `${BASE_URL}/gp/aod/ajax/ref=auto_load_aod?asin=${data.asin}&pc=dp`, + url: `${BASE_URL}/dp/${data.asin}?th=1&psc=1`, label: labels.OFFERS, userData: { data: { ...data, - description: element.text().trim(), + description, }, }, }, @@ -172,15 +157,14 @@ router.addHandler(labels.PRODUCT, async ({ $, crawler, request }) => { router.addHandler(labels.OFFERS, async ({ $, request }) => { const { data } = request.userData; - for (const offer of $('#aod-offer')) { - const element = $(offer); + const price = $('.a-price .a-offscreen').first().text().trim(); + const sellerName = $('#sellerProfileTriggerId, #merchant-info a').first().text().trim(); - await Dataset.pushData({ - ...data, - sellerName: element.find('div[id*="soldBy"] a[aria-label]').text().trim(), - offer: element.find('.a-price .a-offscreen').text().trim(), - }); - } + await Dataset.pushData({ + ...data, + sellerName, + offer: price, + }); }); ``` diff --git a/sources/academy/webscraping/scraping_basics_python/03_devtools_extracting_data.md b/sources/academy/webscraping/scraping_basics_python/03_devtools_extracting_data.md index f864362f8a..2cbea5548c 100644 --- a/sources/academy/webscraping/scraping_basics_python/03_devtools_extracting_data.md +++ b/sources/academy/webscraping/scraping_basics_python/03_devtools_extracting_data.md @@ -78,7 +78,7 @@ In the next lesson, we'll start with our Python project. First we'll be figuring ### Extract the price of IKEA's most expensive artificial plant -At IKEA's [Artificial plants & flowers listing](https://www.ikea.com/se/en/cat/artificial-plants-flowers-20492/), use CSS selectors and HTML elements manipulation in the **Console** to extract the price of the most expensive artificial plant (sold in Sweden, as you'll be browsing their Swedish offer). Before opening DevTools, use your judgment to adjust the page to make the task as straightforward as possible. Finally, use JavaScript's [`parseInt()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) function to convert the price text into a number. +At IKEA's [Artificial plants & flowers listing](https://www.ikea.com/se/en/cat/artificial-plants-flowers-20492/), use CSS selectors and HTML elements manipulation in the **Console** to extract the price of the most expensive artificial plant (sold in Sweden, as you'll be browsing their Swedish offer). Before opening DevTools, use your judgment to adjust the page to make the task as straightforward as possible. Finally, use JavaScript's [`parseInt()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt) function to convert the price text into a number. You might also need [`replace()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace) to handle spaces.
Solution @@ -90,8 +90,8 @@ At IKEA's [Artificial plants & flowers listing](https://www.ikea.com/se/en/cat/a 1. Notice that the price is structured into two elements, with the integer separated from the currency, under a class named `plp-price__integer`. This structure is convenient for extracting the value. 1. In the **Console**, execute `document.querySelector('.plp-price__integer')`. This returns the element representing the first price in the listing. Since `document.querySelector()` returns the first matching element, it directly selects the most expensive plant's price. 1. Save the element in a variable by executing `price = document.querySelector('.plp-price__integer')`. - 1. Convert the price text into a number by executing `parseInt(price.textContent)`. - 1. At the time of writing, this returns `699`, meaning [699 SEK](https://www.google.com/search?q=699%20sek). + 1. Convert the price text into a number by executing `parseInt(price.textContent.replace(' ', ''))`. The price text contains spaces as thousand separators, so `.replace(' ', '')` strips them before parsing. You'll explore this technique further in the [Extracting data](./07_extracting_data.md#removing-dollar-sign-and-commas) lesson. + 1. At the time of writing, this returns `1299`, meaning [1 299 SEK](https://www.google.com/search?q=1299%20sek).
From d527f8373a06b9b5984587983d3776fbf18dde32 Mon Sep 17 00:00:00 2001 From: Arto Gahr Date: Fri, 29 May 2026 14:00:32 +0200 Subject: [PATCH 4/4] docs: add build settings screenshot for managing source code lesson --- .../images/build-settings.webp | Bin 0 -> 15890 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 sources/academy/platform/expert_scraping_with_apify/images/build-settings.webp diff --git a/sources/academy/platform/expert_scraping_with_apify/images/build-settings.webp b/sources/academy/platform/expert_scraping_with_apify/images/build-settings.webp new file mode 100644 index 0000000000000000000000000000000000000000..fc6037dc6074df10a62558269b0191e3dc57c610 GIT binary patch literal 15890 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 L>FYn!|G)h|IcVAw literal 0 HcmV?d00001