diff --git a/website/src/components/Button.jsx b/website/src/components/Button.jsx new file mode 100644 index 000000000000..453a0a81d342 --- /dev/null +++ b/website/src/components/Button.jsx @@ -0,0 +1,23 @@ +import Link from '@docusaurus/Link'; +import clsx from 'clsx'; +import React from 'react'; + +import styles from './Button.module.css'; +import CrawleeSvg from '../../static/img/crawlee-logo-monocolor.svg'; + +export default function Button({ children, to, withIcon, type = 'primary', className, isBig }) { + return ( + + + {withIcon && } + {children} + + + ); +} diff --git a/website/src/components/Button.module.css b/website/src/components/Button.module.css new file mode 100644 index 000000000000..e1cd82da6117 --- /dev/null +++ b/website/src/components/Button.module.css @@ -0,0 +1,63 @@ +.button { + display: inline-flex; + align-items: center; + text-align: center; + padding: 8px 16px; + border-radius: 8px; + font-family: (--ifm-font-family-base); + font-size: 16px; + font-style: normal; + font-weight: 500; + line-height: 24px; + cursor: pointer; + transition: background-color 0.2s; + + svg { + margin-right: 8px; + } +} + +.buttonPrimary { + background-color: var(--color-black-action); + color: var(--color-text-on-primary); + border: none; + + path { + stroke: var(--color-text-on-primary); + &:first-child { + fill: var(--color-text-on-primary); + } + } +} + +.buttonPrimary:hover { + background-color: var(--color-primary-action-hover); +} + +.buttonSecondary { + background-color: var(--color-background); + color: var(--color-text); + border: 1px solid var(--color-border); + + path { + stroke: var(--color-black-action); + &:first-child { + fill: var(--color-black-action); + } + } +} + +.buttonSecondary:hover { + border: 1px solid var(--color-text); +} + +.big { + padding: 12px 24px; +} + +/* TABLET */ +@media (min-width: 768px) { + .button { + width: auto; + } +} diff --git a/website/src/components/CopyButton.jsx b/website/src/components/CopyButton.jsx new file mode 100644 index 000000000000..073e66fbd50f --- /dev/null +++ b/website/src/components/CopyButton.jsx @@ -0,0 +1,34 @@ +/* eslint-disable max-len */ +import clsx from 'clsx'; +import React, { useState } from 'react'; + +import styles from './CopyButton.module.css'; + +export default function CopyButton({ copyText, compact = false, className }) { + const [copied, setCopied] = useState(false); + const copy = async () => { + await navigator.clipboard.writeText(copyText); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + return ; +} diff --git a/website/src/components/CopyButton.module.css b/website/src/components/CopyButton.module.css new file mode 100644 index 000000000000..162462d989f3 --- /dev/null +++ b/website/src/components/CopyButton.module.css @@ -0,0 +1,37 @@ +.copyButton { + all: unset; + display: inline-flex; + align-items: center; + justify-content: center; + box-sizing: border-box; + cursor: pointer; + fill: var(--color-icon); + + svg { + flex-shrink: 0; + } +} + +.copyButtonDefault { + width: 28px; + height: 28px; + background-color: var(--color-editor); + border: 1px solid var(--color-border); + border-radius: 6px; + transition: background-color 0.12s ease-out; + + &:hover { + background-color: var(--color-hover); + } + + svg { + padding: 1px; + } +} + +.copyButtonCompact { + svg { + width: 16px; + height: 16px; + } +} \ No newline at end of file diff --git a/website/src/components/Homepage/HomepageCliExample.jsx b/website/src/components/Homepage/HomepageCliExample.jsx new file mode 100644 index 000000000000..23f80d5963fb --- /dev/null +++ b/website/src/components/Homepage/HomepageCliExample.jsx @@ -0,0 +1,23 @@ +import React from 'react'; + +import CopyButton from '../CopyButton'; +import styles from './HomepageCliExample.module.css'; + +const cliCommand = `npx crawlee create my-crawler`; + +export default function CliExample() { + return ( +
+
+ Or start with a template from our CLI +
+
+                {cliCommand}
+                
+            
+
+ Built with 🤍 by Apify. Forever free and open-source. +
+
+ ); +} diff --git a/website/src/components/Homepage/HomepageCliExample.module.css b/website/src/components/Homepage/HomepageCliExample.module.css new file mode 100644 index 000000000000..3d74c58e90be --- /dev/null +++ b/website/src/components/Homepage/HomepageCliExample.module.css @@ -0,0 +1,50 @@ +.cliExampleSection { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + padding: 16px; +} + +.cliExampleTitle { + color: var(--color-text-muted); + font-size: 18px; + font-style: normal; + font-weight: 400; + line-height: 28px; + margin-bottom: 16px; +} + +.cliExampleCodeBlock { + width: 100%; + padding: 12px 16px; + background-color: var(--color-editor); + border: 1px solid var(--color-border); + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; + font-size: 14px; + line-height: 20px; + + /* TABLET */ + @media (min-width: 768px) { + max-width: 526px; + } +} + +.cliExampleSubtitle { + color: var(--color-text-subtle); + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; +} + +/* TABLET */ +@media (min-width: 768px) { + .cliExampleSection { + padding: 64px 0; + } +} diff --git a/website/src/components/Homepage/HomepageCtaSection.jsx b/website/src/components/Homepage/HomepageCtaSection.jsx new file mode 100644 index 000000000000..2f317c6dd605 --- /dev/null +++ b/website/src/components/Homepage/HomepageCtaSection.jsx @@ -0,0 +1,48 @@ +import ThemedImage from '@theme/ThemedImage'; +import clsx from 'clsx'; +import React from 'react'; + +import styles from './HomepageCtaSection.module.css'; +import Button from '../Button'; + +export default function HomepageCtaSection({ showJs, showPython }) { + return ( +
+

Get started now!

+
+ Crawlee won’t fix broken selectors for you (yet), but it makes + building and maintaining reliable crawlers faster and easier—so + you can focus on what matters most. +
+
+ {showJs && ( + + )} + {showPython && ( + + )} +
+ +
+ ); +} diff --git a/website/src/components/Homepage/HomepageCtaSection.module.css b/website/src/components/Homepage/HomepageCtaSection.module.css new file mode 100644 index 000000000000..e96d87ea9224 --- /dev/null +++ b/website/src/components/Homepage/HomepageCtaSection.module.css @@ -0,0 +1,67 @@ +.ctaSection { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + padding: 16px; + padding-bottom: 0; + gap: 24px; +} + +.ctaTitle { + color: var(--color-text); + font-family: 'Lota Grotesque'; + font-size: 36px; + font-style: normal; + font-weight: 400; + line-height: 46px; + margin: 0; +} + +.ctaDescription { + color: var(--color-text-muted); + font-size: 18px; + font-style: normal; + font-weight: 400; + line-height: 28px; + max-width: 780px; +} + +.ctaButtonContainer { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; + gap: 16px; + width: 100%; +} + +.ctaImage { + z-index: -1; + margin-top: -100px; +} + +#ctaDecorativeCircle { + width: 120px; + height: 120px; + left: -60px; + top: -60px; +} + +/* TABLET */ +@media (min-width: 768px) { + .ctaSection { + padding-top: 80px; + } + + .ctaTitle { + font-size: 48px; + line-height: 56px; + } + + .ctaButtonContainer { + flex-direction: row; + } +} diff --git a/website/src/components/Homepage/HomepageHeroSection.jsx b/website/src/components/Homepage/HomepageHeroSection.jsx new file mode 100644 index 000000000000..8cdde06f31c4 --- /dev/null +++ b/website/src/components/Homepage/HomepageHeroSection.jsx @@ -0,0 +1,31 @@ +import React from 'react'; + +import styles from './HomepageHeroSection.module.css'; +import homepageStyles from '../../pages/index.module.css'; + +export default function HomepageHeroSection() { + return ( +
+

+ Build reliable web scrapers. Fast. +

+
+

+ Crawlee is a web scraping library for JavaScript and Python. It + handles blocking, crawling, proxies, and browsers for you. +

+
+
+
+
+ ); +} diff --git a/website/src/components/Homepage/HomepageHeroSection.module.css b/website/src/components/Homepage/HomepageHeroSection.module.css new file mode 100644 index 000000000000..24a33e3560ff --- /dev/null +++ b/website/src/components/Homepage/HomepageHeroSection.module.css @@ -0,0 +1,81 @@ +.hero { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 32px 0; + h1 { + padding-inline: 12px; + } +} + +.heroTitle { + color: var(--color-text); + font-size: 52px; + line-height: 60px; + font-weight: 400; + text-align: center; + margin: 0 0 16px 0; +} + +.heroSubtitle { + color: var(--color-text-muted); + font-size: 18px; + line-height: 28px; + font-weight: 400; + text-align: center; + margin: 0 16px; + max-width: 792px; +} + +#separatorHeroHeader { + display: none; +} + +#separatorHeroHeader2 { + display: none; +} + +#heroDecorativeCircle { + width: 60px; + height: 60px; + right: -60px; + top: 0px; +} + +/* TABLET */ +@media (min-width: 768px) { + .hero { + padding: 64px 0 0 0; + h1 { + padding-inline: 24px; + } + } + .heroTitle { + font-size: 54px; + line-height: 64px; + margin: 0 16px 24px 16px; + } + .heroSubtitle { + margin: 0 16px 30px 16px; + } + #separatorHeroHeader { + display: none; + } + #separatorHeroHeader2 { + display: block; + } +} + +/* DESKTOP */ +@media (min-width: 1024px) { + .hero { + padding: 120px 0 0 0; + } + .heroSubtitle { + margin: 30px 16px; + } + #separatorHeroHeader { + display: block; + } +} diff --git a/website/src/components/Homepage/LanguageInfoWidget.jsx b/website/src/components/Homepage/LanguageInfoWidget.jsx new file mode 100644 index 000000000000..2ceeb616ec4b --- /dev/null +++ b/website/src/components/Homepage/LanguageInfoWidget.jsx @@ -0,0 +1,56 @@ +import ThemedImage from '@theme/ThemedImage'; +import clsx from 'clsx'; +import React from 'react'; + +import Button from '../Button'; +import CopyButton from '../CopyButton'; +import styles from './LanguageInfoWidget.module.css'; + +export default function LanguageInfoWidget({ + language, + command, + githubUrl, + to, +}) { + return ( +
+ {language === 'JavaScript' && ( + + )} + {language === 'Python' && ( + + )} +
+ + +
+ {command && ( + + {command} + + )} +
+ ); +} diff --git a/website/src/components/Homepage/LanguageInfoWidget.module.css b/website/src/components/Homepage/LanguageInfoWidget.module.css new file mode 100644 index 000000000000..fc53b7ed63a7 --- /dev/null +++ b/website/src/components/Homepage/LanguageInfoWidget.module.css @@ -0,0 +1,64 @@ +.languageGetStartedContainer { + margin: 0; + display: flex; + flex-direction: column; + align-items: center; +} + +.languageGetStartedContainer img { + height: 40px; + margin-bottom: 16px; +} + +.buttonContainer { + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + margin-bottom: 16px; + iframe { + margin-left: 20px; + } +} + +.buttonContainer:has(+ code) { + gap: 12px; + padding-left: 20px; + iframe { + margin-left: 0px; + } +} + +.commandContainer { + margin: 0; + padding: 0; + color: var(--color-text); + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: 16px; + background-color: transparent; + border: 0; + display: flex; + align-items: center; +} + +.commandContainer button { + opacity: 0; + transition: opacity var(--ifm-transition-fast) ease-in; +} + +.commandContainer:hover button, +.commandContainer button:hover { + opacity: 1; +} + +/* TABLET */ +@media (min-width: 768px) { + .languageGetStartedContainer { + margin: 24px 0 40px 0; + } + .buttonContainer:has(+ code) { + flex-direction: row; + } +} diff --git a/website/src/components/Homepage/LanguageSwitch.jsx b/website/src/components/Homepage/LanguageSwitch.jsx new file mode 100644 index 000000000000..7f497018d870 --- /dev/null +++ b/website/src/components/Homepage/LanguageSwitch.jsx @@ -0,0 +1,50 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import styles from './LanguageSwitch.module.css'; +import clsx from 'clsx'; + +export default function LanguageSwitch({ + options = ['JavaScript', 'Python'], + defaultOption = 'JavaScript', + onChange, +}) { + const [activeOption, setActiveOption] = useState(defaultOption) + const [backgroundStyle, setBackgroundStyle] = useState({}) + const optionRefs = useRef < (HTMLButtonElement | null)[] > ([]) + + const updateBackgroundStyle = useCallback(() => { + const activeIndex = options.indexOf(activeOption) + const activeElement = optionRefs.current[activeIndex] + if (activeElement) { + const { offsetLeft, offsetWidth } = activeElement + setBackgroundStyle({ + transform: `translateX(${offsetLeft}px)`, + width: `${offsetWidth}px`, + }) + } + }, [activeOption, options]) + + useEffect(() => { + updateBackgroundStyle() + }, [updateBackgroundStyle]) + + const handleOptionClick = (option) => { + setActiveOption(option) + onChange?.(option) + } + + return ( +
+ {options.map((option, index) => ( + + ))} +
+
+ ) +} diff --git a/website/src/components/Homepage/LanguageSwitch.module.css b/website/src/components/Homepage/LanguageSwitch.module.css new file mode 100644 index 000000000000..66f706ad19d6 --- /dev/null +++ b/website/src/components/Homepage/LanguageSwitch.module.css @@ -0,0 +1,41 @@ +.languageSwitch { + z-index: 1; + display: inline-flex; + position: relative; + background-color: var(--color-background-subtle); + border-radius: 6px; + padding: 4px; +} + +.switchOption { + position: relative; + z-index: 1; + padding: 6px 16px; + font-size: 14px; + font-weight: 500; + color: var(--color-text-muted); + background: none; + border: none; + cursor: pointer; + transition: color 0.3s ease; +} + +.switchOption:hover { + color: var(--color-text); +} + +.switchOption.active { + color: var(--color-text); +} + +.switchBackground { + position: absolute; + top: 4px; + bottom: 4px; + left: 0; + border-radius: 6px; + background-color: var(--color-background); + transition: + transform 0.3s ease, + width 0.3s ease; +} diff --git a/website/src/components/Homepage/RiverSection.jsx b/website/src/components/Homepage/RiverSection.jsx new file mode 100644 index 000000000000..0e77a5ead7f7 --- /dev/null +++ b/website/src/components/Homepage/RiverSection.jsx @@ -0,0 +1,22 @@ +import Link from '@docusaurus/Link'; +import clsx from 'clsx'; +import React from 'react'; + +import styles from './RiverSection.module.css'; + +export default function RiverSection({ title, description, content, reversed, to }) { + return ( +
+
+
+

{title}

+

{description}

+ + Learn more + +
+
{content}
+
+
+ ); +} diff --git a/website/src/components/Homepage/RiverSection.module.css b/website/src/components/Homepage/RiverSection.module.css new file mode 100644 index 000000000000..fc59416bc5c4 --- /dev/null +++ b/website/src/components/Homepage/RiverSection.module.css @@ -0,0 +1,148 @@ +/* Base styles */ +.riverWrapper { + width: 100%; + border-top: 1px solid var(--color-separator); + border-bottom: 1px solid var(--color-separator); +} + +.riverContainer { + max-width: 1200px; + margin: 0 auto; + display: flex; + flex-direction: column; + + /* Tablet layout */ + @media (min-width: 768px) { + flex-direction: row; + + &.riverReversed { + flex-direction: row-reverse; + } + } +} + +.riverSection { + width: 100%; + + /* Tablet layout */ + @media (min-width: 768px) { + min-width: 0; + flex-basis: 50%; + flex-grow: 0; + } +} + +.riverText { + padding: 24px 16px; + + /* Tablet layout */ + @media (min-width: 768px) { + padding: 40px 32px; + } + + /* Desktop layout */ + @media (min-width: 1024px) { + padding: 80px; + } +} + +/* Text styles */ +.riverTitle { + flex: 1; + margin-top: 0; + margin-bottom: 12px; + font-size: 32px; + font-weight: 400; + line-height: 40px; + + /* Desktop layout */ + @media (min-width: 1024px) { + max-width: 440px; + } +} + +.riverDescription { + margin-bottom: 24px; + color: var(--color-text-muted); + font-size: 16px; + line-height: 24px; + + /* Desktop layout */ + @media (min-width: 1024px) { + max-width: 440px; + } +} + +.riverButton { + cursor: pointer; + padding: 8px 12px; + background-color: transparent; + border: 1px solid var(--color-border); + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + font-size: 16px; + line-height: 24px; + transition: background-color 0.12s ease-out; + width: fit-content; + color: var(--color-text); + + &:hover { + background-color: var(--color-hover); + color: var(--color-text); + } + + path { + stroke: var(--color-icon); + } +} + +.riverButton::after { + content: '→'; + margin-inline: 4px; + transition: margin 0.3s ease; +} + +.riverButton:hover { + color: var(--color-text); + &::after { + margin: 0 0 0 8px; + } +} + +.riverContent { + min-height: 180px; + background-color: var(--color-editor); + border-top: 1px solid var(--color-separator); + display: flex; + flex-direction: column; + overflow: hidden; + + :global(.code-block) { + flex-grow: 1; + + :global(.codeBlockContent_biex) { + height: 100%; + + pre, + code { + height: 100%; + } + } + } + + /* Tablet layout */ + @media (min-width: 768px) { + border-top: none; + border-left: 1px solid var(--color-separator); + } + + .riverReversed & { + /* Tablet layout */ + @media (min-width: 768px) { + border-left: none; + border-right: 1px solid var(--color-separator); + } + } +} diff --git a/website/src/components/Homepage/ThreeCardsWithIcon.jsx b/website/src/components/Homepage/ThreeCardsWithIcon.jsx new file mode 100644 index 000000000000..f1a224de1dd4 --- /dev/null +++ b/website/src/components/Homepage/ThreeCardsWithIcon.jsx @@ -0,0 +1,26 @@ +import Link from '@docusaurus/Link'; +import React from 'react'; + +import styles from './ThreeCardsWithIcon.module.css'; + +export default function ThreeCardsWithIcon({ cards }) { + return ( +
+ {cards?.map((card, index) => ( +
+
{card.icon}
+

{card.title}

+

{card.description}

+ {card.actionLink && ( + + {card.actionLink.text} + + )} +
+ ))} +
+ ); +} diff --git a/website/src/components/Homepage/ThreeCardsWithIcon.module.css b/website/src/components/Homepage/ThreeCardsWithIcon.module.css new file mode 100644 index 000000000000..37c08cdd5983 --- /dev/null +++ b/website/src/components/Homepage/ThreeCardsWithIcon.module.css @@ -0,0 +1,94 @@ +.cardsWrapper { + display: flex; + flex-direction: column; + border-block: 1px solid var(--color-separator); + + @media (min-width: 768px) { + flex-direction: row; + } +} + +/* Card styles */ +.cardItem { + display: flex; + flex: 1; + flex-direction: column; + padding: 40px 24px; + background: var(--color-card-background); + + border-bottom: 1px solid var(--color-separator); + &:last-child { + border-bottom: 0; + } + + @media (min-width: 768px) { + border-bottom: 0; + border-right: 1px solid var(--color-separator); + &:last-child { + border-right: 0; + } + } +} + +.cardIcon { + margin-bottom: 16px; + display: flex; + align-items: center; + justify-content: center; + + width: 72px; + height: 72px; + + border-radius: 6px; + border: 1px solid var(--color-separator); + background: var(--color-background); +} + +.cardIcon img { + width: 50px; +} + +.cardTitle { + margin: 0; + margin-bottom: 8px; + color: var(--color-text); + font-size: 26px; + font-style: normal; + font-weight: 400; + line-height: 34px; +} + +.cardDescription { + color: var(--color-text-muted); + font-family: var(--ifm-font-family-base); + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + margin: 0; + margin-bottom: 12px; +} + +.cardAction { + color: var(--color-text-muted); + font-family: var(--ifm-font-family-base); + font-size: 16px; + font-style: normal; + font-weight: 650; + line-height: 24px; + width: fit-content; + margin-top: auto; +} + +.cardAction::after { + content: '→'; + margin-left: 4px; + transition: margin 0.3s ease; +} + +.cardAction:hover { + color: var(--color-text); + &::after { + margin-left: 8px; + } +} diff --git a/website/src/components/RunnableCodeBlock.module.css b/website/src/components/RunnableCodeBlock.module.css index 5cbeabc560b5..fce26f125198 100644 --- a/website/src/components/RunnableCodeBlock.module.css +++ b/website/src/components/RunnableCodeBlock.module.css @@ -2,7 +2,7 @@ display: inline-block; padding: 3px 10px; position: absolute; - top: 9px; + top: calc(var(--ifm-pre-padding) / 2); right: 9px; z-index: 1; font-size: 16px; diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 70934d8ba452..75737699db61 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -2,7 +2,7 @@ html[data-theme='dark'] { --ifm-navbar-background-color: #1a1b23; - --ifm-background-color: #1a1b23; + --ifm-background-color: #1a1b21; --ifm-background-surface-color: #242736; --ifm-font-color-base: #f2f3fb; @@ -31,11 +31,16 @@ html[data-theme='dark'] { --color-black-action: #fff; --color-icon: #b2b8cc; --color-hover: #2d313e; + --color-primary-action-hover: #d1d5e4; + --color-editor: #282a36; } :root { /* use default system font based on https://devhints.io/css-system-font-stack */ - --ifm-font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + --ifm-font-family-base: + -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', + 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', + sans-serif; --ifm-heading-font-family: 'Lota Grotesque', sans-serif; --ifm-font-weight-semibold: 600; --ifm-font-color-base: #242736; @@ -95,12 +100,15 @@ html[data-theme='dark'] { --color-black-action: #272d3e; --color-icon: #555d76; --color-hover: #eef0f8; + --color-primary-action-hover: #2b3143; + --color-editor: #f6f8fa; } @font-face { font-family: 'Lota Grotesque'; - src: url('/font/lota.woff2') format('woff2'), - url('/font/lota.woff') format('woff'); + src: + url('/font/lota.woff2') format('woff2'), + url('/font/lota.woff') format('woff'); font-weight: 600; } @@ -138,15 +146,24 @@ div[class*='navbarSearchContainer'] { position: static; } -html[data-theme="dark"] .DocSearch-Button { +html[data-theme='dark'] .DocSearch-Button { background: none; border: 1px solid var(--docsearch-muted-color); } -html[data-theme="dark"] .DocSearch-Button .DocSearch-Search-Icon { +html[data-theme='dark'] .DocSearch-Button .DocSearch-Search-Icon { color: var(--docsearch-muted-color); } +.DocSearch-Button:hover { + box-shadow: none !important; +} + +.navbar, +.main-wrapper { + justify-content: center; +} + html.plugin-pages .main-wrapper { overflow-x: hidden; } @@ -205,7 +222,7 @@ aside > div > a > b { .navbar__items { height: 28px; @media (min-width: 768px) { - height: 40px; + height: 40px; } } @@ -213,7 +230,8 @@ aside > div > a > b { gap: 16px; } -.navbar__item, .navbar__link { +.navbar__item, +.navbar__link { font-size: 16px; font-weight: 500; line-height: 24px; /* 150% */ @@ -265,12 +283,28 @@ aside > div > a > b { background-size: cover; } +.navbar .icon[href*='github']::before { + background-image: url('/static/img/github-brand.svg'); +} + +html[data-theme='dark'] .navbar .navbar__link[href*='github']:before { + background-image: url('/static/img/github-brand-dark.svg'); +} + +.navbar .icon[href*='discord']::before { + background-image: url('/static/img/discord-brand.svg'); +} + +html[data-theme='dark'] .navbar .navbar__link[href*='discord']:before { + background-image: url('/static/img/discord-brand-dark.svg'); +} + .navbar svg[class*='iconExternalLink'], aside svg[class*='iconExternalLink'] { display: none; } -header.hero div[class^=heroButtons] { +header.hero div[class^='heroButtons'] { justify-content: inherit; } @@ -308,11 +342,12 @@ nav.navbar .dropdown__menu { border: 1px solid var(--color-border); } -.dropdown__menu .dropdown__link--active{ +.dropdown__menu .dropdown__link--active { background: transparent; } -.dropdown__menu .dropdown__link:hover, .dropdown__menu .dropdown__link--active:hover { +.dropdown__menu .dropdown__link:hover, +.dropdown__menu .dropdown__link--active:hover { background: var(--color-hover); } @@ -350,7 +385,12 @@ html.plugin-pages { html.plugin-pages h2 { font-size: 36px; - line-height: 48px; + line-height: 46px; + + @media (min-width: 768px) { + font-size: 48px; + line-height: 56px; + } } html.plugin-docs .theme-doc-markdown { @@ -430,7 +470,10 @@ html[data-theme='dark'] } html[data-theme='dark'] .theme-doc-sidebar-menu .menu__link--active, -html[data-theme='dark'] .theme-doc-toc-desktop .table-of-contents .table-of-contents__link--active { +html[data-theme='dark'] + .theme-doc-toc-desktop + .table-of-contents + .table-of-contents__link--active { color: #f2f3fb; } @@ -522,6 +565,12 @@ html.plugin-typedoc-api .theme-doc-sidebar-menu > li:nth-child(6)::before { opacity: 0.4; } +.code-block.theme-code-block { + margin-bottom: 0; + border-radius: 0; + box-shadow: none; +} + html[data-theme='dark'] .runnable-code-block svg .apify-logo { fill: #fff; } @@ -543,14 +592,15 @@ html[data-theme='dark'] .runnable-code-block svg .apify-logo { .prism-code.language-json .token-line::before, .prism-code.language-json5 .token-line::before, .prism-code.language-XML .token-line::before, -.prism-code.language-js .token-line::before { +.prism-code.language-js .token-line::before, +.prism-code.language-python .token-line::before { counter-increment: line-number; content: counter(line-number); margin-right: calc(var(--ifm-pre-padding) * 0.8); text-align: right; min-width: 1.5rem; display: inline-block; - opacity: .3; + opacity: 0.3; left: var(--ifm-pre-padding); } @@ -564,18 +614,18 @@ html[data-theme='dark'] .runnable-code-block svg .apify-logo { border: 1px solid #b3b8d2; } -div[class^=announcementBar_] { +div[class^='announcementBar_'] { background: #d05c29; color: #fff; } -div[class^=announcementBar_] button { +div[class^='announcementBar_'] button { color: #fff; } .markdown blockquote { --ifm-alert-background-color: var(--ifm-color-info-contrast-background); - --ifm-alert-background-color-highlight: rgba(84,199,236,.15); + --ifm-alert-background-color-highlight: rgba(84, 199, 236, 0.15); --ifm-alert-foreground-color: var(--ifm-color-info-contrast-foreground); --ifm-alert-border-color: var(--ifm-color-info-dark); --ifm-code-background: var(--ifm-alert-background-color-highlight); @@ -878,4 +928,4 @@ div[class^=announcementBar_] button { .DocSearch-NoResults-Prefill-List ul { padding: 0; } -} \ No newline at end of file +} diff --git a/website/src/pages/index.js b/website/src/pages/index.js index 155daa672658..6d0e6aeef8fb 100644 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -1,102 +1,38 @@ /* eslint-disable max-len */ -import Link from '@docusaurus/Link'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; -import Admonition from '@theme/Admonition'; import CodeBlock from '@theme/CodeBlock'; import Layout from '@theme/Layout'; -import clsx from 'clsx'; -import React from 'react'; -import LiteYouTubeEmbed from 'react-lite-youtube-embed'; +import React, { useState } from 'react'; import styles from './index.module.css'; -import Highlights from '../components/Highlights'; -import 'react-lite-youtube-embed/dist/LiteYouTubeEmbed.css'; +import HomepageCliExample from '../components/Homepage/HomepageCliExample'; +import HomepageCtaSection from '../components/Homepage/HomepageCtaSection'; +import HomepageHeroSection from '../components/Homepage/HomepageHeroSection'; +import LanguageInfoWidget from '../components/Homepage/LanguageInfoWidget'; +import LanguageSwitch from '../components/Homepage/LanguageSwitch'; import RunnableCodeBlock from '../components/RunnableCodeBlock'; -function Hero() { +function LanguageGetStartedSection() { return ( -
-
-
-
-
-

- Crawlee is a web
scraping and browser
automation library -

-

- Crawlee is a web
scraping
and browser
automation
library -

-
-
-
-
-

It helps you build reliable crawlers. Fast.

-
-
-
-
-
- Get Started - -
-
-
-
-
-
- -
-
- - npx crawlee create my-crawler - -
-
-
-
- ); -} - -function Features() { - return ( -
-
-
-

Reliable crawling 🏗️

-

- Crawlee won't fix broken selectors for you (yet), but it helps you build and maintain your crawlers faster. -

-

- When a website adds JavaScript rendering, you don't have to rewrite everything, only switch to - one of the browser crawlers. When you later find a great API to speed up your crawls, flip the switch back. -

-

- It keeps your proxies healthy by rotating them smartly with good fingerprints that make your crawlers - look human-like. It's not unblockable, - but it will save you money in the long run. -

-

- Crawlee is built by people who scrape for a living and use it every day to scrape millions of pages. - Meet our community on Discord. -

-
-
-
- -
-
-
+
+ +
+
); } -const example = `import { PlaywrightCrawler } from 'crawlee'; +const jsExample = `import { PlaywrightCrawler } from 'crawlee'; // PlaywrightCrawler crawls the web using a headless browser controlled by the Playwright library. const crawler = new PlaywrightCrawler({ @@ -130,126 +66,107 @@ const data = await crawler.getData(); console.table(data.items); `; -function ActorExample() { - return ( -
-
-

