From dff07b9a754a93ab760b824a028dd62068e8ac8d Mon Sep 17 00:00:00 2001 From: Vlada Dusek Date: Fri, 10 Jan 2025 15:41:45 +0100 Subject: [PATCH] docs: Add Crawlee for Python v0.5 release blog post (#2804) Closes: https://github.com/apify/crawlee-python/issues/880 --------- Co-authored-by: Daniel Lee --- .../blog/2025/01-10/img/import_crawlers.webp | Bin 0 -> 42458 bytes website/blog/2025/01-10/index.md | 266 ++++++++++++++++++ website/blog/authors.yml | 11 +- 3 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 website/blog/2025/01-10/img/import_crawlers.webp create mode 100644 website/blog/2025/01-10/index.md diff --git a/website/blog/2025/01-10/img/import_crawlers.webp b/website/blog/2025/01-10/img/import_crawlers.webp new file mode 100644 index 0000000000000000000000000000000000000000..adfc64c26842c237201378659eaca2407d2f094c GIT binary patch literal 42458 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 Vu`cluzyo*KS&RQ;jQy|kKLFa9pGyD$ literal 0 HcmV?d00001 diff --git a/website/blog/2025/01-10/index.md b/website/blog/2025/01-10/index.md new file mode 100644 index 000000000000..cb09a1ad0018 --- /dev/null +++ b/website/blog/2025/01-10/index.md @@ -0,0 +1,266 @@ +--- +slug: crawlee-for-python-v05 +title: Crawlee for Python v0.5 +description: Announcing the Crawlee for Python v0.5 release. +authors: [VladaD] +--- + +Crawlee for Python v0.5 is now available! This is our biggest release to date, bringing new ported functionality from the [Crawlee for JavaScript](https://github.com/apify/crawlee), brand-new features that are exclusive to the Python library (for now), a new consolidated package structure, and a bunch of bug fixes and further improvements. + +## Getting started + +You can upgrade to the latest version straight from [PyPI](https://pypi.org/project/crawlee/): + +```shell +pip install --upgrade crawlee +``` + +Check out the full changelog on our [website](https://www.crawlee.dev/python/docs/changelog#050-2025-01-02) to see all the details. If you are updating from an older version, make sure to follow our [Upgrading to v0.5](https://www.crawlee.dev/python/docs/upgrading/upgrading-to-v0x#upgrading-to-v05) guide for a smooth upgrade. + +## New package structure + +We have introduced a new consolidated package structure. The goal is to streamline the development experience, help you find the crawlers you are looking for faster, and improve the IDE's code suggestions while importing. + +### Crawlers + +We have grouped all crawler classes (and their corresponding crawling context classes) into a single sub-package called `crawlers`. Here is a quick example of how the imports have changed: + +```diff +- from crawlee.beautifulsoup_crawler import BeautifulSoupCrawler, BeautifulSoupCrawlingContext ++ from crawlee.crawlers import BeautifulSoupCrawler, BeautifulSoupCrawlingContext +``` + +Look how you can see all the crawlers that we have, isn't that cool! + +![Import from crawlers subpackage.](./img/import_crawlers.webp) + +### Storage clients + +Similarly, we have moved all storage client classes under `storage_clients` sub-package. For instance: + +```diff +- from crawlee.memory_storage_client import MemoryStorageClient ++ from crawlee.storage_clients import MemoryStorageClient +``` + +This consolidation makes it clearer where each class belongs and ensures that your IDE can provide better autocompletion when you are looking for the right crawler or storage client. + +## Continued parity with Crawlee JS + +We are constantly working toward feature parity with our JavaScript library, [Crawlee JS](https://github.com/apify/crawlee). With v0.5, we have brought over more functionality: + +### HTML to text context helper + +The `html_to_text` crawling context helper simplifies extracting text from an HTML page by automatically removing all tags and returning only the raw text content. It's available in the [`ParselCrawlingContext`](https://www.crawlee.dev/python/api/class/ParselCrawlingContext#html_to_text) and [`BeautifulSoupCrawlingContext`](https://www.crawlee.dev/python/api/class/BeautifulSoupCrawlingContext#html_to_text). + +```python +import asyncio + +from crawlee.crawlers import ParselCrawler, ParselCrawlingContext + + +async def main() -> None: + crawler = ParselCrawler() + + @crawler.router.default_handler + async def handler(context: ParselCrawlingContext) -> None: + context.log.info('Crawling: %s', context.request.url) + text = context.html_to_text() + # Continue with the processing... + + await crawler.run(['https://crawlee.dev']) + + +if __name__ == '__main__': + asyncio.run(main()) +``` + +In this example, we use a [`ParselCrawler`](https://www.crawlee.dev/python/api/class/ParselCrawler) to fetch a webpage, then invoke `context.html_to_text()` to extract clean text for further processing. + +### Use state + +The [`use_state`](https://www.crawlee.dev/python/api/class/UseStateFunction) crawling context helper makes it simple to create and manage persistent state values within your crawler. It ensures that all state values are automatically persisted. It enables you to maintain data across different crawler runs, restarts, and failures. It acts as a convenient abstraction for interaction with [`KeyValueStore`](https://www.crawlee.dev/python/api/class/KeyValueStore). + +```python +import asyncio + +from crawlee import Request +from crawlee.configuration import Configuration +from crawlee.crawlers import ParselCrawler, ParselCrawlingContext + + +async def main() -> None: + # Create a crawler with purge_on_start disabled to retain state across runs. + crawler = ParselCrawler( + configuration=Configuration(purge_on_start=False), + ) + + @crawler.router.default_handler + async def handler(context: ParselCrawlingContext) -> None: + context.log.info(f'Crawling {context.request.url}') + + # Retrieve or initialize the state with a default value. + state = await context.use_state('state', default_value={'runs': 0}) + + # Increment the run count. + state['runs'] += 1 + + # Create a request with always_enqueue enabled to bypass deduplication and ensure it is processed. + request = Request.from_url('https://crawlee.dev/', always_enqueue=True) + + # Run the crawler with the start request. + await crawler.run([request]) + + # Fetch the persisted state from the key-value store. + kvs = await crawler.get_key_value_store() + state = await kvs.get_auto_saved_value('state') + crawler.log.info(f'Final state after run: {state}') + + +if __name__ == '__main__': + asyncio.run(main()) +``` + +Please note that the `use_state` is an experimental feature. Its behavior and interface may evolve in future versions. + +## Brand new features + +In addition to porting features from JS, we are introducing new, Python-first functionalities that will eventually make their way into Crawlee JS in the coming months. + +### Crawler's stop method + +The [`BasicCrawler`](https://www.crawlee.dev/python/api/class/BasicCrawler), and by extension, all crawlers that inherit from it, now has a [`stop`](https://www.crawlee.dev/python/api/class/BasicCrawler#stop) method. This makes it easy to halt the crawling when a specific condition is met, for instance, if you have found the data you were looking for. + +```python +import asyncio + +from crawlee.crawlers import ParselCrawler, ParselCrawlingContext + + +async def main() -> None: + crawler = ParselCrawler() + + @crawler.router.default_handler + async def handler(context: ParselCrawlingContext) -> None: + context.log.info('Crawling: %s', context.request.url) + + # Extract and enqueue links from the page. + await context.enqueue_links() + + title = context.selector.css('title::text').get() + + # Condition when you want to stop the crawler, e.g. you + # have found what you were looking for. + if 'Crawlee for Python' in title: + context.log.info('Condition met, stopping the crawler.') + await crawler.stop() + + await crawler.run(['https://crawlee.dev']) + + +if __name__ == '__main__': + asyncio.run(main()) +``` + +### Request loaders + +There are new classes [`RequestLoader`](https://www.crawlee.dev/python/api/class/RequestLoader), [`RequestManager`](https://www.crawlee.dev/python/api/class/RequestManager) and [`RequestManagerTandem`](https://www.crawlee.dev/python/api/class/RequestManagerTandem) that manage how Crawlee accesses and stores requests. They allow you to use other component (service) as a source for requests and optionally you can combine it with a [`RequestQueue`](https://www.crawlee.dev/python/api/class/RequestQueue). They let you plug in any request source, and combine the external data sources with Crawlee's standard `RequestQueue`. + +You can learn more about these new features in the [Request loaders guide](https://www.crawlee.dev/python/docs/guides/request-loaders). + +```python +import asyncio + +from crawlee.crawlers import ParselCrawler, ParselCrawlingContext +from crawlee.request_loaders import RequestList, RequestManagerTandem +from crawlee.storages import RequestQueue + + +async def main() -> None: + rl = RequestList( + [ + 'https://crawlee.dev', + 'https://apify.com', + # Long list of URLs... + ], + ) + + rq = await RequestQueue.open() + + # Combine them into a single request source. + tandem = RequestManagerTandem(rl, rq) + + crawler = ParselCrawler(request_manager=tandem) + + @crawler.router.default_handler + async def handler(context: ParselCrawlingContext) -> None: + context.log.info(f'Crawling {context.request.url}') + # ... + + await crawler.run() + + +if __name__ == '__main__': + asyncio.run(main()) +``` + +In this example we combine a [`RequestList`](https://www.crawlee.dev/python/api/class/RequestList) with a [`RequestQueue`](https://www.crawlee.dev/python/api/class/RequestQueue). However, instead of the `RequestList` you can use any other class that implements the [`RequestLoader`](https://www.crawlee.dev/python/api/class/RequestLoader) interface to suit your specific requirements. + +### Service locator + +The [`ServiceLocator`](https://www.crawlee.dev/python/api/class/ServiceLocator) is primarily an internal mechanism for managing the services that Crawlee depends on. Specifically, the [`Configuration`](https://www.crawlee.dev/python/api/class/ServiceLocator), [`StorageClient`](https://www.crawlee.dev/python/api/class/ServiceLocator), and [`EventManager`](https://www.crawlee.dev/python/api/class/ServiceLocator). By swapping out these components, you can adapt Crawlee to suit different runtime environments. + +You can use the service locator explicitly: + +```python +import asyncio + +from crawlee import service_locator +from crawlee.configuration import Configuration +from crawlee.crawlers import ParselCrawler, ParselCrawlingContext +from crawlee.events import LocalEventManager +from crawlee.storage_clients import MemoryStorageClient + + +async def main() -> None: + service_locator.set_configuration(Configuration()) + service_locator.set_storage_client(MemoryStorageClient()) + service_locator.set_event_manager(LocalEventManager()) + + crawler = ParselCrawler() + + # ... + + +if __name__ == '__main__': + asyncio.run(main()) +``` + +Or pass the services directly to the crawler instance, and they will be set under the hood: + +```python +import asyncio + +from crawlee.configuration import Configuration +from crawlee.crawlers import ParselCrawler, ParselCrawlingContext +from crawlee.events import LocalEventManager +from crawlee.storage_clients import MemoryStorageClient + + +async def main() -> None: + crawler = ParselCrawler( + configuration=Configuration(), + storage_client=MemoryStorageClient(), + event_manager=LocalEventManager(), + ) + + # ... + + +if __name__ == '__main__': + asyncio.run(main()) +``` + +## Conclusion + +We are excited to share that Crawlee v0.5 is here. If you have any questions or feedback, please open a [GitHub discussion](https://github.com/apify/crawlee-python/discussions). If you encounter any bugs, or have an idea for a new feature, please open a [GitHub issue](https://github.com/apify/crawlee-python/issues). diff --git a/website/blog/authors.yml b/website/blog/authors.yml index 9cb5502a7bb0..ea38d6caa64a 100644 --- a/website/blog/authors.yml +++ b/website/blog/authors.yml @@ -40,6 +40,7 @@ LukasP: image_url: ./img/lukasp.webp socials: github: Patai5 + MatejV: name: Matěj Volf title: Web Automation Engineer @@ -47,6 +48,7 @@ MatejV: image_url: https://avatars.githubusercontent.com/u/31281386?v=4 socials: github: mvolfik + SatyamT: name: Satyam Tripathi title: Community Member of Crawlee @@ -54,4 +56,11 @@ SatyamT: image_url: https://avatars.githubusercontent.com/u/69134468?v=4 socials: github: triposat - \ No newline at end of file + +VladaD: + name: Vlada Dusek + title: Developer of Crawlee for Python + url: https://github.com/vdusek + image_url: https://avatars.githubusercontent.com/u/25082181?v=4 + socials: + github: vdusek