自学Python之Scrapy爬虫:(一)爬虫基础


版权声明:一记录己之所学,一方便后人,转载的同学请注明出处。 https://blog.csdn.net/cc_xz/article/details/78710314

转载的老板请注明出处:http://blog.csdn.net/cc_xz/article/details/78710314万分感谢!

在本篇中,你将了解到:
1.爬虫概念的基本论述。
2.Python的虚拟环境。
3.如何创建一个Scrapy项目。
4.Scrapy框架结构及执行流程。


爬虫概念的基本论述:
什么是爬虫?
当你看到这篇文章时,是使用浏览器打开了CSDN这个网站中的某个页面。也就是说,你给浏览器传递一个CSDN的URL链接,通过浏览器到到CSDN的服务器中,请求关于这个URL链接的数据,当服务器返回的数据,实际上是由JavaScript、HTTP、CSS、链接等代码所组成的成堆的代码。而浏览器将负责将这些代码解析成文字、图片、网站样式等。
(上述流程的流程图)
这是查看网页的正常流程。而当我们希望获取到这个页面中的某段文字、数据时,只需要复制粘贴就可以了。但如果我们希望获取CSDN中所有的文章或文章内的某段内容时,由我们手动的数据URL,复制某段内容,粘贴到一个Doc或数据库中,这样机械性的任务,就可以由程序(爬虫)来完成。


网络爬虫的工作原理


什么是Scrapy?
如果我们只希望爬取很少的内容,那么使用Python中的一些网络模块、Xpath模块等可以轻易的写出一个爬虫。但在测试环境中可能没有问题,但如果涉及到关于代理IP、访问者身份(例如很有网站中有很多内容都必须在登陆后才能查看)等时,往往就会显得力不从心了。而Scrapy就是将上述中提到的和没提到的功能全部都集成了的一个框架(请注意框架和模块的区别)。

Python虚拟环境:
搭建一个虚拟环境
在Windows中存在着VMware这样的虚拟机,它们存在的意义是在于在一个系统的基础上安装多个其他的操作系统,并且这些系统中所安装的软件、配置等不会对其他系统造成影响。同样的在Python中也存在这样的虚拟环境:Virtualenv。
它可以使用我们在开发不同的程序时建立不同的虚拟环境,而每个虚拟环境中的第三方模块、框架都可以分别的安装,这样可以避免不同的模块、框架之间的冲突。例如,在程序A中需要某库的2.0版本,但在程序B中,需要这个库的3.0版本,如果在一个Python环境中同时安装2.0和3.0,在实际使用中可能会引起冲突。而在2个虚拟环境中分别安装2.0和3.0版本,则不会出现这种问题。
而在Python中,绝大部分的第三方库和框架,都可以通过pip工具安装,默认安装Python是自带pip工具的,如果你的系统中不存在pip工具,则到:https://pypi.python.org/pypi/pip中选择合适的版本进行安装。

下面演示如何安装Virtulenv:

C:/Users>pip install virtualenv

使用pip安装virtualenv,但由于pip命令默认会到国外下载地址去下载所需的软件,但下载速度往往比较慢,这时可以使用豆瓣的镜像地址来下载。例如:

C:/Users>pip install -i https://pypi.douban.com/simple/ virtualenv

其中,通过-i指定所需的镜像地址,https:为豆瓣源的地址。最终输出结果为:

Installing collected packages: virtualenv
Successfully installed virtualenv-15.1.0

新建一个虚拟环境,会在CMD当前所在的目录下创建一个新的虚拟环境,命令为:

D:/Python/virtualEnv>virtualenv testEnv

在CMD中的输出为:

Using base prefix 'c://users//administrator//appdata//local//programs//python//python35'
New python executable in
D:/Python/virtualEnv/testEnv/Scripts/python.exe
Installing setuptools, pip, wheel...done.

查看新建的虚拟环境中的目录为:

D:/Python/virtualEnv>cd testEnv
D:/Python/virtualEnv/testEnv>dir

在CMD中的输出为:

