手把手教你写网络爬虫(4)

作者:拓海 (https://github.com/tuohai666)

摘要:从零开始写爬虫,初学者的速成指南!

封面:

上期我们理性的分析了为什么要学习Scrapy,理由只有一个,那就是免费,一分钱都不用花!

咦?怎么有人扔西红柿?好吧,我承认电视看多了。不过今天是没得看了,为了赶稿,又是一个不眠夜。。。言归正传,我们将在这一期介绍完Scrapy的基础知识, 如果想深入研究,大家可以参考官方文档,那可是出了名的全面,我就不占用公众号的篇幅了。

架构简介

下面是Scrapy的架构,包括组件以及在系统中发生的数据流的概览(红色箭头所示)。 之后会对每个组件做简单介绍,数据流也会做一个简要描述。

组件

Engine: 引擎负责控制数据流在系统中所有组件中流动,并在相应动作发生时触发事件。

Scheduler: 调度器从引擎接受Request并将他们入队,以便之后引擎请求他们时提供给引擎。

Downloader: 下载器负责获取页面数据并提供给引擎,而后提供给Spider。

Spiders: Spider是Scrapy用户编写的用于分析Response并提取Item或提取更多需要下载的URL的类。 每个Spider负责处理特定网站。

Item Pipeline: 负责处理被Spider提取出来的Item。典型的功能有清洗、 验证及持久化操作。

Downloader middlewares: 下载器中间件是在Engine及Downloader之间的特定钩子(specific hooks),处理Downloader传递给Engine的Response。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。

Spider middlewares: 是在Engine及Spider之间的特定钩子(specific hook),处理Spider的输入(Response)和输出(Items及Requests)。 其提供了一个简便的机制,通过插入自定义代码来扩展Scrapy功能。

数据流

Scrapy中的数据流由执行引擎控制,其过程如下:

  1. Engine从Spider获取第一个需要爬取URL(s)。
  2. Engine用Scheduler调度Requests,并向Scheduler请求下一个要爬取的URL。
  3. Scheduler返回下一个要爬取的URL给Engine。
  4. Engine将URL通过Downloader middlewares转发给Downloader。
  5. 一旦页面下载完毕,下载器生成一个该页面的Response,并将其通过Downloader middlewares发送给Engine。
  6. 引擎从Downloader中接收到Response并通过Spider middlewares发送给Spider处理。
  7. Spider处理Response并返回爬取到的Item及新的Request给Engine。
  8. Engine将爬取到的Item给Item Pipeline,然后将Request给Scheduler。
  9. 从第一步开始重复这个流程,直到Scheduler中没有更多的URLs。

架构就是这样,流程和我第二篇里介绍的迷你架构差不多,但扩展性非常强大。

One more thing

Scrapy基于事件驱动网络框架 Twisted 编写,Twisted是一个异步非阻塞框架。一说到网络通信框架就会提什么同步、异步、阻塞和非阻塞,到底是些啥玩意啊?为啥老是有人暗示或者明示异步=非阻塞?比如Scrapy文档里:Scrapy is written with Twisted, a popular event-driven networking framework for Python. Thus, it’s implemented using a non-blocking (aka asynchronous) code for concurrency. 这种说法对吗?举个栗子:

出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)

1. 老张把水壶放到火上,立等水开。(同步阻塞)

  老张觉得自己有点傻。

2. 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)

  老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~~的噪音。

3. 老张把响水壶放到火上,立等水开。(异步阻塞)

  老张觉得这样傻等意义不大。

4. 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)

  老张觉得自己聪明了。

所谓同步异步,只是对于水壶而言。普通水壶,同步;响水壶,异步。虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。

所谓阻塞非阻塞,仅仅对于老张而言。立等的老张,阻塞;看电视的老张,非阻塞。情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。

入门教程

创建项目

在开始爬取之前,您必须创建一个新的Scrapy项目。 进入您打算存储代码的目录中,运行下列命令:

scrapy startproject tutorial

该命令将会创建包含下列内容的 tutorial 目录:

tutorial/
scrapy.cfg # 项目的配置文件
tutorial/ # 该项目的python模块。之后您将在此加入代码
__init__.py
items.py # 项目中的item文件
pipelines.py # 项目中的pipelines文件
settings.py # 项目的设置文件
spiders/ # 放置spider代码的目录
__init__.py

编写第一个爬虫

Spider是用户编写用于从单个网站(或者一些网站)爬取数据的类。其包含了一个用于下载的初始URL,以及如何跟进网页中的链接以及如何分析页面中的内容的方法。

以下为我们的第一个Spider代码,保存在 tutorial/spiders 目录下的 quotes_spider.py文件中:

import scrapy

