使用 Scrapy 抓取 Python.org
Scrapy 是一个非常流行的开源 Python 抓取框架,用于提取数据。它最初只是为了抓取而设计的,但它也已经发展成为一个强大的网络爬行解决方案。
在我们之前的食谱中,我们使用 Requests 和 urllib2 来获取数据,并使用 Beautiful Soup 来提取数据。 Scrapy 提供所有这些功能以及许多其他内置模块和扩展。 当涉及到使用 Python 进行抓取时,它也是我们的首选工具。
Scrapy 提供了许多值得一提的强大功能:
-
内置扩展,用于发出 HTTP 请求并处理压缩、身份验证、缓存、操作用户代理和 HTTP 标头
-
内置支持使用 CSS 和 XPath 等选择器语言选择和提取数据,以及支持利用正则表达式选择内容和链接
-
处理语言和非标准编码声明的编码支持
-
灵活的 API 可重用和编写自定义中间件和管道,这提供了一种干净、简单的方法来实现任务,例如自动下载资产(例如图像或媒体)以及将数据存储在文件系统、S3、数据库等存储中
准备工作
使用 Scrapy 创建抓取工具有多种方法。 一种是编程模式,我们在代码中创建爬虫和蜘蛛。 还可以从模板或生成器配置 Scrapy 项目,然后使用 scrapy 命令从命令行运行 scraper。 本书将遵循编程模式,因为它更有效地将代码包含在单个文件中。 当我们使用 Scrapy 整理具体的、有针对性的食谱时,这将有所帮助。
这不一定是比使用命令行执行更好的运行 Scrapy 抓取工具的方法,这只是本书的设计决策。 归根结底,这本书不是关于 Scrapy 的(还有其他书籍只介绍 Scrapy),而是更多地阐述了在抓取时可能需要做的各种事情,以及最终在云中创建功能性抓取工具即服务的过程。
怎么做
这个配方的脚本是 01/03_events_with_scrapy.py。 以下是代码:
import scrapy
from scrapy.crawler import CrawlerProcess
class PythonEventsSpider(scrapy.Spider):
name = 'pythoneventsspider'
start_urls = ['https://www.python.org/events/python-events/',]
found_events = []
def parse(self, response):
for event in response.xpath('//ul[contains(@class, "list-recent-events")]/li'):
event_details = dict()
event_details['name'] = event.xpath('h3[@class="event-title"]/a/text()').extract_first()
event_details['location'] = event.xpath('p/span[@class="event-location"]/text()').extract_first()
event_details['time'] = event.xpath('p/time/text()').extract_first()
self.found_events.append(event_details)
if __name__ == "__main__":
process = CrawlerProcess({ 'LOG_LEVEL': 'ERROR'})
process.crawl(PythonEventsSpider)
spider = next(iter(process.crawlers)).spider
process.start()
for event in spider.found_events: print(event)
以下运行脚本并显示输出:
~ $ python 03_events_with_scrapy.py
{'name': 'PyCascades 2018', 'location': 'Granville Island Stage, 1585
Johnston St, Vancouver, BC V6H 3R9, Canada', 'time': '22 Jan. – 24 Jan. '}
{'name': 'PyCon Cameroon 2018', 'location': 'Limbe, Cameroon', 'time': '24
Jan. – 29 Jan. '}
{'name': 'FOSDEM 2018', 'location': 'ULB Campus du Solbosch, Av. F. D.
Roosevelt 50, 1050 Bruxelles, Belgium', 'time': '03 Feb. – 05 Feb. '}
{'name': 'PyCon Pune 2018', 'location': 'Pune, India', 'time': '08 Feb. – 12
Feb. '}
{'name': 'PyCon Colombia 2018', 'location': 'Medellin, Colombia', 'time':
'09 Feb. – 12 Feb. '}
{'name': 'PyTennessee 2018', 'location': 'Nashville, TN, USA', 'time': '10
Feb. – 12 Feb. '}
{'name': 'PyCon Pakistan', 'location': 'Lahore, Pakistan', 'time': '16 Dec.
– 17 Dec. '}
{'name': 'PyCon Indonesia 2017', 'location': 'Surabaya, Indonesia', 'time':
'09 Dec. – 10 Dec. '}
相同的结果,但使用另一种工具。让我们快速回顾一下它是如何工作的。
怎么运行的
我们将在后面的章节中详细介绍 Scrapy,但让我们快速浏览一下这段代码,感受一下它是如何完成这个抓取的。 Scrapy 中的一切都围绕着创建 spider。spider 根据我们提供的规则在互联网上的页面上爬行。该 spider 仅处理一个页面,因此它并不是真正的 spider。但它展示了我们将在后面的 Scrapy 示例中使用的模式。
spider 是使用派生自 Scrapy spider 类之一的类定义创建的。我们的派生自 scrapy.Spider 类。
class PythonEventsSpider(scrapy.Spider):
name = 'pythoneventsspider'
start_urls = ['https://www.python.org/events/python-events/',]
每个 spider 都有一个 name ,还有一个或多个 start_url 告诉它从哪里开始爬行。
这个 spider 有一个字段来存储我们找到的所有事件:
found_events = []
然后,spider 有一个 parse 方法名称,它将为 spider 收集的每个页面调用。
def parse(self, response):
for event in response.xpath('//ul[contains(@class, "list-recentevents")]/li'):
event_details = dict()
event_details['name'] = event.xpath('h3[@class="eventtitle"]/a/text()').extract_first()
event_details['location'] = event.xpath('p/span[@class="eventlocation"]/text()').extract_first()
event_details['time'] = event.xpath('p/time/text()').extract_first()
self.found_events.append(event_details)
此方法的实现使用 XPath 选择来从页面获取事件(XPath 是 Scrapy 中导航 HTML 的内置方法)。 它们与其他示例类似地构建 event_details 字典对象,然后将其添加到found_events 列表中。
其余代码以编程方式执行 Scrapy 爬虫。
process = CrawlerProcess({ 'LOG_LEVEL': 'ERROR'})
process.crawl(PythonEventsSpider)
spider = next(iter(process.crawlers)).spider
process.start()
首先创建一个 CrawlerProcess,它执行实际的爬行和许多其他任务。 我们将 ERROR 的 LOG_LEVEL 传递给它以防止大量 Scrapy 输出。 将其更改为 DEBUG 并重新运行以查看差异。
接下来我们告诉爬虫进程使用我们的 Spider 实现。 我们从该爬行器获取实际的 spider 对象,以便我们可以在爬行完成时获取 items。 然后我们通过调用 process.start() 来启动整个过程。
爬行完成后,我们可以迭代并打印出找到的项目。
for event in spider.found_events: print(event)
这个例子确实没有触及 Scrapy 的任何强大之处。我们将在本书后面详细介绍一些更高级的功能。 |