在Python中检查图像URL是否指向真实图像

4
我正在编写一个Python脚本,用于从一系列url下载图片。该脚本在某种程度上有效。我不希望它下载那些不存在的图片。通过使用状态码来处理一些图片,我已经解决了部分问题,但仍然会出现一些不需要的图片。像这些:

bad image enter image description here

这是我的代码:

import os
import requests
import shutil
import random
import urllib.request

def sendRequest(url):
    try:
        page = requests.get(url, stream = True, timeout = 1)

    except Exception:
        print('error exception')
        pass

    else:
        #HERE IS WHERE I DO THE STATUS CODE
        print(page.status_code)
        if (page.status_code == 200):
            return page

    return False

def downloadImage(imageUrl: str, filePath: str):
    img = sendRequest(imageUrl)

    if (img == False):
        return False

    with open(filePath, "wb") as f:
        img.raw.decode_content = True

        try:
            shutil.copyfileobj(img.raw, f)
        except Exception:
            return False

    return True

os.chdir('/Users/nikolasioannou/Desktop')
os.mkdir('folder')

fileURL = 'http://www.image-net.org/api/text/imagenet.synset.geturls?wnid=n04122825'
data = urllib.request.urlopen(fileURL)

output_directory = '/Users/nikolasioannou/Desktop/folder'

line_count = 0

for line in data:
    img_name = str(random.randrange(0, 10000)) + '.jpg'
    image_path = os.path.join(output_directory, img_name)
    downloadImage(line.decode('utf-8'), image_path)
    line_count = line_count + 1
#print(line_count)

感谢您的时间。欢迎提出任何想法。
诚挚地, 尼古拉斯

你可以检查JPEG或PNG的头部和魔数序列。 - juliusmh
谢谢您的快速回复!抱歉,我对Python还比较新,我该怎么做呢?@juliusmh - user9245495
可能是重复的问题:如何检查文件是否为有效的图像文件? - juliusmh
你遇到的问题是获取到了非图片文件,比如HTML页面,还是获取到了无用的占位符图片? - Kevin J. Chase
1个回答

4

您可以检查JPEG或PNG标头及其各自的魔术序列,这通常是有效图像的很好指示器。请查看this以获取更多问题。

您可以查看文件签名(也称为魔数)here。然后,您只需检查response.raw的前n字节即可。

我稍微修改了您的sendRequest / download函数,您应该能够硬编码更多有效的图像文件扩展名,而不仅仅是JPG魔术数字。最终,我测试了代码并且它可以工作(在我的机器上)。只有有效的JPG图像被保存。请注意,我删除了stream = True标志,因为图像非常小,您不需要流式传输。保存变得不那么神秘。看一下:

def sendRequest(url):
    try:
        page = requests.get(url)

    except Exception as e:
        print("error:", e)
        return False

    # check status code
    if (page.status_code != 200):
        return False

    return page

def downloadImage(imageUrl: str, filePath: str):
    img = sendRequest(imageUrl)

    if (img == False):
        return False

    if not img.content[:4] == b'\xff\xd8\xff\xe0': return False

    with open(filePath, "wb") as f:
        f.write(img.content)

    return True

您可以尝试使用Pillow和BytesIO打开图像。
>>> from PIL import Image
>>> from io import BytesIO

>>> i = Image.open(BytesIO(img.content))

尝试执行并查看是否会抛出错误。但第一种解决方案似乎更轻量级-在那里不应该得到任何错误结果。您还可以在im.content中检查字符串"<html>",如果找到则中止-这非常简单,可能也非常有效。

我猜我困惑的地方是文件签名有什么作用?它们会告诉我关于图像文件的什么信息,我怎么知道哪个文件签名适用于一个带有错误URL的图像? - user9245495
基本上,如果服务器的响应或URL不存在等情况,您就不知道服务器会做出什么反应。因此,像JPEG或PNG图像这样的文件具有一系列定义好的常量字节,以便应用程序可以检测文件类型而不依赖于扩展名。您的问题与URL无关。您下载了一堆字节,并且想要检查这些字节是否是图像。我更新了我的问题以进行签名检查。 - juliusmh
哦,我明白了,@juliusmh。谢谢你的解释。我会去看看答案。 - user9245495
啊,天哪,我犯了一个错误,更新后的解决方案现在应该可以工作了吧?希望如此 :) - juliusmh
谢谢,我会尝试一下。 - user9245495
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接