爬虫工作量由小到大的思维转变---<第七章 Scrapy超越控制台===代码运行scrapy+多线程爬取+数据交互>
前言:
针对留言的问题:
scrapy谁告诉你只能在控制台启动的?你是抖和BILI看多了吧!!
- ---看我的,让你玩出花;
正文:
传统方式 vs 脚本方式
在Scrapy框架中,传统方式一般是指通过终端(或命令行)启动Scrapy项目,而脚本方式是指在Python环境中直接运行一个或多个Scrapy爬虫。
传统方式:
- 命令行启动: 通过运行scrapy crawl spidername在终端或命令提示符中启动爬虫。
- 配置分散: 命令行参数和项目的settings.py文件分散配置。
- 单一进程: 一次通常只能运行一个爬虫,且运行多个爬虫需要多个终端窗口。
- 输出受限: 数据通常输出到预设位置,改变输出需修改设置或使用指令参数。
脚本方式:
- Python脚本启动: 通过创建一个Python脚本,使用Scrapy API来配置和运行爬虫。
- 集中配置: 脚本中统一设置,包括爬虫参数和自定义设置。
- 并发运行: 同一个脚本中可以配置和运行多个爬虫实例,实现并行爬取。
- 灵活输出: 数据可以在脚本中按需处理,并动态指定输出位置。
案例:
# Python脚本内部运行Scrapy
from scrapy.crawler import CrawlerProcess
from myproject.spiders.spider_one import SpiderOne
from myproject.spiders.spider_two import SpiderTwoprocess = CrawlerProcess(get_project_settings())# 定时运行不同的爬虫
process.crawl(SpiderOne)
process.crawl(SpiderTwo)# 开始爬取
process.start() # 脚本会在这里阻塞直到所有爬虫执行完毕# 爬取完成后,例如,发送邮件通知或生成报告
send_email_notification()
generate_report()
在此案例中,两个爬虫SpiderOne和SpiderTwo会并发执行,它们各自独立工作,不共享数据或状态。
实现多线程/多实例爬虫
Scrapy框架提供了良好的支持,但对于大规模数据采集任务,引入多线程和多实例运行可以显著提升数据爬取效率
虽然CrawlerProcess在单个进程中管理多个爬虫,使用Python的多线程或多进程库可以实现更高级别的并发。这允许每个爬虫在自己的进程或线程中运行,有利于CPU密集型或I/O密集型的任务。
多线程案例:
from threading import Thread
from scrapy.crawler import CrawlerRunner
from scrapy.utils.project import get_project_settingssettings = get_project_settings()
runner = CrawlerRunner(settings)def run_spider(spider):runner.crawl(spider)runner.join()# 启动线程以运行爬虫
thread1 = Thread(target=run_spider, args=(SpiderOne,))
thread2 = Thread(target=run_spider, args=(SpiderTwo,))thread1.start()
thread2.start()thread1.join()
thread2.join()
多进程案例:
from multiprocessing import Process
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settingsdef run_spider(spider):process = CrawlerProcess(get_project_settings())process.crawl(spider)process.start()# 创建并启动进程
process1 = Process(target=run_spider, args=(SpiderOne,))
process2 = Process(target=run_spider, args=(SpiderTwo,))process1.start()
process2.start()process1.join()
process2.join()
在讨论多线程和多进程时,应注意Python的全局解释器锁(GIL)限制,它使得真正的并行只能在多进程中实现,而非多线程。对于CPU密集型任务,应优先选择多进程来提高效率。然而,Scrapy以I/O操作为主,因此通常情况下,使用多线程或CrawlerProcess就已经足够。
小总结:
通过结合Scrapy的CrawlerProcess和Python的多线程/多进程技术,开发者可以为他们的应用构建一个强大的爬虫集群,实现高效的数据采集。在数据采集的世界中,这种能力是无价的,可以显著减少采集时间,同时提取大量信息。
交互性扩展
为了提升Scrapy爬虫的适用性和灵活性,有时需要在运行时传递参数,或者处理爬虫返回的数据!Scrapy支持在启动爬虫时传递参数,这对于需要根据不同条件抓取页面的情况非常有用。
案例:
假设我们有一个爬虫需要根据用户输入的关键词搜索数据:
from scrapy.crawler import CrawlerProcess
from scrapy.utils.project import get_project_settings
from myproject.spiders import MySpiderprocess = CrawlerProcess(get_project_settings())# 运行爬虫,并将关键词作为参数传递
keyword = 'python'
process.crawl(MySpider, keyword=keyword)
process.start()
在上面的脚本示例中,我们通过crawl方法传递了一个关键字参数到爬虫。在爬虫中,你可以通过__init__方法访问这个参数。
import scrapyclass MySpider(scrapy.Spider):name = 'myspider'def __init__(self, keyword=None, *args, **kwargs):super(MySpider, self).__init__(*args, **kwargs)self.keyword = keyword # 使用传递的关键词def start_requests(self):url = f'http://search.example.com/?q={self.keyword}'yield scrapy.Request(url, self.parse)def parse(self, response):# 爬虫的解析逻辑pass
从爬虫获取并处理数据
Scrapy爬虫可以将抓取到的数据作为Items返回。然后,这些Items可以在Pipeline中进一步处理。如果需要在脚本中直接处理数据,可以在爬虫中捕获这些数据并将其返回。
import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy import signals
from twisted.internet import reactor
from scrapy.utils.project import get_project_settingsclass MySpider(scrapy.Spider):# ... 其他代码 ...def parse(self, response):# 解析逻辑,提取数据yield {'url': response.url}def item_collected(item):print(item['url']) # 打印或处理Itemrunner = CrawlerRunner(get_project_settings())def crawl_job():spider = MySpider(keyword='python')deferred = runner.crawl(spider)deferred.addBoth(lambda _: reactor.stop()) # 当爬虫结束时停止reactorrunner.signals.connect(item_collected, signal=signals.item_scraped)reactor.run() # 启动事件循环crawl_job()