scrapy的github地址:https://github.com/scrapy/scrapy
scrapy
scrapy是一个功能强大的网络爬虫框架
scrapy不是一个函数功能库,而是一个爬虫框架
requesus+beautifulsoup库 和 scrapy爬虫比较
requesus+beautifulsoup和Scrapy是python爬虫的两种技术路线
①requests和beautifulsoup都是库,scrapy是框架
②scrapy框架中可以加入requests和beautifulsoup
③scrapy基于twisted,性能是最大的优势(scrapy是基于异步结构设计,可以同时向多个网站发起爬取请求)
④scrapy方便扩展,提供了很多内置的功能
⑤scrapy内置的css和xpath selector非常方便,beautifulsoup最大的缺点就是慢
网络爬虫爬取的快与慢,要结合特定的情况来考虑。对于一些很成熟的网站,都有反爬技术,反爬技术反而要求爬虫爬取的速度不能太快,否则就会被反爬技术发现并屏蔽ip
选择哪个技术路线
非常小的需求,requests库。
不太小的需求,Scrapy框架。
定制程度很高的需求(不考虑规模),自搭框架,requests>Scrapy。
scrapy爬虫框架
scrapy爬虫框架一共包含7个部分,称为“5+2”结构。
5个部分是框架的主体部分,另外包含2个中间件
scrapy其实是大量借鉴了django的设计理念
中间件之一
中间件之二
spiders是整个爬虫框架最核心的一个单元
scrapy工程目录结构
scrapy.cfg 部署Scrapy爬虫的配置文件——将爬虫放在特定的服务器上,并且在服务器配置好相关的操作接口。对于本机使用的爬虫来讲,不需要改变部署的配置文件
如果希望优化爬虫功能,我们需要修改settings.py中的配置项
spiders文件夹是存放具体的某个网站的爬虫,比如说知乎的爬虫,比如说伯乐在线的爬虫
scrapy爬虫的常用命令
scrapy是为持续运行设计的专业爬虫框架,提供操作的Scrapy命令行
在Scrapy框架下,一个工程是一个最大的单元。一个工程可以相当于一个大的Scapy框架,在一个框架中可以有多个爬虫,每个爬虫相当于框架中的一个spider模块。
示例1
定义一个工程,名字叫python123demo
在工程中产生一个Scrapy爬虫
genspider命令——需要用户给出爬虫的名字(demo)以及所爬取的网站(python123.io)
配置产生的spider爬虫——修改demo.py文件
# -*- coding: utf-8 -*- import scrapy class DemoSpider(scrapy.Spider): name = 'demo' allowed_domains = ['python123.io'] start_urls = ['http://python123.io/ws/demo.html'] # self是面向对象类所属关系的标记 # response是从网络中返回内容所存储的或对应的对象,我们要把response的内容写到一个html文件中 def parse(self, response): fname = response.url.split('/')[-1] # 从响应的url中提取文件名字作为我们保存在本地的文件名 with open(fname, 'wb') as f: f.write(response.body) self.log('Saved file %s.' % fname) # 经过这样的改造我们的demo.py就能够爬取一个网页并且把网页的内容保存为一个html网页
运行爬虫
我们看到的demo.py其实是scrapy提供的简化版的代码
yield的关键字的使用
生成器的使用一般与循环搭配
Scrapy爬虫的基本使用
Scrapy爬虫的数据类型:
- Request类(向网络上提交请求的内容)
- Response类(从网络中爬取内容的封装类)
- Item类(由spider产生的信息封装的类)
Request类和request库中的request类不是一个定义,但是十分相似
实例之股票数据Scrapy爬虫
stocks.py
# -*- coding: utf-8 -*- import scrapy import re class StocksSpider(scrapy.Spider): name = 'stocks' #allowed_domains = ['baidu.com']没用 start_urls = ['http://quote.eastmoney.com/stocklist.html'] def parse(self, response): for href in response.css('a::attr(href)').extract(): try: stock = re.findall(r'[s][hz]\d{6}',href)[0] #获取其中的股票代码 url = 'https://gupiao.baidu.com/stock/' + stock + '.html'#生成一个百度股票对应页面的链接 yield scrapy.Request(url,callback=self.parse_stock) #callback给出了处理这个url对应响应的处理函数 except: continue def parse_stock(self,response): infoDict = {} stockInfo = response.css('.stock_bets') name = stockInfo.css('.bets-name').extract()[0] keyList = stockInfo.css('dt').extract() valueList = stockInfo.css('dd').extract() #将提取的信息保存在字典中 for i in range(len(keyList)): key = re.findall(r'>.*</dt>',keyList[i])[0][1:-5] try: val = re.findall(r'\d+\.?.*</dd>',valueList[i])[0][0:-5] except: val = '--' infoDict[key] = val #将股票名称进行更新 infoDict.update( # {'股票名称':re.findall('\s.*\(',name)[0].split()[0] + \ # re.findall('\>.*\<',name)[0][1:-1]}) {'股票名称': re.findall('\s.*\(',name)[0].split()[0] + \ re.findall('\>.*\<',name)[0][1:-1]}) #到此,infoDict这个字典保存了一个股票页面提取的股票名称及相关信息 #下面我们将相关信息给到后续处理的ITEM Pipelines模块 yield infoDict
pipelines.py
# -*- coding: utf-8 -*- # Define your item pipelines here # # Don't forget to add your pipeline to the ITEM_PIPELINES setting # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html #这里面的每一个类都是对一个item进行处理的一个过程 class BaidustocksPipeline(object): def process_item(self, item, spider): return item class BaidustocksInfoPipeline(object): def open_spider(self,spider): #对应当一个爬虫被调用时对应的pipeline启动方法 self.f = open('BaiduStockInfo.txt','w') def close_spider(self,spider): #一个爬虫关闭时pipeline对应的方法 self.f.close() def process_item(self,item,spider): #对每一个item项进行处理时对应的方法,也是pipeline中最主体的函数 try: line = str(dict(item)) + '\n' self.f.write(line) except: pass return item
pipelines.py文件处理完了,这里面我们写了一个新的类,怎样让框架找到这个类呢?需要修改一个配置文件settings.py
在里面找到ITEM_PIPELINES,修改
修改前:
修改后:
之后用 scrapy crawl stocks 来运行
从东方财富网获得股票的列表,并针对每一个股票列表生成一个百度股票的链接,并向这个百度股票的链接进行信息爬取。对于爬取后的信息,经过spider的处理,我们提取出其中的关键信息形成字典,并且将这个字典以item类的形式给到了item-pipelines进行后续处理
速度优化