scrapy-playwright не работает через celery в проекте django
Такая проблема - мне необходимо приложение на Scrapy-playwright для парсинга некоторых сайтов. В данном случае - ББР банка (Задание учебное, поэтому валюты подсасываются парсингом - задание препода)
Написал сначала отдельно и проверил - работает ли парсер. Работал и работает дальше. Правда пока пытался найти ошибку поломал и тот код, но там и другие пауки присутствуют, так что не проблема
Добавил задачу в celery (Весь код приложу ниже), все настроил. Мне нужно хранить валюты в виде json внутри redis для дальнейшего пересчёта цен. И вот проблема в том, что паук то запускается, но вот когда доходит до начала парсинга - он не находит страницы. Playwright её просто не подгружает. Перерыл кучу источников, но так и не нашел ответа. Подскажите пж, в чем ошибка?
Ошибка
2025-01-18 06:09:48 [scrapy.utils.log] INFO: Scrapy 2.12.0 started (bot: scrapybot)
2025-01-18 06:09:48 [scrapy.utils.log] INFO: Versions: lxml 5.3.0.0, libxml2 2.11.7, cssselect 1.2.0, parsel 1.9.1, w3lib 2.2.1, Twisted 24.11.0, Python 3.11.5 (tags/v3.11.5:cce6ba9, Aug 24 2023, 14:38:34) [MSC v.1936 64 bit (AMD64)], pyOpenSSL 24.3.0 (OpenSSL 3.4.0 22 Oct 2024), cryptography 44.0.0, Platform Windows-10-10.0.22621-SP0
Соединение с Redis установлено успешно.
2025-01-18 06:09:48 [scrapy.addons] INFO: Enabled addons:
[]
2025-01-18 06:09:48 [scrapy.utils.log] DEBUG: Using reactor: twisted.internet.selectreactor.SelectReactor
2025-01-18 06:09:48 [scrapy.extensions.telnet] INFO: Telnet Password: ecbd6048a80cea89
2025-01-18 06:09:48 [scrapy.middleware] INFO: Enabled extensions:
['scrapy.extensions.corestats.CoreStats',
'scrapy.extensions.telnet.TelnetConsole',
'scrapy.extensions.logstats.LogStats']
2025-01-18 06:09:48 [scrapy.crawler] INFO: Overridden settings:
{}
2025-01-18 06:09:49 [scrapy.middleware] INFO: Enabled downloader middlewares:
['scrapy.downloadermiddlewares.offsite.OffsiteMiddleware',
'scrapy.downloadermiddlewares.httpauth.HttpAuthMiddleware',
'scrapy.downloadermiddlewares.downloadtimeout.DownloadTimeoutMiddleware',
'scrapy.downloadermiddlewares.defaultheaders.DefaultHeadersMiddleware',
'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware',
'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-01-18 06:09:49 [scrapy.middleware] INFO: Enabled spider middlewares:
['scrapy.spidermiddlewares.httperror.HttpErrorMiddleware',
'scrapy.spidermiddlewares.referer.RefererMiddleware',
'scrapy.spidermiddlewares.urllength.UrlLengthMiddleware',
'scrapy.spidermiddlewares.depth.DepthMiddleware']
2025-01-18 06:09:49 [scrapy.middleware] INFO: Enabled item pipelines:
[]
2025-01-18 06:09:49 [scrapy.core.engine] INFO: Spider opened
2025-01-18 06:09:49 [scrapy.extensions.logstats] INFO: Crawled 0 pages (at 0 pages/min), scraped 0 items (at 0 items/min)
2025-01-18 06:09:49 [scrapy.extensions.telnet] INFO: Telnet console listening on 127.0.0.1:6023
2025-01-18 06:09:49 [scrapy.core.engine] DEBUG: Crawled (200) <GET https://bbr.ru/> (referer: None)
2025-01-18 06:09:50 [scrapy.core.scraper] ERROR: Spider error processing <GET https://bbr.ru/> (referer: None)
Traceback (most recent call last):
File "C:\Users\malay\Documents\GitHub\Web\venv\Lib\site-packages\twisted\internet\defer.py", line 2017, in _inlineCallbacks
result = context.run(gen.send, result)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\malay\Documents\GitHub\Web\ScrapyParsers\ScrapyParsers\spiders\BBR_Currensy.py", line 36, in parse
page = response.meta["ScrapyParsers"]
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
KeyError: 'ScrapyParsers'
2025-01-18 06:09:50 [scrapy.core.engine] INFO: Closing spider (finished)
2025-01-18 06:09:50 [scrapy.statscollectors] INFO: Dumping Scrapy stats:
{'downloader/request_bytes': 207,
'downloader/request_count': 1,
'downloader/request_method_count/GET': 1,
'downloader/response_bytes': 31771,
'downloader/response_count': 1,
'downloader/response_status_count/200': 1,
'elapsed_time_seconds': 1.090688,
'finish_reason': 'finished',
'finish_time': datetime.datetime(2025, 1, 17, 20, 9, 50, 218415, tzinfo=datetime.timezone.utc),
'httpcompression/response_bytes': 201174,
'httpcompression/response_count': 1,
'items_per_minute': None,
'log_count/DEBUG': 2,
'log_count/ERROR': 1,
'log_count/INFO': 10,
'response_received_count': 1,
'responses_per_minute': None,
'scheduler/dequeued': 1,
'scheduler/dequeued/memory': 1,
'scheduler/enqueued': 1,
'scheduler/enqueued/memory': 1,
'spider_exceptions/KeyError': 1,
'start_time': datetime.datetime(2025, 1, 17, 20, 9, 49, 127727, tzinfo=datetime.timezone.utc)}
2025-01-18 06:09:50 [scrapy.core.engine] INFO: Spider closed (finished)
Паук
import scrapy
import redis
import json
class BbrCurrensySpider(scrapy.Spider):
name = "BBR_Currensy"
allowed_domains = ["bbr.ru"]
start_urls = ["https://bbr.ru/"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Настроим соединение с Redis
self.redis_client = redis.StrictRedis(
host='localhost',
port=6379,
db=0,
decode_responses=True
)
# Проверим подключение
try:
self.redis_client.ping() # Проверяет соединение
print("Соединение с Redis установлено успешно.")
except redis.ConnectionError:
print("Ошибка подключения к Redis.")
def start_requests(self):
yield scrapy.Request(
self.start_urls[0],
meta={"playwright": True, "playwright_include_page": True},
callback=self.parse
)
async def parse(self, response):
page = response.meta["ScrapyParsers"]
if not page:
self.logger.error("Page not found in meta!")
return
self.logger.info(f"Page found: {page.url}")
# Закрываем страницу после выполнения
html_content = await page.content()
await page.close()
# Передаём контент в Scrapy для дальнейшего парсинга
response = scrapy.http.HtmlResponse(
url=page.url, body=html_content, encoding='utf-8'
)
new_rates = {}
for review in response.css(".css-13tn1x7.e314cwc4"):
exchange_rate = review.css('span.css-11ctayd.exmh6wy0::text').get().replace(",", ".")
currency_name = review.css('span.css-90qv05.e314cwc2::text').get()
new_rates[currency_name] = float(exchange_rate)
try:
self.redis_client.set("exchange_rates", json.dumps(new_rates))
print("Данные успешно сохранены в Redis.")
except redis.RedisError as e:
print(f"Ошибка при сохранении данных в Redis: {e}")
celery.py
import os
from celery import Celery
from ScrapyParsers.ScrapyParsers.spiders.BBR_Currensy import BbrCurrensySpider
# Установка переменной окружения для Django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TomikoTradeProject.settings')
app = Celery('TomikoApp')
# Загрузка настроек из Django
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks()
@app.task
def crawl_spider():
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
settings = get_project_settings()
process = CrawlerProcess(settings)
process.crawl(BbrCurrensySpider)
process.start()
tasks.py
from celery import shared_task
from celery import Celery
from TomikoTradeProject.celery import crawl_spider
from cars.models import Cars
from TomikoTradeProject.celery import app
import redis
import json
import scrapy
from scrapy.crawler import CrawlerProcess
from ScrapyParsers.ScrapyParsers.spiders.BBR_Currensy import BbrCurrensySpider
redis_client = redis.StrictRedis(host="localhost", port=6379, db=0)
app = Celery('tasks')
@app.task
def run_scrapy_spider():
crawl_spider()
Пример рабочего паука
import scrapy
class VkClipsSpider(scrapy.Spider):
name = "VK_clips"
allowed_domains = ["vk.com"]
start_urls = ["https://vk.com/clips/tomiko_trade"]
def start_requests(self):
yield scrapy.Request(
self.start_urls[0],
meta={"playwright": True, "playwright_include_page": True},
callback=self.parse_with_playwright
)
async def parse_with_playwright(self, response):
page = response.meta["parsers"]
# Закрываем страницу после выполнения
html_content = await page.content()
await page.close()
# Передаём контент в Scrapy для дальнейшего парсинга
response = scrapy.http.HtmlResponse(
url=page.url, body=html_content, encoding='utf-8'
)
for review in response.css(".vkitGridItem__root--6OepO"):
yield {
"Link" : self.start_urls[0] + review.css('a::attr(href)').get(),
"Prewiew" : review.css('img.vkuiImageBase__img.vkuiImageBase__img--objectFit-cover::attr(src)').get())
}
Решение было найдено. По какой-то причине паук "не подсасывал" настройки. По итогу их всего-то и надо было прописать внутри паука.
А вот и рабочий и допиленный паучок:
import scrapy
import redis
import json
class BbrCurrensySpider(scrapy.Spider):
name = "BBR_Currensy"
allowed_domains = ["bbr.ru"]
start_urls = ["https://bbr.ru/"]
custom_settings = {
"TWISTED_REACTOR": "twisted.internet.asyncioreactor.AsyncioSelectorReactor",
"DOWNLOAD_HANDLERS": {
"https": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
"http": "scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler",
}
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Настроим соединение с Redis
self.redis_client = redis.StrictRedis(
host='localhost',
port=6379,
db=0,
decode_responses=True
)
# Проверим подключение
try:
self.redis_client.ping() # Проверяет соединение
print("Соединение с Redis установлено успешно.")
except redis.ConnectionError:
print("Ошибка подключения к Redis.")
def start_requests(self):
yield scrapy.Request(
self.start_urls[0],
meta=dict(
playwright=True,
playwright_include_page=True
)
)
async def parse(self, response):
page = response.meta["playwright_page"]
if not page:
self.logger.error("Page not found in meta!")
return
self.logger.info(f"Page found: {page.url}")
# Закрываем страницу после выполнения
html_content = await page.content()
await page.close()
# Передаём контент в Scrapy для дальнейшего парсинга
response = scrapy.http.HtmlResponse(
url=response.url, body=html_content, encoding='utf-8'
)
new_rates = {}
for review in response.css(".css-13tn1x7.e314cwc4"):
exchange_rate = review.css('span.css-11ctayd.exmh6wy0::text').get().replace(",", ".")
currency_name = review.css('span.css-90qv05.e314cwc2::text').get()
new_rates[currency_name] = float(exchange_rate)
try:
self.redis_client.set("exchange_rates", json.dumps(new_rates))
print("Данные успешно сохранены в Redis.")
except redis.RedisError as e:
print(f"Ошибка при сохранении данных в Redis: {e}")