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. 最終的效果圖

在這裏插入圖片描述
在這裏插入圖片描述
以上就是整個的流程,比昨天有學到的新東西。

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