Scrapy工作流程与注意点
- 从spiders抓取请求requests经过爬虫中间件发送给引擎
- 引擎将请求发送给调度器,调度器对请求进行调度
- 调度器将调度排序好的请求发送给引擎(调度器维护的是一个LIFO的队列,广度优先)
- 引擎将请求经过下载中间件发送给下载器
- 下载器将请求返回的response经过下载中间件发送给引擎
- 引擎将下载器返回的response返回给爬虫中间件
- 爬虫中间件将解析的字段与request返回给引擎
- 引擎将解析的字段发送给项目管道,将新的request发送给调度器
- 注意点
- 第四点是我们能应对反爬的最后一步,换代理,加cookie,换ua等等
- 第五点最好是能在这里校验数据,不是正确的数据不传给spider
Scrapy的基本方法
基本流程:
- 命令行
1
2
3scrapy startproject projectname
scrapy genspider spidername hostname
scrapy crawl spidername
这里注意爬虫名称不要和项目名称相同
- 最简化的scrapy爬虫的执行流程
- scrapy的name是用来标识是哪个爬虫的
- 不重写start_requests(),默认scrapy调用的是start_requests()获取start_url中的url
- 在执行第二步就默认回调parse()默认解析器
item主要函数
- process_item(self,item,spider)必须实现的函数,用来处理item,常用的就是处理items,需要这个item就返回,不需要就Dropitem
- open_spider(self,spider)不是必须实现的函数,当爬虫运行的时候调用
- close_spider(self,spider)不是必须实现的函数,当爬虫结束的时候调用
- from_crawler(cls,crawler)不是必须实现的函数,当爬虫运行时调用,比open_spider()还早,常用于初始化爬虫的一些属性。
- 注意
- yield管道里的时候就是yield item
- yield调度器里就是yield requests
中间件
中间件最重要的作用就是拦截
下载中间件三大函数
- process_request(request,spider)处理拦截
- process_response(request,response,spider)处理拦截
- process_exception(request,exception,spider)常用来处理process_request的异常
process_request
- 返回None,默认返回这个值,表示当前这层的下载中间件过了,去往下一层的下载中间件
- 返回request,返回这个值就是把当前请求重新丢回调度器队列,相当于重试
- 返回response,直接伪造一个返回值,把这个值丢到爬虫中解析
- raises IgnoreRequest,抛弃当前的请求
process_response
- 返回response,默认返回到了下载中间件当中。
- 返回request,返回到队列当中,重新请求
- 返回raises IgnoreRequest,忽略了之前的请求也要忽略它对应的response
process_exception
- 返回None,就是不处理传给下一个中间件
- 返回response,错误被纠正了,进入正常的流程
- 返回request,返回给队列重新请求
CookieMiddleware
单spider多cookie session
1
2for i,url in enumerate(url):
yield scrapy.Request("xxxx.com",meta={'cookiejar':i},callback=self.parse_page)注意
需要注意的是 cookiejar meta key 不是“黏性的”,需要再之后的request请求中接着传递1
2
3def parse_page(self,response):
# do some processing
return scrapy.Request("xxxx.com",meta={'cookiejar':response.meta['cookiejar']},callback=self.parse_other_page)
注意事项
- 中间件后面的数字越小越远离下载器,数字越大越靠近下载器
- 数字越小的request越先通过,数字越大的response越先通过
- 这里比较重要的是retry模块还有CookieMiddleware模块
爬虫中间件
爬虫中间件的函数
- from_crawler
- process_spider_input
- process_spider_output
- process_spider_exception
- process_start_request
from_crawler(必须的)
会被manager调用
process_start_request
和process_spider_output很像,只能返回request
scrapy middleware的实例DepthMiddleware
scrapy的数据收集
收集404的页面demo
1 | handle_httpstatus_list = [404] |
Request和Response
Reuqests对象
基础参数:
- url 请求的网址
- meta 用来在页面之间传递的参数
- callback 请求回来的response处理函数
- header 设置页面的请求头
- cookie 请求页面cookie
高级参数:
- encoding 请求的转换编码
- priority 链接优先级
- dont_filter 强制不过滤
- errback 和callback一对,当请求成功时调用callback,请求失败调用errback
方法:
- copy() 复制一个一模一样的对象,常用是deepcopy()
- replace() 对对象参数进行替换
meta中常用的key:
Response对象
基础参数:
- url 请求的url
- body 请求回来的html
- meta 用来在页面之间传递数据
- headers 页面的headers数据
- cookies 设置页面的cookie
- Request 发出这个response对应的request对象
方法:
- copy() 与request相同
- replace() 与request相同
- urljoin() 将页面的相对路劲传入,返回绝对路径
- follow() 传入一个相对路径直接返回一个request对象
scrapy的暂停和重启
- 暂停实例
scrapy crawl spider lagou -s JOBDIR=job_info/001
spidername = lagou
workfilename = job_info
且每次请求都要求不一样的
- 启动实例
再次运行暂停的命令
自定义扩展
参考scrapy源码中的extensions这个包,可以查看corestats这个文件的源码
去重与入库
url去重
- 用造好的轮子
- scrapy-deltafetch(前两个原理相同)
- scrapy-crawl-once
- scrapy-redis
scrapy-redis-bloomfilters
自己造轮子
- 自己写的init_add_request方法实现轻量级的去重
scrapy_redis
- 运行爬虫:scrapy crawl 爬虫
- redis-cli lpush myspider:start_urls xxx.com
redis-cli sadd myspider:start_urls xxx.com
scrapy-rabbitmq-link
SCHEDULR = “scrapy_rabbitmq_link.scheduler.SaaS”
RABBITMQ_CONNECTION_PARAMETERS=’amqp://guest:guest@localhost:5672/‘
SCHEDULR_REQUEUE_ON_STATUS=[500]
DOWNLOADER_MIDDLEWARES = {
‘scrapy_rabbitmq_link.middleware.RabbitMQMiddleware’:999
}
celery
from celery import Celery
app = Celery(‘hello’,broker=’amqp://guest@localhost//‘)
@app.task
def hello():
return ‘hello worlds’
编写代码中的小tip
- 错误回调函数
1 | from traceback import format_exc |
通用爬虫-CrawlSpider
class scrapy.spiders.CrawlSpider
- 爬取一般网站常用的spider
- 定义一些规则来提供跟进link的方便机制
链接提取器(link extractors)
- 导入scrapy.linkextractors import LinkExtractor
- 每个link extractor 有唯一的公共方法是extract_links,他接收一个Response对象,返回scrapy.link.Link对象
常用参数
class scrapy.linkextractors.lxmlhtml.LxmlLinkExtractor()
- allow() -正则
- deny() - 正则
- allow_domains() - 域名
- deny_domains() - 域名