1.理解同步和异步的取别
同步:下一个方法依赖于上一个方法的结果
异步:下一个方法不依赖于上一个方法的结果
上面图片中遇到的问题就是访问处理后一个请求需要上一次请求处理完毕,这样耗费的时间比较长,而且如果中途哪一个请求响应失败 ,会影响到后续的请求处理,所以引入了异步请求处理机制 而scrapy框架就为我们很好的解决了这一问题运行机制如下图所示:
Scrapy运行原理如下图所示:
上图文件解析
ScrapyEngine 引擎
Scheduler 调度器(证合url分配url,去除重复url请求)
Pipeline io存储,储存到本地
Downloader 向服务器发送请求,并拿到响应
Spiders 我们自己写的爬虫文件
1.安装scrapy
pip install scrapy -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
2.创建scrapy项目
1. 创建项目 >> scrapy startproject 【项目名字】
![](https://i-blog.csdnimg.cn/blog_migrate/0a1f35fb0dcd302c93540972de078e7f.png)
2. 进入项目 >> cd 【项目名字】
上图文件作用解析:
spiders 放置爬虫文件
items.py封装一些传递给pipelines.py的管道数据,也可以使用字典代替
middlewares.py中间件
pipelines.py io持久化存储
settings.py 项目的配置文件
3. 创建爬虫文件 >> scrapy genspider 【爬虫名字】 “【HOST地址】“
(host地址的作用可以进行过滤,比如Host的地址写的是‘taobao.com’,jd.com不属于淘宝的,就会被过滤)
![](https://i-blog.csdnimg.cn/blog_migrate/1cd488ce39a3383c06edc8670d37c2df.png)
4. 运行爬虫文件 >> scrapy crawl 【爬虫名字】
# -*- coding: utf-8 -*-
import scrapy
class S1Spider(scrapy.Spider):
name = 's1'
allowed_domains = ['blog.csdn.net']
def start_requests(self):# 爬虫开始,执行的方法,相当于start_urls
yield scrapy.Request( # 向调度器发送一个Request对象
url='http://blog.csdn.net/', # 请求地址,默认Get方式
callback=self.parse2 # 得到响应后,调用的函数
)
'''
自定义的回调函数
'''
def parse2(self,response): # 得到响应后,调用的函数
print(response.body) # response.body 得到字节类型的数据
print(response.xpath('//div[@class="nav_com"]//li/a/text()').extract())
import scrapy
class S2Spider(scrapy.Spider):
name = 's2'
allowed_domains = ['blog.csdn.net']
start_urls = ['http://blog.csdn.net/']
def parse(self, response):
print('-' * 30)
# print(response.body)
print(response.xpath('//div[@class="nav_com"]//li/a/text()').extract())
print('-' * 30)
spider中的py文件内容
中间件使用
def process_request(self, request, spider):
# =================================================================================
# 1. from Scrapy.http.headers import Headers
# request.headers是字节对象不能直接使用字典
# 2. Headers中传递字典类型的参数,并赋值给request.headers
# =================================================================================
request.headers = Headers(
{
'User-Agent': user_agent.get_user_agent_pc()
}
)
print("*"*300)
# =================================================================================
# request.meta['proxy'] = 'http://【ip】:【port】'
# 这个例子是访问代理IP接口取到的代理IP地址,实际开发中,会使用IP池(后续课程中会讲)
# =================================================================================
# request.meta['proxy'] = 'http://' + ur.urlopen(
# 'http://api.ip.data5u.com/dynamic/get.html?order=d314e5e5e19b0dfd19762f98308114ba&sep=3').read().decode(
# 'utf-8').strip()
# Called for each request that goes through the downloader
# middleware.
# Must either:
# - return None: continue processing this request
# - or return a Response object
# - or return a Request object
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
return None
管道文件pipeline.py文件的使用
Scrapy异步抓取
import scrapy
class CsdnBlogSpider(scrapy.Spider):
name = 'csdn_blog'
allowed_domains = ['blog.csdn.net']
keyword = 'python'
def start_requests(self):
for pn in range(1,2):
url = 'https://so.csdn.net/so/search/s.do?p=%s&q=%s&t=blog&domain=&o=&s=&u=&l=&f=&rbg=0' % (pn,self.keyword)
yield scrapy.Request(
url=url,
callback=self.parse
)
def parse(self, response):
href_s = response.xpath('//div[@class="limit_width"]/a/@href').extract()
for href in href_s:
yield scrapy.Request(
url=href,
callback=self.parse2
)
def parse2(self,response):
# 定义一个空的字典:my_dict = {}或my_dict = dict()
'''
my_dict = {'name': 'yyh', 'age': 18, 'sex': 'male'}
等价于
my_dict = dict({'name': 'yyh', 'age': 18, 'sex': 'male'})
'''
item = dict(
title = response.xpath('//h1[@class="title-article"]/text()').extract_first(),
data = response.body
)
yield item
#Pipeline.py文件下:
class MyscrapyPipeline(object):
def process_item(self, item, spider):
with open('blog_html/%s.html'%item['title'],'wb') as f:
f.write(item['data'])
return item