我比较喜欢用写博客的方式来分析我的思路顺便分享我的代码
爬虫文件:pic360spider.py
# -*- coding: utf-8 -*-
import scrapy
import json
from urllib.parse import urlencode
from scrapy.http import Request
from pic360.items import Pic360Item
class Pic360spiderSpider(scrapy.Spider):
name = 'pic360spider'
allowed_domains = ['image.so.com']
start_urls = ['https://image.so.com/zj?ch=beauty&sn=120&listtype=new&temp=1',
'https://image.so.com/zj?ch=beauty&sn=150&listtype=new&temp=1',
'https://image.so.com/zj?ch=beauty&sn=180&listtype=new&temp=1',
'https://image.so.com/zj?ch=beauty&sn=210&listtype=new&temp=1']
def parse(self, response):
json_res = json.loads(response.text)
# 因为响应是一个json类型的实体,所已我用json解析一下
if 'list' in json_res:
for one_group in json_res.get('list'):
pic_id = one_group.get('id')
group_title = one_group.get('group_title')
params = {'ch':'beauty','id':pic_id}
paras = urlencode(params)
# 当要为URL加入查询字符串的时候,这种方式很不错!
group_url = 'https://image.so.com/zvj?'+paras
yield Request(url=group_url,meta={'title':group_title},callback=self.par_group)
# 对于meta原信息的传递,我感觉可以很熟练的应用了
# meta理论上可以传递任意类型的数据给下一层函数
def par_group(self,response):
group_title = response.meta['title']
json_resp = json.loads(response.text)
image_urls = []
if 'list' in json_resp:
for pic_mes in json_resp.get('list'):
pic_url = pic_mes.get('pic_url')
# 这里注意在图片的url必须是一个列表,而从json中提取的是一个字符串,所以要用【】
image_urls.append(pic_url)
item = Pic360Item({'image_url':image_urls,'group_title':group_title})
# 对于item的赋值,我们这里用字典的方式也是可以的
# 这里我们减少了返回item的返回个数,通过将多个url添加到一个item的image_url中去,我们可以在一个item中返回多个url,注意:对于图片的下载我们的链接字段必须是列表
yield item
管道文件:pipelines.py
from scrapy.pipelines.images import ImagesPipeline
from scrapy.http import Request
from scrapy.exceptions import DropItem
class Pic360Pipeline(object):
def open_spider(self,spider):
self.file = open('pic_mess.txt','w')
# 爬虫开始运行时就会自动调用这个方法,打开文件
def process_item(self, item, spider):
self.file.write(item['group_title'])
self.file.write('\n')
return item
# 用于处理item的函数(必须的),也是必须返回item
def close_spider(self,spider):
self.file.close()
# 爬虫结束时自动调用,关闭文件
# 使用open_spider和close_spider避免了多次的IO操作
class MyImagePipeline(ImagesPipeline):
# 自定义的图片下载管道
def get_media_requests(self, item, info):
for image_url in item['image_url']:
# 从这里我们可以看出来为什么image_url必须是一个列表了!
yield Request(url=image_url)
# 下载图片
def item_completed(self, results, item, info):
# 上面一个方法返回的results会自动传到这个方法中去
image_path = [x['path'] for ok, x in results if ok]
if not image_path:
raise DropItem("此条目中没有图片")
item['image_path'] = image_path
return item
# 也必须返回一个item
设置文件:settings.py
ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 2
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'Referer': 'https://image.so.com/zv?ch=beauty',
'Host': 'image.so.com',
}
ITEM_PIPELINES = {
'pic360.pipelines.Pic360Pipeline': 300,
'pic360.pipelines.MyImagePipeline':1
}
IMAGES_STORE = 'D:/pictures/360pic'
# 这个是图片下载存储的位置
IMAGES_URLS_FIELD = 'image_url'
# 这个指明了图片下载管道应该在哪个字段获取图片下载的链接
IMAGES_RESULT_FIELD = 'image'
# 这个是图片结果的存储位置
# 这些设置是图片下载管道必须的(当然你可以选择默认的字段就不用)
items文件:items.py
from scrapy import Item,Field
class Pic360Item(Item):
image_url = Field()
image = Field()
group_title = Field()
image_path = Field()