scrapy报错twisted.internet.error.ReactorAlreadyInstalledError: reactor already installed

博客讲述了在默认安装Scrapy最新版本后遇到与Gerapy_pyppeteer不兼容的问题,通过回退Scrapy版本到2.5.1解决了这一问题,确保了代码的正常运行。

默认安装scrapy最新版本,不能正常使用Gerapy_pyppeteer,

回退版本到scrapy==2.5.1之后代码可以正常运行

pip install scrapy==2.5.1

#这是我的spider from scrapy import Spider, Request, signals from Myproject.handlers import PlaywrightPageHandler class DemoSpider(Spider): name = "demo" allowed_domains = ["quotes.toscrape.com"] start_urls = ["http://quotes.toscrape.com"] # 添加起始URL async def start(self): for url in self.start_urls: yield Request( url, meta={ "playwright": True, "playwright_include_page": True, "playwright_page_goto_kwargs": {"wait_until": "domcontentloaded"} } ) page_handler = None @classmethod def from_crawler(cls, crawler, *args, **kwargs): spider = super().from_crawler(crawler, *args, **kwargs) return spider async def spider_opened(self): # 初始化页面处理器 self.page_handler = PlaywrightPageHandler(self.crawler) # 调用页面处理器的spider_opened方法 await self.page_handler.spider_opened(self) async def parse(self, response): self.logger.info(f'Parsing page: {response.url}') if self.crawler.engine.close_spider: return quotes = response.css('div.quote') for quote in quotes: yield { 'text': quote.css('span.text::text').get(), 'author': quote.css('small.author::text').get(), 'tags': quote.css('div.tags a.tag::text').getall() } if self.crawler.stats.get_value('item_scraped_count', 0) >= 1: self.crawler.engine.close_spider(self, 'closespider_itemcount') #这是我的hendlers from playwright.async_api import Page, Browser, BrowserContext from scrapy import Request, Spider import logging import asyncio class PlaywrightPageHandler: def __init__(self, crawler): self.crawler = crawler self.logger = logging.getLogger(__name__) self.browser: Browser = None self.context: BrowserContext = None self.page: Page = None self.is_closed = False async def spider_opened(self, spider): try: from playwright.async_api import async_playwright self.playwright = await async_playwright().start() self.browser = await self.playwright.chromium.launch( headless=True, timeout=60000 ) self.context = await self.browser.new_context() self.logger.info('✅ Playwright浏览器已启动') except Exception as e: self.logger.error(f'❌ Playwright初始化失败: {e}') raise async def spider_closed(self, spider): await self.close() async def wait_for_element(self, page: Page, selector: str, timeout=30000): try: await page.wait_for_selector(selector, timeout=timeout) return True except Exception as e: self.logger.error(f'等待元素失败: {e}') return False async def extract_data(self, page: Page, selector: str): return await page.eval_on_selector_all(selector, 'elements => elements.map(el => el.textContent)') async def _safe_close(self, close_task, resource_name): try: await close_task self.logger.info(f"✅ {resource_name}已安全关闭") except Exception as e: self.logger.error(f"❌ 关闭{resource_name}时出错: {str(e)}") async def close(self): try: if self.page: await self._safe_close(self.page.close) if self.context: await self._safe_close(self.context.close) if self.browser: await self._safe_close(self.browser.close) if self.playwright: await self._safe_close(self.playwright.stop) finally: self.page = None self.context = None self.browser = None self.playwright = None #z这是我的middlewares # Define here the models for your spider middleware # # See documentation in: # https://docs.scrapy.org/en/latest/topics/spider-middleware.html from scrapy import signals # useful for handling different item types with a single interface from itemadapter import ItemAdapter class MyprojectSpiderMiddleware: # Not all methods need to be defined. If a method is not defined, # scrapy acts as if the spider middleware does not modify the # passed objects. @classmethod def from_crawler(cls, crawler): # This method is used by Scrapy to create your spiders. s = cls() crawler.signals.connect(s.spider_opened, signal=signals.spider_opened) return s def process_spider_input(self, response, spider): # Called for each response that goes through the spider # middleware and into the spider. # Should return None or raise an exception. return None def process_spider_output(self, response, result, spider): # Called with the results returned from the Spider, after # it has processed the response. # Must return an iterable of Request, or item objects. for i in result: yield i def process_spider_exception(self, response, exception, spider): # Called when a spider or process_spider_input() method # (from other spider middleware) raises an exception. # Should return either None or an iterable of Request or item objects. pass async def process_start(self, start): # Called with an async iterator over the spider start() method or the # maching method of an earlier spider middleware. async for item_or_request in start: yield item_or_request def spider_opened(self, spider): spider.logger.info("Spider opened: %s" % spider.name) class MyprojectDownloaderMiddleware: # Not all methods need to be defined. If a method is not defined, # scrapy acts as if the downloader middleware does not modify the # passed objects. @classmethod def from_crawler(cls, crawler): # This method is used by Scrapy to create your spiders. s = cls() crawler.signals.connect(s.spider_opened, signal=signals.spider_opened) return s def process_request(self, request, spider): # Called for each request that goes through the downloader # middleware. # Must either: # - return None: continue processing this request # - or return a Response object # - or return a Request object # - or raise IgnoreRequest: process_exception() methods of # installed downloader middleware will be called return None def process_response(self, request, response, spider): # Called with the response returned from the downloader. # Must either; # - return a Response object # - return a Request object # - or raise IgnoreRequest return response def process_exception(self, request, exception, spider): # Called when a download handler or a process_request() # (from other downloader middleware) raises an exception. # Must either: # - return None: continue processing this exception # - return a Response object: stops process_exception() chain # - return a Request object: stops process_exception() chain pass def spider_opened(self, spider): spider.logger.info("Spider opened: %s" % spider.name) #这是我的终端报错 PS D:\Experience\Myproject> scrapy crawl demo 2025-08-15 17:50:58 [scrapy.utils.log] INFO: Scrapy 2.13.3 started (bot: Myproject) 2025-08-15 17:50:58 [scrapy.utils.log] INFO: Versions: {'lxml': '6.0.0', 'libxml2': '2.11.9', 'cssselect': '1.3.0', 'parsel': '1.10.0', 'w3lib': '2.3.1', 'Twisted': '25.5.0', 'Python': '3.11.7 (tags/v3.11.7:fa7a6f2, Dec 4 2023, 19:24:49) [MSC v.1937 ' '64 bit (AMD64)]', 'pyOpenSSL': '25.1.0 (OpenSSL 3.5.1 1 Jul 2025)', 'cryptography': '45.0.5', 'Platform': 'Windows-10-10.0.26100-SP0'} 2025-08-15 17:50:58 [scrapy.addons] INFO: Enabled addons: [] 2025-08-15 17:50:58 [asyncio] DEBUG: Using selector: SelectSelector 2025-08-15 17:50:58 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.asyncioreactor.AsyncioSelectorReactor 2025-08-15 17:50:58 [scrapy.utils.log] DEBUG: Using asyncio event loop: asyncio.windows_events._WindowsSelectorEventLoop 2025-08-15 17:50:58 [scrapy.extensions.telnet] INFO: Telnet Password: 03f3c389c88275d3 2025-08-15 17:50:58 [scrapy.middleware] INFO: Enabled extensions: ['scrapy.extensions.corestats.CoreStats', 'scrapy.extensions.telnet.TelnetConsole', 'scrapy.extensions.logstats.LogStats'] 2025-08-15 17:50:58 [scrapy.crawler] INFO: Overridden settings: {'BOT_NAME': 'Myproject', 'CONCURRENT_REQUESTS_PER_DOMAIN': 1, 'DOWNLOAD_DELAY': 1, 'FEED_EXPORT_ENCODING': 'utf-8', 'NEWSPIDER_MODULE': 'Myproject.spiders', 'ROBOTSTXT_OBEY': True, 'SPIDER_MODULES': ['Myproject.spiders']} 2025-08-15 17:50:58 [asyncio] DEBUG: Using proactor: IocpProactor 2025-08-15 17:50:58 [scrapy-playwright] INFO: Started loop on separate thread: <ProactorEventLoop running=True closed=False debug=False> 2025-08-15 17:50:59 [scrapy.middleware] INFO: Enabled downloader middlewares: ['scrapy.downloadermiddlewares.offsite.OffsiteMiddleware', 'scrapy.downloadermiddlewares.robotstxt.RobotsTxtMiddleware', 'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware', 'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware', 'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware', 'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware', 'Myproject.middlewares.MyprojectDownloaderMiddleware', 'scrapy.downloadermiddlewares.retry.RetryMiddleware', 'scrapy.downloadermiddlewares.redirect.MetaRefreshMiddleware', 'scrapy.downloadermiddlewares.httpcompression.HttpCompressionMiddleware', 'scrapy.downloadermiddlewares.redirect.RedirectMiddleware', 'scrapy.downloadermiddlewares.cookies.CookiesMiddleware', 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware', 'scrapy.downloadermiddlewares.stats.DownloaderStats'] 2025-08-15 17:50:59 [scrapy.middleware] INFO: Enabled spider middlewares: ['scrapy.spidermiddlewares.start.StartSpiderMiddleware', 'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware', 'scrapy.spidermiddlewares.referer.RefererMiddleware', 'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware', 'scrapy.spidermiddlewares.depth.DepthMiddleware'] 2025-08-15 17:50:59 [scrapy.middleware] INFO: Enabled item pipelines: [] 2025-08-15 17:52:18 [scrapy.core.engine] INFO: Spider opened 2025-08-15 17:52:18 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min) 2025-08-15 17:52:18 [demo] INFO: Spider opened: demo 2025-08-15 17:52:18 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023 2025-08-15 17:52:18 [scrapy-playwright] INFO: Starting download handler 2025-08-15 17:52:18 [scrapy-playwright] INFO: Starting download handler 2025-08-15 17:52:59 [scrapy.crawler] INFO: Received SIGINT, shutting down gracefully. Send again to force 2025-08-15 17:52:59 [scrapy.core.engine] INFO: Closing spider (shutdown) 2025-08-15 17:52:59 [scrapy.core.engine] DEBUG: Crawled (404) <GET http://quotes.toscrape.com/robots.txt> (referer: None) 2025-08-15 17:53:00 [scrapy-playwright] INFO: Launching browser chromium 2025-08-15 17:53:00 [scrapy-playwright] INFO: Browser chromium launched 2025-08-15 17:53:00 [scrapy-playwright] DEBUG: Browser context started: 'default' (persistent=False, remote=False) 2025-08-15 17:53:00 [scrapy-playwright] DEBUG: [Context=default] New page created, page count is 1 (1 for all contexts) 2025-08-15 17:53:00 [scrapy-playwright] DEBUG: [Context=default] Request: <GET http://quotes.toscrape.com/> (resource type: document) 2025-08-15 17:53:03 [scrapy-playwright] DEBUG: [Context=default] Response: <200 http://quotes.toscrape.com/> 2025-08-15 17:53:03 [scrapy-playwright] DEBUG: [Context=default] Request: <GET http://quotes.toscrape.com/static/bootstrap.min.css> (resource type: stylesheet, referrer: http://quotes.toscrape.com/) 2025-08-15 17:53:03 [scrapy-playwright] DEBUG: [Context=default] Request: <GET http://quotes.toscrape.com/static/main.css> (resource type: stylesheet, referrer: http://quotes.toscrape.com/) 2025-08-15 17:53:04 [scrapy-playwright] DEBUG: [Context=default] Response: <200 http://quotes.toscrape.com/static/main.css> 2025-08-15 17:53:04 [scrapy-playwright] DEBUG: [Context=default] Response: <200 http://quotes.toscrape.com/static/bootstrap.min.css> 2025-08-15 17:53:04 [scrapy.core.engine] DEBUG: Crawled (200) <GET http://quotes.toscrape.com> (referer: None) ['playwright'] 2025-08-15 17:53:04 [demo] INFO: Parsing page: http://quotes.toscrape.com/ 2025-08-15 17:53:04 [scrapy.statscollectors] INFO: Dumping Scrapy stats: {'downloader/request_bytes': 450, 'downloader/request_count': 2, 'downloader/request_method_count/GET': 2, 'downloader/response_bytes': 11489, 'downloader/response_count': 2, 'downloader/response_status_count/200': 1, 'downloader/response_status_count/404': 1, 'elapsed_time_seconds': 45.949575, 'finish_reason': 'shutdown', 'finish_time': datetime.datetime(2025, 8, 15, 9, 53, 4, 931829, tzinfo=datetime.timezone.utc), 'items_per_minute': 0.0, 'log_count/DEBUG': 14, 'log_count/INFO': 18, 'playwright/browser_count': 1, 'playwright/context_count': 1, 'playwright/context_count/max_concurrent': 1, 'playwright/context_count/persistent/False': 1, 'playwright/context_count/remote/False': 1, 'playwright/page_count': 1, 'playwright/page_count/max_concurrent': 1, 'playwright/request_count': 3, 'playwright/request_count/method/GET': 3, 'playwright/request_count/navigation': 1, 'playwright/request_count/resource_type/document': 1, 'playwright/request_count/resource_type/stylesheet': 2, 'playwright/response_count': 3, 'playwright/response_count/method/GET': 3, 'playwright/response_count/resource_type/document': 1, 'playwright/response_count/resource_type/stylesheet': 2, 'response_received_count': 2, 'responses_per_minute': 2.6666666666666665, 'robotstxt/request_count': 1, 'robotstxt/response_count': 1, 'robotstxt/response_status_count/404': 1, 'scheduler/dequeued': 1, 'scheduler/dequeued/memory': 1, 'scheduler/enqueued': 1, 'scheduler/enqueued/memory': 1, 2025-08-15 17:53:04 [scrapy-playwright] INFO: Closing browser 2025-08-15 17:53:04 [scrapy-playwright] DEBUG: Browser disconnected 2025-08-15 17:53:05 [scrapy-playwright] INFO: Closing download handler PS D:\Experience\Myproject> 还需要提供什么我一并发给你,帮我检查下到底是哪里出问题了。
08-16
有时候,我们天真无邪的使用urllib库或Scrapy下载HTML网页时会发现,我们要提取的网页元素并不在我们下载到的HTML之中,尽管它们在浏览器里看起来唾手可得。 这说明我们想要的元素是在我们的某些操作下通过js事件动态生成的。举个例子,我们在刷QQ空间或者微博评论的时候,一直往下刷,网页越来越长,内容越来越多,就是这个让人又爱又恨的动态加载。 爬取动态页面目前来说有两种方法 分析页面请求(这篇介绍这个) selenium模拟浏览器行为(霸王硬上弓,以后再说) 言归正传,下面介绍一下通过分析页面请求的方法爬取动态加载页面的思路。中心思想就是找到那个发请求的javascript文件所发的请求。 举两个例子,京东评论和上证股票。 后注:本文的两个例子都是get请求,可以显示的在浏览器中查看效果,如果是post请求,需要我们在程序中构造数据,构造方法可以参考我从前的一篇博文Scrapy定向爬虫教程(六)——分析表单并回帖。 京东评论 这是一个比较简单的例子。 首先我们随便找一个热卖的商品,评论比较多。 就这个吧威刚(ADATA) SU800 256G 3D NAND SATA3固态硬盘。 点进去看看这个页面现在的状况 图一 滚动条给的第一印象感觉这页仿佛没多少内容。 键盘F12打开开发者工具,选择Network选项卡,选择JS(3月12日补:除JS选项卡还有可能在XHR选项卡中,当然也可以通过其它抓包工具),如下图 图二 然后,我们来拖动右侧的滚动条,这时就会发现,开发者工具里出现了新的js请求(还挺多的),不过草草翻译一下,很容易就能看出来哪个是取评论的,如下图 图三 好,复制出js请求的目标ur
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值