Scrapy - 将图像下载到新创建的文件夹,该文件夹具有刚刚在数据库中保存的记录的 ID

问题描述 投票:0回答:1

我在 SO 上发现了类似的问题,但我无法将其拼凑起来使其发挥作用。

我正在从一个网站上抓取数据,其中也有图像。我的

items.py
看起来像这样:

import scrapy

class BookPipeline(scrapy.Item):
    title = scrapy.Field()
    author = scrapy.Field()
    ...
    image_urls = scrapy.Field()
    images = scrapy.Field()

my_spider.py
中如下:

class MySpider(scrapy.Spider):
     ...
     custom_settings = {
        'ITEM_PIPELINES': {
            'project.pipelines.BookPipeline': 400,
            'project.pipelines.CustomImageNamePipeline': 1
        },
        'IMAGES_STORE': 'book_images'
    }
     ...

当我运行蜘蛛时,它会将图像保存到

book_images
文件夹中,这很好。

不过,我想要实现的是将图像保存到

book_images/ID-OF-JUST-SAVED-BOOK-IN-DATABASE
,因此文件将保存在这种结构中:

  • book_images/323/some-hash-name.jpg
  • book_images/323/another-hash-name.jpg
  • book_images/323/different-hash-name.jpg
  • book_images/5211/hash-name.jpg

我在自定义管道中看到了一些代码片段,但不幸的是我无法使其工作。

另外,我还不知道 - 是先用Scrapy下载图片,然后在数据库中创建记录,还是反过来?

编辑:我尝试过这样的事情:

pipeline.py

import scrapy
from scrapy.pipelines.images import ImagesPipeline
import os

class BookPipeline:
  ...

class CustomImageNamePipeline(ImagesPipeline):
    def get_media_requests(self, item, info):
        return [scrapy.Request(x, meta={'image_dir': item["uni_id"]}) 
                for x in item.get('image_urls', [])]

    def file_path(self, request, response=None, info=None, *, item=None):
        url = request.url
        media_guid = hashlib.sha1(to_bytes(url)).hexdigest()
        media_ext = os.path.splitext(url)[1]

        return f'{request.meta["image_dir"]}/%s%s' % (media_guid, media_ext)

但是没有保存文件,也没有错误消息。此外,控制台中没有提及任何下载的文件。

python scrapy scrapy-pipeline
1个回答
0
投票

为此,您需要创建两个管道;一个用于将数据保存到数据库并获取插入记录的 ID,另一个是图像管道。执行顺序取决于

ITEM_PIPELINES
定义中的优先级设置。在这种情况下,您需要数据库管道的数量小于图像管道的数量,即

ITEM_PIPELINES = {
    "project.pipelines.SQLiteDBPipeline": 300,
    "project.pipelines.CustomImageNamePipeline": 400,
}

在数据库管道的

process_item
方法中将项目保存到数据库并将返回的 id 添加到项目定义中。使用 sqlite 的示例如下:

import sqlite3

class SQLiteDBPipeline:

    def __init__(self) -> None:
        self.conn = sqlite3.connect("sqlite.db")

    def process_item(self, item, spider):
        cur = self.conn.cursor()
        # your code to insert into database goes here
        cur.execute("SQL QUERY HERE")
        id = cur.lastrowid
        item["id"] = id
        cur.close()
        return item

在图像管道中,使用 id 值创建所需的文件路径,例如

import hashlib
from scrapy.pipelines.images import ImagesPipeline

class CustomImageNamePipeline(ImagesPipeline):
    def file_path(self, request, response=None, info=None, *, item=None):
        # create a hash using the image url
        image_url_hash = hashlib.shake_256(request.url.encode()).hexdigest(16)
        return f"{item['id']}/{image_url_hash}.jpg"

注意:将

id
作为项目定义中的字段包含在内,以便上述代码正常工作。

import scrapy

class BookItem(scrapy.Item):
    id = scrapy.Field()
    title = scrapy.Field()
    author = scrapy.Field()
    ...
    image_urls = scrapy.Field()
    images = scrapy.Field()
© www.soinside.com 2019 - 2024. All rights reserved.