视频教学网址:
https://www.bilibili.com/video/BV124411A7Ep

部分源代码都是我自己手打的已经上传到Github:
https://github.com/CocaineCong/Python_Spider_demo

这边是高级篇,基础篇在另一篇博客
https://blog.csdn.net/weixin_45304503/article/details/105581137

如果有什么问题欢迎指正,一起交流,一起学习

需要md文件的可以评论或是私信
Python爬虫Scrapy笔记_python

4)爬虫第四步Scrapy 框架

scrapy的效率特别高

框架原理

Python爬虫Scrapy笔记_Python_02

安装 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\2PPEQJOKYCVGUJRKM3LY9.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