Try Crawlee out 👾

- - Crawlee requires Node.js 16 or higher. - -

- The fastest way to try Crawlee out is to use the Crawlee CLI and choose - the Getting started example. - The CLI will install all the necessary dependencies and add boilerplate code for you to play with. -

- - npx crawlee create my-crawler - -

- If you prefer adding Crawlee into your own project, try the example below. - Because it uses PlaywrightCrawler we also need to install Playwright. - It's not bundled with Crawlee to reduce install size. -

- - npm install crawlee playwright - - - {{ - code: example, - hash: 'eyJ1IjoiRWdQdHczb2VqNlRhRHQ1cW4iLCJ2IjoxfQ.eyJpbnB1dCI6IntcbiAgICBcImNvZGVcIjogXCJpbXBvcnQgeyBQbGF5d3JpZ2h0Q3Jhd2xlciB9IGZyb20gJ2NyYXdsZWUnO1xcblxcbi8vIENyYXdsZXIgc2V0dXAgZnJvbSB0aGUgcHJldmlvdXMgZXhhbXBsZS5cXG5jb25zdCBjcmF3bGVyID0gbmV3IFBsYXl3cmlnaHRDcmF3bGVyKHtcXG4gICAgLy8gVXNlIHRoZSByZXF1ZXN0SGFuZGxlciB0byBwcm9jZXNzIGVhY2ggb2YgdGhlIGNyYXdsZWQgcGFnZXMuXFxuICAgIGFzeW5jIHJlcXVlc3RIYW5kbGVyKHsgcmVxdWVzdCwgcGFnZSwgZW5xdWV1ZUxpbmtzLCBwdXNoRGF0YSwgbG9nIH0pIHtcXG4gICAgICAgIGNvbnN0IHRpdGxlID0gYXdhaXQgcGFnZS50aXRsZSgpO1xcbiAgICAgICAgbG9nLmluZm8oYFRpdGxlIG9mICR7cmVxdWVzdC5sb2FkZWRVcmx9IGlzICcke3RpdGxlfSdgKTtcXG5cXG4gICAgICAgIC8vIFNhdmUgcmVzdWx0cyBhcyBKU09OIHRvIC4vc3RvcmFnZS9kYXRhc2V0cy9kZWZhdWx0XFxuICAgICAgICBhd2FpdCBwdXNoRGF0YSh7IHRpdGxlLCB1cmw6IHJlcXVlc3QubG9hZGVkVXJsIH0pO1xcblxcbiAgICAgICAgLy8gRXh0cmFjdCBsaW5rcyBmcm9tIHRoZSBjdXJyZW50IHBhZ2VcXG4gICAgICAgIC8vIGFuZCBhZGQgdGhlbSB0byB0aGUgY3Jhd2xpbmcgcXVldWUuXFxuICAgICAgICBhd2FpdCBlbnF1ZXVlTGlua3MoKTtcXG4gICAgfSxcXG5cXG4gICAgLy8gVW5jb21tZW50IHRoaXMgb3B0aW9uIHRvIHNlZSB0aGUgYnJvd3NlciB3aW5kb3cuXFxuICAgIC8vIGhlYWRsZXNzOiBmYWxzZSxcXG5cXG4gICAgLy8gQ29tbWVudCB0aGlzIG9wdGlvbiB0byBzY3JhcGUgdGhlIGZ1bGwgd2Vic2l0ZS5cXG4gICAgbWF4UmVxdWVzdHNQZXJDcmF3bDogMjAsXFxufSk7XFxuXFxuLy8gQWRkIGZpcnN0IFVSTCB0byB0aGUgcXVldWUgYW5kIHN0YXJ0IHRoZSBjcmF3bC5cXG5hd2FpdCBjcmF3bGVyLnJ1bihbJ2h0dHBzOi8vY3Jhd2xlZS5kZXYnXSk7XFxuXFxuLy8gRXhwb3J0IHRoZSBlbnRpcmV0eSBvZiB0aGUgZGF0YXNldCB0byBhIHNpbmdsZSBmaWxlIGluXFxuLy8gLi9zdG9yYWdlL2tleV92YWx1ZV9zdG9yZXMvcmVzdWx0LmNzdlxcbmNvbnN0IGRhdGFzZXQgPSBhd2FpdCBjcmF3bGVyLmdldERhdGFzZXQoKTtcXG5hd2FpdCBkYXRhc2V0LmV4cG9ydFRvQ1NWKCdyZXN1bHQnKTtcXG5cXG4vLyBPciB3b3JrIHdpdGggdGhlIGRhdGEgZGlyZWN0bHkuXFxuY29uc3QgZGF0YSA9IGF3YWl0IGNyYXdsZXIuZ2V0RGF0YSgpO1xcbmNvbnNvbGUudGFibGUoZGF0YS5pdGVtcyk7XFxuXCJcbn0iLCJvcHRpb25zIjp7ImNvbnRlbnRUeXBlIjoiYXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOCIsIm1lbW9yeSI6NDA5Nn19.WKB14SjgTceKYyhONw2oXTkiOao6X4-UAS7cIuwqGvo', - }} - -
-
- ); -} +const pythonExample = `import asyncio -const npmInstall = `npm install apify -npm install -g apify-cli`; -const exampleActor = `import { PlaywrightCrawler, Dataset } from 'crawlee'; +from crawlee.crawlers import PlaywrightCrawler, PlaywrightCrawlingContext -// Import the \`Actor\` class from the Apify SDK. -import { Actor } from 'apify'; -// Set up the integration to Apify. -await Actor.init(); +async def main() -> None: + crawler = PlaywrightCrawler( + max_requests_per_crawl=5, # Limit the crawl to 5 requests at most. + headless=False, # Show the browser window. + browser_type='firefox', # Use the Firefox browser. + ) -// Crawler setup from the previous example. -const crawler = new PlaywrightCrawler({ - // ... -}); -await crawler.run(['https://crawlee.dev']); + # Define the default request handler, which will be called for every request. + @crawler.router.default_handler + async def request_handler(context: PlaywrightCrawlingContext) -> None: + context.log.info(f'Processing {context.request.url} ...') -// Once finished, clean up the environment. -await Actor.exit(); -`; + # Extract and enqueue all links found on the page. + await context.enqueue_links() + + # Extract data from the page using Playwright API. + data = { + 'url': context.request.url, + 'title': await context.page.title(), + 'content': (await context.page.content())[:100], + } -const apifyPush = `apify login # so the CLI knows you -apify init # and the Apify platform understands your project -apify push # time to ship it!`; + # Push the extracted data to the default dataset. + await context.push_data(data) -function Deployment() { + # Run the crawler with the initial list of URLs. + await crawler.run(['https://crawlee.dev']) + + # Export the entire dataset to a JSON file. + await crawler.export_data('results.json') + + # Or work with the data directly. + data = await crawler.get_data() + crawler.log.info(f'Extracted data: {data.items}') + + +if __name__ == '__main__': + asyncio.run(main())`; + +function CodeExampleSection() { + const [activeOption, setActiveOption] = useState('JavaScript'); return ( -
-
-

