1import { Actor, log } from 'apify';
2import { CheerioCrawler, Dataset } from 'crawlee';
3
4interface Input {
5 companyIco: string,
6}
7
8await Actor.init();
9
10const getHeaderValue = (rawHeaders: string[], name: string): string | null | undefined => {
11 return rawHeaders[rawHeaders.findIndex((h) => h.toLowerCase() === name) + 1];
12};
13
14const getFileMetadata = (prefix: string, rawHeaders: string[]) => {
15 const contentType = getHeaderValue(rawHeaders, 'content-type') ?? 'text/plain';
16 const filename = getHeaderValue(rawHeaders, 'content-disposition')?.match(/filename="(.*)"/)?.[1] || 'unknown';
17
18 return {
19 contentType,
20 filename: `${prefix}-${+new Date()}-${filename.replace(/[^a-zA-Z0-9_.-]/g, '-')}`,
21 };
22};
23
24
25const {
26 companyIco,
27} = await Actor.getInput<Input>() ?? {} as Input;
28
29const kvs = await Actor.openKeyValueStore();
30
31const LABELS = {
32 START: 'START',
33 SBIRKA_LISTIN: 'SBIRKA_LISTIN',
34 LISTINA: 'LISTINA',
35} as const;
36
37const crawler = new CheerioCrawler({
38 // TODO: Is this using proxy properly?
39 proxyConfiguration: await Actor.createProxyConfiguration(),
40 maxConcurrency: 10,
41 requestHandler: async ({ enqueueLinks, request, $, sendRequest }) => {
42 if (request.label === LABELS.START) {
43 log.info('Enqueuing urls from search page...');
44 await enqueueLinks({
45 selector: 'a[href^="./vypis-sl"]',
46 label: LABELS.SBIRKA_LISTIN,
47 });
48 } else if (request.label === LABELS.SBIRKA_LISTIN) {
49 log.info('Enqueuing URLs from document list...');
50 await enqueueLinks({
51 selector: 'a[href^="./vypis-sl-detail"]',
52 label: LABELS.LISTINA,
53 });
54 } else if (request.label === LABELS.LISTINA) {
55 const links = $('a[href^=/ias/content/download]').toArray();
56 log.info(`Found ${links.length} links`);
57
58 await Promise.allSettled(links.map(async (link) => {
59 log.info('Downloading document...');
60 const downloadUrl = `https://or.justice.cz${link.attribs.href}`;
61 const response = await sendRequest({ url: downloadUrl });
62 // For some reason, we can only access raw headers
63 const { contentType, filename } = getFileMetadata('file', response.rawHeaders);
64 await Actor.setValue(filename, response.rawBody, { contentType });
65 await Actor.pushData({
66 url: request.url,
67 filename,
68 fileUrl: kvs.getPublicUrl(filename),
69 });
70
71 }));
72 }
73 },
74});
75
76const startUrl = new URL('https://or.justice.cz/ias/ui/rejstrik-$firma');
77startUrl.searchParams.set('ico', companyIco.replace(/[^0-9]/, ''));
78
79await crawler.run([
80 { url: startUrl.toString(), label: LABELS.START },
81]);
82
83// Gracefully exit the Actor process. It's recommended to quit all Actors with an exit()
84await Actor.exit();