常见实践¶
本节介绍使用 Scrapy 时的常见实践。这些实践涵盖许多主题,通常不属于任何其他特定章节。
从脚本运行 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):
# 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
。此类是一个薄包装器,封装了一些简单的辅助程序来运行多个爬虫,但它不会以任何方式启动或干扰现有的 reactor。
使用此类时,应在计划爬虫后显式运行 reactor。建议您使用 CrawlerRunner
而不是 CrawlerProcess
,如果您的应用程序已使用 Twisted 并且您希望在同一 reactor 中运行 Scrapy。
请注意,您还必须在爬虫完成后自行关闭 Twisted reactor。这可以通过将回调添加到 CrawlerRunner.crawl
方法返回的延迟中来实现。
以下是如何使用它的示例,以及在 MySpider
运行完成后手动停止 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)
from twisted.internet import reactor
d.addBoth(lambda _: reactor.stop())
reactor.run() # the script will block here until the crawling is finished
相同的示例,但使用非默认 reactor,只有在使用 CrawlerRunner
时才需要调用 install_reactor
,因为 CrawlerProcess
已经自动执行了此操作。
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"})
from scrapy.utils.reactor import install_reactor
install_reactor("twisted.internet.asyncioreactor.AsyncioSelectorReactor")
runner = CrawlerRunner()
d = runner.crawl(MySpider)
from twisted.internet import reactor
d.addBoth(lambda _: reactor.stop())
reactor.run() # the script will block here until the crawling is finished
另请参阅
在同一进程中运行多个爬虫¶
默认情况下,当您运行 scrapy crawl
时,Scrapy 会为每个进程运行一个爬虫。但是,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 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()
from twisted.internet import reactor
d.addBoth(lambda _: reactor.stop())
reactor.run() # the script will block here until all crawling jobs are finished
相同的示例,但通过链接延迟依次运行爬虫
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
class MySpider1(scrapy.Spider):
# Your first spider definition
...
class MySpider2(scrapy.Spider):
# Your second spider definition
...
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() # the script will block here until the last crawl call is finished
不同的爬虫可以为相同的设置设置不同的值,但是当它们在同一进程中运行时,由于设计或某些限制,可能无法使用这些不同的值。在实践中,不同设置的结果是不同的
SPIDER_LOADER_CLASS
及其值使用的设置 (SPIDER_MODULES
、SPIDER_LOADER_WARN_ONLY
(对于默认设置))无法从每个爬虫的设置中读取。这些设置在创建CrawlerRunner
或CrawlerProcess
对象时应用。对于
TWISTED_REACTOR
和ASYNCIO_EVENT_LOOP
,使用第一个可用的值,如果爬虫请求不同的 reactor,则会引发异常。这些设置在安装 reactor 时应用。对于
REACTOR_THREADPOOL_MAXSIZE
、DNS_RESOLVER
及解析器使用的设置 (DNSCACHE_ENABLED
、DNSCACHE_SIZE
、DNS_TIMEOUT
(对于 Scrapy 中包含的设置)使用第一个可用的值。这些设置在启动 reactor 时应用。
另请参阅
分布式爬取¶
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
避免被封禁¶
某些网站实施了某些措施来阻止机器人爬取它们,这些措施的复杂程度各不相同。绕过这些措施可能很困难且很棘手,有时可能需要特殊的基础设施。如有疑问,请考虑联系 商业支持。
在处理此类网站时,请记住以下一些提示
从浏览器中众所周知的用户代理池中轮换您的用户代理(在 Google 上搜索以获取列表)
禁用 Cookie(请参阅
COOKIES_ENABLED
),因为某些网站可能会使用 Cookie 来发现机器人行为使用下载延迟(2 或更高)。请参阅
DOWNLOAD_DELAY
设置。如果可能,使用 Common Crawl 获取页面,而不是直接访问网站
使用轮换 IP 池。例如,免费的 Tor 项目 或付费服务,如 ProxyMesh。一个开源替代方案是 scrapoxy,一个超级代理,您可以将自己的代理附加到它上面。
如果您仍然无法阻止您的机器人被封禁,请考虑联系 商业支持。