Deploy to the cloud ☁️

-

- Crawlee is developed by Apify, the web scraping and automation platform. - You can deploy a Crawlee project wherever you want (see our deployment guides for AWS - Lambda and Google Cloud), but using the  - Apify platform will give you the best experience. With a few simple steps, - you can convert your Crawlee project into a so-called Actor. Actors are serverless micro-apps that are easy to develop, run, - share, and integrate. The infra, proxies, and storages are ready to go. Learn more about Actors. -

-

- 1️⃣ First, install the Apify SDK to your project, as well as the Apify CLI. The SDK will help with the Apify integration, - while the CLI will help us with the initialization and deployment. -

- - {npmInstall} - -

- 2️⃣ The next step is to add Actor.init() to the beginning of your main script and Actor.exit() to the end of it. - This will enable the integration to the Apify Platform, so the cloud - storages (e.g. RequestQueue) will be used. The code should look like this: -

- - {{ - code: exampleActor, - hash: 'eyJ1IjoiRWdQdHczb2VqNlRhRHQ1cW4iLCJ2IjoxfQ.eyJpbnB1dCI6IntcbiAgICBcImNvZGVcIjogXCJpbXBvcnQgeyBQbGF5d3JpZ2h0Q3Jhd2xlciB9IGZyb20gJ2NyYXdsZWUnO1xcblxcbi8vIEltcG9ydCB0aGUgYEFjdG9yYCBjbGFzcyBmcm9tIHRoZSBBcGlmeSBTREsuXFxuaW1wb3J0IHsgQWN0b3IgfSBmcm9tICdhcGlmeSc7XFxuXFxuLy8gU2V0IHVwIHRoZSBpbnRlZ3JhdGlvbiB0byBBcGlmeS5cXG5hd2FpdCBBY3Rvci5pbml0KCk7XFxuXFxuLy8gQ3Jhd2xlciBzZXR1cCBmcm9tIHRoZSBwcmV2aW91cyBleGFtcGxlLlxcbmNvbnN0IGNyYXdsZXIgPSBuZXcgUGxheXdyaWdodENyYXdsZXIoe1xcbiAgICAvLyBVc2UgdGhlIHJlcXVlc3RIYW5kbGVyIHRvIHByb2Nlc3MgZWFjaCBvZiB0aGUgY3Jhd2xlZCBwYWdlcy5cXG4gICAgYXN5bmMgcmVxdWVzdEhhbmRsZXIoeyByZXF1ZXN0LCBwYWdlLCBlbnF1ZXVlTGlua3MsIHB1c2hEYXRhLCBsb2cgfSkge1xcbiAgICAgICAgY29uc3QgdGl0bGUgPSBhd2FpdCBwYWdlLnRpdGxlKCk7XFxuICAgICAgICBsb2cuaW5mbyhgVGl0bGUgb2YgJHtyZXF1ZXN0LmxvYWRlZFVybH0gaXMgJyR7dGl0bGV9J2ApO1xcblxcbiAgICAgICAgLy8gU2F2ZSByZXN1bHRzIGFzIEpTT04gdG8gLi9zdG9yYWdlL2RhdGFzZXRzL2RlZmF1bHRcXG4gICAgICAgIGF3YWl0IHB1c2hEYXRhKHsgdGl0bGUsIHVybDogcmVxdWVzdC5sb2FkZWRVcmwgfSk7XFxuXFxuICAgICAgICAvLyBFeHRyYWN0IGxpbmtzIGZyb20gdGhlIGN1cnJlbnQgcGFnZVxcbiAgICAgICAgLy8gYW5kIGFkZCB0aGVtIHRvIHRoZSBjcmF3bGluZyBxdWV1ZS5cXG4gICAgICAgIGF3YWl0IGVucXVldWVMaW5rcygpO1xcbiAgICB9LFxcblxcbiAgICAvLyBVbmNvbW1lbnQgdGhpcyBvcHRpb24gdG8gc2VlIHRoZSBicm93c2VyIHdpbmRvdy5cXG4gICAgLy8gaGVhZGxlc3M6IGZhbHNlLFxcblxcbiAgICAvLyBVbmNvbW1lbnQgdGhpcyBvcHRpb24gdG8gc2NyYXBlIHRoZSBmdWxsIHdlYnNpdGUuXFxuICAgIG1heFJlcXVlc3RzUGVyQ3Jhd2w6IDIwLFxcbn0pO1xcblxcbi8vIEFkZCBmaXJzdCBVUkwgdG8gdGhlIHF1ZXVlIGFuZCBzdGFydCB0aGUgY3Jhd2wuXFxuYXdhaXQgY3Jhd2xlci5ydW4oWydodHRwczovL2NyYXdsZWUuZGV2J10pO1xcblxcbi8vIEV4cG9ydCB0aGUgZW50aXJldHkgb2YgdGhlIGRhdGFzZXQgdG8gYSBzaW5nbGUgZmlsZSBpblxcbi8vIC4vc3RvcmFnZS9rZXlfdmFsdWVfc3RvcmVzL3Jlc3VsdC5jc3ZcXG5jb25zdCBkYXRhc2V0ID0gYXdhaXQgY3Jhd2xlci5nZXREYXRhc2V0KCk7XFxuYXdhaXQgZGF0YXNldC5leHBvcnRUb0NTVigncmVzdWx0Jyk7XFxuXFxuLy8gT3Igd29yayB3aXRoIHRoZSBkYXRhIGRpcmVjdGx5LlxcbmNvbnN0IGRhdGEgPSBhd2FpdCBjcmF3bGVyLmdldERhdGEoKTtcXG5jb25zb2xlLmxvZyhkYXRhLml0ZW1zLnNsaWNlKDAsIDUpKTtcXG5cXG4vLyBPbmNlIGZpbmlzaGVkLCBjbGVhbiB1cCB0aGUgZW52aXJvbm1lbnQuXFxuYXdhaXQgQWN0b3IuZXhpdCgpO1xcblwiXG59Iiwib3B0aW9ucyI6eyJjb250ZW50VHlwZSI6ImFwcGxpY2F0aW9uL2pzb247IGNoYXJzZXQ9dXRmLTgiLCJtZW1vcnkiOjQwOTZ9fQ.Te7qi0ocWNsH3ujFkgIv8AO9GQ5Wk4DZeQ9-zHTy7Vo', - }} - -

