视频教学网址:
https://www.bilibili.com/video/BV124411A7Ep
部分源代码都是我自己手打的已经上传到Github:
https://github.com/CocaineCong/Python_Spider_demo
这边是高级篇,基础篇在另一篇博客
https://blog.csdn.net/weixin_45304503/article/details/105581137
如果有什么问题欢迎指正,一起交流,一起学习。
需要md文件的可以评论或是私信
4)爬虫第四步Scrapy 框架
scrapy的效率特别高
框架原理
安装 scrapy 以及pypiwin32 (用轮子装,win系统下要安装pypiwin32)
如果实在ubuntu下,还需要安装第三方库
‘sudo apt-get install python-dev python-pip libxm12-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev’
创建爬虫:
1.创建项目:‘scrapy startproject [爬虫的名字]’ (记得进入pycharmprojects的页面来进行创建)
2.创建爬虫:进入到项目所在的路径,执行命令:‘scrapy genspider [爬虫的名字] [爬虫的域名]’ 放在spider上创建
项目的结构:
1.items.py :用来存放爬虫爬取下来的数据类型
2.middlewares.py: 用来存放各种中间件的文件
3.pipelines.py:用来将items的模型存储到本地磁盘中
4.settings.py: 本爬虫的一些配置信息(比如请求头、多久发送一次、ip代理池等等)
5.scrapy.cfg:项目的配置文件
6.spiders包:以后所有的爬虫,都是存放到这里面
糗事百科Scrapy爬虫笔记:
1.response是一个’scrapy.http.response.html.HtmlResponse’对象.可以执行’xpath’和’css’语法来提取数据。
2.提取出来的数据,是一个’Selector’或者是一个’SelectorList’对象。如果想要获取其中的字符串,那么应该执行’getall’或是’get’方法
3.getall方法:获取Selector中的所有文本,返回的是一个列表。get方法:是获取selector中的第一个文本,返回的是str类型.
4.如果数据解析回来,要传给pipline处理.那么可以使用’yield’来返.或是可以收集到所有的item,最后统一使用return返回.
5.item:建议在item.py中定义好模型.以后就不要使用字典了.在最后将数据变成字典就行了
6.pipeline:这个是专门用来保存数据地.其中三个方法是会经常用到的
*open_spider(self,spider):当爬虫被打开的时候就会被调用
*process_item(self,item,spider):当爬虫有item传过来的时候就会被调用
*close_spider(self,spider):当爬虫关闭的时候会被调用
要激活piplilne , 应该在settings.py中设置ITEM_PIPELINES将其激活.
7.JsonItemExporter和JsonLinesItemExporter:
1)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): # 爬虫开始
print("爬虫开始")
def process_item(self, item, spider): # item是爬虫返回的数据
self.exporter.export_item(item) # 用export_item来进行返回
return item
def close_spider(self,spider): #爬虫结束
self.exporter.finish_exporting()
self.fp.close()
print("爬虫结束了")
2)JsonLinesItemExporter:这个是每次调用export_item的时候就吧这个item存储到硬盘当中.坏处是每一个字典是一行,整个文件不是一个满足json格式的文件.好处就是每次处理数据的时候就直接存储到硬盘当中,这样不会消耗内存,数据也比较安全.
from scrapy.exporters import JsonLinesItemExporter # 导入json导出器,有lines, 一行一行的来,数据量多的话就用这个
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): # 爬虫开始
print("爬虫开始")
def process_item(self, item, spider): # item是爬虫返回的数据
self.exporter.export_item(item) # 用export_item来进行返回
return item
def close_spider(self,spider): #爬虫结束
# self.exporter.finish_exporting() 也不需要什么结束导入
self.fp.close()
print("爬虫结束了")
创建CrawlSpider爬虫:
scrapy genspider -c crawl [爬虫名字] [域名]
LinkExtractors连接提取器:
这个可以不用程序员自己提取想要的url,然后发送请求.这些工作可以交给LinkExtractors,他会在所有爬取的也面中找到满足规则的url,实现自动的爬取.
class scrapy.linkextractors.LinkExtractor(
allow=(), #允许的url 所有满足这个正则表达式的url都会被提取
deny=(), #禁止的urkl 所有满足这个正则表达式的url都不会被提取
allow_domains(), #允许的域名 只有在这个里面指定的域名的url才会被提取
deny_extensions=None,
restrict_xpaths=(), #严格的xpath和allow共同过滤链接
tahs=('a','area'),
attrs=('herf'),
unique=True,
process_value=None
)
Rule规则类:
class scrapy.spiders.Rule(
link_extractor,
callback=None, #满足这个规则的url就应该执行哪个回调函数.因为CrawlSpider使用了parse作为回调函数,因此不要副高parse作为回调函数自己的回调函数
cb_kwargs=None,
follow=None, # 是否需要跟进默认True
process_links=None, # 从link_extractor中获取到连接后会传递给这个函数,用来过滤不需要爬取的连接
process_request=None
)
CrawlSpider:
需要使用LinkExtractor和Rule这两个东西决定爬虫给的具体走向
1.allow设置规则的方法:要能够限制在我们想要的url上面,不要跟其他url产生相同的正则表达式即可。
2.什么情况下使用follow:如果在爬取页面的时候,需要将满足当前条件的url再进行跟进,那么就设置为True。否则设置为False.
3.什么情况下该指定callback:如果这个url对应的页面,只是为了获取更多的url,并不需要里面的数据,那么可以不指定callback。如果想要获取url对应的页面中的数据,那么就需要指定一个callback来进行数据的处理,储存等。
关于Scrapy Shell
再爬虫中使用xpath,beautifulsoup,正则表达式,css选择器等等,但是scrapy是一个比较重的框架,每次运行起来都要等待一段时间,因此要去验证我们写的提取规则是否正确,是一个麻烦的事情,因此scrapy提供了一个shell可以方便测试规则。
在cmd打开终端,进入到scrapy项目所在的目录,然后进入到scrapy框架所在的虚拟环境中,输入命令,scrapy shell 链接。就会进入到scrapy的shell环境中,你可以跟在爬虫中的parse方法一样使用了。
![img](file:///C:\Users\Administrator\AppData\Roaming\Tencent\Users\294350394\TIM\WinTemp\RichOle\2PPEQJOKYCVGUJRKM3L
Y9.png)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kp9vxKUZ-1589109719682)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200427175429280.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vgxSaAAt-1589109719686)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200427193820317.png)]
1.可以方便我们做一些数据提取的测试代码
2.如果想要执行scrapy命令,那么毫无疑问,肯定是要想进入scrapy所在的环境中,我自己电脑的python环境只有一个,是定义到全局的,所以就可以直接进行命令
3.如果想要读取某个项目的配置信息,那么应该先进入到这个项目中。再执行’scrapy shell’命令
Request和Response对象
1.Request对象
class Request(object_ref):
def __init__(self, url, callback=None, method='GET', headers=None, body=None,
cookies=None, meta=None, encoding='utf-8', priority=0,
dont_filter=False, errback=None, flags=None, cb_kwargs=None):
self._encoding = encoding # this one has to be set first
self.method = str(method).upper()
self._set_url(url)
self._set_body(body)
assert isinstance(priority, int), "Request priority not an integer: %r" % priority
self.priority = priority
if callback is not None and not callable(callback):
raise TypeError('callback must be a callable, got %s' % type(callback).__name__)
if errback is not None and not callable(errback):
raise TypeError('errback must be a callable, got %s' % type(errback).__name__)
self.callback = callback
self.errback = errback
self.cookies = cookies or {}
self.headers = Headers(headers or {}, encoding=encoding)
self.dont_filter = dont_filter
self._meta = dict(meta) if meta else None
self._cb_kwargs = dict(cb_kwargs) if cb_kwargs else None
self.flags = [] if flags is None else list(flags)
Request对象在我们写爬虫,爬取一页的数据需要重新发送一个请求的时候调用。这个类需要传递一些参数,其中比较常用的参数有:
1.url:这个request对象发送请求的url。
2.callback:在下载器下载完相应的数据后执行的回调函数。
3.method:请求的方法。默认为GET方法,可以设置为其他方法。
4.headers:请求头,对于一些固定的设置,放在settings.py中指定就可以了。对于那些非固定的,可以在发送请求的时候指定。
5.meta:比较常用。用于在不同的请求之间传递数据用的。
6.encoding:编码,默认的为utf-8,使用默认的就可以了
7.dot_filter:表示不由调度器过滤,在执行多次重复的请求的时候用得比较多。比如验证码。
8.errback:在发生错误的时候执行的函数
2.Response对象
Response对象一般是有scrapy给你自动构建的。因此开发者不需要关心如何创建response对象,而且如何使用他。response对象有很多属性,可以用来提取数据的。主要有一下属性
1.meta:从其他请求传过来的meta属性,可以用来保持多个请求之间的数据连接。
2.encoding:返回当前字符串编码和解码的格式
3.text:将返回的数据作为unicode字符串返回
4.body:将返回来的数据作为bytes字符串返回
5.xpath:xpath选择器
6.css:css选择器
发送POST请求:
如果需要post请求的话,就需要使用request的子类fromrequest来实现,如果想要在爬虫一开始的时候就发送给post请求的话,那么需要在爬虫类中重写start_requests(self)方法,并且不再调用start_urls里的url.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EtmvonGf-1589109719687)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200427205155524.png)]
3.模拟登陆人人网
1.想发送post请求,那么推荐使用scrapy.FormRequest方法。可以方便的指定表单数据
2.如果想在爬虫一开始的时候就发送post请求,那么应该重写start_requests方法,发送请求
4.模拟登陆豆瓣
下载图片和文件
scrapy为下载item中包含的文件,提供了一个可重用的item pipelines。这写pipeline有些共同的方法和结构,我们称之为media pipeline。一般使用files pipeline或是images pipeline
好处:
1.避免重复下载过的文件
2.可以方便指定下载文件的存储路径
3.可以将下载的图片转换成通用的格式,比如png或是jpg
4.可以方便的生成缩量图
5.可以方便的检测图片的宽和高,确保他们满足最小限制
6.异步下载,效率非常高
下载文件的Files Pipeline:
使用Files Pipeline下载文件的话,按照一下步骤来完成
1.定义好一个Item,然后再这个item中定义两个属性,分别是file_urls以及files。file_urls是用来存储需要下载的文件的url链接,需要给一个列表。
2.当文件下载完成后,会把文件下载的相关信息存储到item的files属性中。比如下载路径,下载的url和文件的效验码等。
3.再配置文件settings.py中配置FILES_STORE,这个配置是用来设置文件下载下来的路径
4.启动pipeline:再ITEM_PIPELINES中设置scrapy.pipelines.files.FilesPipeline:1
下载图片的Images Pipeline:
但是用Images Pipeline下载文件的时候,按照一下步骤来完成:
1.定义好一个Item,然后再这个item中定义两个属性,分别为image_urls以及images。images_urls
是用来储存需要下载的图片的url链接,需要给一个列表。
2.当文件下载完成后,会把文件下载的相关信息存储到item的images属性中。比如下载路径、下载的url和图片的校验码等
3.再配置文件settings.py中配置IMAGES_STORE,这个配置是用来设置图片下载下来的路径
4.启动pipeline:在ITEM_PIPELINES中设置scrapy.pipelines.images.ImagesPipeline:1
下载器中间件:
Downloader Middlewares:
下载器中间件是引擎和下载器之间通信的中间将。在这个中间件中。我们可以设置代理,更换请求头等来达到反发爬虫的目的。要写中间器,可以在下载器中实现两个方法。一个是process_request(self,request,spider),这个方法是在请求发送之前会执行的,还有一个是process_response(self,request,reponse,spider),这个方法是数据下载到引擎之前执行。
process_request(self,request,spider):
这个方法是下载器在发送请求之前会执行的。一般可以在这个里面设置一些随机代理ip
1.参数:
。request:发送请求request对象
。spider:发送请求的spider对象
2.返回值:
。返回None:如果返回None,Scrapy将继续处理该request,执行其他中间件中的相应方法,知道合适的下载器处理函数被调用。
。返回Response对象:Scrapy将不会调用任何其他的process_request方法,将直接返回这个response对象。已经激活的中间件process_reponse()方法则会在每个response返回时被调用。
。 返回Request对象:不再使用之前的request对象去下载数据,而是使用现在的request对象返回数据
。如果这个方法抛出异常,则会调用process_exception方法。
process_response(self,request,spider):
1.参数:
。request:request对象
。response: response对象
。spider:spider对象
2.返回值:
。返回Response对象:会将这个新的response对象传给其他中间件,最终传给爬虫
。 返回Request对象:下载器链被切断,返回的request会重新被下载器调度下载
。如果这个方法抛出异常,则会调用request的errback方法,如果没有指定这个方法,就会抛出一个异常
随机请求头:
如果一直都是用一个请求头,那么可能会被服务器抓到的。所以就要用到很多的请求头。
http://www.useragentstring.com/pages/useragentstring.php
这个网址上面的有许多请求头,而且都是比较新的!舒服
代理ip
建议就是芝麻代理。
5)爬虫第五步分布式爬虫
scrapy 是一个框架,但是本身就不支持分布式的,就是要加上scrapy-redis
Advantage:
1.可以充分利用多台机器的带宽
2.可以充分利用多台机器的ip地址
3.多台机器做,爬取效率高。
Problem:
1.分布式爬虫是好多台机器再同时运行,如何保证不同的机器爬取亚眠的时候不会出现重复爬取的问题
2.同样,分布式爬虫在不同的机器运行,在把数据爬完后如何保存在同一个地方.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NJPhoeuh-1589109719690)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200505183902415.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EJy4pAbh-1589109719691)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200505184027346.png)]
redis是一种支持分布式的nosql数据库,是非关系型数据库,同时的保存在内存当中,同时redis可以定时把内存数据同步到磁盘,既可以将数据持久化.比memcached支持更多的数据结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MsfDus9R-1589109719692)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20200505184707682.png)]
Redis
1.启动redis:
sudo service redis-server start
2.连接上redis-server
redus-cli -h [ip] -p [端口]
3.添加
set key value
eg:set username xiaotou
将字符串值value关联到key。如果key已经持有其他值,set命令就复写旧值,无视其类型。并且默认的时期时间是永久,即永远不会过期。
4.删除:
del key
eg:del username
5.设置过期时间:
expire key timeout (秒)
6.列表操作:
1.在列表的左边添加元素:
lpush key value
将值value插入到列表key的表头中,如果key不存在,一个空列表会被创建并执行lpush操作。当key存在当不是列表类型是,将返回一个错误.
2.在列表右边添加元素:
lpush key value
将值value插入到列表key的表尾。如果key不存在,一个空列表会被创建并执行rpush的操作。当key存在当不是列表类型是,返回一个错误。
3.查看列表中的元素
lrange key start stop
返回列表key中指定区间内的元素,区间以偏移量start和stop指定,如果要左边的第一个到最后一个lrange key 0-1 0是第一个。-1是最后一个
4.移除列表中的元素:
1)移除并返回列表key的头元素
lpop key
2)移除并返回列表的尾元素
rpop key
3)移除并返回列表的中的元素
lrem key count value
将删除key这个列表中,count个值为value
4)指定返回第几个元素
lindex key index
将返回key这个列表中索引index这个元素
5)获取列表中的元素个数
llen key
eg:llen langueage
6)删除指定元素
lrem key count value
eg: lrem languages 0 php
根据参数count的值,移除列表中与参数value相等的元素。count的值可以是一下几种
count>0:从表头开始向表尾搜索,移除与value相等的元素,数量是count
count<0:从表尾开始先表头搜索,移除与value相等的元素,数量为count的绝对值
count=0:移除表中所有与value相等的值
7.set集合操作
1.添加元素
sadd set value1 value2
eg: sadd team xiaotuo datuo
2.查看元素
smembers set
eg: smember set
3.移除元素
srem set member...
eg : srem team xiaotuo datuo
4.查看集合中的元素个数
scard set
eg:scard teaml
5.获取多个集合的交集
sinter set1 set2
eg: sinter team1 team2
6.获取多个集合的并集
sunion set1 set2
eg:sunion team1 team2
7.获取多个集合的差集
sdiff set1 set2
eg:sdiff team1 team2
8.hash哈希操作
redis中的字典。
1.添加新值
hset key field value
eg:hset website baidu baidu.com
2.获取哈希中的field对应的值
hget key field
eg:hget website baidu
3.删除field中的某个field
hdel key field
eg:hdel website baidu
4.获取某个哈希中所有的field和value
hgetall key
eg:hgetall website
5.获取某个哈希中所有的值
hvals key
eg:hvals website
6.判断哈希中是否存在某个field
hexists key field
eg:hexists website baidu
7.获取哈希中总共的键值对
hlen field
eg:hlen website