常见做法
本节介绍了使用 Scrapy 时的一些常见做法。这些内容涵盖了许多主题,并且通常不属于其他特定部分。
从脚本运行 Scrapy
您可以使用 API 从脚本运行 Scrapy,而不是通常通过 scrapy crawl
运行 Scrapy。
请记住,Scrapy 构建在 Twisted 异步网络库之上,因此您需要在 Twisted reactor 中运行它。
您可以用作运行 spider 的第一个实用工具是 scrapy.crawler.CrawlerProcess
。这个类会为您启动一个 Twisted reactor,配置日志记录和设置关闭处理程序。所有 Scrapy 命令都使用这个类。
下面是一个使用它运行单个 spider 的示例。
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 项目中,还有一些额外的辅助工具可用于在项目内导入这些组件。您可以将 spider 的名称传递给 CrawlerProcess
来自动导入您的 spider,并使用 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。
使用这个类时,需要在调度您的 spider 后显式运行 reactor。如果您的应用程序已经在使用 Twisted 并且希望在同一个 reactor 中运行 Scrapy,建议您使用 CrawlerRunner
而不是 CrawlerProcess
。
请注意,在 spider 完成运行后,您还需要自行关闭 Twisted reactor。这可以通过向 CrawlerRunner.crawl
方法返回的 deferred 添加回调来实现。
下面是一个使用示例,以及一个在 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
另请参阅
在同一进程中运行多个 spider
默认情况下,当您运行 scrapy crawl
时,Scrapy 每个进程运行一个 spider。然而,Scrapy 支持使用内部 API 在同一进程中运行多个 spider。
下面是一个同时运行多个 spider 的示例
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
同样的示例,但通过链式连接 deferred 顺序运行 spider
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 时,reactor 设置 每个 spider 不应具有不同的值。此外,不能为每个 spider 定义 爬虫前设置。
另请参阅
分布式爬取
Scrapy 不提供任何内置工具来以分布式(多服务器)方式运行爬取。但是,有一些方法可以分布式爬取,这些方法取决于您计划如何分布它们。
如果您有许多 spider,分发负载的显而易见的方法是设置许多 Scrapyd 实例并在这些实例之间分发 spider 运行。
如果您想通过多台机器运行单个(大型)spider,通常的做法是分割要爬取的 URL,并将它们发送到每个单独的 spider。下面是一个具体的示例
首先,您准备要爬取的 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 服务器上启动 spider 运行。spider 将接收一个 (spider) 参数 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
避免被封禁
一些网站会采取某些措施阻止机器人爬取,其复杂程度各不相同。绕过这些措施可能很困难和棘手,有时可能需要特殊的基础设施。如有疑问,请考虑联系商业支持。
处理这类网站时,请记住以下一些技巧
从一个包含浏览器常用 user agent 的池中轮换使用(可以谷歌搜索获取列表)
禁用 cookie(参见
COOKIES_ENABLED
),因为有些网站可能会使用 cookie 来识别机器人行为使用下载延迟(2 秒或更高)。参见
DOWNLOAD_DELAY
设置。如果可能,使用 Common Crawl 来获取页面,而不是直接访问网站
使用一个包含轮换 IP 的池。例如,免费的 Tor project 或像 ProxyMesh 这样的付费服务。一个开源的替代方案是 scrapoxy,这是一个超级代理,您可以将自己的代理附加到其上。
使用封禁规避服务,例如 Zyte API,它提供了一个 Scrapy 插件和额外功能,如AI web scraping
如果您仍然无法阻止您的机器人被封禁,请考虑联系商业支持。