Scrapy抓站:大批量下载360指定专题下的照片并保存到sql和本地文件夹下

目标网站:http://images.so.com/ (摄影专栏)

1. 新建项目

scrapy startproject images360(名目名)

2. 新建Spider

直接用Pycharm打开上一步所创建的项目,在最下面的Terminal处执行该命令:

scrapy genspider images images.so.com

3. 分析目标网站的种种

  1. 进入目标网站所要爬取的摄影专题的页面下
    在这里插入图片描述
  2. 下拉滚动栏查看图片,显而易见可知是Ajax请求数据的方式,我以前的博客中有对该请求方式的详细说明,f12直接查看XHR下的请求信息:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    可以看到请求方式是GET方式,其是带有参数的请求方式,分析参数得:
    ch指所选的专题名称,sn指该页显示30张图片,listtype指一种排序方式,temp指不知名参数,可变参数只有sn,所以该参数作为翻页更新请求的突破点
  3. 查看数据所在地及其返回格式:
    在这里插入图片描述
    可得数据返回格式为JSON,很好处理。

4. 构造请求

在 images.py 文件下重写定义 start_requests() 方法:

# 此方法用于生成初始请求,它必须返回一个迭代的对象。
    # 此方法会默认使用 start_urls 中的URL来构造 Request,而且 Request 是GET请求方式
    # 若启动时以 POST 请求方式来访问某个站点,可以直接重写这个方法,发送 POST请求时,使用 FormRequest 即可
    def start_requests(self):
        # 设置请求头,因为之 get 请求,其请求参数跟在 url 地址后 ,可以这样设置
        data = {
            'ch':'photography',
            'listtype':'new'
        }
        base_url = 'https://image.so.com/zjl?'
        # 生成 50 次的请求
        for page in range(1,self.settings.get('MAX_PAGE')+1):
            data['sn'] = page * 30
            # 使用 urlencode() 方法,将字典转换为 url 的 GET 参数
            params = urlencode(data)
            url = base_url + params
            print(url)
            yield Request(url,self.parse)

在 setting.py 中定义最大爬取页数的变量,以及修改 ROBOTSTXT_OBEY 的值:

# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# 爬取的页数
MAX_PAGE = 50

5. 定义提取信息的字段

修改 Item.py ,定义自己的字段类:

import scrapy
from scrapy import Field


class ImageItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    collection = table = 'images'
    # 照片的 id
    id = Field()
    # 照片类型
    pic_desc = Field()
    # 照片地址
    url = Field()
    # 照片名字
    title = Field()
    # 照片缩略图
    thumb = Field()

6. 编写 Spider 方法

修改 images.py ,重写 parse() 方法:

    def parse(self, response):
        result = json.loads(response.text)
        for image in result.get('list'):
            item = ImageItem()
            item['id'] = image.get('id')
            item['pic_desc'] = image.get('pic_desc')
            item['title'] = image.get('title')
            item['url'] = image.get('qhimg_url')
            item['thumb'] = image.get('qhimg_thumb')
            yield item

7. 存储信息

7.1 Mysql保存

执行该功能的是 Item Pipeline 项目管道,所以在 pipelines.py 中重写方法并配置连接数据库的相关信息:

  1. 建立数据库,表,字段等信息
    在这里插入图片描述
  2. 实现自己 PymysqlPipeline
class PymysqlPipeline(object):
    #连接数据库
    def __init__(self):
        self.connect = pymysql.connect(
            host = 'localhost',
            database = 'image_360',
            user = 'root',
            password = '123456',
            charset = 'utf8',
            port = 3306
        )
        # 创建游标对象
        self.cursor = self.connect.cursor()

    # 此方法是必须要实现的方法,被定义的 Item Pipeline 会默认调用这个方法对 Item 进行处理
    def process_item(self,item,spider):
        cursor = self.cursor
        sql = 'insert into image_information(id,pic_desc,title,url,thumb) values (%s,%s,%s,%s,%s)'
        cursor.execute(sql,(
            item['id'],item['pic_desc'],item['title'],item['url'],item['thumb']
        ))
        # 提交数据库事务
        self.connect.commit()

        return item

7.2 本地文件保存

同样在 pipelines.py 添加如下的代码即可:

# 文件下载和图片下载,支持异步和多线程
class ImagePipeline(ImagesPipeline):
    # 该方法返回保存的每张图片的名字
    def file_path(self, request, response=None, info=None):
        url = request.url
        file_name = url.split('/')[-1]
        return file_name
    # 单个 Item 完成下载时的处理方法,并不是每张图片都可以下载成功,所以如果某张图片下载失败就不需要保存到数据库
    def item_completed(self, results, item, info):
        image_path = [x['path'] for ok,x in results if ok]
        if not image_path:
            raise DropItem('Image Downloaded Failed')
        return item

    def get_media_requests(self, item, info):
        yield Request(item['url'])

8. 执行程序

在最下面的Terminal处执行该命令:

scrapy crawl images

9. 最终的效果图

在这里插入图片描述
在这里插入图片描述
以上就是整个的流程,比昨天有学到的新东西。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章