2017/11/07  15:04    <DIR>          .
2017/11/07  15:04    <DIR>          ..
2017/08/22  18:20    <DIR>          Include
2017/11/07  15:04    <DIR>          Lib
2017/11/07  15:04                60 pip-selfcheck.json
2017/11/07  15:04    <DIR>          Scripts
2017/11/07  15:04    <DIR>          tcl
               1 个文件             60 字节
               6 个目录 133,604,843,520 可用字节

其中,在Scripts目录中,存放了很多Python脚本,其中,执行activate.bat脚本,即可进入到当前虚拟环境中。例如:

D:/Python/virtualEnv/testEnv>cd Scripts
D:/Python/virtualEnv/testEnv/Scripts>activate.bat
(testEnv) D:/Python/virtualEnv/testEnv/Scripts>python
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) 
[MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

当使用activate.bat进入虚拟环境后,再执行python命令,进入到python的开发环境中即可。另外,如果当前系统中存在2.7+以及3.5+等不同的Python版本时,需要在创建虚拟环境时,指定Python的版本。例如:

D:/Python/virtualEnv>virtualenv -p C:/Users/Administrator/AppData/Local/Programs/Python/Python35/python.exe testEnv3.5

虚拟环境管理工具:
由于在使用virtualenv时,每次创建、使用不同的虚拟环境时,都需要进入到相应的目录下,这时使用virtualenv的管理工具,则可以免除这些操作。
安装virtualenvwrapper的命令为:

D:/Python/virtualEnv>pip install -i https://pypi.douban.com/simple/ virtualenvwrapper-win

值得注意的是,Windows环境下的用户安装-win版本,而Linux版本的用户则直接使用virtualenvwrapper即可。
当安装virtualenvwrapper后,使用workon命令,可以将当前所有的虚拟环境列出:

D:/Python/virtualEnv>workon

pythonVirtual
TestScrapy

使用Virtualenvwrapper新建虚拟环境:

D:/Python/virtualEnv>mkvirtualenv test
Using base prefix 'c://users//administrator//appdata//local//programs//python//python35'
New python executable in D:/Python/Code/Evns/test/Scripts/python.exe
Installing setuptools, pip, wheel...done.

(test) D:/Python/virtualEnv>

这时新建的虚拟环境,默认是在:C:/Users/Administrator/Envs 中。但可以通过设置Windows中的环境变量,修改Envs的路径。在桌面中右击计算机—>属性—>高级系统设置—>高级—>环境变量—>系统变量。
新建一个名为:WORKON_HOME的系统变量,其值就定义为你希望的Envs的路径。
例如:
这里写图片描述

C:/Users/Administrator>workon pythonVirtual
(pythonVirtual) C:/Users/Administrator>

在虚拟环境中,可以使用pip命令安装各种库,而不会对其他虚拟环境,或其他项目进行修改。

创建Scrapy项目:

C:/Users/Administrator>mkvirtualenv TestScrapy  
(TestScrapy) C:/Users/Administrator>pip install -i https://pypi.douban.com/simple/ scrapy

但是在按照scrapy的过程中,出现以下错误:
这里写图片描述
继续在CMD中创建Scrapy模板:

(MyScrapy) C:/Users/Administrator>d:
(MyScrapy) D:/Python/Evns>cd MyScrapy
(MyScrapy) D:/Python/Evns/MyScrapy>scrapy startproject JobboleScrapy

输出结果为:

New Scrapy project 'JobboleScrapy', using template directory 'd://python//evns/
/myscrapy//lib//site-packages//scrapy//templates//project', created in: D:/Python/Evns/MyScrapy/JobboleScrapy

You can start your first spider with:
    cd JobboleScrapy
    scrapy genspider example example.com

然后直接打开新创建的JobboleScripts即可。

Scrapy的框架结构及执行流程:
下图中描述了Scrapy的结构,包括各个组件在系统中运行时所产生的数据流流向:
本图需要替换
将此图重画,加强流程的描述

Scrapy组件
Scrapy Engine(Scrapy引擎)
Scrapy引擎负责控制”数据流”在系统中交给哪个组件进行处理,并在相应的处理动作发生时触发相关的事件。例如,当Scrapy将一个页面爬取完毕后,会向调度器请求下一个需要爬取的URL。(当然实际上Scrapy是通过Twisted实现的基于事件驱动的网络引擎)


调度器(Scheduler)
调度器(Scheduler)从引擎接受URL请求(Request)对象并将他们存储到请求队列中,以便之后引擎请求URL对象时提供给引擎。除此之外,调度器(Scheduler)实现的其他功能如下:

通过脑图解释Scheduler调度器

1、还提供了过滤重复URL对象功能:
通过创建一个URL的”指纹”副本,用来过滤重复的URL。也就是对每个Request(请求对象)做创建一个”指纹”,并且会保证相同的界面只创建一个指纹,例如,在包含查询字符串的URL中,虽然它们的URL不同,但实际上都是指向同一个页面,并且返回的内容也相同。这时,由于他们的Request指纹相同,后续的URL都会被过滤掉。
另外,也会根据Session ID来区分重复的Request,即:在同一个网站对,服务器会对每个用户(已登陆并产生Cookie)同服务器之间的连接(会话)进行统计,也就是创建一个Session ID,针对Session ID对客户端的身份进行标记并跟踪(这是由于HTTP协议是无状态协议,例如用户在购物车中添加了一件商品,服务器接受添加商品的Request后,并不知道是由哪个用户添加的,所以通过Session ID标记)。
也就是说Session ID实际上是存储在服务器和浏览器中的一个数据结构,Scheduler会通过这个数据结构对Request进行去重。

2、对Request进行优先级设定
可以通过Scheduler(调度器)对不同URL的优先级进行排序。

3、对Request进行本地序列化存储
Scheduler(调度器)也支持一个已经进行序列化的队列,用于在暂停爬虫后,可以快速的接着上次停止的地方继续开始爬取。
序列化时需要指定一个目录,并且创建或调用一个用于存储序列化的Json文件(通过命令行指定)。在Scrapy Engine(Scrapy引擎)向Scheduler申请一个Request(请求对象)时,Scheduler会优先从内存中获取对象,如果当前内存中没有Request,再从磁盘中获取队列(优先级)。
但在将Request添加到请求队列中时,会优先将Request存储到磁盘中。
并且,当Scheduler对Request序列化失败后,会将本次结果记录到Log中。


下载器(Downloader)
下载器(Downloader)顾名思义是用来将Request请求交给fetch()函数,再由fetch()函数向WEB服务器发起获取页面的请求。
在Downloader类中,有4个关键的对象,这4个关键对象中保存了大量关于Downloader运行时所需的配置参数:

通过脑图解释Downloader

1、slots(追踪)
slots实际上是用来存储Slot对象的字典,在字典中,Key是每个Request所对应的域名;Value则是一个Slot对象。
而Slot对象实际上是用来控制同一个域名下的Request下载请求(例如:http://www.runoob.com/html/html-tutorial.htmlhttp://www.runoob.com/css/css-tutorial.html在同一个域名下,但实际上是两个不同的页面),例如,针对该域名在发送Request下载请求时的并发数、对请求结果下载的延迟、而且支持随机延时等。主要是控制了对一个域名的访问策略,从而避免了流程、并发、速率等所造成的IP被封等情况。
同时,slots也可以实现一部分的策略缓存。对于同一个域名的访问策略可以通过slots获取,而无需每次都配置解析,根据Key(URL)获取slots中的Slot对象,如果没有,则创建一个新的Slot对象。
最后,slots对象中包含一个gc函数,每个60秒,就会对其中的Request进行确认,将清空slots中的缓存。

2、active(活动)
active是一个活动的集合,用于记录当前Downloader中正在下载的Request的集合。

3、handlers(下载管理器)
它实际上是一个DownloadHandlers对象(元组),其中存储了很多handlers,对于不同的协议(例如http、https、ftp等),会根据不同的handler调用不同的download_request函数。

实际上Downloader中还包括:DownloaderMiddleware,但我们将这个下载中间价模块在后边单独说明。


爬虫(Spiders)
每次Scheduler发送给Downloader的Request(请求对象),在Downloader将相关页面下载后将Response(返回结果),Scrapy Engine(Scrapy引擎)会将Response交给Spider(爬虫)进行分析(由使用着定义的Xpath、CSS规则),每个Spider负责一个网站(一般是这样)。
当然,在Downloader将Response(返回结果)交给Spider之前,还需要SpiderMiddleware(爬虫中间件),这个在后面单独说明。

通过脑图解释Spider爬虫

现在来看一下Spider:
首先,Spider支持由使用者定义如何爬取某个或某些网站,包括爬取的动作(是否继续跟进爬取子链接)、如何从Response(返回结果)中提取将希望提取出的数据进行结构化处理(组成一个Item)。

当你创建了一个Scrapy项目时,会默认使用Spider,它是默认的Spider,并且其他的Spider必须继承自该类,包括Scrapy自带的其他的Spider或自定义的Spider。
现在来介绍一下默认的Spider中常用的需赋值的变量以及需重写的函数:

需赋值的变量:
name
通过name变量来定义该Spider的名字,由于在一个Scrapy中,可能会存在多个Spider,所以当Scrapy在对每个Spider初始化时,会通过其”name”的值作为Key值,所以名称必须是唯一的。
所以一般情况下,会使用该Spider需要爬取的网站的域名来作为Spider的名称。


allowed_domains
在allowed_domains中定义了允许当前Spider爬取的域名列表,当OffsiteMiddleware(它是SpidersMiddleware中的一个模块,用于过滤无需此Spider爬取的Request)生效后,域名不在此列表中的URL将不会被此Spider进行解析。


start_urls
默认情况下,Spider从会优先从此列表中的获取URL并开始爬取。


custom_settings
这是一个dict(字典),可以在其中定义对于此Spider的所有设置,例如,对网站的最大并发数、IP的最大并发数等。对于常用的设置,会在后续文章中说明。


custom_settings
这是一个dict(字典),可以在其中定义对于此Spider的所有设置,例如,对网站的最大并发数、IP的最大并发数等。对于常用的设置,会在后续文章中说明。


可重写的函数:
start_requests()
当Spider开始爬取,但未指定种子URL时(通过start_urls定义的列表)该函数将被调用,且只会被调用一次。所以start_requests()函数一般用来启动时登陆网站。并且,此函数必须返回一个可迭代对象,该对象必须包含Spider用于爬取的第一个Request(请求对象)。
当然如果通过start_urls指定了种子URL后,start_requests()函数将使用的start_urls中的URL生成Request(通过调用make_requests_from_url()实现)。


make_requests_from_url(url)
这个函数会接受一个URL,并返回一个用于爬取的Request对象。该函数在初始化Request时,会被start_requests()函数所调用,用于创建Request。
在默认情况下(未被重载),该函数所返回的Request对象将以parse()作为回调函数(将Request对象传递给parse()并调用)。


parse(response)
如果没有在make_requests_from_url()中指定回调函数时,parse()函数是Spider中默认处理response的函数。parse负责处理response并返回处理的结果(从response中提取结果等)以及添加新的URL等。


closed(reason)
当该Spider即将被关闭后,该函数将被调用。


当然,除了上述讨论的默认的Spider以外,还包括了”CrawlSpider”,它是用来提取链接的,当然你也可以通过继承此Spider,从而实现属于你自己的Spider。还包括”XMLFeedSpider”,顾名思义,它是用来对XML文件进行爬取的Spider。以及”CSVFeedSpider”,同样的它是用来对CSV格式的文件进行爬取的(就是Excel的格式)。以及”SitemapSpider”是用来对整站进行爬取的。
当然这些不在本次的讨论范围之内,在后续的文章中会陆续说明。
通过脑图解释Spider中的变量、函数以及其他子类


项目管道(Item Pipeline)
当Item被Spider创建并赋值完毕后,Item将被传递给Item Pipeline后,将由Item Pipeline对Item进行处理并觉得该Item是否会通过此Pipeline,也就是当此Item Pipeline将某Item处理完毕后,再由其他的Item Pipeline继续处理。

以下是item pipeline的一些典型应用:

  • 清理HTML数据
  • 验证爬取的数据(检查item包含某些字段)
  • 查重(并丢弃)
  • 将爬取结果保存到数据库中

而上述的这些操作,都可以根据实际情况创建Pipeline来进行处理,例如,你可以分别创建4个Pipeline来处理这些任务,也可以在一个Pipeline中将这些任务完成。

通过流程图解释Pipeline中的流程

现在来讨论一下在Pipeline中可以重写实现的函数:

process_item(self, item, spider):
每个item pipeline组件都需要调用该函数,这个函数必须返回一个具有数据的dict,或是 Item (或任何继承类)对象, 或是抛出 DropItem 异常(抛弃此Item不再处理),被丢弃的item将不会被之后的pipeline组件所处理。
参数:
item – 被Spider爬取后提取出的Item
spider – 爬取该Item的Spider

open_spider(self, spider):
当spider被开启时,这个方法被调用。
参数: spider – 被开启的spider

close_spider(self, spider):
当spider被关闭时,这个方法被调用
参数: spider – 被关闭的spider

同Spider一样,如果你希望把Item保存成JSON、CSV、DB等容器中,Scrapy也提供了相应的Pipeline,当然这不在本篇的范围之中。后续的文章会有专门的分析。


中途总结一下
好吧我真的承认,上面的内容真的非常枯燥,但如果你已经理解了这些内容的话,你会发现,实际上我在描述那些组件的时候,就是按照他们的工作流程来描述的。
现在来具体梳理一下他们的工作流程:
1、Scrapy Engine(Scrapy引擎)从Spider(爬虫)中提取出种子URL(放在List中,可能有多个,甚至有多个Spider)。
2、然后Scrapy Engine将种子URL交给Scheduler(调度器),再由Scheduler将URL生成一个Request(请求对象)。
3、然后Scheduler将Request返回给Scrapy Engine,再由Scrapy交给Downloader进行下载。
4、当通过Request将页面下载完毕后,Downloader将生成一个Response(返回结果),并再Response发送给Scrapy Engine。
5、Scrapy Engine将接收到的Response发送给Spider进行处理。
6、Spider将通过使用者定义的爬取规则爬取Response中的内容,并生成一个Item。再将新的URL同Item一起发送给Scrapy Engine。
7、Scrapy Engine会根据Spider返回的结果,将Item传递给Item Pipeline(开始步骤8);以及将新的URL传递给Scheduler(调度器)(重复步骤2)。
8、Item Pipeline接收到Item后,会根据定义的内容,对Item进行处理。最终将希望获取到的数据进行保存。
9、最终Scrapy将成功爬取定义中所有的内容。

通过流程图解释Scrapy的流程

当然,上面说到的不靠谱的流程全都是异步完成的。


钩子
在编程中有一种概念叫”钩子”,钩子实际上是处理程序内部(当然系统也可以啦)消息的一种方式。每当特定的消息发出(例如Scrapy Endine将Request传递给Downloader)后,钩子函数(也可以是类啦)就会先捕获该消息,也就是先取得当前程序的控制权。这时钩子函数可以对该消息进行处理,也可以不做任何操作而继续续传递该消息,或者强制终止消息的传递。对于不同的钩子(注意这点)程序(系统)会维护维护一个钩子链,后加入的钩子会更先取得程序的控制权。
当然在设置钩子函数时,需要定义触发条件,以便满足该条件时,程序及时调用。


Downloader Middleware(下载器中间件)
DownloaderMiddleware是依赖于Scrapy的钩子处理框架,它是用于全局处理Scrapy中的Request和Response的底层模块。也就是说,针对Request和Response会有多个实现不同功能的钩子类。
例如,如果我们希望在Downloader处理Request时添加Cookie,就可以使用Scrapy提供的CookiesMiddleware。
或者我们希望在Downloader处理Request时添加HTTP代理功能,就可以使用Scrapy提供的HttpProxyMiddleware。
当然,除了Scrapy提供的一些常用的Middleware(中间件)以外,还可以自定义Middleware。这些在后续的实际案例中都会有说明。


Spider Middleware(爬虫中间件)
SpiderMiddleware是在Scrapy Engine和Spiders以及Spiders和Item Pipeline之间的钩子处理框架。它负责处理关于Spiders的接收到的Response以及发送的Item和Request。同DownloaderMiddleware一样的是,可以根据具体的使用要求分别处理。

通过流程图解释Scrapy的流程

现在再来看的话,是不是就明了多了?

原创文章,作者:Carrie001128,如若转载,请注明出处:https://blog.ytso.com/244628.html

(0)
上一篇 2022年4月17日
下一篇 2022年4月17日

发表回复

登录后才能评论