使用Crawlee的异步爬虫存在用户数据交叉污染问题求助
问题描述
使用Crawlee的PlaywrightCrawler编写多用户爬虫程序,支持用户上传网站链接并爬取该链接下的所有内部链接,但并发运行多个用户的爬取任务时,不同用户的爬取数据会出现共享,爬取链路发生混合。
问题根源
- 全局Dataset共享:Crawlee默认的
Dataset是全局单例对象,多个Crawler实例调用context.push_data()时,所有数据会写入同一个全局Dataset,导致不同用户的爬取数据混杂。 - 队列隔离隐患:虽然每个PlaywrightCrawler默认会创建独立的内存RequestQueue,但未显式配置的情况下,复杂场景中可能出现意外的队列共享问题。
解决方案
为每个用户的爬取任务配置完全独立的资源,彻底隔离不同用户的爬取链路和数据:
- 为每个用户创建唯一命名的
Dataset,确保数据单独存储。 - 显式创建专属的
RequestQueue,避免不同用户的爬取请求队列混合。 - 保留局部的
crawled_data列表,确保每个用户的爬取结果单独归集。
修改后的完整代码
import asyncio from crawlee.playwright_crawler import PlaywrightCrawler, PlaywrightCrawlingContext from crawlee import EnqueueStrategy, Dataset, RequestQueue async def run_crawler(username, urls) -> list: # 为每个用户创建独立的请求队列和数据集 request_queue = await RequestQueue.open(f'queue-{username}') dataset = await Dataset.open(f'dataset-{username}') crawler = PlaywrightCrawler( max_requests_per_crawl=10, retry_on_blocked=True, max_request_retries=5, request_queue=request_queue, dataset=dataset, ) crawled_data = [] @crawler.router.default_handler async def request_handler(context: PlaywrightCrawlingContext) -> None: context.log.info(f'Processing {context.request.url} ...') # 提取页面文本内容 try: text_content = await context.page.evaluate("document.querySelector('main').innerText") except: text_content = await context.page.evaluate("document.body.innerText") data = { 'username': username, 'url': context.request.url, 'title': await context.page.title(), "context": text_content, } # 将数据推送到当前用户的专属数据集 await context.push_data(data) crawled_data.append(data) # 仅将当前域名的内部链接加入当前用户的请求队列 await context.enqueue_links( strategy=EnqueueStrategy.SAME_DOMAIN, ) await crawler.run(urls) # 导出当前用户的专属数据集到本地文件 await dataset.export_to_json(f'data/{username}_results.json') return crawled_data async def run_multiple_crawlers(user_requests): tasks = [run_crawler(username, urls) for username, urls in user_requests] # 并发执行所有用户的爬取任务 results = await asyncio.gather(*tasks) return results # 示例:多用户爬取请求列表 user_requests = [ ('user1', ['https://github.com']), # 修正原代码中URL的标点错误 ('user2', ['https://stackoverflow.com']), ] asyncio.run(run_multiple_crawlers(user_requests))
关键修改点说明
- 独立请求队列:通过
RequestQueue.open(f'queue-{username}')为每个用户创建唯一标识的请求队列,彻底隔离不同用户的爬取链路。 - 专属数据集:用
Dataset.open(f'dataset-{username}')创建用户专属数据集,确保push_data()操作仅写入当前用户的爬取数据,避免全局共享。 - URL错误修正:修正原示例中
https://github,com的标点错误,改为正确的https://github.com。
内容的提问来源于stack exchange,提问作者Yusif Abasov
相关产品推荐
相关产品推荐