class QuotesSpider(scrapy.Spider):
name = "quotes" def start_requests(self):
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) def parse(self, response):
page = response.url.split("/")[-2]
filename = 'quotes-%s.html' % page
with open(filename, 'wb') as f:
f.write(response.body)
self.log('Saved file %s' % filename)
 

为了创建一个Spider,你必须继承 scrapy.Spider 类, 且定义以下三个属性:

  1. name: 用于区别Spider。 该名字必须是唯一的,您不可以为不同的Spider设定相同的名字。
  2. start_urls: 包含了Spider在启动时进行爬取的url列表。 因此,第一个被获取到的页面将是其中之一。 后续的URL则从初始的URL获取到的数据中提取。
  3. parse() 是spider的一个方法。 被调用时,每个初始URL完成下载后生成的Response 对象将会作为唯一的参数传递给该函数。 该方法负责解析返回的数据(response data),提取数据以及生成需要进一步处理的URL的 Request 对象。

运行我们的爬虫

进入项目的根目录,执行下列命令启动spider:

scrapy crawl quotes

这个命令启动用于爬取 quotes.toscrape.com 的spider,你将得到类似的输出:

-- :: [scrapy.core.engine] INFO: Spider opened
-- :: [scrapy.extensions.logstats] INFO: Crawled pages (at pages/min), scraped items (at items/min)
-- :: [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:
-- :: [scrapy.core.engine] DEBUG: Crawled () <GET http://quotes.toscrape.com/robots.txt> (referer: None)
-- :: [scrapy.core.engine] DEBUG: Crawled () <GET http://quotes.toscrape.com/page/1/> (referer: None)
-- :: [scrapy.core.engine] DEBUG: Crawled () <GET http://quotes.toscrape.com/page/2/> (referer: None)
-- :: [quotes] DEBUG: Saved file quotes-.html
-- :: [quotes] DEBUG: Saved file quotes-.html
-- :: [scrapy.core.engine] INFO: Closing spider (finished)
 

提取数据

我们之前只是保存了HTML页面,并没有提取数据。现在升级一下代码,把提取功能加进去。至于如何使用浏览器的开发者模式分析网页,之前已经介绍过了。

import scrapy

class QuotesSpider(scrapy.Spider):
name = "quotes"
start_urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
] def parse(self, response):
for quote in response.css('div.quote'):
yield {
'text': quote.css('span.text::text').extract_first(),
'author': quote.css('small.author::text').extract_first(),
'tags': quote.css('div.tags a.tag::text').extract(),
}
 

再次运行这个爬虫,你将在日志里看到被提取出的数据:

-- :: [scrapy.core.scraper] DEBUG: Scraped from < http://quotes.toscrape.com/page/1/>
{'tags': ['life', 'love'], 'author': 'André Gide', 'text': '“It is better to be hated for what you are than to be loved for what you are not.”'}
-- :: [scrapy.core.scraper] DEBUG: Scraped from < http://quotes.toscrape.com/page/1/>
{'tags': ['edison', 'failure', 'inspirational', 'paraphrased'], 'author': 'Thomas A. Edison', 'text': "“I have not failed. I've just found 10,000 ways that won't work.”"}

保存爬取的数据

最简单存储爬取的数据的方式是使用 Feed exports:

scrapy crawl quotes -o quotes.json

该命令将采用 JSON 格式对爬取的数据进行序列化,生成quotes.json文件。

在类似本篇教程里这样小规模的项目中,这种存储方式已经足够。如果需要对爬取到的item做更多更为复杂的操作,你可以编写 Item Pipeline,tutorial/pipelines.py在最开始的时候已经自动创建了。

下一步

系列写到这里,组里对下一步的计划产生了分歧,本人的意思是系列已经接近尾声了,可领导的意思是,连载可以正式开始了! What? 这不能忍啊!所以我立即做了一个艰难的决定,连载正式开始!详情下回分解,再见!

