Common Practices (常见实践)
Run Scrapy from a script (从脚本运行 Scrapy)
您可以使用 API 从脚本运行 Scrapy,而不是通常通过 scrapy crawl 运行 Scrapy 的方式。
请记住,Scrapy 是基于 Twisted 异步网络库构建的,因此您需要它在 Twisted reactor 内部运行。
您可以用来运行爬虫的第一个工具是 scrapy.crawler.CrawlerProcess。这个类将为您启动一个 Twisted reactor,配置日志记录并设置关闭处理程序。这个类是所有 Scrapy 命令使用的类。
以下是如何使用它运行单个爬虫的示例。
import scrapy
from scrapy.crawler import CrawlerProcess
class MySpider(scrapy.Spider):
# 你的爬虫定义
...
process = CrawlerProcess(
settings={
"FEEDS": {
"items.json": {"format": "json"},
},
}
)
process.crawl(MySpider)
process.start() # 脚本将在此处阻塞,直到抓取完成
在 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' 是项目中的一个爬虫的名称。
process.crawl("followall", domain="scrapy.org")
process.start() # 脚本将在此处阻塞,直到抓取完成
还有另一个 Scrapy 工具可以对抓取过程提供更多控制:scrapy.crawler.CrawlerRunner。这个类是一个薄封装器,封装了一些简单的辅助函数来运行多个爬虫,但它不会以任何方式启动或干扰现有的 reactor。
使用这个类,在调度完爬虫后应该显式地运行 reactor。如果您的应用程序已经在使用 Twisted 并且您想在同一个 reactor 中运行 Scrapy,建议您使用 CrawlerRunner 而不是 CrawlerProcess。
请注意,您还需要在爬虫完成后自行关闭 Twisted reactor。这可以通过向 CrawlerRunner.crawl 方法返回的 deferred 添加回调来实现。
以下是其用法示例,以及一个在 MySpider 完成运行后手动停止 reactor 的回调。
import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging
from scrapy.utils.reactor import install_reactor
class MySpider(scrapy.Spider):
# 你的爬虫定义
...
install_reactor("twisted.internet.asyncioreactor.AsyncioSelectorReactor")
configure_logging({"LOG_FORMAT": "%(levelname)s: %(message)s"})
runner = CrawlerRunner()
d = runner.crawl(MySpider)
from twisted.internet import reactor
d.addBoth(lambda _: reactor.stop())
reactor.run() # 脚本将在此处阻塞,直到抓取完成
相同的示例,但使用不同的 reactor。
import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging
from scrapy.utils.reactor import install_reactor
class MySpider(scrapy.Spider):
custom_settings = {
"TWISTED_REACTOR": "twisted.internet.epollreactor.EPollReactor",
}
# 你的爬虫定义
...
install_reactor("twisted.internet.epollreactor.EPollReactor")
configure_logging({"LOG_FORMAT": "%(levelname)s: %(message)s"})
runner = CrawlerRunner()
d = runner.crawl(MySpider)
from twisted.internet import reactor
d.addBoth(lambda _: reactor.stop())
reactor.run() # 脚本将在此处阻塞,直到抓取完成
另请参阅
Running multiple spiders in the same process (在同一进程中运行多个爬虫)
默认情况下,当您运行 scrapy crawl 时,Scrapy 每个进程运行一个爬虫。但是,Scrapy 支持使用内部 API 在每个进程中运行多个爬虫。
以下是同时运行多个爬虫的示例:
import scrapy
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
class MySpider1(scrapy.Spider):
# 你的第一个爬虫定义
...
class MySpider2(scrapy.Spider):
# 你的第二个爬虫定义
...
settings = get_project_settings()
process = CrawlerProcess(settings)
process.crawl(MySpider1)
process.crawl(MySpider2)
process.start() # 脚本将在此处阻塞,直到所有抓取任务完成
使用 CrawlerRunner 的相同示例:
import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging
from scrapy.utils.project import get_project_settings
from scrapy.utils.reactor import install_reactor
class MySpider1(scrapy.Spider):
# 你的第一个爬虫定义
...
class MySpider2(scrapy.Spider):
# 你的第二个爬虫定义
...
install_reactor("twisted.internet.asyncioreactor.AsyncioSelectorReactor")
configure_logging()
settings = get_project_settings()
runner = CrawlerRunner(settings)
runner.crawl(MySpider1)
runner.crawl(MySpider2)
d = runner.join()
from twisted.internet import reactor
d.addBoth(lambda _: reactor.stop())
reactor.run() # 脚本将在此处阻塞,直到所有抓取任务完成
相同的示例,但通过链式 deferred 顺序运行爬虫:
from twisted.internet import defer
from scrapy.crawler import CrawlerRunner
from scrapy.utils.log import configure_logging
from scrapy.utils.project import get_project_settings
from scrapy.utils.reactor import install_reactor
class MySpider1(scrapy.Spider):
# 你的第一个爬虫定义
...
class MySpider2(scrapy.Spider):
# 你的第二个爬虫定义
...
install_reactor("twisted.internet.asyncioreactor.AsyncioSelectorReactor")
settings = get_project_settings()
configure_logging(settings)
runner = CrawlerRunner(settings)
@defer.inlineCallbacks
def crawl():
yield runner.crawl(MySpider1)
yield runner.crawl(MySpider2)
reactor.stop()
from twisted.internet import reactor
crawl()
reactor.run() # 脚本将在此处阻塞,直到最后一个 crawl 调用完成
注意
在同一进程中运行多个爬虫时,reactor 设置不应该每个爬虫有不同的值。此外,预爬虫设置不能按爬虫定义。
另请参阅
Distributed crawls (分布式抓取)
Scrapy 不提供任何内置的设施来以分布式(多服务器)方式运行抓取。但是,有几种方法可以分布式抓取,具体取决于您计划如何分布式它们。
如果您有许多爬虫,最明显的负载分配方式是设置许多 Scrapyd 实例,并在这些实例之间分配爬虫运行。
如果您想在许多机器上运行一个(大型)爬虫,通常的做法是划分要抓取的 URL 并将其发送到每个独立的爬虫。这是一个具体的示例:
首先,您准备要抓取的 URL 列表,并将它们放入单独的文件/URL 中:
http://somedomain.com/urls-to-crawl/spider1/part1.listhttp://somedomain.com/urls-to-crawl/spider1/part2.listhttp://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
Avoiding getting banned (避免被禁止)
有些网站会采取某些措施来阻止机器人抓取它们,其复杂程度各不相同。绕过这些措施可能很困难且棘手,有时可能需要特殊的 инфраструктура。如有疑问,请考虑联系商业支持。
处理这类网站时请记住以下几点:
- 从知名浏览器用户代理池中轮换您的用户代理(谷歌搜索以获取列表)。
- 禁用 cookie (参见
COOKIES_ENABLED),因为某些网站可能会使用 cookie 来识别机器人行为。 - 使用下载延迟(2 或更高)。参见
DOWNLOAD_DELAY设置。 - 如果可能,使用 Common Crawl 获取页面,而不是直接访问网站。
- 使用轮换 IP 池。例如,免费的 Tor 项目或 ProxyMesh 等付费服务。一个开源替代品是 scrapoxy,一个可以附加您自己代理的超级代理。
- 使用反禁服务,例如 Zyte API,它提供了一个 Scrapy 插件和附加功能,如 AI 网络抓取。
如果您仍然无法防止您的机器人被禁止,请考虑联系商业支持。