scrapy-spash: объект ответа SplashRequest отличается между вызовами сканированием scrapy и CrawlerProcess - PullRequest
1 голос
/ 10 марта 2019

Я хотел бы использовать scrapy-splash, чтобы получить html и скриншот png целевой страницы. Я должен быть в состоянии вызвать это программно. Согласно spashy doc , указав

endpoint='render.json'

и передаваемый аргумент

'png': 1

должен привести к объекту ответа ('scrapy_splash.response.SplashJsonResponse') с атрибутом .data, который содержит декодированные данные JSON, представляющие снимок экрана png целевой страницы.

Когда паук (здесь он называется «поиск») вызывается с помощью

scrapy crawl search

Результат, как и ожидалось, с response.data ['png'], содержащим данные png.

Однако, если он вызывается через CrawlerProcess scrapy, возвращается другой объект ответа: 'scrapy.http.response.html.HtmlResponse'. Этот объект не имеет атрибут .data.

Вот код:

import scrapy
from scrapy.crawler import CrawlerProcess
from scrapy_splash import SplashRequest
import base64

RUN_CRAWLERPROCESS = False

if RUN_CRAWLERPROCESS:
    from crochet import setup
    setup()

class SpiderSearch(scrapy.Spider):
    name = 'search'
    user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'

    def start_requests(self):
        urls = ['https://www.google.com/search?q=test', ]
        splash_args = {
            'html': 1,
            'png': 1,
            'width': 1920,
            'wait': 0.5,
            'render_all': 1,
        }
        for url in urls:
            yield SplashRequest(url=url, callback=self.parse, endpoint='render.json', args=splash_args, )

    def parse(self, response):
        print(type(response))
        for result in response.xpath('//div[@class="r"]'): 
            url = str(result.xpath('./a/@href').extract_first())
            yield {
                'url': url
            }

        png_bytes = base64.b64decode(response.data['png'])
        with open('google_results.png', 'wb') as f:
            f.write(png_bytes)

        splash_args = {
            'html': 1,
            'png': 1,
            'width': 1920,
            'wait': 2,
            'render_all': 1,
            'html5_media': 1,
        }
        # cue the subsequent url to be fetched (self.parse_result omitted here for brevity)
        yield SplashRequest(url=url, callback=self.parse_result, endpoint='render.json', args=splash_args)

if RUN_CRAWLERPROCESS:
    runner = CrawlerProcess({'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36'})
    #d = runner.crawl(SpiderSearch)
    #d.addBoth(lambda _: reactor.stop())
    #reactor.run()
    runner.crawl(SpiderSearch)
    runner.start()

Отдых:

RUN_CRAWLERPROCESS = False 

и вызов по

scrapy crawl search

ответ типа

class 'scrapy_splash.response.SplashJsonResponse'

Но настройка

RUN_CRAWLERPROCESS = True 

и запуск сценария с CrawlerProcess приводит к ответу типа

class 'scrapy.http.response.html.HtmlResponse'

(ps. У меня были некоторые проблемы с ReactorNotRestartable, поэтому я принял вязание крючком, как описано в этом посте , которое, похоже, решило проблему. Признаюсь, я не понимаю, почему, но предполагаю, что это не связано. ..)

Есть мысли о том, как это отладить?

1 Ответ

1 голос
/ 11 марта 2019

Если вы запускаете этот код как отдельный скрипт, модуль настроек никогда не будет загружен, и ваш сканер не будет знать о промежуточном программном обеспечении Splashy (что и добавляет атрибут .data, на который вы ссылаетесь в .parse) .

Вы можете загрузить эти настройки в своем скрипте, вызвав get_project_settings и передав результат своему сканеру:

from scrapy.utils.project import get_project_settings

# ...

project_settings = get_project_settings()


process = CrawlerProcess(project_settings)
...