爬虫框架
-scrapy、pyspider、crawley等
Scrapy框架
1、scrapy框架介绍
-https://doc.scrapy.org/en/latest/
-http://scrapy-chs.readthedocs.io/zh_CN/latest/index.html
-安装:利用pip或conda
2、scrapy概述及原理
-包含各个部件
-ScrapyEngine:神经中枢、大脑、核心
-Scheduler调度器:引擎发来的request请求,调度器需要处理,然后交换引擎(只是负责处理请求,该打包打包,该封装封装)
-Downloader下载器:把引擎发来的requests发出请求,得到response(只负责得到一个结果)
-Spider爬虫:负责把下载器得到的网页/结果进行分解,分解成数据+链接(我们能够继续往下爬就是因为我们永远可以在当前页面获得下一个我们要爬的页面链接)
-ItemPipeline管道:详细处理Item
-DownloaderMiddleware下载中间件:自定义下载的功能扩展组件
-SpiderMiddleware爬虫中间件:对spider进行功能扩展
绿色箭头表示数据流动的方向,ScrapyEngine->Scheduler->Downloader(可以看成得到一个网页,然后发给Spiders)
->Spiders(处理得到Item和网址,把Item给itemPipeline,把网址给Sheduler)->ItemPipeline(把数据进行打包处理好,该存的存)
->Sheduler接着又是一个循环
循环往复,这就是一个爬虫。
详解图:
3、爬虫项目大概流程
-新建项目:scrapy startprject xxx
-明确需要目标/产出:编写item.py
-制作爬虫:地址 spider/xxspider.py (负责把下载的东西 啾~分解出来)
-存储内容:pipelines.py
-----------------------------------------------------------------------------------------------------
(1)——ItemPipeline
-对应的是pipelines文件
-爬虫提取数据存入item后,item中保存的数据需要进一步处理,比如清洗、去重、存储等
-pipeline需要处理process_item函数
-process_item:
-spider提取出来的item作为参数传入,同时传入的还有spider(每得到一次item调用一次pipeline)
-此方法必须实现
-必须返回一个Item对象(返回的item和传入的item应该是一个),被丢弃的item不会被之后的pipeline处理(这个流水线上可能有好多好多pipeline,在使用时pipline必须注册,然后根据pipline的值的大小挨个去调用,所有的pipline都必须被调用,每个pipline干相应的活,干完就完了)
-__init__:构造函数 : 进行一些必要的参数初始化
-open_spider(spider):spider对象被开启的时候调用
-close_spider(spider):当spider对象被关闭的时候调用
pipline流水线如下:
(2)——Spider
-对应的是文件夹spiders下的文件
-__init__:初始化爬虫名称,start_urls列表
-start_requests:生成Requests对象交给Scrapy下载并返回response(可以理解为我们心心念念的那个页面)
-parse:根据返回的response解析出相应的item,item自动进入pipline:如果需要,解析出url,url自动交给requests模块, 一直循环下去
-start_request:此方法仅能被调用一次,读取start_urls内容并启动循环过程
-name:设置爬虫名称
-start_urls:设置开始第一批爬取的url
-allow_domains:spider允许爬取的域名列表
-start_request(self):只被调用一次
-parse
-log:日志记录
(3)——中间件(DownloaderMiddlewares)
-中间件是处于引擎和下载器中间的一层组件。可以有很多个,被按顺序加载执行
-作用是对发出的请求和返回的结果进行预处理
-在middlewares文件中,需要在settings中设置以便生效,一般一个中间件完成一项功能
-必须实现以下一个或者多个方法
-process_request(self,request,spider)
-在request通过的时候被调用
-必须返回None或Response或Request或raise IgnoreRequest(异常)
-None:scrapy将继续处理该request
-Request:scrapy会停止调用process_request并重新调度返回的request
-Response:scrapy不会调用其他的process_request或者process_exception,直接讲该response作为结 果,同时会调用process_response函数
-process_response(self , request , response , spider)
-跟process_request大同小异
-每次返回结果的时候会自动调用
-可以有多个,按顺序调用
-案例代码:
import random,base64
#从settings设置文件中导入值
from settings import USER_AGENTS
from settings import PROXIES
#随机的User-Agent
class RandomUserAgent(object):
def process_request(self,request,spider):
useragent = random.choice(USER_AGENTS)
request.headers.setdefault("User-Agent",useragent)
class RandomProxy(object):
def process_request(self,request,spider):
proxy = random.choice(PROXIES)
if proxy['user_passwd'] is None:
#没有代理账户验证的代理使用方式
request.meta['proxy'] = "http://"+proxy['ip_port']
else:
#对帐户密码进行base64编码转换
base64_userpasswd = base64.b64encode(proxy['user_passwd'])
#对应到代理服务器的信令格式里
request.headers['Proxy-Authorization'] = 'Basic'+base64_userpasswd
request.meta['proxy'] = "http://"+proxy['ip_port']
-设置settings相关代码
(4)——去重
-为了放置爬虫陷入死循环,需要去重
-即在spider中的parse函数中,返回Request的时候加上dont_filter=False参数
myspider(scrapy.Spider):
def parse(...):
...
yield scrapy.Request(url=url,callback=self.parse,dont_filter=False)
(5)-如何在scrapy使用selenium
-可以放入中间件中的process_request函数中
-在函数中调用selenium,完成爬取后返回Response
class MyMiddleWare(object):
def procee_request(...):
driver = webdriver.Firefox()
html = driver.page_source
driver.quit()
return HtmlRequesponse(url=request.url,encoding='utf-8',body=html,request=request)
4、Scrapy命令
(1)scrapy命令行格式
>scrapy<command>[options] (scrapy命令放在command里)
(2)Scrapy常用命令
命令 | 说明 | |
startproject | 创建一个新工程 | scrapy startproject <name>[dir] |
genspider | 创建一个爬虫 | scrapy genspider[options],name. |
settings | 获得爬虫配置信息 | scarpy settings[options] |
crawl | 运行一个爬虫 | scrapy crawl<spider> |
list | 列出工程中所有爬虫 | scrapy list |
shell | 启动URL调试命令行 | scrapy shell [url] |
——scrapy-shell
-https://segmentfault.com/a/1190000013199636?utm_source=tag-newesst
-shell
-启动
-Linux:ctrl+T,打开终端,然后输入scrapy shell"url:xxxx"
-windows:scrapy shell “url:xxx”
-启动后自动下载指定url的网页
-下载完成后,url的内容保存在response的变量中,如果需要,我们需要调用response
(3)命令使用——创建爬虫实例
Scrapy爬虫的使用步骤:
-创建一个工程和Spider模板——>编写spider——>编写item pipline——>优化配置策略
---------------------------------------------------------------------------------------------------------------
1)通过创建命令 scrapy startproject python123demo,生成目录如下:
注:scrapy.cfg:部署Scrapy爬虫的配置文件,这里的部署是指将这样的爬虫放在特定的服务器上,并且在服务器上配置好相关的操作接口,对于本机来讲,不需要改变部署的配置文件
2)通过以下提示命令创建第一个爬虫文件:
You can start your first spider with:
cd python123demo
scrapy genspider example example.com
-----------------------------------------------
D:\Python37\cources\统一包管理>cd python123demo
D:\Python37\cources\统一包管理\python123demo>scrapy genspider demo python123.io
3)此时我们创建的名称为demo,接着改写demo里的代码,完善功能
# -*- coding: utf-8 -*-
import scrapy
#类名叫什么无所谓但是必须继承于scrapy.spider
class DemoSpider(scrapy.Spider):
name = "demo" #当前爬虫的名字叫demo
# 最开始用户提交给命令行的域名,
# 指的是爬虫过程中,只能爬取这个域名以下的链接
#allowed_domains = ['python123.io']
#scrapy框架所要爬取页面的初始页面(爬虫启动时最开始的url链接)
start_urls = ['http://python123.io/ws/demo.html']
#对于返回的页面,进行解析页面通用的方法
#parse()用于处理响应,解析内容形成字典,发现新的URL爬取请求
def parse(self, response):
#定义文件名称(从响应url中提取文件名字作为我们本地的文件名称)
fname = response.url.split('/')[-1]
#将返回的内容保存为文件
with open(fname,'wb') as f:
f.write(response.body)
self.log('Saved file %s.' % fname)
pass
4)编写好之后,打开终端,输入scrapy crawl demo,会在目录中生成一个demo.html
附:
注:yield关键字 yield <——>生成器
-生成器是一个不断产生值的函数
-包含yield语句的函数是一个生成器
-生成器每次产生一个值(yield语句),函数被冻结,被唤醒后再产生一个值
#生成器
def gen(n):
for i in range(n):
yield i**2
for i in gen(10):
print(i," ",end="")
print("\n")
#传统写法
def square(n):
ls = [i**2 for i in range(n)]
return ls
for i in square(10):
print(i," ",end="")
运行:
0 1 4 9 16 25 36 49 64 81
0 1 4 9 16 25 36 49 64 81
5、scrapy爬虫的数据类型
Scrapy爬虫的数据类型
Request类、Response类、Item类
-------------------------------------------------------------------------
(1)Request类 class scrapy.http.Request()
-Request对象表示一个http请求,由Spider生成,由Downloader执行
属性或方法 | 说明 |
.url | Request对应的请求URL地址 |
.method | 对应的请求方法,'GET' 'POST'等 |
.header | 字典类型风格的请求头(对网站请求的http头部进行自定义) |
.body | 请求内容主体,一般是字符串类型 |
.meta | 用户添加的扩展信息,在Scrapy内部模块间传递信息使用 |
.copy() | 复制该请求 |
(2)Response类 class scrapy.http.Response()
-Response对象表示一个HTTP响应,由Downloader生成,由Spider处理(爬取到的内容保存在response中)
属性或方法 | 说明 |
.url | Response对应的URL地址 |
.status | HTTP状态码,默认是200 |
.headers | Response对应的是头部信息(是返回的http的头信息) |
.body | Response对应的内容信息,字符串类型(网页的代码) |
.flag | 一组标记 |
.request | 产生Response类型对应的Request对象 |
.copy() | 复制该响应 |
.xpath() | 允许使用xpath语法选择内容 |
.css(0 | 允许使用css语法选取内容 |
(3)Item类 class scrapy.item.Item()
-Item对象表示一个从HTML页面中提取的信息内容
-由Spider生成,由Item Pipeline处理
-Item类似字典类型,可以按照字典类型操作
6、Scrapy提取信息的方法
Scrapy爬虫支持多种HTML信息提取方法
-Beautiful Soup、lxml、re、XPath Selector、CSS Selector
——CSS Selector的基本使用
CSS Selector由W3C组织维护并规范
附:selector
-选择器,允许用户使用选择器来选择自己想要的内容
-response.selector.xpath:response.xpath是selector.xpath的快捷方式
-response.seletor.css:response.css是他的快捷方式
-selector.extract:把结点的内容用unicode形式返回
-selector.re:允许用户通过正则选取内容
7、股票数据Scrapy爬虫
(1)功能描述
技术路线:Scrapy
目标:获取上交所和深交所所有股票的名称和交易信息
输出:保存到文件中
我们生成了一个spider,它能从东方财富网股票列表的中间,找到每一个股票的代码,并且生成与百度股票相关的URL链接,并向这个百度股票链接进行信息爬取,对于爬取后的信息经过spider的处理我们提取出其中的关键信息形成字典,并且将这个字典以item的形式给到itempipelines进行后续处理。
(2)获取股票列表:
东方财富网:http://quote.eastmoney.com/stocklist.html
获取个股信息:
百度股票:https://gupiao.baidu.com/stock/
单个股票:https://gupiao.baidu.com/stock/sz002439.html
(3)编写Spider
修改对返回页面的处理,修改对新增URL爬取请求的处理
# -*- coding: utf-8 -*-
'''
爬取相关的内容,对爬过来的内容做解析,从东方财富网股票列表的中间,
找到每一个股票的代码,并且生成与百度股票相关的URL链接
'''
import scrapy
import re
class StocksSpider(scrapy.Spider):
name = 'stocks'
#东方财富网
start_urls = ['http://quote.eastmoney.com/stocklist.html']
def parse(self, response):
#首先定义一个for循环,这for循环可以对页面中每个a标签的URL链接进行提取
#编写的extract让我们可以关注到正确的链接上
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/'
#callback给出了处理这个url对应响应的处理函数,为了与当前页面处理函数进行区分,定义了parse_stock
yield scrapy.Request(url,callback=self.parse_stock)
except:
continue
#这个函数最终要返回提取的信息给Item Pipeline
def parse_stock(self,response):
#由于item是一个字典类型,首先生成一个空字典
infoDict = ()
#找到stock-bets区域
stockInfo = response.css('.stock-bets')
#然后在这个区域中,检索bets的name,并且把相关的字符串提取出来,此时我们拿到了股票的名字
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]})
#将信息给到item Pipeline模块,把parse_stock函数定义成一个生成器,定义为yield
yield infoDict
(4)编写Pipelines
-配置pipelines.py文件
-定义对爬取项(Scraped Item)的处理类,此处为BaidustocksInfoPipeline类
# -*- 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
class BaidustocksPipeline(object):
def process_item(self, item, spider):
return item
'''
在每一个piplines中有三个方法,每个方法对应一个函数
'''
class BaidustocksInfoPipeline(object):
#当一个爬虫调用时,pipelines启动的方法
def open_spider(self,spider):
self.f = open('BaiduStockInfo.txt','w')
#当爬虫关闭时,pipelines调用的函数
def close_spider(self,spider):
self.f.close()
#process_item指的是对每一个item项处理时对应的方法,也是pipelines中最重要的方法
def process_item(self,item,spider):
#希望将得到的股票信息存入一个文件
try:
line = str(dict(item)) +'\n'
self.f.write(line)
except:
pass
return item
-配置ITEM_PIPELINES选项(编写完了pipelines文件,如何让爬虫找到其中的类?在settings.py中配置)
ITEM_PIPELINES = {
#'BaiduStocks.pipelines.BaidustocksPipeline': 300,
'BaiduStocks.pipelines.BaidustocksInfoPipeline': 300,
}
至此我们基本完成了,运行一下
(5)配置并发连接选项(优化爬虫)
在settings.py文件
选项 | 说明 |
CONCURRENT_REQUESTS | Downloader最大并发请求下载数量,默认32 |
CONCURRENT_ITEMS | Item Pipeline最大并发ITEM处理数量,默认100 |
CONCURRENT_REQUESTS_PER_DOMAIN | 每个目标域名最大的并发请求数量,默认8 |
CONCURRENT_REQUESTS_PER_IP | 每个目标IP最大的并发请求数量,默认0,非0有效(事实上后两个参数只有一个能发挥作用,通过改变并发数量优化爬虫速度) |