一 、介绍
Scrapy一个开源和协作的框架,其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的,使用它可以以快速、简单、可扩展的方式从网站中提取所需的数据。但目前Scrapy的用途十分广泛,可用于如数据挖掘、监测和自动化测试等领域,也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。
Scrapy 是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架。因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发。整体架构大致如下
1、scrapy的工作流程
第一步:爬虫从spiders爬行器中发送request请求给engine引擎。
第二部:引擎发送request请求给scheduler调度器,调度器从中调度请求,并要求下一个请求进来。
第三部:调度程序将第一个请求返回给引擎。
第四部:引擎将请求发送给下行加载程序downloader,通过下行加载中间件,并且准备下载网页。
第五步:一旦页面完成下载后,Downloader会生成一个响应(使用该页面)并将其发送到引擎,通过Downloader中间件到达downloader下载器
第六步:引擎接收来自Downloader的响应,并将其发送给爬行器进行处理,通过爬行器中间件,到达爬行器
第七步:爬行器处理响应信息,并将处理后的结果通过爬行器中间件返回到引擎。
第八步:引擎将处理过的数据发送到项目管道,然后将处理过的请求发送到调度程序,并请求可能的下一个请求进行爬行。
总结:
1、引擎负责控制系统所有组件之间的数据流,并在某些动作发生时触发事件
2、调度器用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL的优先级队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
3、下载器用于下载网页内容, 并将网页内容返回给EGINE,下载器是建立在twisted这个高效的异步模型上的
4、爬虫器SPIDERS是开发人员自定义的类,用来解析responses,并且提取items,或者发送新的请求
5、项目管道在items被提取后负责处理它们,主要包括清理、验证、持久化(比如存到数据库)等操作
6、下载器中间件位于Scrapy引擎和下载器之间,主要用来处理从EGINE传到DOWLOADER的请求request,已经从DOWNLOADER传到EGINE的响应response,你可用该中间件做以下几件事情
1、在发送到下载程序之前处理一个请求(即在剪贴簿发送请求到网站之前);
2、在将其传递给蜘蛛之前,更改接收到响应;
3、发送一个新的请求,而不是传递接收到的响应;
4、在不获取web页面的情况下对爬行器进行响应;
5、悄悄地放弃一些请求。
7、爬虫中间件
位于EGINE和SPIDERS之间,主要工作是处理SPIDERS的输入(即responses)和输出(即requests)
官网链接:https://docs.scrapy.org/en/latest/topics/architecture.html
二 安装
#Windows平台
1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
3、pip3 install lxml
4、pip3 install pyopenssl
5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
7、执行pip3 install 下载目录Twisted-17.9.0-cp36-cp36m-win_amd64.whl
8、pip3 install scrapy
#Linux平台
1、pip3 install scrapy
三 命令行工具
#1 查看帮助
scrapy -h 或者是scrapy -l
scrapy <command> -h
#2 有两种命令:其中Project-only必须切到项目文件夹下才能执行这种命令是局部命令,而Global的命令则不需要,是属于全局命令
Global commands:全局命令
1、startproject #创建一个爬虫项目,相当于创建了个爬虫的整体框架,
scrapy startproject 项目名称
2、genspider #创建一个爬虫程序,可以在配置文件中将网址域名注释调,该网址域名的作用就是在网站的html中只允许爬该网址的数据
scrapy genspider 爬虫名称 需要爬的网址域名
3、settings #如果是在项目目录下,则得到的是该项目中settings的配置,否则不是,
scrapy settings --get=settings文件中的配置变量
4、runspider #运行一个独立的python文件,不必创建项目,这个python文件可以是一个爬虫程序
scrapy runspider 爬虫文件的绝对路径
5、shell # 在交互式调试,如选择器规则正确与否都是在该交互式环境下测试的,相当于get下来数据后将数据封装到了个response对象内。
scrapy shell url地址
view(response);response.text;response.body;
6、fetch #独立于程单纯地爬取一个页面,可以拿到请求头
scrapy fetch url地址
scrapy fetch --header url地址
7、 view #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
scrapy view rul路径 #如果页面显示内容不全,不全的内容则是ajax请求实现的,以此快速定位问题
8、version #
scrapy version 查看scrapy的版本
scrapy version -v 查看scrapy所依赖的库版本
Project-only commands:局部命令,必须在项目目录下才能执行
1、crawl #运行爬虫,必须创建项目程序才行,确保配置文件settings中ROBOTSTXT_OBEY = False,机器人策略,关闭该策略后就不会受机器人策略的影响
scrapy crawl 爬虫项目文件名 #和之前的runspider方法相比该方法必须在项目目录下运行且受机器人策略的影响
scrapy crawl 爬虫项目文件名 --nolog #运行爬虫项目不打印日志
scrapy crawl 爬虫项目文件名 -a 参数名=参数值 #给爬虫项目传参,但是项目内部必须重新__int__方法来接受外部参数
scrapy crawl 爬虫项目文件名 -a 参数名=参数值 --nolog #运行爬虫项目不打印日志并且传参数给爬虫项目
2、check #检测项目(所有的爬虫程序)中有无语法错误
scrapy check
3、list #列出项目中所包含的爬虫名
scrapy list
4、edit #编辑器,一般不用
5、 parse # #以此可以验证我们的回调函数是否正确,回调函数一般写在爬虫程序文件下
scrapy parse url地址 --callback 回调函数
6、bench #压力测试
scrapy bench
#3 官网链接
https://docs.scrapy.org/en/latest/topics/commands.html
总结:在命令行的命令或者是参数都是不用加引号的,
爬虫项目的settings配置文件添加配置时都变量名必须要大写,否则不识别
四 项目结构以及爬虫应用简介
project_name/
scrapy.cfg
project_name/
__init__.py
items.py
pipelines.py
settings.py
spiders/
__init__.py
爬虫1.py
爬虫2.py
爬虫3.py
文件说明:
- scrapy.cfg 整个项目的主配置信息,用来部署scrapy时使用,爬虫相关的配置信息在settings.py文件中。
- items.py 设置数据存储模板,用于结构化数据,如:Django的Model
- pipelines 数据处理行为,如:一般结构化的数据持久化
- settings.py 配置文件,如:递归的层数、并发数,延迟下载等。强调:配置文件的选项必须大写否则视为无效,正确写法USER_AGENT='xxxx'
- spiders 爬虫目录,如:创建文件,编写爬虫规则
注意:一般创建爬虫文件时,以网站域名命名
import scrapy
class XiaoHuarSpider(scrapy.spiders.Spider):
name = "xiaohuar" # 爬虫名称 *****
allowed_domains = ["xiaohuar.com"] # 允许爬取的域名
start_urls = [
"http://www.xiaohuar.com/hua/", # 其实URL地址,该url地址是程序默认创建的,如果需要爬取该地址外的其他地址数据就需要自己处理请求参数,重新请求方法
]
def parse(self, response):
# 访问起始URL并获取结果后的回调函数
import sys,os
sys.stdout=io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030')
五 Spiders
#在项目目录下新建:entrypoint.py
from scrapy.cmdline import execute
execute(['scrapy', 'crawl', 'xiaohua'])
强调:配置文件的选项必须是大写,如X='1'
# -*- coding: utf-8 -*-
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
class BaiduSpider(CrawlSpider):
name = 'xiaohua'
allowed_domains = ['www.xiaohuar.com']
start_urls = ['http://www.xiaohuar.com/v/']
# download_delay = 1
rules = (
Rule(LinkExtractor(allow=r'p-d-d+.html$'), callback='parse_item',follow=True,),
)
def parse_item(self, response):
if url:
print('======下载视频==============================', url)
yield scrapy.Request(url,callback=self.save)
def save(self,response):
print('======保存视频==============================',response.url,len(response.body))
import time
import hashlib
m=hashlib.md5()
m.update(str(time.time()).encode('utf-8'))
m.update(response.url.encode('utf-8'))
filename=r'E:\mv\%s.mp4' %m.hexdigest()
with open(filename,'wb') as f:
f.write(response.body)
https://docs.scrapy.org/en/latest/topics/spiders.html
六 Selectors
response.selector.css()
response.selector.xpath()
可简写为
response.css()
response.xpath()
1、查找标签
response.css('div a ')#通过标签名查找标签的内容
response.xpath('//body/a') #开头的//代表从整篇文档中寻找,body之后的/代表body的儿子
response.xpath('//body//a') #开头的//代表从整篇文档中寻找,body之后的//代表body的子子孙孙
总结://与/的区别:儿子和后代的区别,但是得到的都是一个列表
2、查找标签下的内容(text方法)
response.css('div a::text')#先定位标签然后通过::的方式查找标签的内容
response.xpath('//body//a/text()')
3、查找标签下的内容
extract与extract_first:从selector对象中解出内容
response.xpath('//div/a/text()').extract() #得到的是个解析后的内容,是个列表形式
response.xpath('//div/a/text()').extract_first()#得到的是个解析后的内容,是个字符串形式
4、查找标签下的属性:
response.xpath('//div/a/@href').extract_first() #得到的是标签的href属性,可以跟标签的任意属性xpath的属性加前缀@
response.css('div a::attr(href)').extract_first()
5、嵌套查找加混合查找
response.xpath('//div').css('a').xpath('@href').extract_first()
6、设置查找默认值
response.xpath('//div[@id="xxx"]').extract_first(default="not found")#为了防止查找不到匹配的标签而设置默认值
7、按照标签属性查找
response.xpath('//div[@id="images"]/a[@href="image3.html"]/text()').extract() #如果按照属性查找标签就必须加上中括号,再写匹配规则
response.css('#images a[@href="image3.html"]/text()').extract()
8、按照标签属性模糊查找
response.xpath('//a[contains(@href,"image")]/@href').extract()#如果按照标签属性模糊查找就必须加上contains()方法,然后再在里面写匹配规则,匹配规则用逗号隔开
response.css('a[href*="image"]::attr(href)').extract()
response.xpath('//a[contains(@href,"image")]/img/@src').extract()
response.css('a[href*="imag"] img::attr(src)').extract()
response.xpath('//*[@href="image1.html"]')
response.css('*[href="image1.html"]')
9、正则表达式查找
response.xpath('//a/text()').re(r'Name: (.*)')
response.xpath('//a/text()').re_first(r'Name: (.*)')
10、xpath相对路径
>>> res=response.xpath('//a[contains(@href,"3")]')[0]
>>> res.xpath('img')
[<Selector xpath='img' data='<img src="image3_thumb.jpg">'>]
>>> res.xpath('./img')
[<Selector xpath='./img' data='<img src="image3_thumb.jpg">'>]
>>> res.xpath('.//img')
[<Selector xpath='.//img' data='<img src="image3_thumb.jpg">'>]
>>> res.xpath('//img') #这就是从头开始扫描
11、、带变量的xpath
response.xpath('//div[@id=$xxx]/a/text()',xxx='images').extract_first()#带变量查找
response.xpath('//div[count(a)=$yyy]/@id',yyy=5).extract_first() #求有5个a标签的div的id