安装scrapy框架
- 安装
scrapy:pip install scrapy - windows下,还需要安装
pypiwin32,防止报错。pip install scrapy。报Visual C++ 14.0 is required错误的话,安装Visual C++ Build Tools,Visual C++ Build Tools 2015下载地址,下载链接在网页的中间位置
创建项目和爬虫
- 创建项目:
scapy startproject [项目名称] - 创建爬虫:命令行下,进入项目所在的路径,执行命令
scapy genspider [爬虫名字] [爬取的域名]。注意:爬虫的名字不能和项目名字一样。
项目目录结构
items.py:用来存放爬虫爬取下来数据的模型。middlewares.py:用来存放各种中间件的文件。pipelines.py:用来将items的模型存储到本地磁盘中。settings.py:本爬虫的一些配置信息(比如请求头、多久发送一次请求、ip代理池等)。scrapy.cfg:项目的配置文件。spiders包:以后所有的爬虫,都是存放到这个里面。
修改settings.py代码
在做一个爬虫之前,一定要记得修改setttings.py中的设置。两个地方是强烈建议设置的。
ROBOTSTXT_OBEY设置为False。默认是True。即遵守机器协议,那么在爬虫的时候,scrapy首先去找robots.txt文件,如果没有找到。则直接停止爬取。DEFAULT_REQUEST_HEADERS添加User-Agent。这个也是告诉服务器,我这个请求是一个正常的请求,不是一个爬虫。- 如果要激活
pipeline,应该在settings.py中,设置ITEM_PIPELINES。 - 设置延时时间,
DOWNLOAD_DELAY = 1
运行scrapy项目
运行scrapy项目。需要在终端,进入项目所在的路径,然后scrapy crawl [爬虫名字]即可运行指定的爬虫。如果不想每次都在命令行中运行,那么可以把这个命令写在一个文件中。以后就在pycharm中执行运行这个文件就可以了。比如现在新创建一个文件叫做start.py,然后在这个文件中填入以下代码:
1 | from scrapy import cmdline |
糗事百科Scrapy爬虫笔记
response是一个scrapy.http.response.html.HtmlResponse对象。可以执行xpath和css语法提取数据。- 提取出来的数据,是一个
Selector或者是一个SelectorList对象。如果想要获取其中的字符串。使用getall或者get方法。 getall方法:获取Selector中的所有文本。返回的是一个列表。get方法:获取的是Selector中的第一个文本。返回的是一个str类型。- 如果数据解析回来,要传给
pipeline处理。那么可以使用yield来返回。或者是收集所有的item。最后统一使用return返回。 item:建议在items.py中定义好模型。以后就不要使用字典。pipeline:这个是专门用来保存数据的。其中有三个方法是会经常用的。open_spider(self,spider):当爬虫被打开的时候执行process_item(self,item,spider):当爬虫有item传过来的时候会被调用close_spider(self,spider):当爬虫关闭的时候会被调用
要激活pipeline,应该在settings.py中,设置ITEM_PIPELINES。示例如下:
1 | ITEM_PIPELINES = { |
JsonItemExporter和JsonLinesItemExporter:
保存json数据的时候,可以使用这两个类,让操作变得更简单。
JsonItemExport
每次把数据添加到内存中。最后统一写到磁盘中。
- 优点:存储的数据是一个满足json规则的数据
- 缺点:如果数据量比较大,比较耗内存
1 | from scrapy.exporters import JsonItemExporter |
JsonLinesItemExporter
每次调用export_item的时候把这个item存在到磁盘中
- 优点:每次处理数据的时候,直接存储到磁盘中,不耗内存。数据也比较安全。
- 缺点:每一个字典是一行,整个文件不是一个满足
json格式的文件。
1 | from scrapy.exporters import JsonLinesItemExporter |
CrawlSpider
可以实现,只要满足某个条件的url,都进行爬取。CrawlSpider继承自Spider,只不过是在之前的基础之上增加了新的功能,可以定义爬取的url的规则,以后scrapy碰到满足条件的url都进行爬取,而不用手动的yield Request。
创建CrawlSpider爬虫
之前创建爬虫的方式是通过scrapy genspider [爬虫名字] [域名]的方式创建的。如果想要创建CrawlSpider爬虫,那么应该通过以下命令创建:
1 | scrapy genspider -t crawl [爬虫名字] [域名] |
LinkExtractors链接提取器
使用LinkExtractors可以不用程序员自己提取想要的url,然后发送请求。这些工作都可以交给LinkExtractors,他会在所有爬的页面中找到满足规则的url,实现自动的爬取。以下对LinkExtractors类做一个简单的介绍:
1 | class scrapy.linkextractors.LinkExtractor( |
主要参数讲解:
allow:允许的url。所有满足这个正则表达式的url都会被提取。deny:禁止的url。所有满足这个正则表达式的url都不会被提取。allow_domains:允许的域名。只有在这个里面指定的域名的url才会被提取。deny_domains:禁止的域名。所有在这个里面指定的域名的url都不会被提取。restrict_xpaths:严格的xpath。和allow共同过滤链接。
Rule规则类
1 | class scrapy.spiders.Rule( |
主要参数讲解:
link_extractor:一个LinkExtractor对象,用于定义爬取规则。callback:满足这个规则的url,应该要执行哪个回调函数。因为CrawlSpider使用了parse作为回调函数,因此不要覆盖parse作为回调函数自己的回调函数。follow:指定根据该规则从response中提取的链接是否需要跟进。process_links:从link_extractor中获取到链接后会传递给这个函数,用来过滤不需要爬取的链接。
使用说明
LinkExtractor和Rule决定爬虫的具体走向。
allow设置规则的方法:能够限制在我们想要的url上,不与其它的url相同的正则表达式即可。follow使用场景:如果在爬取页面的时候,需要将满足当前条件的url再进行跟进,那么就设置为True。否则设置为False。callback使用场景:如果这个url对应的页面,只是为了获取更多的url,并不需要里面的数据,可以不指定callback。如果想获取url对应页面中的数据,那么久需要制定一个callback。
1 | from scrapy.linkextractors import LinkExtractor |
Scrapy Shell
我们想要在爬虫中使用xpath、beautifulsoup、正则表达式、css选择器等来提取想要的数据。但是因为scrapy是一个比较重的框架。每次运行起来都要等待一段时间。因此要去验证我们写的提取规则是否正确,是一个比较麻烦的事情。因此scrapy提供了一个shell,用来方便的测试规则。当然也不仅仅局限于这一个功能。
打开Scrapy Shell
- 打开
cmd终端,进入到scrapy框架所在的虚拟环境中,输入命令scrapy shell [链接]。就会进入到scrapy的shell环境中。在这个环境中,你可以跟在爬虫的parse方法中一样使用了 - 如果想读取某个项目的配置信息,应先进入到这个项目目录,再执行
scrapy shell命令
Request和Response对象
Request对象
Request对象在我们写爬虫,爬取一页的数据需要重新发送一个请求的时候调用。这个类需要传递一些参数,其中比较常用的参数有:
url:这个request对象发送请求的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:将返回的数据作为bytes字符串返回xpath:xpath选择器css:css选择器
发送POST请求
有时候想要在请求数据的时候发送post请求,那么这时候需要使用Request的子类FormRequest来实现。如果想要在爬虫一开始的时候就发送POST请求,需要在爬虫类中重写start_request(self)方法,并且不再调用start_urls里的url。
下载文件和图片
Scrapy为下载item中包含的文件(比如图片)提供了一个可重用的item pipelines。这些pipeline有些共同的方法和结构(我们称之为media pipeline)。一般来说会使用Files Pipeline或Images Pipeline。
scrapy内置下载文件方法的优点
- 避免重新下载最近已经下载过的数据
- 可以方便的指定文件存储的路径
- 可以将下载的图片转换成通用的格式。比如
png或jpg。 - 可以方便的生成缩略图
- 可以方便的检测图片的宽和高,确保他们满足最小限制
- 异步下载,效率非常高
下载文件的Files Pipline
当使用Files Pipline下载文件的时候,按照以下步骤来完成:
- 定义好一个
Item,然后在这个Item中定义两个属性,分别为file_urls以及files。file_urls是用来存储需要下载的图片的url链接,需要给一个列表 - 当文件下载完成后,会把文件下载的相关信息存储到
item的files属性中。比如下载路径、下载的url和文件的校验码等 - 在配置文件
settings.py中配置FILES_STORE,这个配置是用来设置文件下载下来的路径 - 启动
pipeline:在ITEM_PIPELINES中设置scrapy.pipeline.files.FilesPipeline: 1
下载图片的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
Downloader Middlewares(下载器中间件)
下载器中间件是引擎和下载器之间通信的中间件。在这个中间件中可以设置代理、更换请求头等来达到反反爬虫的目的。要写下载器中间件,可以在下载器中实现两个方法。一个是process_request(self, request, spider),这个方法是在请求发送之前会执行,还有一个是process_response(self, request, response, spider),这个方法是数据下载到引擎之前执行。
process_request(self, request, spider):
这个方法是下载器在发送请求之前会执行的。一般可以在这个里面设置随机代理ip等。
- 参数:
request:发送请求的request对象spider:发送请求的spider对象
- 返回值:
- 返回
None:如果返回None,Scrapy将继续处理该request,执行其它中间件中的相应方法,知道合适的下载器处理函数被调用, - 返回
Response对象:Scrapy将不会调用任何其它的process_request方法,将直接返回这个response对象。已经激活的中间件的process_response()方法则会在每个response返回时调用。(有疑问) - 返回
Request对象:不在使用之前的request对象去下载数据,而是根据现在返回的request对象返回数据。 - 如果这个方法中抛出了异常,则会调用
process_exception方法
process_response(self, request, response, spider)
这个是下载器下载的数据到引擎中间会执行的方法
- 参数:
- request:request对象
- response:被处理的response对象
- spider:spider对象
- 返回值:
- 返回
Response对象:会将这个新的response对象传给其它中间件,最终传给爬虫 - 返回
Request对象:下载器链被切断,返回的request会重新被下载器调度下载 - 如果抛出一个异常,那么调用
request的errback方法,如果没有指定这个方法,那么会抛出一个异常。
随机请求头中间件
爬虫在频繁访问一个页面的时候,这个请求头如果一直保持一直。那么很容易被服务器发现,从而禁止这个请求头的访问。因此我们要在访问这个页面之前随机的更改请求头,这样才可以避免爬虫被抓。随机更改请求头,可以在下载器中间件中实现。在请求发送给服务器之前,随机的选择一个请求头。示例代码如下:
1 | import random |
ip代理池中间件
购买代理
芝麻地理,太阳代理等
使用ip代理池
1 | # 中间件 |
使用独享代理的话:
1 | # 中间件 |
redis
redis是一种支持分布式的nosql数据库,他的数据是保存在内存中,同时redis可以定时把内存数据同步到磁盘,即可以将数据持久化,并且他比memcached支持更多的数据结构(string,list列表[队列和栈],set[集合],sorted set[有序集合],hash(hash表))。相关参考文档:Redis 命令参考
redis使用场景:
- 登录会话存储:存储在
redis中,与memcached相比,数据不会丢失。 - 排行版/计数器:比如一些秀场类的项目,经常会有一些前多少名的主播排名。还有一些文章阅读量的技术,或者新浪微博的点赞数等。
- 作为消息队列:比如
celery就是使用redis作为中间人。 - 当前在线人数:还是之前的秀场例子,会显示当前系统有多少在线人数。
- 一些常用的数据缓存:比如我们的BBS论坛,板块不会经常变化的,但是每次访问首页都要从
mysql中获取,可以在redis中缓存起来,不用每次请求数据库。 - 把前200篇文章缓存或者评论缓存:一般用户浏览网站,只会浏览前面一部分文章或者评论,那么可以把前面200篇文章和对应的评论缓存起来。用户访问超过的,就访问数据库,并且以后文章超过200篇,则把之前的文章删除。
- 好友关系:微博的好友关系使用redis实现。
- 发布和订阅功能:可以用来做聊天软件。
redis和memcached的比较
| memecached | redis | |
|---|---|---|
| 类型 | 纯内存数据库 | 内存磁盘同步数据库 |
| 数据类型 | 在定义value时就要固定数据类型 | 不需要 |
| 虚拟内存 | 不支持 | 支持 |
| 过期策略 | 支持 | 支持 |
| 存储数据安全 | 不支持 | 可以将数据同步到dump.db中 |
| 灾难恢复 | 不支持 | 可以将磁盘中的数据恢复到内存中 |
| 分布式 | 支持 | 主从同步 |
| 订阅与发布 | 不支持 | 支持 |
其它机器访问本机redis服务器
想要让其他机器访问本机的redis服务器。那么要修改redis.conf的配置文件,将bind改成bind [自己的ip地址或者0.0.0.0],其他机器才能访问。
注意:bind绑定的是本机网卡的ip地址,而不是想让其他机器连接的ip地址。如果有多块网卡,那么可以绑定多个网卡的ip地址。如果绑定到额是0.0.0.0,那么意味着其他机器可以通过本机所有的ip地址进行访问。
对redis的操作
对redis的操作可以用两种方式,第一种方式采用redis-cli,第二种方式采用编程语言,比如Python、PHP和JAVA等。下面使用redis-cli对redis进行操作。
字符串操作:
启动redis:
1 | sudo service redis-server start |
连接上redis-server:
1 | redis-cli -h [ip] -p [端口] |
添加:set key value。将字符串值value关联到key。如果key已经持有其他值,set命令就覆写旧值,无视其类型。并且默认的过期时间是永久,即永远不会过期。
1 | set username xiaotuo |
删除:del key
1 | del username |
设置过期时间:
1 | expire key timeout(单位为秒) |
也可以在设置值的时候,一同指定过期时间:
1 | set key value EX timeout |
查看过期时间:ttl key
1 | ttl username |
查看当前redis中的所有key:
1 | keys * |
列表操作
在列表左边添加元素:将值value插入到列表key的表头。如果key不存在,一个空列表会被创建并执行lpush操作。当key存在但不是列表类型时,将返回一个错误。
1 | lpush key value |
在列表右边添加元素:将值value插入到列表key的表尾。如果key不存在,一个空列表会被创建并执行rpush操作。当key存在但不是列表类型时,返回一个错误。
1 | rpush key value |
查看列表中的元素:返回列表key中指定区间内的元素,区间以偏移量start和stop指定,如果要左边的第一个到最后的一个lrange key 0 -1。
1 | lrange key start stop |
移除列表中的元素:
移除并返回列表key的头元素:
1 | lpop key |
移除并返回列表的尾元素:
1 | rpop key |
移除并返回列表key的中间元素:将删除key这个列表中,值为value的元素,count为指定个数
1 | lrem key count value |
指定返回第几个元素:将返回key这个列表中,索引为index的这个元素。
1 | lindex key index |
获取列表中的元素个数:llen key
1 | llen languages |
删除指定的元素:lrem key count value
1 | lrem languages 0 php |
根据参数count的值,移除列表中与参数value相等的元素。count的值可以是以下几种:
- count > 0:从表头开始向表尾搜索,移除与
value相等的元素,数量为count。 - count < 0:从表尾开始向表头搜索,移除与
value相等的元素,数量为count的绝对值。 - count = 0:移除表中所有与
value相等的值。
set集合的操作
添加元素:sadd set value1 value2....
1 | sadd team xiaotuo datuo |
查看元素:smembers set
1 | smembers team |
移除元素:srem set member...
1 | srem team xiaotuo datuo |
查看集合中的元素个数:scard set
1 | scard team1 |
获取多个集合的交集:sinter set1 set2
1 | sinter team1 team2 |
获取多个集合的并集:sunion set1 set2
1 | sunion team1 team2 |
获取多个集合的差集:sdiff set1 set2
1 | sdiff team1 team2 |
hash哈希操作
添加一个新值:hset key field value。将哈希表key中的域field的值设为value。如果key不存在,一个新的哈希表被创建并进行hset操作。如果域field已经存在于哈希表中,旧值将被覆盖。
1 | hset website baidu baidu.com |
获取哈希中的field对应的值:hget key field
1 | hget website baidu |
删除field中的某个field:hdel key field
1 | hdel website baidu |
获取某个哈希中所有的field和value:hgetall key
1 | hgetall website |
获取某个哈希中所有的field:hkeys key
1 | hkeys website |
获取某个哈希中所有的值:hvals key
1 | hvals website |
判断哈希中是否存在某个field:hexists key field
1 | hexists website baidu |
获取哈希中键值对的数量:hlen field
1 | hlen website |
其它知识点用到再补充
Scrapy-Redis分布式爬虫组件
Scrapy是一个框架,他本身是不支持分布式的。如果我们想要做分布式的爬虫,就需要借助一个组件叫做Scrapy-Redis,这个组件正是利用了Redis可以分布式的功能,集成到Scrapy框架中,使得爬虫可以进行分布式。可以充分的利用资源(多个ip、更多带宽、同步爬取)来提高爬虫的爬行效率。
分布式爬虫的优点
- 可以充分利用多台机器的带宽。
- 可以充分利用多台机器的ip地址。
- 多台机器做,爬取效率更高。
分布式爬虫必须要解决的问题
- 分布式爬虫是好几台机器在同时运行,如何保证不同的机器爬取页面的时候不会出现重复爬取的问题。
- 同样,分布式爬虫在不同的机器上运行,在把数据爬完后如何保证保存在同一个地方。
安装
通过pip install scrapy-redis即可安装。
Scrapy-Redis架构
这块少图,有空了找个高清的。
Item Pipeline在接收到数据后发送给了Redis、Scheduler调度器调度数据也是从Redis中来的、并且其实数据去重也是在Redis中做的。
编写Scrapy-Redis分布式爬虫
要将一个Scrapy项目变成一个Scrapy-redis项目只需修改以下三点就可以了:
- 将爬虫的类从
scrapy.Spider变成scrapy_redis.spiders.RedisSpider;或者是从scrapy.CrawlSpider变成scrapy_redis.spiders.RedisCrawlSpider。 - 将爬虫中的
start_urls删掉。增加一个redis_key="xxx"。这个redis_key是为了以后在redis中控制爬虫启动的。爬虫的第一个url,就是在redis中通过这个发送出去的。 - 在配置文件中增加如下配置:
1 | # Scrapy-Redis相关配置 |
运行爬虫
- 在爬虫服务器上。进入爬虫文件所在的路径,然后输入命令:
scrapy runspider [爬虫名字]。 - 在
Redis服务器上,推入一个开始的url链接:redis-cli> lpush [redis_key] start_url开始爬取。