- 3️⃣ Then you will need to sign up for the Apify account. Once you have it, - use the Apify CLI to log in via apify login. The last two steps also involve the Apify CLI. Call the apify - init first, which will add Apify config to your project, and finally run the apify push to deploy it. -

- - {apifyPush} - +
+
+
+ setActiveOption(option)} + /> +
+
+
+
+ {activeOption === 'JavaScript' && ( + + {{ + code: jsExample, + hash: 'eyJ1IjoiRWdQdHczb2VqNlRhRHQ1cW4iLCJ2IjoxfQ.eyJpbnB1dCI6IntcbiAgICBcImNvZGVcIjogXCJpbXBvcnQgeyBQbGF5d3JpZ2h0Q3Jhd2xlciB9IGZyb20gJ2NyYXdsZWUnO1xcblxcbi8vIENyYXdsZXIgc2V0dXAgZnJvbSB0aGUgcHJldmlvdXMgZXhhbXBsZS5cXG5jb25zdCBjcmF3bGVyID0gbmV3IFBsYXl3cmlnaHRDcmF3bGVyKHtcXG4gICAgLy8gVXNlIHRoZSByZXF1ZXN0SGFuZGxlciB0byBwcm9jZXNzIGVhY2ggb2YgdGhlIGNyYXdsZWQgcGFnZXMuXFxuICAgIGFzeW5jIHJlcXVlc3RIYW5kbGVyKHsgcmVxdWVzdCwgcGFnZSwgZW5xdWV1ZUxpbmtzLCBwdXNoRGF0YSwgbG9nIH0pIHtcXG4gICAgICAgIGNvbnN0IHRpdGxlID0gYXdhaXQgcGFnZS50aXRsZSgpO1xcbiAgICAgICAgbG9nLmluZm8oYFRpdGxlIG9mICR7cmVxdWVzdC5sb2FkZWRVcmx9IGlzICcke3RpdGxlfSdgKTtcXG5cXG4gICAgICAgIC8vIFNhdmUgcmVzdWx0cyBhcyBKU09OIHRvIC4vc3RvcmFnZS9kYXRhc2V0cy9kZWZhdWx0XFxuICAgICAgICBhd2FpdCBwdXNoRGF0YSh7IHRpdGxlLCB1cmw6IHJlcXVlc3QubG9hZGVkVXJsIH0pO1xcblxcbiAgICAgICAgLy8gRXh0cmFjdCBsaW5rcyBmcm9tIHRoZSBjdXJyZW50IHBhZ2VcXG4gICAgICAgIC8vIGFuZCBhZGQgdGhlbSB0byB0aGUgY3Jhd2xpbmcgcXVldWUuXFxuICAgICAgICBhd2FpdCBlbnF1ZXVlTGlua3MoKTtcXG4gICAgfSxcXG5cXG4gICAgLy8gVW5jb21tZW50IHRoaXMgb3B0aW9uIHRvIHNlZSB0aGUgYnJvd3NlciB3aW5kb3cuXFxuICAgIC8vIGhlYWRsZXNzOiBmYWxzZSxcXG5cXG4gICAgLy8gQ29tbWVudCB0aGlzIG9wdGlvbiB0byBzY3JhcGUgdGhlIGZ1bGwgd2Vic2l0ZS5cXG4gICAgbWF4UmVxdWVzdHNQZXJDcmF3bDogMjAsXFxufSk7XFxuXFxuLy8gQWRkIGZpcnN0IFVSTCB0byB0aGUgcXVldWUgYW5kIHN0YXJ0IHRoZSBjcmF3bC5cXG5hd2FpdCBjcmF3bGVyLnJ1bihbJ2h0dHBzOi8vY3Jhd2xlZS5kZXYnXSk7XFxuXFxuLy8gRXhwb3J0IHRoZSBlbnRpcmV0eSBvZiB0aGUgZGF0YXNldCB0byBhIHNpbmdsZSBmaWxlIGluXFxuLy8gLi9zdG9yYWdlL2tleV92YWx1ZV9zdG9yZXMvcmVzdWx0LmNzdlxcbmNvbnN0IGRhdGFzZXQgPSBhd2FpdCBjcmF3bGVyLmdldERhdGFzZXQoKTtcXG5hd2FpdCBkYXRhc2V0LmV4cG9ydFRvQ1NWKCdyZXN1bHQnKTtcXG5cXG4vLyBPciB3b3JrIHdpdGggdGhlIGRhdGEgZGlyZWN0bHkuXFxuY29uc3QgZGF0YSA9IGF3YWl0IGNyYXdsZXIuZ2V0RGF0YSgpO1xcbmNvbnNvbGUudGFibGUoZGF0YS5pdGVtcyk7XFxuXCJcbn0iLCJvcHRpb25zIjp7ImNvbnRlbnRUeXBlIjoiYXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOCIsIm1lbW9yeSI6NDA5Nn19.WKB14SjgTceKYyhONw2oXTkiOao6X4-UAS7cIuwqGvo', + }} + + )} + {activeOption === 'Python' && ( + + {pythonExample} + + )}
+
+
); } export default function Home() { - const SvgLogo = require('../../static/img/crawlee-logo.svg').default; const { siteConfig } = useDocusaurusContext(); return ( - - - - - - -
-
-
- -
+ +
+ + +
+ + +
+
+
); diff --git a/website/src/pages/index.module.css b/website/src/pages/index.module.css index 7bd26776d29e..7d3f93a473e9 100644 --- a/website/src/pages/index.module.css +++ b/website/src/pages/index.module.css @@ -1,254 +1,179 @@ -/** - * CSS files with the .module.css suffix will be treated as CSS modules - * and scoped locally. - */ +/************* PAGE LAYOUT *************/ -.buttons { - display: flex; - align-items: center; - justify-content: center; +#homepageContainer { + width: calc(100% - 32px) !important; + max-width: 1200px !important; + border-left: 1px solid var(--color-separator); + border-right: 1px solid var(--color-separator); + margin: 0 16px; } -.tagline { - font-family: 'Lota Grotesque', sans-serif; - font-size: 64px; - font-weight: 600; - line-height: 80px; - letter-spacing: 0; - text-align: left; - transition: all var(--ifm-transition-fast); - -} - -.taglineGap { - color: transparent !important; -} - -.relative { +.dashedSeparator { position: relative; -} - -.codeBlock { - position: absolute; - top: 40%; - max-width: 400px; width: 100%; + border-bottom: 1px dashed var(--color-separator); } -.heroBanner { - padding-top: 100px; - padding-bottom: 4rem; -} - -.heroBanner h1:nth-child(1) { - background: linear-gradient(225deg, #FFB200 0.1%, #FFB100 8.15%, #FFAF02 15.6%, #FEAB04 22.55%, #FDA606 29.08%, #FCA00A 35.28%, #FB980E 41.26%, #FA9013 47.1%, #F98618 52.89%, #F77B1E 58.73%, #F56F24 64.71%, #F3632B 70.91%, #F25532 77.44%, #EF473A 84.39%, #ED3842 91.84%, #EB284B 99.9%), - #FCA000; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - background-clip: text; +.dashedSeparatorVertical { + position: relative; + border-right: 1px dashed var(--color-separator); } -.heroBanner h1:nth-child(2) { +.dashedDecorativeCircle { + width: 120px; + height: 120px; + border: 1px dashed var(--color-separator); + border-radius: 50%; position: absolute; - top: 0; - z-index: 1; - width: calc(100% - 2rem); -} - -.heroBanner h1::selection, -.heroBanner h1 span::selection { - color: rgb(36, 39, 54) !important; - -webkit-text-fill-color: rgb(36, 39, 54); - background: #B4D7FE !important; - -webkit-background-clip: unset; - background-clip: unset; -} - -html[data-theme='dark'] .heroBanner ::selection { - color: #fff !important; - -webkit-text-fill-color: #fff; - background: #385477 !important; -} - -html .heroBanner h2 { - font-style: normal; - font-weight: 400; - font-size: 24px; - line-height: 40px; - color: #41465d; - margin-top: 8px; - margin-bottom: 24px; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -} - -html[data-theme='dark'] .heroBanner h2 { - color: #b3b8d2; -} - -.heroBanner code { - background: #272c3d; - padding: 10px 20px; -} - -.heroBanner button { - opacity: 0.4; - padding: 5px 8px; - margin-top: -2px; -} - -.heroBanner button span { - width: 16px; - height: 16px; - padding: 0; - margin: 0; -} + transform: translate(-50%, -50%); +} + +.fadedOutSeparator { + border: none; + height: 1px; + background-image: + linear-gradient( + 90deg, + transparent, + transparent 50%, + var(--color-background) 50%, + var(--color-background) 100% + ), + linear-gradient( + 90deg, + var(--color-separator) 0%, + transparent 50%, + var(--color-separator) 100% + ); + background-size: + 6px 1px, + 100% 1px; +} + +.trianglesSeparator { + width: 100%; + height: 32px; + background-position: center; + background-repeat: repeat-x; + background-image: url('../../static/img/triangles_light.svg'); -.heroBanner code span { - color: #f2f3fb; -} + html[data-theme='dark'] & { + background-image: url('../../static/img/triangles_dark.svg'); + } -.logoBlur { - position: absolute; - width: 680px; - height: 680px; - top: -120px; - left: -100px; - z-index: -1; + /* TABLET */ + @media (min-width: 768px) { + background-position: unset; + background-repeat: repeat; + height: 52px; + } } -.heroButtons { - display: flex; - align-items: center; - gap: 1rem; - margin-bottom: 50px; +/* most separators and decorations are not displayed on mobile */ +.dashedSeparatorVertical, +.dashedDecorativeCircle, +.fadedOutSeparator { + display: none; } -@media only screen and (min-device-width: 1101px) and (max-device-width: 1440px) { - .tagline { - font-size: 56px; - line-height: 78px; +/* TABLET */ +@media (min-width: 768px) { + .dashedSeparatorVertical, + .dashedDecorativeCircle, + .fadedOutSeparator { + display: block; } -} -@media only screen and (min-device-width: 997px) and (max-device-width: 1100px) { - .tagline { - font-size: 48px; - line-height: 64px; + #homepageContainer { + width: calc(100% - 80px) !important; + margin: 0 40px; } } -@media only screen and (max-device-width: 996px) { - .codeBlock { - position: relative; - top: 50px; +/* DESKTOP */ +@media (min-width: 1024px) { + .dashedSeparatorVertical, + .dashedDecorativeCircle, + .fadedOutSeparator { + display: block; } - .logoBlur { - display: none; + #homepageContainer { + width: calc(100% - 128px) !important; + margin: 0 64px; } } -@media only screen and (max-device-width: 736px) { - .heroBanner { - padding-top: 20px; - padding-bottom: 2rem; - } +/************* LANGUAGE GET STARTED SECTION *************/ - .tagline { - font-size: 32px; - line-height: 48px; - } - - .tagline br { - display: none; +.languageGetStartedSection { + display: flex; + flex-direction: column; + gap: 32px; + margin: 0 0 24px 0; +} + +/* TABLET */ +@media (min-width: 768px) { + .languageGetStartedSection { + flex-direction: row; + align-items: stretch; + justify-content: space-around; + gap: 0; + margin: 0; } +} - .hideSmall { - display: none; - } +/************* CODE EXAMPLE SECTION *************/ - .codeBlock { - top: 0; - } +.languageSwitchContainer { + place-self: center; + margin: 32px 0 16px 0; } -@media only screen and (max-device-width: 450px) { - .codeBlock code { - font-size: 0.8em; +/* TABLET */ +@media (min-width: 768px) { + .codeExampleSection { + position: relative; } - .heroButtons { - flex-direction: column; - align-items: flex-start !important; + .languageSwitchContainer { + margin: 0; + position: absolute; + top: calc(46px - 18px); + left: calc(50% - 90px); } - .heroBanner button { - opacity: 0; + .decorativeRow { + position: relative; + height: 46px; + border-bottom: 1px dashed var(--color-separator); + + &::before { + content: ' '; + position: absolute; + left: 60px; + height: 100%; + border-right: 1px dashed var(--color-separator); + } + + &::after { + content: ' '; + position: absolute; + right: 60px; + height: 100%; + border-left: 1px dashed var(--color-separator); + } } -} -@media only screen and (max-device-width: 350px) { - .codeBlock code { - font-size: 0.7em; + .codeBlockContainer { + margin: 0 60px; + border-left: 1px dashed var(--color-separator); + border-right: 1px dashed var(--color-separator); + } + .codeBlockContainer > div { + border-radius: 0; + margin: 0; + box-shadow: none; } -} - -.tagline span { - color: var(--ifm-color-primary); -} - -.getStarted { - font-size: 18px; - line-height: 28px; - padding: 12px 24px; - background: #ed3545; - border-radius: 8px; - color: white; - font-weight: 600; -} - -.getStarted:hover { - color: white; - background: #ad1934; -} - -html[data-theme='dark'] .getStarted { - border-color: #585e76; -} - -.try { - padding-top: 20px; -} - -.try, .features { - color: #41465d; -} - -html[data-theme='dark'] .try, -html[data-theme='dark'] .features { - color: #b3b8d2; -} - -.features > * { - margin: 2em 0; -} - -.bottomLogo path:nth-child(1) { - fill: url(#gradient-1) !important; - stroke: url(#gradient-1) !important; -} - -.crawleeForPython { - font-size: 44px; - font-weight: 700; - line-height: 60px; - text-align: center; - margin-bottom: 80px; -} - -.crawleeForPython a { - background: linear-gradient(90deg, #833ab4, #fd1d1d 50%, #fcb045); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: #0000; } diff --git a/website/src/pages/js.js b/website/src/pages/js.js new file mode 100644 index 000000000000..bb355bb8bbf9 --- /dev/null +++ b/website/src/pages/js.js @@ -0,0 +1,387 @@ +/* eslint-disable max-len */ +import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; +import CodeBlock from '@theme/CodeBlock'; +import Layout from '@theme/Layout'; +import ThemedImage from '@theme/ThemedImage'; +import React from 'react'; + +import commonStyles from './index.module.css'; +import styles from './js.module.css'; +import Button from '../components/Button'; +import HomepageCliExample from '../components/Homepage/HomepageCliExample'; +import HomepageCtaSection from '../components/Homepage/HomepageCtaSection'; +import HomepageHeroSection from '../components/Homepage/HomepageHeroSection'; +import LanguageInfoWidget from '../components/Homepage/LanguageInfoWidget'; +import RiverSection from '../components/Homepage/RiverSection'; +import ThreeCardsWithIcon from '../components/Homepage/ThreeCardsWithIcon'; +import RunnableCodeBlock from '../components/RunnableCodeBlock'; + +function GetStartedSection() { + return ( +
+ +
+ ); +} + +const example = `import { PlaywrightCrawler } from 'crawlee'; + +const crawler = new PlaywrightCrawler({ + async requestHandler({ request, page, enqueueLinks, pushData, log }) { + const title = await page.title(); + log.info(\`Title of \${request.loadedUrl} is '\${title}'\`); + + await pushData({ title, url: request.loadedUrl }); + await enqueueLinks(); + }, + + // Uncomment this option to see the browser window. + // headless: false, +}); + +await crawler.run(['https://crawlee.dev']); +`; + +function CodeExampleSection() { + return ( +
+
+
+ + {{ + code: example, + hash: 'eyJ1IjoiRWdQdHczb2VqNlRhRHQ1cW4iLCJ2IjoxfQ.eyJpbnB1dCI6IntcbiAgICBcImNvZGVcIjogXCJpbXBvcnQgeyBQbGF5d3JpZ2h0Q3Jhd2xlciB9IGZyb20gJ2NyYXdsZWUnO1xcblxcbi8vIENyYXdsZXIgc2V0dXAgZnJvbSB0aGUgcHJldmlvdXMgZXhhbXBsZS5cXG5jb25zdCBjcmF3bGVyID0gbmV3IFBsYXl3cmlnaHRDcmF3bGVyKHtcXG4gICAgLy8gVXNlIHRoZSByZXF1ZXN0SGFuZGxlciB0byBwcm9jZXNzIGVhY2ggb2YgdGhlIGNyYXdsZWQgcGFnZXMuXFxuICAgIGFzeW5jIHJlcXVlc3RIYW5kbGVyKHsgcmVxdWVzdCwgcGFnZSwgZW5xdWV1ZUxpbmtzLCBwdXNoRGF0YSwgbG9nIH0pIHtcXG4gICAgICAgIGNvbnN0IHRpdGxlID0gYXdhaXQgcGFnZS50aXRsZSgpO1xcbiAgICAgICAgbG9nLmluZm8oYFRpdGxlIG9mICR7cmVxdWVzdC5sb2FkZWRVcmx9IGlzICcke3RpdGxlfSdgKTtcXG5cXG4gICAgICAgIC8vIFNhdmUgcmVzdWx0cyBhcyBKU09OIHRvIC4vc3RvcmFnZS9kYXRhc2V0cy9kZWZhdWx0XFxuICAgICAgICBhd2FpdCBwdXNoRGF0YSh7IHRpdGxlLCB1cmw6IHJlcXVlc3QubG9hZGVkVXJsIH0pO1xcblxcbiAgICAgICAgLy8gRXh0cmFjdCBsaW5rcyBmcm9tIHRoZSBjdXJyZW50IHBhZ2VcXG4gICAgICAgIC8vIGFuZCBhZGQgdGhlbSB0byB0aGUgY3Jhd2xpbmcgcXVldWUuXFxuICAgICAgICBhd2FpdCBlbnF1ZXVlTGlua3MoKTtcXG4gICAgfSxcXG5cXG4gICAgLy8gVW5jb21tZW50IHRoaXMgb3B0aW9uIHRvIHNlZSB0aGUgYnJvd3NlciB3aW5kb3cuXFxuICAgIC8vIGhlYWRsZXNzOiBmYWxzZSxcXG5cXG4gICAgLy8gQ29tbWVudCB0aGlzIG9wdGlvbiB0byBzY3JhcGUgdGhlIGZ1bGwgd2Vic2l0ZS5cXG4gICAgbWF4UmVxdWVzdHNQZXJDcmF3bDogMjAsXFxufSk7XFxuXFxuLy8gQWRkIGZpcnN0IFVSTCB0byB0aGUgcXVldWUgYW5kIHN0YXJ0IHRoZSBjcmF3bC5cXG5hd2FpdCBjcmF3bGVyLnJ1bihbJ2h0dHBzOi8vY3Jhd2xlZS5kZXYnXSk7XFxuXFxuLy8gRXhwb3J0IHRoZSBlbnRpcmV0eSBvZiB0aGUgZGF0YXNldCB0byBhIHNpbmdsZSBmaWxlIGluXFxuLy8gLi9zdG9yYWdlL2tleV92YWx1ZV9zdG9yZXMvcmVzdWx0LmNzdlxcbmNvbnN0IGRhdGFzZXQgPSBhd2FpdCBjcmF3bGVyLmdldERhdGFzZXQoKTtcXG5hd2FpdCBkYXRhc2V0LmV4cG9ydFRvQ1NWKCdyZXN1bHQnKTtcXG5cXG4vLyBPciB3b3JrIHdpdGggdGhlIGRhdGEgZGlyZWN0bHkuXFxuY29uc3QgZGF0YSA9IGF3YWl0IGNyYXdsZXIuZ2V0RGF0YSgpO1xcbmNvbnNvbGUudGFibGUoZGF0YS5pdGVtcyk7XFxuXCJcbn0iLCJvcHRpb25zIjp7ImNvbnRlbnRUeXBlIjoiYXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOCIsIm1lbW9yeSI6NDA5Nn19.WKB14SjgTceKYyhONw2oXTkiOao6X4-UAS7cIuwqGvo', + }} + +
+
+
+
+ ); +} + +const benefitsCodeBlockCrawler = `{ + useFingerprints: true, + fingerprintOptions: { + fingerprintGeneratorOptions: { + browsers: [BrowserName.chrome, BrowserName.firefox], + devices: [DeviceCategory.mobile], + locales: ['en-US'], + }, + }, +}, +`; + +const benefitsCodeBlockHeadless = `const crawler = new AdaptivePlaywrightCrawler({ + renderingTypeDetectionRatio: 0.1, + async requestHandler({ querySelector, enqueueLinks }) { + // The crawler detects if JS rendering is needed + // to extract this data. If not, it will use HTTP + // for follow-up requests to save time and costs. + const $prices = await querySelector('span.price') + await enqueueLinks(); + }, +}); +`; + +function BenefitsSection() { + return ( +
+

What are the benefits?

+ + {benefitsCodeBlockCrawler} + + } + + to="/docs/guides/avoid-blocking" + /> +
+ + } + reversed + to="/docs/quick-start#choose-your-crawler" + /> +
+ + {benefitsCodeBlockHeadless} + + } + to="/api/core" + /> +
+ ); +} + +function OtherFeaturesSection() { + return ( +
+

What else is in Crawlee?

+
+
+
+ +
+

+ Auto scaling +

+
+ Crawlers automatically adjust concurrency based + on available system resources. Avoid memory + errors in small containers and run faster in + large ones. +
+
+
+
+ +
+

+ Smart proxy rotation +

+
+ Crawlee uses a pool of sessions represented by + different proxies to maintain the proxy + performance and keep IPs healthy. Blocked + proxies are removed from the pool automatically. +
+
+
+
+ + ), + title: 'Queue and storage', + description: + 'Pause and resume crawlers thanks to a persistent queue of URLs and storage for structured data.', + }, + { + icon: ( + + ), + title: 'Handy scraping utils', + description: + 'Sitemaps, infinite scroll, contact extraction, large asset blocking and many more utils included.', + }, + { + icon: ( + + ), + title: 'Routing & middleware', + description: + 'Keep your code clean and organized while managing complex crawls with a built-in router that streamlines the process.', + }, + ]} + /> +
+
+ ); +} + +function DeployToCloudSection() { + return ( +
+
+

Deploy to cloud

+
+ Crawlee, by Apify, works anywhere, but Apify offers the best + experience. Easily turn your project into an Actor—a + serverless micro-app with built-in infra, proxies, and + storage. +
+ +
+
+
+
+
+
1
+
+
+ Install Apify SDK and Apify CLI. +
+
+
+
+
2
+
+
+ Add
Actor.init()
to the begining and{' '} +
Actor.exit()
to the end of your code. +
+
+
+
+
3
+
+
+ Use the Apify CLI to push the code to the Apify + platform. +
+
+
+
+ ); +} + +function BuildFastScrapersSection() { + return ( +
+
+
+

Crawlee helps you build scrapers faster

+ + ), + title: 'Zero setup required', + description: + 'Use on the templates, install Crawlee and go. No CLI required, no complex file structure, no boilerplate.', + actionLink: { + text: 'Get started', + href: '/docs/quick-start', + }, + }, + { + icon: ( + + ), + title: 'Reasonable defaults', + description: + 'Unblocking, proxy rotation and other core features are already turned on. But also very configurable.', + actionLink: { + text: 'Learn more', + href: '/docs/guides/configuration', + }, + }, + { + icon: ( + + ), + title: 'Helpful community', + description: + 'Join our Discord community of over Xk developers and get fast answers to your web scraping questions.', + actionLink: { + text: 'Join Discord', + href: 'https://discord.gg/jyEM2PRvMU', + }, + }, + ]} + /> +
+ ); +} + +export default function JavascriptHomepage() { + const { siteConfig } = useDocusaurusContext(); + return ( + +
+ + +
+ + +
+
+
+ +
+ +
+ +
+ + +
+ + ); +} diff --git a/website/src/pages/js.module.css b/website/src/pages/js.module.css new file mode 100644 index 000000000000..6ed199f45fdd --- /dev/null +++ b/website/src/pages/js.module.css @@ -0,0 +1,295 @@ +/************** BENEFITS SECTION ***********/ + +.benefitsSection { + margin-bottom: 60px; + + h2 { + margin: 32px 0; + text-align: center; + + /* TABLET */ + @media (min-width: 768px) { + margin: 80px 0; + } + } +} + +/************** OTHER FEATURES SECTION ***********/ + +.otherFeaturesSection { + display: flex; + flex-direction: column; + + h2 { + padding: 32px 12px; + + text-align: center; + color: var(--color-text); + font-weight: 400; + + line-height: 46px !important; + font-size: 36px !important; + + @media (min-width: 768px) { + line-height: 56px !important; + font-size: 48px !important; + margin: 80px 0 64px; + padding: 32px 24px; + } + } + margin-bottom: 40px; + + @media (min-width: 768px) { + margin-bottom: 80px; + } +} + +.cardsWithContentContainer { + display: flex; + flex-direction: column; + gap: 20px; + background-position-x: 5px; + background-image: url('../../static/img/triangles_light.svg'); + + html[data-theme='dark'] & { + background-image: url('../../static/img/triangles_dark.svg'); + } + + @media (min-width: 768px) { + gap: 48px; + } +} + +.cardsWithImageContainer { + display: flex; + flex-direction: column; + gap: 20px; + width: 100%; + + @media (min-width: 768px) { + gap: 32px; + flex-direction: row; + } +} + +.cardWithImage { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + background: var(--color-card-background); + border-block: 1px solid var(--color-separator); + + @media (min-width: 768px) { + border: 1px solid var(--color-separator); + } + + &:first-child { + border-left: 0; + } + &:last-child { + border-right: 0; + } +} + +.cardWithImage img { + width: 100%; + height: 250px; + object-fit: cover; +} + +.cardWithImage:last-child img { + object-position: left 90%; +} + +.cardWithImageText { + padding: 40px 24px; + border-top: 1px solid var(--color-separator); +} + +.cardWithImageTitle { + margin: 0; + + color: var(--color-text); + font-size: 26px; + font-style: normal; + font-weight: 400; + line-height: 34px; +} + +.cardWithImageDescription { + margin-top: 12px; + color: var(--color-text-muted); + font-family: var(--ifm-font-family-base); + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; +} + +/************** DEPLOY TO CLOUD SECTION ***********/ + +.deployToCloudSection { + padding: 32px 16px; + display: flex; + flex-direction: column; + align-items: center; + gap: 48px; +} + +.deployToCloudLeftSide { + display: flex; + flex-direction: column; + flex-basis: 50%; + gap: 24px; + text-align: center; + font-style: normal; + font-weight: 400; + + h2 { + color: var(--color-text); + font-family: 'Lota Grotesque'; + font-size: 38px; + line-height: 46px; + } +} + +.deployToCloudDescription { + color: var(--color-text-muted); + font-size: 16px; + line-height: 24px; +} + +.deployToCloudRightSide { + display: flex; + flex-direction: column; + gap: 24px; + flex-basis: 50%; + position: relative; +} + +.deployToCloudStep { + display: flex; + flex-direction: row; + gap: 16px; + align-items: center; +} + +.deployToCloudStepNumber { + display: flex; + justify-content: center; + align-items: center; + width: 72px; + height: 72px; + padding: 16px; + border-radius: 8px; + border: 1px solid var(--color-separator); + background: var(--color-background); + color: var(--color-text-muted); + font-size: 16px; + font-style: normal; + font-weight: 400; + line-height: 24px; + z-index: 1; + div { + display: flex; + justify-content: center; + align-items: center; + height: 40px; + width: 40px; + border-radius: 50%; + border: 1px dashed var(--color-separator); + } +} + +.deployToCloudStepText { + display: inline-flex; + align-items: baseline; + flex-wrap: wrap; + gap: 4px; + color: var(--color-text); + font-size: 14px; + font-style: normal; + font-weight: 500; + line-height: 20px; + + pre { + margin: 0; + padding: 0; + background-color: transparent; + } +} + +#verticalStepLine { + position: absolute; + left: 36px; + height: 100%; + z-index: 0; +} + +/* TABLET */ +@media (min-width: 768px) { + .deployToCloudSection { + padding: 96px 40px; + flex-direction: row; + } + .deployToCloudLeftSide { + text-align: left; + + h2 { + color: var(--color-text); + font-family: 'Lota Grotesque'; + font-size: 48px; + line-height: 58px; + } + } + .deployToCloudDescription { + font-size: 18px; + line-height: 28px; + } +} + +/************** BUILD SCRAPERS FAST SECTION ***********/ + +.buildFastScrapersSection { + position: relative; + + padding: 40px 0 32px; + + border-bottom: 1px solid var(--color-separator); + + h2 { + margin: 0; + padding: 32px 0; + text-align: center; + color: var(--color-text); + font-weight: 400; + padding-inline: 12px; + + line-height: 46px !important; + font-size: 36px !important; + + @media (min-width: 768px) { + padding-inline: 24px; + + line-height: 56px !important; + font-size: 48px !important; + padding: 80px 0 64px; + } + } + + div[class*='dashedDecorativeCircle'] { + display: none; + } + + @media (min-width: 1024px) { + padding: 80px 0 60px; + div[class*='dashedDecorativeCircle'] { + display: block; + } + } +} + +.buildFastScrapersContent { + border-block: 1px solid var(--color-separator); +} diff --git a/website/static/img/animated-crawlee-logo-dark.svg b/website/static/img/animated-crawlee-logo-dark.svg new file mode 100644 index 000000000000..f33b08b25563 --- /dev/null +++ b/website/static/img/animated-crawlee-logo-dark.svg @@ -0,0 +1,646 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/static/img/animated-crawlee-logo-light.svg b/website/static/img/animated-crawlee-logo-light.svg new file mode 100644 index 000000000000..c4f256be9a10 --- /dev/null +++ b/website/static/img/animated-crawlee-logo-light.svg @@ -0,0 +1,590 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/website/static/img/arrow_right.svg b/website/static/img/arrow_right.svg new file mode 100644 index 000000000000..37e30d2e2ac2 --- /dev/null +++ b/website/static/img/arrow_right.svg @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/website/static/img/auto-scaling-dark.webp b/website/static/img/auto-scaling-dark.webp new file mode 100644 index 000000000000..942f7b1ff4f3 Binary files /dev/null and b/website/static/img/auto-scaling-dark.webp differ diff --git a/website/static/img/auto-scaling-light.webp b/website/static/img/auto-scaling-light.webp new file mode 100644 index 000000000000..a93153df0d22 Binary files /dev/null and b/website/static/img/auto-scaling-light.webp differ diff --git a/website/static/img/community-dark-icon.svg b/website/static/img/community-dark-icon.svg new file mode 100644 index 000000000000..d7ab5225c1b9 --- /dev/null +++ b/website/static/img/community-dark-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/website/static/img/community-light-icon.svg b/website/static/img/community-light-icon.svg new file mode 100644 index 000000000000..5159a6100027 --- /dev/null +++ b/website/static/img/community-light-icon.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/website/static/img/crawlee-logo-monocolor.svg b/website/static/img/crawlee-logo-monocolor.svg new file mode 100644 index 000000000000..628e0bae2c96 --- /dev/null +++ b/website/static/img/crawlee-logo-monocolor.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/website/static/img/defaults-dark-icon.svg b/website/static/img/defaults-dark-icon.svg new file mode 100644 index 000000000000..8ba564aba490 --- /dev/null +++ b/website/static/img/defaults-dark-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/website/static/img/defaults-light-icon.svg b/website/static/img/defaults-light-icon.svg new file mode 100644 index 000000000000..f10fcc7c22be --- /dev/null +++ b/website/static/img/defaults-light-icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/website/static/img/js_dark.png b/website/static/img/js_dark.png new file mode 100644 index 000000000000..29cd7392a7b8 Binary files /dev/null and b/website/static/img/js_dark.png differ diff --git a/website/static/img/js_light.png b/website/static/img/js_light.png new file mode 100644 index 000000000000..97c34ff52a2c Binary files /dev/null and b/website/static/img/js_light.png differ diff --git a/website/static/img/queue-dark-icon.svg b/website/static/img/queue-dark-icon.svg new file mode 100644 index 000000000000..4b06fd0c00fa --- /dev/null +++ b/website/static/img/queue-dark-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/website/static/img/queue-light-icon.svg b/website/static/img/queue-light-icon.svg new file mode 100644 index 000000000000..d31ffda4bd15 --- /dev/null +++ b/website/static/img/queue-light-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/website/static/img/routing-dark-icon.svg b/website/static/img/routing-dark-icon.svg new file mode 100644 index 000000000000..26585e9b7fe9 --- /dev/null +++ b/website/static/img/routing-dark-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/website/static/img/routing-light-icon.svg b/website/static/img/routing-light-icon.svg new file mode 100644 index 000000000000..0e11097d73ae --- /dev/null +++ b/website/static/img/routing-light-icon.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/website/static/img/scraping-utils-dark-icon.svg b/website/static/img/scraping-utils-dark-icon.svg new file mode 100644 index 000000000000..76db00a841ec --- /dev/null +++ b/website/static/img/scraping-utils-dark-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/website/static/img/scraping-utils-light-icon.svg b/website/static/img/scraping-utils-light-icon.svg new file mode 100644 index 000000000000..10e9c73b0e33 --- /dev/null +++ b/website/static/img/scraping-utils-light-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/website/static/img/smart-proxy-dark.webp b/website/static/img/smart-proxy-dark.webp new file mode 100644 index 000000000000..6eec515a00a1 Binary files /dev/null and b/website/static/img/smart-proxy-dark.webp differ diff --git a/website/static/img/smart-proxy-light.webp b/website/static/img/smart-proxy-light.webp new file mode 100644 index 000000000000..d38f23f3eed0 Binary files /dev/null and b/website/static/img/smart-proxy-light.webp differ diff --git a/website/static/img/triangles_dark.svg b/website/static/img/triangles_dark.svg new file mode 100644 index 000000000000..e1239828e4a5 --- /dev/null +++ b/website/static/img/triangles_dark.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/static/img/triangles_light.svg b/website/static/img/triangles_light.svg new file mode 100644 index 000000000000..bd26bc94823c --- /dev/null +++ b/website/static/img/triangles_light.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/website/static/img/zero-setup-dark-icon.svg b/website/static/img/zero-setup-dark-icon.svg new file mode 100644 index 000000000000..9d70adcd8b95 --- /dev/null +++ b/website/static/img/zero-setup-dark-icon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/website/static/img/zero-setup-light-icon.svg b/website/static/img/zero-setup-light-icon.svg new file mode 100644 index 000000000000..97b41580475d --- /dev/null +++ b/website/static/img/zero-setup-light-icon.svg @@ -0,0 +1,6 @@ + + + + + +