赞
踩
目录
Scrapy架构图(Scrapy 使用了 Twisted异步网络库来处理网络通讯):
Downloader Middlewares (下载器中间件)
process_request(self, request, spider)
process_response(self, request, response, spider)
写一个爬虫,需要做很多的事情。比如:发送网络请求、数据解析、数据存储、反反爬虫机制(更换ip代理、设置请求头等)、异步请求等。这些工作如果每次都要自己从零开始写的话,比较浪费时间。因此Scrapy
把一些基础的东西封装好了,在他上面写爬虫可以变的更加的高效(爬取效率和开发效率)。因此真正在公司里,一些上了量的爬虫,都是使用Scrapy
框架来解决。
流程图(1):
流程图(2):
pip install scrapy
即可安装。注意:
- 在
ubuntu
上安装scrapy
之前,需要先安装以下依赖:sudo apt-get install python3-dev build-essential python3-pip libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev
,然后再通过pip install scrapy
安装。- 如果在
windows
系统下,提示这个错误ModuleNotFoundError: No module named 'win32api'
,那么使用以下命令可以解决:pip install pypiwin32
。
要使用Scrapy
框架创建项目,需要通过命令来创建。首先进入到你想把这个项目存放的目录。然后使用以下命令创建:
scrapy startproject [项目名称]
以下介绍下主要文件的作用:
items
的模型存储到本地磁盘中。使用命令创建一个爬虫:
scrapy genspider qsbk "qiushibaike.com"
创建了一个名字叫做qsbk
的爬虫,并且能爬取的网页只会限制在qiushibaike.com
这个域名下。
爬虫代码解析:
- import scrapy
-
- class QsbkSpider(scrapy.Spider):
- name = 'qsbk'
- allowed_domains = ['qiushibaike.com']
- start_urls = ['http://qiushibaike.com/']
-
- def parse(self, response):
- pass
其实这些代码我们完全可以自己手动去写,而不用命令。只不过是不用命令,自己写这些代码比较麻烦。
要创建一个Spider,那么必须自定义一个类,继承自scrapy.Spider
,然后在这个类中定义三个属性和一个方法。
parse
方法。这个是个固定的写法。这个方法的作用有两个,第一个是提取想要的数据。第二个是生成下一个请求的url。修改settings.py
代码:
在做一个爬虫之前,一定要记得修改setttings.py
中的设置。两个地方是强烈建议设置的。
ROBOTSTXT_OBEY
设置为False。默认是True。即遵守机器协议,那么在爬虫的时候,scrapy首先去找robots.txt文件,如果没有找到。则直接停止爬取。DEFAULT_REQUEST_HEADERS
添加User-Agent
。这个也是告诉服务器,我这个请求是一个正常的请求,不是一个爬虫。完成的爬虫代码:
爬虫部分代码:
- import scrapy
- from abcspider.items import QsbkItem
-
- class QsbkSpider(scrapy.Spider):
- name = 'qsbk'
- allowed_domains = ['qiushibaike.com']
- start_urls = ['https://www.qiushibaike.com/text/']
-
- def parse(self, response):
- outerbox = response.xpath("//div[@id='content-left']/div")
- items = []
- for box in outerbox:
- author = box.xpath(".//div[contains(@class,'author')]//h2/text()").extract_first().strip()
- content = box.xpath(".//div[@class='content']/span/text()").extract_first().strip()
- item = QsbkItem()
- item["author"] = author
- item["content"] = content
- items.append(item)
- return items
items.py部分代码:
- import scrapy
- class QsbkItem(scrapy.Item):
- author = scrapy.Field()
- content = scrapy.Field()
pipeline部分代码:
- import json
-
- class AbcspiderPipeline(object):
- def __init__(self):
-
- self.items = []
-
- def process_item(self, item, spider):
- self.items.append(dict(item))
- print("="*40)
- return item
-
- def close_spider(self,spider):
- with open('qsbk.json','w',encoding='utf-8') as fp:
- json.dump(self.items,fp,ensure_ascii=False)
settings.py中要把 ITEM_PIPELINES 打开
'qsbk.pipelines.QsbkPipeline': 300, #300代表pipeline的优先级,值越小,优先级越高
运行scrapy项目:
运行scrapy项目。需要在终端,进入项目所在的路径,然后scrapy crawl [爬虫名字]
即可运行指定的爬虫。如果不想每次都在命令行中运行,那么可以把这个命令写在一个文件中。以后就在pycharm中执行运行这个文件就可以了。比如现在工程的根目录新创建一个文件叫做start.py
,然后在这个文件中填入以下代码:
- from scrapy import cmdline
-
- cmdline.execute("scrapy crawl qsbk".split())
一、创建项目和爬虫:
二、糗事百科Scrapy爬虫笔记:
*`open_spider(self,spider)`:当爬虫被打开的时候执行。
*`process_item(self,item,spider)`:当爬虫有item传过来的时候会被调用,一般用来保存数据。
*`close_spider(self,spider)`:当爬虫关闭的时候会被调用。
要激活piplilne,应该在`settings.py`中,设置`ITEM_PIPELINES`示例如下:
- ITEM_PIPELINES = {
- 'qsbk.pipelines.QsbkPipeline': 300,
- }
在上一个糗事百科的爬虫案例中。我们是自己在解析完整个页面后获取下一页的url,然后重新发送一个请求。有时候我们想要这样做,只要满足某个条件的url,都给我进行爬取。那么这时候我们就可以通过CrawlSpider
来帮我们完成了。CrawlSpider
继承自Spider
,只不过是在之前的基础之上增加了新的功能,可以定义爬取的url的规则,以后scrapy碰到满足条件的url都进行爬取,而不用手动的yield Request
。
之前创建爬虫的方式是通过scrapy genspider [爬虫名字] [域名]
的方式创建的。如果想要创建CrawlSpider
爬虫,那么应该通过以下命令创建:
scrapy genspider -t crawl [爬虫名字] [域名]
使用LinkExtractors
可以不用程序员自己提取想要的url,然后发送请求。这些工作都可以交给LinkExtractors
,他会在所有爬的页面中找到满足规则的url
,实现自动的爬取。以下对LinkExtractors
类做一个简单的介绍:
- class scrapy.linkextractors.LinkExtractor(
- allow = (),
- deny = (),
- allow_domains = (),
- deny_domains = (),
- deny_extensions = None,
- restrict_xpaths = (),
- tags = ('a','area'),
- attrs = ('href'),
- canonicalize = True,
- unique = True,
- process_value = None
- )
主要参数讲解:
定义爬虫的规则类。以下对这个类做一个简单的介绍:
- class scrapy.spiders.Rule(
- link_extractor,
- callback = None,
- cb_kwargs = None,
- follow = None,
- process_links = None,
- process_request = None
- )
主要参数讲解:
LinkExtractor
对象,用于定义爬取规则。CrawlSpider
使用了parse
作为回调函数,因此不要覆盖parse
作为回调函数自己的回调函数。 需要使用`LinkExtractor
`和`Rule`。这两个东西决定爬虫的具体走向。
我们想要在爬虫中使用xpath、beautifulsoup、正则表达式、css选择器等来提取想要的数据。但是因为scrapy
是一个比较重的框架。每次运行起来都要等待一段时间。因此要去验证我们写的提取规则是否正确,是一个比较麻烦的事情。因此Scrapy
提供了一个shell,用来方便的测试规则。当然也不仅仅局限于这一个功能。
打开终端,进入到Scrapy
项目所在的目录,然后进入到scrapy
框架所在的虚拟环境中,输入命令scrapy shell [链接]
。就会进入到scrapy的shell环境中。在这个环境中,你可以跟在爬虫的parse
方法中一样使用了。
Request对象在我们写爬虫,爬取一页的数据需要重新发送一个请求的时候调用。这个类需要传递一些参数,其中比较常用的参数有:
Response对象一般是由Scrapy给你自动构建的。因些开发者不需要关心如何创建Response对象,而是如何使用他。Response对象有
很多属性,可以用来提取数据的。主要有以下属性:
有时候我们想在在请求数据的时候发送post请求,那么这时候需要使用Request的子类FormRequest来实现。如果想要在爬虫一开始
的时候就发送POST请求,那么需要在爬虫类中重写start_requests(delf)方法,并且不再调用 start_urls里的url。
1、想要发送post请求,那么推荐使用`scrpay.FormRequest`方法。可以方便的指定表单数据。
2、如果想在爬虫一开始的时候就发送post请求,那么应该重写`start_requests`方法。在这个方法中,发送post请求。
待完善
Scrapy为下载item中包含的文件(比如在攫取到产品时,同时也想保存对应的图片)提供了一个可重用的item pipelinses.
这些pipeline有些共同的方法和结构(我们称之为media pipeline)。一般来说你会使用Files Pipeline或者Images Pipelines。
当使用Files Pipeline 下载文件的时候,按照以下步骤来完成:
当使用Images Pipeline下载图片的时候,按照以下步骤来完成:
下载器中间件是引擎和下载器之间通信的中间件。在这个中间件中我们可以设置代理、更换请求头等来达到反反爬虫的目的,要写下载器中间件,可以在下载器中实现两个方法。一个是process_request(self, request, spider),这个方法会在请求发送之前执行,还有一个是process_response(self, request, response, spider),这个方法是数据下载到引擎之间执行。
这个方法是下载器在发送请求之前会执行的。一般可以在这里面设置随机代理ip等。
1、参数:
。request:发送请求的request对象。
。 spider:发送请求的spider对象。
2、返回值:
。 返回None:如果返回None,Scrapy将继续处理该request,执行其它 中间件中的相应方法,直到合适的下载器处理函数被调用。
。返回Response对象:Scrapy将不会调用任何其他的process_request方法,将直接返回response对象,已经激活中间件的process_response()方法则会在每个response返回时被调用。
。返回Request对象:不再使用之前的request对象去下载数据,而是根据现在返回的request对象返回数据。
。如果这个方法中抛出了异常,则会调用process_exception方法。
这个方法是下载器在发送请求之前会执行的。一般可以在这里面设置随机代理ip等。
1、参数:
。request:request对象。
。 response:被处理的response对象。
。spider:spider对象。
2、返回值:
。 返回Response:会将这个新的response对象传给其他中间件,最终传给爬虫。
。返回Response对象:下载器链接切断,返回response会重新被下载器调度下载。
。如果抛出一个异常,那么调用response的errback方法,如果没有指定这个方法,那么会抛出一个异常。
爬虫在频繁访问一个页面的时候,这个请求头如果一直保持一致。那么很容易被服务器发现,从面禁止掉这个请求头的访问,因此我们要在访问这个页面之前随机的更改请求头,这样才可以避免爬虫被抓。随机更改请求头,可以在下载器中间件中实现,在请求发送给服务器之前,随机选择一个请求头。这样就可以避免总傅一个请求头了。示例代码如下:
- class UserAgentDownloadMiddleware(object):
- USER_AGENTS = [
- 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36',
- 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; Browzar)',
- 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:64.0) Gecko/20100101 Firefox/64.0',
- 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; MyIE2; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0)'
- ]
-
- def process_request(self, request, spider):
- user_agent = random.choice(self.USER_AGENTS)
- request.headers['User-Agent'] = user_agent
user-agent列表网址:http://useragentstring.com/pages/useragentstring.php?typ=Browser
购买代理:
在以下代理商中购买代理:
等购买代理。
使用ip代理池:
示例代码如下:
- # 开放代理池设置
- # class IPProxyDownloadMiddleware(object):
- # PROXIES = [
- # '171.41.81.164:9999',
- # '171.41.81.253:9999',
- # '171.41.81.96:9999',
- # '171.41.81.93:9999',
- # '112.85.171.65:9999',
- # '117.114.149.66:53281'
- # '106.12.217.43:8118'
- #
- # ]
- #
- # def process_request(self, request, spider):
- # proxy = random.choice(self.PROXIES)
- # request.meta['proxy'] = "http://" + proxy
-
- # 独享代理池设置
- class IPProxyDownloadMiddleware(object):
-
- def process_request(self, request, spider):
- proxy = '121.199.6.124:16816'
- user_password = "970138074:rcdj35ur"
- request.meta['proxy'] = "http://" + proxy
- # bytes
- b64_user_password = base64.b64encode(user_password.encode('utf-8'))
- request.headers['Proxy-Authorization'] = 'Basic' + b64_user_password.decode('utf-8')
Boss直聘有很高的反爬虫机制,只要用同一个ip访问多个职位列表页,就会被封掉ip。采用代理ip方式可以解决问题。
redis
是一种支持分布式的nosql
数据库,他的数据是保存在内存中,同时redis
可以定时把内存数据同步到磁盘,即可以将数据持久化,并且他比memcached
支持更多的数据结构(string
,list列表[队列和栈]
,set[集合]
,sorted set[有序集合]
,hash(hash表)
)。相关参考文档:http://redisdoc.com/index.html
redis
中,与memcached
相比,数据不会丢失。celery
就是使用redis
作为中间人。BBS
论坛,板块不会经常变化的,但是每次访问首页都要从mysql
中获取,可以在redis
中缓存起来,不用每次请求数据库。redis
实现。redis
和memcached
的比较:memcached | redis | |
---|---|---|
类型 | 纯内存数据库 | 内存磁盘同步数据库 |
数据类型 | 在定义value时就要固定数据类型 | 不需要 |
虚拟内存 | 不支持 | 支持 |
过期策略 | 支持 | 支持 |
存储数据安全 | 不支持 | 可以将数据同步到dump.db中 |
灾难恢复 | 不支持 | 可以将磁盘中的数据恢复到内存中 |
分布式 | 支持 | 主从同步 |
订阅与发布 | 不支持 | 支持 |
redis
在ubuntu
系统中的安装与启动 sudo apt-get install redis-server
sudo apt-get purge --auto-remove redis-server
启动:redis
安装后,默认会自动启动,可以通过以下命令查看:
ps aux|grep redis
如果想自己手动启动,可以通过以下命令进行启动:
sudo service redis-server start
停止:
sudo service redis-server stop
redis
安装所在的路径然后执行redis-server.exe redis.windows.conf
就可以运行了。redis
和mysql
以及mongo
是一样的,都提供了一个客户端进行连接。输入命令redis-cli
(前提是redis安装路径已经加入到环境变量中了)就可以连接到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
:
sudo service redis-server start
redis-server
: redis-cli -h [ip] -p [端口]
添加:
- set key value
- 如:
- set username xiaotuo
将字符串值value
关联到key
。如果key
已经持有其他值,set
命令就覆写旧值,无视其类型。并且默认的过期时间是永久,即永远不会过期。
删除:
- del key
- 如:
- del username
设置过期时间:
expire key timeout(单位为秒)
也可以在设置值的时候,一同指定过期时间:
- set key value EX timeout
- 或:
- setex key timeout value
查看过期时间:
- ttl key
- 如:
- ttl username
查看当前redis
中的所有key
:
keys *
列表操作:
在列表左边添加元素:
lpush key value
将值value
插入到列表key
的表头。如果key
不存在,一个空列表会被创建并执行lpush
操作。当key
存在但不是列表类型时,将返回一个错误。
在列表右边添加元素:
rpush key value
将值value插入到列表key的表尾。如果key不存在,一个空列表会被创建并执行RPUSH操作。当key存在但不是列表类型时,返回一个错误。
查看列表中的元素:
lrange key start stop
返回列表key
中指定区间内的元素,区间以偏移量start
和stop
指定,如果要左边的第一个到最后的一个lrange key 0 -1
。
移除列表中的元素:
key
的头元素: lpop key
rpop key
移除并返回列表key
的中间元素:
lrem key count value
将删除key
这个列表中,count
个值为value
的元素。
指定返回第几个元素:
lindex key index
将返回key
这个列表中,索引为index
的这个元素。
获取列表中的元素个数:
- llen key
- 如:
- llen languages
删除指定的元素:
- lrem key count value
- 如:
- lrem languages 0 php
根据参数 count 的值,移除列表中与参数 value 相等的元素。count
的值可以是以下几种:
value
相等的元素,数量为count
。value
相等的元素,数量为count
的绝对值。value
相等的值。set
集合的操作:
- sadd set value1 value2....
- 如:
- sadd team xiaotuo datuo
- smembers set
- 如:
- smembers team
- srem set member...
- 如:
- srem team xiaotuo datuo
- scard set
- 如:
- scard team1
- sinter set1 set2
- 如:
- sinter team1 team2
- sunion set1 set2
- 如:
- sunion team1 team2
- sdiff set1 set2
- 如:
- sdiff team1 team2
hash
哈希操作:
添加一个新值:
- hset key field value
- 如:
- hset website baidu baidu.com
将哈希表key
中的域field
的值设为value
。
如果key
不存在,一个新的哈希表被创建并进行 HSET
操作。如果域 field
已经存在于哈希表中,旧值将被覆盖。
获取哈希中的field
对应的值:
- hget key field
- 如:
- hget website baidu
删除field
中的某个field
:
- hdel key field
- 如:
- hdel website baidu
获取某个哈希中所有的field
和value
:
- hgetall key
- 如:
- hgetall website
获取某个哈希中所有的field
:
- hkeys key
- 如:
- hkeys website
获取某个哈希中所有的值:
- hvals key
- 如:
- hvals website
判断哈希中是否存在某个field
:
- hexists key field
- 如:
- hexists website baidu
获取哈希中总共的键值对:
- hlen field
- 如:
- hlen website
事务操作:Redis事务可以一次执行多个命令,事务具有以下特征:
开启一个事务:
multi
以后执行的所有命令,都在这个事务中执行的。
执行事务:
exec
会将在multi
和exec
中的操作一并提交。
取消事务:
discard
会将multi
后的所有命令取消。
监视一个或者多个key
:
watch key...
监视一个(或多个)key,如果在事务执行之前这个(或这些) key被其他命令所改动,那么事务将被打断。
取消所有key
的监视:
unwatch
发布/订阅操作:
publish channel message
subscribe channel
Scrapy
是一个框架,他本身是不支持分布式的。如果我们想要做分布式的爬虫,就需要借助一个组件叫做Scrapy-Redis
,这个组件正是利用了Redis
可以分布式的功能,集成到Scrapy
框架中,使得爬虫可以进行分布式。可以充分的利用资源(多个ip、更多带宽、同步爬取)来提高爬虫的爬行效率。
通过pip install scrapy-redis
即可安装。
Scrapy架构图:
Scrapy-Redis架构图:
分布式爬虫架构图:
以上两个图片对比我们可以发现。Item Pipeline
在接收到数据后发送给了Redis
、Scheduler
调度器调度数据也是从Redis
中来的、并且其实数据去重也是在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中通过这个发送出去的。- # Scrapy-Redis相关配置
- # 确保request存储到redis中
- SCHEDULER = "scrapy_redis.scheduler.Scheduler"
-
- # 确保所有爬虫共享相同的去重指纹
- DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
-
- # 设置redis为item pipeline
- ITEM_PIPELINES = {
- 'scrapy_redis.pipelines.RedisPipeline': 300
- }
-
- # 在redis中保持scrapy-redis用到的队列,不会清理redis中的队列,从而可以实现暂停和恢复的功能。
- SCHEDULER_PERSIST = True
-
- # 设置连接redis信息
- REDIS_HOST = '127.0.0.1'
- REDIS_PORT = 6379
scrapy runspider [爬虫名字]
。Redis
服务器上,推入一个开始的url链接:redis-cli> lpush [redis_key] start_url
开始爬取。多台电脑的环境配置
1、获取当前项目所用到的包,pypiwin32==200属于win下的,linux没有,安装时记得删除。
pip3 freeze > requirements.txt
2、如果要在同一台机器上使用多个python版本,可以创建虚拟环境。
- pwp@ubuntu:pip3 install virtualenvwrapper
-
- pwp@ubuntu:fang_spider$ which python3
- /usr/bin/python3
-
- pwp@ubuntu:fang_spider$ mkvirtualenv -p /usr/bin/python3 crawler-env #创建名为crawler-env的虚拟环境
3、安装
pip3 install -r requirements.txt
4、ubuntu下RedisDesktopManager的安装,redis可视化工具
官方网站:https://redisdesktop.com/download
一句命令行解决:sudo snap install redis-desktop-manager
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。