Skip to content

Scraper for basic information - #1

Open
radimkvet wants to merge 11 commits into
mainfrom
feat/basic-features
Open

Scraper for basic information#1
radimkvet wants to merge 11 commits into
mainfrom
feat/basic-features

Conversation

@radimkvet

@radimkvet radimkvet commented Feb 17, 2026

Copy link
Copy Markdown
Owner

Because I can not assign reviewers without you being collaborators first I tag you in this PR.

@oklinov @metalwarrior665
Was instructed by Ondro to also tag @JuanGalilea

@radimkvet radimkvet self-assigned this Feb 17, 2026
@radimkvet
radimkvet marked this pull request as draft February 17, 2026 13:25
@radimkvet

Copy link
Copy Markdown
Owner Author

I am sorry for notifying you so early, forgot there is a button to create the PR as Draft to begin with.
Will not happen next time.

@radimkvet
radimkvet force-pushed the feat/basic-features branch 3 times, most recently from 3cb2181 to 62367e8 Compare February 17, 2026 14:22

@metalwarrior665 metalwarrior665 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, just a few small remarks.

Comment thread .actor/datasetSchema.json Outdated
"type": "boolean"
}
},
"required": ["category", "title", "url", "author", "createdAt", "lastPostAt", "posts", "isAnnouncement"]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having required fields in the dataset output is considered quite dangerous. If any of them is missing, the pushData will throw and you will loose the data. And websites change so one field might be broken. Generally, our approach is to rather push incomplete data and catch the issue in periodic tests/monitoring.

Using required fields is possible but we will only do it when it is validated with TypeScript (library to ensure 1:1 between TS and schema was recently released) and we can ensure these fields are always possible. E.g. currently it is usually not the case because we are pushing "error items" into the default dataset. We want to move to secondary dataset (also a new feature) soon

@radimkvet radimkvet Feb 19, 2026

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I forgot about partially pushing data, and it makes sense. Removed the required field from the schema and added partial push of data on some routes. There is no case where partial data push makes sense to me, so that feature is not used.

Comment thread .actor/inputSchema.json Outdated
"useApifyProxy": true,
"apifyProxyGroups": []
}
},

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In non-generic (that don't work on any website) public Actors, we don't expose the proxy to the user. Usually, we set either datacenter or residential depending which works. Users have no idea what proxies they should use.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, change it o that the proxies are not in the input

Comment thread src/routes/forumCategory.ts Outdated
Comment on lines +53 to +63
const $views = $topic.find(selectors.topic.views).first();
const views = parseNumber($views.text().trim().split(' ')[0]) ?? 0;

const $lastPost = $topic.find(selectors.topic.lastPost).first();
const lastPostAt = $lastPost.find('time').attr('datetime') || $lastPost.text().trim();

const $author = $details.find(selectors.topic.author).first();
const author = $author.text().trim() || 'Unknown';

const $createdAt = $details.find(selectors.topic.createdAt).first();
const createdAt = $createdAt.attr('datetime') || $createdAt.text().trim();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lot of these variables are not reused so unless the extraction is too long, I would try to inline them to the result object so that the reader doesn't need to carry the variables in their head checking if they will be used somewhere.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where it made sense I moved it, but not everything because it made it less readable sometimes when there were a lot of function calls on one line because of the formatter.

Comment thread src/routes/forumCategory.ts Outdated
urls: [url],
label: Label.TOPIC_DETAIL,
userData,
});

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First buffer all the URLs into an array and then enqueue them at once to only do 1 API call instead of many. The same applies for pushing data

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had to buffer all the Request objects instead of URLs because of each URL having different user data, but the push is now in bulk.

Comment thread src/routes/searchStart.ts Outdated
import type { UserData } from '../types/index.js';
import { Label } from '../types/index.js';

export const forumSearchStartHandler = async (ctx: CheerioCrawlingContext) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are missing the userData type here

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the missing typing

Comment thread src/routes/topicDetail.ts
await enqueueLinks({
urls: [nextPageUrl],
label: Label.TOPIC_DETAIL,
userData: topic,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of aggregating the data and pushing at last page it might be better to push each small thing separately? Usually we try to push the smallest thing (e.g. one dataset item = one review) so we don't have to aggregate anything and it has simple schema. But in some more complex use-cases aggregation makes sense. Just be aware that single dataset item can be max 9MB.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking of one item being the topic itself. But here there might be issue with the size limitation which I forgot about. Might check it out if there are bigger file regularly and change it around.

Comment thread src/utils/parser.ts Outdated
const normalized = value.replace(/[,-]/g, '');
const parsed = Number.parseInt(normalized, 10);
return Number.isNaN(parsed) ? null : parsed;
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think your utils are small enough for them to be a single file. Many files can be harder to navigate.

Generally, I recommend starting with fewer files/folders and then refactoring out if the project gets bigger. Some projects never grow in features so there is no need to prepare "future-proof structure".

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found them distinct enough to have them in separate files, but I understand the problem this might present. Will merge them into one utils file.

Comment thread src/main.ts
proxyConfiguration,
requestHandler: router,
// this is 2 requests a second, this should be fine and not overload the site since it is not so frequently used one
maxRequestsPerMinute: 120,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit of issue with this option is that (I think) it doesn't distribute the requests in the minute, it will just stop processing once done. So it could still lead to high bursts at the start (not sure how likely it is in practice). You could solve that by adding maxConcurrency limit as well.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I noticed that it just does huge burst at the beginning and then it slows down. Will try to do this with the maxConcurrency setting.

Comment thread README.md
A topic is a page with posts provide by users.

Start a new [web scraping](https://apify.com/web-scraping) project quickly and easily in TypeScript (Node.js) with our empty project template. It provides a basic structure for the Actor with [Apify SDK](https://docs.apify.com/sdk/js/) and allows you to easily add your own functionality.
## Inputs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally prefer to link the Input page to the Actor Store page, which is derived from the inpjt schema. That way there is only single source of truth for the descriptions, you will often forget to change the readme.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I linked the input page statically in the README.

Comment thread README.md Outdated
@radimkvet
radimkvet marked this pull request as ready for review February 17, 2026 15:18

@metalwarrior665 metalwarrior665 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Please don't force-push after a review, it makes seeing new changes harder.

@radimkvet

Copy link
Copy Markdown
Owner Author

Thanks! Please don't force-push after a review, it makes seeing new changes harder.

Oh I did not know GitHub removes the old lines from the comment (GitLab kept them from what I remember), will adjust workflow for next PR.

@radimkvet

Copy link
Copy Markdown
Owner Author

Thanks! Please don't force-push after a review, it makes seeing new changes harder.

Now I see you already mentioned it last week in another review. I did not notice it. Will change it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants