#这是我的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>
还需要提供什么我一并发给你,帮我检查下到底是哪里出问题了。