Общие практики

В этом разделе описаны распространенные практики использования Scrapy.Они охватывают множество тем и не часто попадают в какой-либо другой конкретный раздел.

Запуск Scrapy из скрипта

Вы можете использовать API для запуска Scrapy из сценария вместо обычного способа запуска Scrapy через scrapy crawl.

Помните, что Scrapy построен на основе асинхронной сетевой библиотеки Twisted, поэтому вам нужно запустить его внутри реактора Twisted.

Первая утилита, которую вы можете использовать для запуска своих пауков, — это scrapy.crawler.CrawlerProcess. Данный класс запустит для вас реактор Twisted, настроив ведение журнала и установив обработчики завершения работы. Данный класс используется всеми командами Scrapy.

Вот пример, показывающий, как запустить с ним одного паука.

import scrapy
from scrapy.crawler import CrawlerProcess

class MySpider(scrapy.Spider):
    # Your spider definition
    ...

process = CrawlerProcess(settings={
    "FEEDS": {
        "items.json": {"format": "json"},
    },
})

process.crawl(MySpider)
process.start() # the script will block here until the crawling is finished

Определяет настройки в словаре в CrawlerProcess. Обязательно ознакомьтесь с документацией на CrawlerProcess, чтобы ознакомиться с подробностями его использования.

Если вы находитесь внутри проекта Scrapy, есть несколько дополнительных помощников, которые вы можете использовать для импорта данных компонентов в проект. Вы можете автоматически импортировать своих пауков, передавая своё имя в CrawlerProcess, и использовать get_project_settings для получения экземпляра Settings с настройками вашего проекта.

Ниже приводится рабочий пример того, как это сделать, на примере проекта testspiders.

from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

process = CrawlerProcess(get_project_settings())

# 'followall' is the name of one of the spiders of the project.
process.crawl('followall', domain='scrapy.org')
process.start() # the script will block here until the crawling is finished

Есть ещё одна утилита Scrapy, которая обеспечивает больший контроль над процессом сканирования: scrapy.crawler.CrawlerRunner. Данный класс представляет собой тонкую оболочку, которая инкапсулирует некоторые простые помощники для запуска нескольких поисковых роботов, но он никоим образом не запускает существующие реакторы и не мешает им.

Используя данный класс, реактор должен запускаться явно после планирования ваших пауков. Рекомендуется использовать CrawlerRunner вместо CrawlerProcess, если ваше приложение уже использует Twisted и вы хотите запустить Scrapy в том же реакторе.

Обратите внимание, что после завершения работы паука вам также придется самостоятельно выключить реактор Twisted. Этого можно добиться, добавив обратные вызовы к deferred, возвращаемым методом CrawlerRunner.crawl.

Вот пример его использования вместе с обратным вызовом для ручной остановки реактора после завершения работы MySpider.

from twisted.internet import reactor
import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging

class MySpider(scrapy.Spider):
    # Your spider definition
    ...

configure_logging({'LOG_FORMAT': '%(levelname)s: %(message)s'})
runner = CrawlerRunner()

d = runner.crawl(MySpider)
d.addBoth(lambda _: reactor.stop())
reactor.run() # the script will block here until the crawling is finished

См.также

twisted:core/howto/reactor-basics

Запуск нескольких пауков в одном процессе

По умолчанию Scrapy запускает одного паука для каждого процесса при запуске scrapy crawl. Однако Scrapy поддерживает запуск нескольких пауков на процесс с помощью внутреннего API.

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

import scrapy
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings

class MySpider1(scrapy.Spider):
    # Your first spider definition
    ...

class MySpider2(scrapy.Spider):
    # Your second spider definition
    ...

settings = get_project_settings()
process = CrawlerProcess(settings)
process.crawl(MySpider1)
process.crawl(MySpider2)
process.start() # the script will block here until all crawling jobs are finished

Тот же пример с использованием CrawlerRunner:

import scrapy
from twisted.internet import reactor
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging
from scrapy.utils.project import get_project_settings

class MySpider1(scrapy.Spider):
    # Your first spider definition
    ...

class MySpider2(scrapy.Spider):
    # Your second spider definition
    ...

configure_logging()
settings = get_project_settings()
runner = CrawlerRunner(settings)
runner.crawl(MySpider1)
runner.crawl(MySpider2)
d = runner.join()
d.addBoth(lambda _: reactor.stop())

reactor.run() # the script will block here until all crawling jobs are finished

Тот же пример, но запуск пауков последовательно путём связывания отложенных:

from twisted.internet import reactor, defer
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging
from scrapy.utils.project import get_project_settings

class MySpider1(scrapy.Spider):
    # Your first spider definition
    ...

class MySpider2(scrapy.Spider):
    # Your second spider definition
    ...

configure_logging()
settings = get_project_settings()
runner = CrawlerRunner(settings)

@defer.inlineCallbacks
def crawl():
    yield runner.crawl(MySpider1)
    yield runner.crawl(MySpider2)
    reactor.stop()

crawl()
reactor.run() # the script will block here until the last crawl call is finished

Распределённые обходы

Scrapy не предоставляет встроенных средств для запуска обхода контента в распределенной (многосерверной) манере. Однако есть несколько способов распространения обходов, которые зависят от того, как вы планируете их распространять.

Если у вас много пауков, очевидный способ распределить нагрузку — установить множество экземпляров Scrapyd и распределить между ними запуски пауков.

Если вместо этого вы хотите запустить одного (большого) паука на многих машинах, то обычно вы разделяете URL-адреса для сканирования и отправляете их каждому отдельному пауку. Вот конкретный пример:

Сначала вы готовите список URL-адресов для сканирования и помещаете их в отдельные файлы/URL-адреса:

http://somedomain.com/urls-to-crawl/spider1/part1.list
http://somedomain.com/urls-to-crawl/spider1/part2.list
http://somedomain.com/urls-to-crawl/spider1/part3.list

Затем вы запускаете паука на 3 разных серверах Scrapyd. Паук получит аргумент (паук) part с номером раздела для сканирования:

curl http://scrapy1.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=1
curl http://scrapy2.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=2
curl http://scrapy3.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=3

Как избежать бана

Некоторые веб-сайты с разной степенью сложности принимают определённые меры для предотвращения их сканирования ботами. Обойти данные меры может быть сложно и сложно, и иногда может потребоваться специальная инфраструктура. Если у вас есть сомнения, обратитесь к коммерческой поддержке.

Вот несколько советов, которые следует помнить при работе с такими сайтами:

  • поверните свой пользовательский агент из пула хорошо известных из браузеров (погуглите, чтобы получить их список)

  • отключите cookie (см. COOKIES_ENABLED), поскольку некоторые сайты могут использовать cookie для отслеживания поведения ботов

  • использовать задержки загрузки (2 или больше). См. настройку DOWNLOAD_DELAY.

  • если возможно, используйте Google кэш для загрузки страниц вместо прямого обращения к сайтам

  • используйте пул чередующихся IP-адресов. Например, бесплатный Tor project или платные сервисы типа ProxyMesh. Альтернативой с открытым исходным кодом является scrapoxy, супер-прокси, к которому вы можете подключить свои собственные прокси.

  • используйте высокораспределенный загрузчик, который обходит запреты внутри, поэтому вы можете просто сосредоточиться на парсинге чистых страниц. Одним из примеров таких загрузчиков является Zyte Smart Proxy Manager.

Если вы по-прежнему не можете предотвратить бан своего бота, подумайте о том, чтобы связаться с коммерческой поддержкой.