scrapy
Scrapy 是用 Python 实现的一个为了爬取网站数据, 提取结构性数据而编写的应用框架.
Scrapy 常应用在包括数据挖掘, 信息处理或存储历史数据等一系列的程序中.
Scrapy 是基于 twisted 框架开发而来, twisted 是一个流行的事件驱动的 python 网络框架. 因此 Scrapy 使用了一种非阻塞 (又名异步) 的代码来实现并发.
安装
- Linux:
- pip install scrapy
- Windows:
- 1. pip install wheel
2. 下载 twisted
http://www.lfd.uci.edu/-gohlke/pythonlibs/#twisted
3. 安装 twisted
进入到下载目录, pip install Twisted-xxx.whl
- pip install pywin32
- pip install scrapy
Scrapy 结构, 执行流程
架构图
各部分作用
Scrapy Engine(引擎): 负责 Spider,ItemPipeline,Downloader,Scheduler 中间的通讯, 信号, 数据传递等.
Scheduler(调度器): 它负责接受引擎发送过来的 Request 请求, 并按照一定的方式进行整理排列, 入队, 当引擎需要时, 交还给引擎.
Downloader(下载器): 负责下载 Scrapy Engine(引擎)发送的所有 Requests 请求, 并将其获取到的 Responses 交还给 Scrapy Engine(引擎), 由引擎交给 Spider 来处理,
Spider(爬虫): 它负责处理所有 Responses, 从中分析提取数据, 获取 Item 字段需要的数据, 并将需要跟进的 URL 提交给引擎, 再次进入 Scheduler(调度器).
Item Pipeline(管道): 它负责处理 Spider 中获取到的 Item, 并进行进行后期处理 (详细分析, 过滤, 存储等) 的地方.
Downloader Middlewares(下载中间件): 你可以当作是一个可以自定义扩展下载功能的组件.
Spider Middlewares(Spider 中间件): 你可以理解为是一个可以自定扩展和操作引擎和 Spider 中间通信的功能组件(比如进入 Spider 的 Responses; 和从 Spider 出去的 Requests)
执行流程图
起始 url 被封装成 request 请求对象, 经引擎传给调度器, 存放在队列中且会对重复的请求对象进行过滤, 之后经引擎传递给下载器去下载数据得到响应对象, 响应对象经引擎返回给 spider 进行解析并封装到 item 对象中, 用 yield 将 item 对象经引擎传给管道进行持久化处理, 若 spider 中解析到新的 url, 重复上述操作.
请求传参
当爬取的数据不在同一页面中时, 要进行请求传参, 不然持久化的数据结果会出错.
使用 meta 进行传参, 如下:
- def parse(self, response):
- ...
- yield scrapy.Request(url, callback=self.detail, meta={"item": item})
- def detail(self, response):
- item = response.meta['item']
- ...
如何提高 scrapy 爬取效率
增大并发数
默认情况下 scrapy 没有开启线程并发, 需在 settings.py 中手动开启. CONCURRENT_REQUESTS=32, 但是并不是只能设置不超过 32, 可以适度增加并发数.
降低日志等级
减少日志信息的输出会降低 CPU 的使用率.
如: LOG_LEVEL = 'INFO'
LOG_FILE = 'path' 将日志输出到指定文件中
禁用 cookie
除非真的需要 cookie, 否则请关闭以提升爬取效率.
COOKIES_ENABLED = False
限制重试
重新请求爬取失败的 url 会减慢爬取速度.
RETRY_ENABLED= False
缩减下载超时
放弃请求响应慢的 url, 可以提高爬取效率.
DOWNLOAD_TIMEOUT = 10 (单位是秒)
如何设置代理池和 UA 池
- ua_list = [
- ]
- ip_list = [
- ]
- # 在下载中间件中
- import random
- def process_request(self, request, spider):
- request.meta['proxy'] = random.choice(ip_list)
- request.headers['User-Agent'] = random.choice(ua_list)
- ...
使用管道进行持久化存储的流程
在 items 中定义字段
获取解析后的数据值
将解析后的数据值存储到 item 对象中
通过 yield 将 item 对象提交到管道
管道中持久化存储代码的编写
settings.py 中开启管道, 并设置优先级
在 scrapy 中使用 selenium
在 spider 的__init__方法中实现浏览器
在 spider 的 closed 方法中关闭浏览器
在下载中间件的响应中使用 spider.browser 获取浏览器对象, 并得到需要的网页源码, 实例化一个新的响应对象, 之后将浏览器获取的页面源码加载到该对象中, 返回这个新的响应对象.
- # spider.py
- def __init__(self):
- self.browser = webdriver.Firefox()
- def closed(self,spider):
- print("spider closed")
- self.browser.close()
- # middlewares.py
- def process_request(self, request, spider):
- if spider.name == 'xxx':
- try:
- spider.browser.get(request.url)
- spider.browser.execute_script('window.scrollTo(0, document.body.scrollHeight)')
- except TimeoutException as e:
- print('超时')
- spider.browser.execute_script('window.stop()')
- time.sleep(2)
- return htmlResponse(url=spider.browser.current_url, body=spider.browser.page_source,
- encoding="utf-8", request=request)
其他知识点
scrapy 为什么要把持久化操作放到管道中, 而不是在爬虫文件中?
放到管道中进行持久化操作的效率会更高
spider 参数是干嘛用的?
用来传递除了 item 对象之外的其他属性等
管道文件中 return item 的作用?
为了让优先级低于本管道类的能拿到 item 对象
在 scrapy 框架中不需要考虑 cookie
原始的 scrapy 为啥不能进行分布式爬虫?
原生 scrapy 的调度器, 管道不能共享.
来源: http://www.bubuko.com/infodetail-3013263.html