补充:
自动请求start_urls列表路径其实是执行了父类中的start_requests方法,默认为GET请求,如果想要发送POST请求,改写此方法即可.
def start_requests(self):
for url in self.start_urls:
yield scrapy.Request(url,callback=self.parse)
# LOG_FILE = './log.txt'
日志文件输出,默认为控制台
基于scrapy进行图片数据的爬取
官方建议:使用ImagesPipeline类,也可以进行视频,音频等多媒体
- 在爬虫文件中只需要解析提取出图片地址,然后将地址提交给管道,该管道类具有请求发送功能
- 配置文件中:IMAGES_STORE = './imgsLib'
- 在管道文件中进行管道类的制定:
- 1.from scrapy.pipelines.images import ImagesPipeline
- 2.将管道类的父类修改成ImagesPipeline
- 3.重写父类的三个方法:
# img.py
class ImgSpider(scrapy.Spider):
name = 'img'
# allowed_domains = ['www.xxx.com']
start_urls = ['http://www.521609.com/daxuemeinv/']
url = 'http://www.521609.com/daxuemeinv/list8%d.html'
pageNum = 1
def parse(self, response):
li_list = response.xpath('//*[@id="content"]/div[2]/div[2]/ul/li')
for li in li_list:
img_src = 'http://www.521609.com'+li.xpath('./a[1]/img/@src').extract_first()
item = ImgproItem()
item['src'] = img_src
yield item
if self.pageNum < 3:
self.pageNum += 1
new_url = format(self.url%self.pageNum)
yield scrapy.Request(new_url,callback=self.parse)
# pipelines.py
from scrapy.pipelines.images import ImagesPipeline
import scrapy
class ImgproPipeline(ImagesPipeline):
#对某一个媒体资源进行请求发送
#item就是接收到的spider提交过来的item
def get_media_requests(self, item, info):
yield scrapy.Request(item['src'])
#制定媒体数据存储的名称
def file_path(self, request, response=None, info=None):
name = request.url.split('/')[-1]
print('正在下载:',name)
return name
#将item传递给下一个即将给执行的管道类
def item_completed(self, results, item, info):
return item
如何提升scrapy爬取数据的效率
只需要将如下五个步骤配置在配置文件中即可
增加并发:
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’禁止cookie:
如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False禁止重试:
对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False减少下载超时:
如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s
请求传参
- 实现深度爬取:爬取多个层级对应的页面数据
- 使用场景:爬取的数据没有在同一张页面中
- 在手动请求的时候传递item:yield scrapy.Request(url,callback,meta={'item':item})
- 将meta这个字典传递给callback
- 在callback中接收meta:item = response.meta['item']
import scrapy
from moviePro.items import MovieproItem
class MovieSpider(scrapy.Spider):
name = 'movie'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://www.4567tv.tv/index.php/vod/show/class/动作/id/1.html']
url = 'https://www.4567tv.tv/index.php/vod/show/class/动作/id/1/page/%d.html'
pageNum = 1
def parse(self, response):
li_list = response.xpath('/html/body/div[1]/div/div/div/div[2]/ul/li')
for li in li_list:
title = li.xpath('./div[1]/a/@title').extract_first()
detail_url = 'https://www.4567tv.tv'+li.xpath('./div[1]/a/@href').extract_first()
item = MovieproItem()
item['title'] = title
#meta参数是一个字典,该字典就可以传递给callback指定的回调函数
yield scrapy.Request(detail_url,callback=self.parse_detail,meta={'item':item})
if self.pageNum < 5:
self.pageNum += 1
new_url = format(self.url%self.pageNum)
yield scrapy.Request(new_url,callback=self.parse)
def parse_detail(self,response):
#接收meta:response.meta
item = response.meta['item']
desc = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[5]/span[2]/text()').extract_first()
item['desc'] = desc
yield item
scrapy中的中间件的应用
- 中间件有两种:Spider中间件,Downloader中间件,我们一般只使用下载中间件
- 下载中间件:批量拦截请求和响应
拦截请求实现功能
- UA伪装:将所有的请求尽可能多的设定成不同的请求载体身份标识
- request.headers['User-Agent'] = 'xxx'
- 代理操作:request.meta['proxy'] = 'http://ip:port'
import random
user_agent_list = [
"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 "
"(KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1",
"Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 "
"(KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
]
PROXY_http = [
'153.180.102.104:80',
'195.208.131.189:56055',
]
PROXY_https = [
'120.83.49.90:9000',
'95.189.112.214:35508',
]
class MovieproDownloaderMiddleware(object):
#拦截正常的请求,参数request就是拦截到的请求对象
def process_request(self, request, spider):
#实现:将拦截到的请求尽可能多的设定成不同的请求载体身份标识
request.headers['User-Agent'] = random.choice(user_agent_list)
#代理操作
if request.url.split(':')[0] == 'http':
request.meta['proxy'] = 'http://'+random.choice(PROXY_http) #http://ip:port
else:
request.meta['proxy'] = 'https://' + random.choice(PROXY_https) # http://ip:port
return None
#拦截响应:参数response就是拦截到的响应
def process_response(self, request, response, spider):
return response
#拦截发生异常的请求
def process_exception(self, request, exception, spider):
#拦截到异常的请求然后对其进行修正,然后重新进行请求发送
# 代理操作
if request.url.split(':')[0] == 'http':
request.meta['proxy'] = 'http://' + random.choice(PROXY_http) # http://ip:port
else:
request.meta['proxy'] = 'https://' + random.choice(PROXY_https) # http://ip:port
return request #将修正之后的请求进行重新发送
拦截响应:篡改响应数据或者直接替换响应对象
-需求: 爬取网易新闻的国内,国际,军事,航空,无人机这五个板块下对应的新闻标题和内容
- 分析:
- 1.每一个板块对应页面中的新闻数据是动态加载出来的
- selenium在scrapy中的应用:
- 实例化浏览器对象:写在爬虫类的构造方法中
- 关闭浏览器:爬虫类中的closed(self,spider)关闭浏览器
- 在中间件中执行浏览器自动化的操作
from time import sleep
from scrapy import signals
from scrapy.http import HtmlResponse
class WangyiproDownloaderMiddleware(object):
def process_request(self, request, spider):
return None
def process_response(self, request, response, spider):#spider就是爬虫文件中爬虫类实例化的对象
#进行所有响应对象的拦截
#1.将所有的响应中那五个不满足需求的响应对象找出
#1.每一个响应对象对应唯一一个请求对象
#2.如果我们可以定位到五个响应对应的请求对象后,就可以通过该请求对象定位到指定的响应对象
#3.可以通过五个板块的url定位请求对象
#总结: url==》request==》response
#2.将找出的五个不满足需求的响应对象进行修正(替换)
#spider.five_model_urls:五个板块对应的url
bro = spider.bro
if request.url in spider.five_model_urls:
bro.get(request.url)
sleep(1)
page_text = bro.page_source #包含了动态加载的新闻数据
#如果if条件程利则该response就是五个板块对应的响应对象
new_response = HtmlResponse(url=request.url,body=page_text,encoding='utf-8',request=request)
return new_response
return response
import scrapy
from selenium import webdriver
from wangyiPro.items import WangyiproItem
class WangyiSpider(scrapy.Spider):
name = 'wangyi'
# allowed_domains = ['www.xxx.com']
start_urls = ['https://news.163.com']
five_model_urls = []
bro = webdriver.Chrome(executable_path=r'D:\tools\chromedriver.exe')
#用来解析五个板块对应的url,然后对其进行手动请求发送
def parse(self, response):
model_index = [3,4,6,7,8]
li_list = response.xpath('//*[@id="index2016_wrap"]/div[1]/div[2]/div[2]/div[2]/div[2]/div/ul/li')
for index in model_index:
li = li_list[index]
#获取了五个板块对应的url
model_url = li.xpath('./a/@href').extract_first()
self.five_model_urls.append(model_url)
#对每一个板块的url进行手动i请求发送
yield scrapy.Request(model_url,callback=self.parse_model)
#解析每一个板块页面中的新闻标题和新闻详情页的url
#问题:response(不满足需求的response)中并没有包含每一个板块中动态加载出的新闻数据
def parse_model(self,response):
div_list = response.xpath('/html/body/div[1]/div[3]/div[4]/div[1]/div/div/ul/li/div/div')
for div in div_list:
title = div.xpath('./div/div[1]/h3/a/text()').extract_first()
detail_url = div.xpath('./div/div[1]/h3/a/@href').extract_first()
item = WangyiproItem()
item['title'] = title
# 对详情页发起请求解析出新闻内容
yield scrapy.Request(detail_url,callback=self.parse_new_content,meta={'item':item})
def parse_new_content(self,response): #解析新闻内容
item = response.meta['item']
content = response.xpath('//*[@id="endText"]//text()').extract()
content = ''.join(content)
item['content'] = content
yield item
#最后执行
def closed(self,spider):
self.bro.quit()