[原创]手把手教你写网络爬虫(4):Scrapy入门的更多相关文章

  1. [原创]手把手教你写网络爬虫(5):PhantomJS实战

    手把手教你写网络爬虫(5) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 大家好!从今天开始,我要与大家一起打造一个属于我们自己的分布式爬虫平台,同时也会对涉及到的技术进行详细介绍.大 ...

  2. [原创]手把手教你写网络爬虫(7):URL去重

    手把手教你写网络爬虫(7) 作者:拓海 摘要:从零开始写爬虫,初学者的速成指南! 封面: 本期我们来聊聊URL去重那些事儿.以前我们曾使用Python的字典来保存抓取过的URL,目的是将重复抓取的UR ...

  3. Python爬虫:手把手教你写迷你爬虫架构

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:我爱学Python 语言&环境 语言:继续用Python开路 ...

  4. 手把手教你写基于C++ Winsock的图片下载的网络爬虫

    手把手教你写基于C++ Winsock的图片下载的网络爬虫 先来说一下主要的技术点: 1. 输入起始网址,使用ssacnf函数解析出主机号和路径(仅处理http协议网址) 2. 使用socket套接字 ...

  5. 手把手教你写电商爬虫-第三课 实战尚妆网AJAX请求处理和内容提取

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 看完两篇,相信大家已经从开始的 ...

  6. 手把手教你写电商爬虫-第四课 淘宝网商品爬虫自动JS渲染

    版权声明:本文为博主原创文章,未经博主允许不得转载. 系列教程: 手把手教你写电商爬虫-第一课 找个软柿子捏捏 手把手教你写电商爬虫-第二课 实战尚妆网分页商品采集爬虫 手把手教你写电商爬虫-第三课 ...

  7. 网络编程懒人入门(八):手把手教你写基于TCP的Socket长连接

    本文原作者:“水晶虾饺”,原文由“玉刚说”写作平台提供写作赞助,原文版权归“玉刚说”微信公众号所有,即时通讯网收录时有改动. 1.引言 好多小白初次接触即时通讯(比如:IM或者消息推送应用)时,总是不 ...

  8. 手把手教你写Sublime中的Snippet

    手把手教你写Sublime中的Snippet Sublime Text号称最性感的编辑器, 并且越来越多人使用, 美观, 高效 关于如何使用Sublime text可以参考我的另一篇文章, 相信你会喜 ...

  9. 手把手教你写LKM rookit! 之 第一个lkm程序及模块隐藏(一)

    唉,一开始在纠结起个什么名字,感觉名字常常的很装逼,于是起了个这<手把手教你写LKM rookit> 我觉得: 你们觉得:...... 开始之前,我们先来理解一句话:一切的操作都是系统调用 ...

随机推荐

  1. 安装LR11 时,安装Microsoft Visual c++2005 sp1运行时组件,就会提示命令行选项语法错误,键入“命令/?”可获取帮肋信息

    1.进入loadrunner-11\Additional Components\IDE Add-Ins\MS Visual Studio .NET 2.安装:LRVS2005IDEAddInSetup ...

  2. (译文)学习ES6非常棒的特性——Async / Await函数

    try/catch 在使用Async/Await前,我们可能这样写: const main = (paramsA, paramsB, paramsC, done) => { funcA(para ...

  3. Alpha冲刺集合

    Alpha冲刺集合 Day1 http://www.cnblogs.com/bugLoser/p/7901016.html Day2 http://www.cnblogs.com/bugLoser/p ...

  4. 201621123040《Java程序设计》第七周学习总结

    1.本周学习总结 1.1思维导图:Java图形界面总结 2.书面作业 2.1GUI中的事件处理 2.1.1写出事件处理模型中最重要的几个关键词. 关键词:事件 事件源 事件监听器 2.1.2任意编写事 ...

  5. C语言:第0次作业

    问题1: 你为什么选择计算机专业?你认为你的条件如何?和这些博主比呢? 感性地讲,高中时意外看到了电影<社交网络>,自那时起就将将马克扎克伯格视为偶像,他天才的智慧和长远的眼光深深吸引了我 ...

  6. Twisted 延迟调用

    延迟(defer)是twisted框架中实现异步的编程体系,使程序设计可以采用事件驱动的机制 1.基本使用 defer可以看作一个管理回调函数的对象,可以向该对象添加需要的回调函数同时也可以指定该组函 ...

  7. nyoj 移位密码

    移位密码 时间限制:1000 ms  |  内存限制:65535 KB 难度:0   描述 移位密码是最简单的一类代替密码,具体算法就是将字母表的字母右移k个位置(k<26),并对字母表长度作模 ...

  8. sql2008r2,以前好好可以用的,但装了vs2017后,连接不上了,服务也停了,结果手动也 启动不了, 无法加载或初始化请求的服务提供程

    日志: 2017-12-14 12:33:17.53 服务器 A self-generated certificate was successfully loaded for encryption.2 ...

  9. WebAPI 跨域解决方案.

    先下载支持跨域的.dll,然后using System.Web.Http.Cors. 我把webapi解决方案部署到IIS上了.测试过后可以解决跨域. 方案一(用了*号,这样有安全隐患.): 直接在w ...

  10. nat和napt技术

    私网IP地址是指内部网络或主机的IP地址,公网IP地址是指在因特网上全球唯一的IP地址. RFC 1918为私有网络预留出了三个IP地址块,如下: A类:10.0.0.0-10.255.255.255 ...