Scrapy框架架构:
Scrapy框架介绍:
- 写一个爬虫,需要做很多的事情。比如:发送网络请求、数据解析、数据存储、反反爬虫机制(更换IP代理、设置请求头等)、异步请求等。这些工作如果每次都要从零开始写,比较浪费时间。因此scrapy把一些基础的东西封装好了,在他上边写爬虫可以变得更加高效(爬取效率和开发效率)。
Scrapy框架模块功能介绍:
- 1.Scrapy Engine(引擎):scrapy框架的核心部分。负责在Spider和ItemPipeline、Downloader、Scheduler中间通信、传递数据等。
- 2.Spider(爬虫):发送需要爬取的链接给引擎,最后引擎把其他模块请求回来的数据再发送给爬虫,爬虫去解析想要的数据。这部分代码是开发者自己写的。
- 3.Scheduler(调度器):负责接收引擎发送过来的请求,并按照一定的方式进行排列和整理,负责调度请求的顺序等。
- 4.Downloader(下载器):负责接收引擎传过来的下载请求,然后去网络上下载对应的数据再交还给引擎。
- 5.Item Pipeline(管道):负责将Spider(爬虫)传递过去的数据进行保存。
- 6.Downloader Middlewares(下载中间件):可以扩展下载器和引擎之间通信功能的中间件。
- 7.Spider Middlewares(Spider中间件):可以扩展引擎和爬虫之间通信功能的中间件。
Scrapy框架安装:
- pip install scrapy --安装框架
- pip install pypiwin32 --windows下Scrapy所依赖的包
创建项目:
- 进入到你想存放的目录。然后使用以下命令创建:
scrapy startproject [项目名称]
- 使用命令创建一个爬虫:
scrapy gensipder [爬虫名称] [爬虫域名]
项目目录结构:
- items.py: 用来存放爬虫爬取下来数据的模型
- middlewares.py: 用来存放各种中间件的文件
- pipelines.py: 用来将items的模型存储到本地磁盘中
- settings.py: 本爬虫的一些配置信息(比如请求头、多久发送一次请求、ip代理池等)
- scrapy.cfg: 项目的配置文件
- spiders包: 以后所有的爬虫,都是存放在这个里面
糗事百科Scrapy爬虫笔记:
- response是一个
scrapy.http.response.html.HtmlResponse
对象。可以执行xpath
和css
语法来提取数据 - 提取出来的数据,是一个
Seletor
或者是一个SelectorList
对象。如果想要获取其中的字符串。那么应该执行getall
或者get
方法 - getall方法:获取
Selector
中的所有文本,返回的是一个列表。 - get方法:获取
Selector
中的第一个文本。返回的是一个str类型。 - 如果数据解析回来,要传给pipline处理。那么可以使用
yield
来返回。 - item:建议在
items.py
中定义好模型。以后就不要使用字典。 - pipeline: 这个是专门用来保存数据的。其中有三个方法是会经常用的。
open_spider(self,spider)
:当爬虫被打开时执行process_item(self,spider)
:当爬虫有数据(item)传过来的时候会被调用。close_spider(self,spider)
:当爬虫关闭的时候会被调用
要激活pipelilne,应该在settings.py
中,设置ITEM_PIPELINES
JsonItemExporter和JsonLinesItemExporter:
- 保存json数据的时候,可以使用这两个类,让操作变得更简单。
JsonItemExporter
:这个是每次把数据添加到内存中,最后统一写入到磁盘中,好处是,存储的数据是一个满足json规则的数据。坏处是如果数据量比较大,那么比较耗内存。示例代码如下:from scrapy.exporters import JsonItemExporter #json导出器 class QsbkPipeline(object): def __init__(self): self.fp = open("duanzi.json","wb") self.exporter = JsonItemExporter(self.fp,ensure_ascii=False,encoding='utf-8') self.exporter.start_exporting() def open_spider(self,spider): #爬虫开始函数 pass def process_item(self, item, spider): self.exporter.export_item(item) return item def close_apider(self,spider): #爬虫结束后,关闭文件 self.exporter.finish_exporting() self.fp.close()
JsonLinesItemExporter
:这个是每次调用export_item
的时候就把这个item存储到应怕中。坏处是每一个字典是一行,整个文件不是一个满足json格式的文件。好处是每次处理数据的十周直接存储到了硬盘中,这样不会消耗内存,数据也比较安全。示例代码如下:from scrapy.exporters import JsonLinesItemExporter class QsbkPipeline(object): def __init__(self): self.fp = open("duanzi.json","wb") self.exporter = JsonLinesItemExporter(self.fp,ensure_ascii=False,encoding='utf-8') def open_spider(self,spider): #爬虫开始函数 pass def process_item(self, item, spider): self.exporter.export_item(item) return item def close_apider(self,spider): #爬虫结束后,关闭文件 # self.exporter.finish_exporting() self.fp.close()
CrawlSpider
- 在糗事百科爬虫案例中,我们是自己在解析完整个页面后获取下一页的url,然后重新发送一个请求。有时候我们想要这样做,只要满足某个条件的url,那么这时候我们可以通过CrawlSpider来帮我们完成了。CrawlSpider继承自Spider,只不过是在之前的基础上增加了新的功能,可以定义爬取的url的规则,以后scrapy碰到满足条件的url都进行爬取,而不是手动的yield Request。
CrawlSpider爬虫:
- 之前创建爬虫的方式是通过
scrapy genspider [爬虫名字][域名]
的方式创建的。如果想要创建CrawlSpider爬虫,那么应该通过以下命令创建:scrapy genspider -t crawl [爬虫名字][域名]
LinkExtractors链接提取器:
-
使用LinkExtractors可以不用程序员自己提取想要的url,然后发送请求。这些工作都可以交给LinkExtractors,它会在所有爬的页面中找到满足规则的url,实现自动的爬取,以下对LinkExtractors类做一个简单的介绍:
class scrapy.linkextractors.LinkExtractor( allow =(), deny =(), allow_domains = (), deny_extensions = None, restrict_xpaths = (), tags = ('a','area'), attrs = ('href'), cononicalize = True, unique = True, process_value = None )
-
主要参数讲解:
- allow:允许的url。所有满足这个正则表达式的url都会被提取。
- deny: 禁止的url。所有满足这个正则表达式的url都不会被提取。
- allow_domains:允许的域名。只有在这个里面指定的域名的url才会被提取。
- deny_doomains: 禁止的域名。所有在这个里面指定的域名的url都不会被提取。
- restrict_xpaths: 严格的xpath。和allow共同过滤链接。
Rule规则类:
-
定义爬虫的规则类。以下对这个类做一个简单的介绍:
class scrapy.spiders.Rule(link_extractor,callback = None,cb_kwargs=None,follow=None,process_links = None,process_request = None)
-
主要参数讲解:
- link_extractor:一个
LinkExtractor
队形,用于定义爬取规则。 - callback:满足这个规则的url,应该要执行哪个回调函数。因为
CrawlSpider
使用了parse作为回调函数,因此不要覆盖parse作为回调函数自己的回调函数。 - follow:指定根据该规则从response中提取的链接是否需要跟进。
- process_links:从link_extractor中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接。
- link_extractor:一个
Request和Response:
Requset对象:
-
Request对象在我们写爬虫,爬取一页的数据需要重新发送一个请求的时候调用。这个类需要传递一些参数,其中比较常用的参数有:
- url:这个requset对象发送请求的url
- callback:在下载器下载完成相应的数据后执行的回调函数
- method:请求的方法。默认为GET方法,可以设置为其他方法。
- headers:请求头,对于一些固定的设置,放在settings.py中指定就可以了。对于那些非固定的,可以在发送请求的时候指定。
- meta:比较常用。用于在不同的请求之间传递数据用的。
- encoding:编码。默认的为utf-8,使用默认的就可以了。
- dot_filter:表示不由调度器过滤。在执行多次重复的请求的时候用的比较多。
- errback:在发生错误的时候执行的函数。
Response对象:
-
Response对象一般是由Scrapy给你自动构建的。因此开发者不需要关心如何创建Response对象,而是如何使用它。Response对象有很多属性,可以用来提取数据的。主要有以下属性:
- meta:从其他请求传过来的meta属性,可以用来保持多个请求之间的数据连接。
- encoding:返回当前字符串编码和解码的格式。
- text:将返回来的数据作为unicode字符串返回。
- body:将返回数据作为byte字符串返回。
- xpath:xpath选择器。
模拟登录:
- 想要发送post请求,那么推荐使用
scrapy.FormRequest
方法。可以方便的指定表单数据。 - 如果想在爬虫一开始的时候久发送post请求,那么应该重写
start_requests
方法。在这个方法中,发送post请求
下载文件和图片:
- Scrapy为下载item中包含的文件(比如在爬取到产品时,同时也想保存对应的图片)提供了一个可重用的
item pipelines
。这些pipeline 有些共同的方法和结构(我们称之为media pipeline)。一般来说你会使用Files Pipeline
或者 Images Pipeline。
为什么要选择使用scrapy内置的下载文件的方法:
- 避免重新下载最近已经下载过的文件。
- 可以方便的指定文件存储的路径。
- 可以将下载的图片转换成通用的格式。比如png或jpg。
- 可以方便的生成缩略图。
- 可以方便的检测图片的宽和高,确保他们满足最小限制。
- 异步下载,效率非常高。
Files Pipeline:
-
当使用
Files Pipeline
下载文件的时候,按照以下步骤来完成。- 定义好一个Item,然后在这个item中定义两个属性,分别为
file_urls
以及files
。file_urls
是用来存储需要下载的文件的url链接,需要给一个列表。 - 当文件下载完成后,会把文件下载的相关信息存储到
item
的files
属性中。比如下载路径、下载的url和文件的校验码等。 - 在配置文件
settings.py
中配置FILES_STORE
,这个配置是用来设置文件下载下来的路径。 - 启动
pipeline
:在ITEM_PIPELINES
中设置scrapy.pipelines.files.FilesPipeline:1
。
- 定义好一个Item,然后在这个item中定义两个属性,分别为
Images Pipeline:
-
当使用
Images Pipeline
下载文件的时候,按照以下步骤来完成:- 定义好一个Item,然后在这个item中定义两个属性,分别为
image_urls
以及images
。image_urls
是用来存储需要下载的图片的url链接,需要给一个列表。 - 当文件下载完成后,会把图片下载的相关信息存储到
item
的images
属性中。比如下载路径、下载的url和文件的校验码等。 - 在配置文件
settings.py
中配置IMAGES_STORE
,这个配置是用来设置图片下载下来的路径。 - 启动
pipeline
:在ITEM_PIPELINES
中设置scrapy.pipelines.images.ImagesPipeline:1
。
- 定义好一个Item,然后在这个item中定义两个属性,分别为
Downloader Middlewares(下载器中间件)
- 下载器中间件是引擎和下载器之间通信的中间件。在这个中间件中我们可以设置代理、更换请求头等来达到反反爬虫的目的。要写下载器中间件,可以在下载器中实现两个方法。一个是
process_request(self,request,spider)
,这个方法是在请求发送之前会执行,还有一个是process_response(self,request,response,spider)
,这个方法是数据下载到引擎之前执行。
process_request(self,request,spider):
-
这个方法是下载器在发送请求之前会执行的。一般可以在这个里面设置随机代理ip等。
-
1.参数:
- request:发送请求的request对象。
- spider:发送请求的spider对象。
-
2.返回值:
- 返回None:如果返回None,Scrapy将继续处理该request,执行其他中间件中的相应方法,直到合适的下载器处理函数被调用。
- 返回Reponse对象:Scrapy将不会调用任何其他的
process_request
方法,将直接返回这个response对象。已经激活的中间件的process_response()方法则会在每个response返回时被调用。 - 返回Request对象:不再使用之前的request对象去下载数据,而是根据现在返回request对象返回数据。
- 如果这个方法中抛出了异常,则会调用process_exception方法。
-
process_response(self,request,response,spider):
- 这个时下载器下载的数据到引擎中间会执行的方法。
- 1.参数:
- request:request对象。
- response:被处理的response对象。
- spider:spider对象。
- 2.返回值:
- 返回Response对象:会将这个新的response对象传给其他中间件,最终传给爬虫。
- 返回Request对象:下载器链被切断,返回的request会重新被下载器调度下载。
- 如果抛出一个异常,那么调用request的errback方法,如果没有指定这个方法,那么会抛出一个异常。
- 1.参数:
随机请求头中间件:
-
爬虫在频繁访问一个页面的时候,这个请求头如果一直保持一致。那么很容易被服务器发现,从而禁止掉这个请求头的访问。因此我们要在访问这个页面之前随机的更改请求头,这样才可以避免爬虫被抓。随机更改请求头,可以在下载中间件中实现。在请求发送给服务器之前,随机的选择一个请求头。这样就可以避免总使用一个请求头了。示例代码如下:
- 1.在
middlewares.py
中的设置#1.引入random import random #2.设置自定义user-agent中间件 class UserAgentDownloadMiddleware(object): #user-agent随机请求头中间件 USER_AGENTS=[ 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4. 0; Acoo Browser 1.98.744; .NET CLR 3.5.30729)', 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4. 0; Acoo Browser 1.98.744; .NET CLR 3.5.30729)', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4. 0; Acoo Browser; GTB5; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; InfoPath.1; .NET CLR 3.5.30729; .NET CLR 3.0.30618)', 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4. 0; SV1; Acoo Browser; .NET CLR 2.0.50727; .NET CLR 3.0.4506. 2152; .NET CLR 3.5.30729; Avant Browser)', 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)', 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; GTB5; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5. 1; SV1) ; Maxthon; InfoPath.1; .NET CLR 3.5.30729; .NET CLR 3. 0.30618)', 'Mozilla/4.0 (compatible; Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; Acoo Browser 1.98.744; .NET CLR 3. 5.30729); Windows NT 5.1; Trident/4.0)', 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Acoo Browser; InfoPath.2; .NET CLR 2.0.50727; Alexa Toolbar)', ] def process_request(self,request,spider): # 随机产生一个user-agent user_agent = random.choice(self.USER_AGENTS) request.headers['User-Agent'] = user_agent
- 2.在
setting.py
中的设置#1.将DOWNLOADER_MIDDLEWARES设置成middlewares中自定义中间件 DOWNLOADER_MIDDLEWARES = { 'useragent_demo.middlewares.UserAgentDownloadMiddleware': 543, }
- 1.在
-
user-agent列表:http://www.useragentstring.com/pages/useragentstring.php?typ=Browser
ip代理池中间件
代理:
- 芝麻代理:http://http.zhimaruanjian.com
- 太阳代理:http://http.taiyangruanjian.com
- 快代理: http://www.kuaidaili.com
- 讯代理: http://www.xdaili.cn
- 蚂蚁代理: http://www.mayidaili.com
使用ip代理池:
- 示例代码如下:
- 在
middlewares.py
中设置:#1.引入random import random #设置ip中间件 class IPProxyDownloadMiddleware(object): #ip代理池中间件 #此处ip可通过代理获取 PROXIES= [ "115.223.127.186:8010", "121.237.148.153:3000", "202.115.142.147:9200", "117.88.177.76:3000", "117.88.177.162:3000", "121.237.149.113:3000", "117.88.176.2:3000", "117.88.176.119:3000", "117.88.4.151:3000", "117.88.5.227:3000" ] def process_request(self,request,spider): proxy = random.choice(self.PROXIES) # 因为获取的时候需要加上协议,若是https协议加上https,若是http,则加上http request.meta['proxy'] ='https://'+ proxy
- 在
setting.py
中设置:#1.将DOWNLOADER_MIDDLEWARES设置成middlewares中自定义ip中间件 DOWNLOADER_MIDDLEWARES = { 'useragent_demo.middlewares.IPProxyDownloadMiddleware': 100, }
- 在
- 使用独享代理:
- 在
middlewares.py
中设置:class IPProxyDownloadMiddleware(object): #ip代理池中间件 - 使用独享代理 def process_request(self,request,spider): proxy = ['独享代理ip及端口'] user_password = "用户名:密码" request.meta['proxy'] ='https://'+ proxy #bytes b64_user_password = base64.b64encode(user_password.encode('utf-8')) request.headers['Proxy-Authorization'] = 'Basic' + b64_user_password.decode('utf-8')
- 在
settings.py
中设置与代理池设置一致
- 在