为什么用美汤解析网页会得到图片的src属性值为base64字符串?

Why does parsing a webpage with beautiful soup leads to src attributes of images with values as base64 strings?

我正在尝试使用 requests 下载网站的 HTML 代码并使用 beautiful soup 解析它以获取我要下载的一些图像的源代码。

import requests, bs4

res = requests.get('https://www.tutti.ch/de/li/bern?q=gartenstuhl')
soup = bs4.BeautifulSoup(res.text, 'html.parser')
images = soup.select('div[style] > img')
for i in images:
    print(i.get('src'))

问题是:我返回数据 URL,这些 URL 似乎对下载图像没有用。

我需要每个 src 属性都是 link,例如 'https://c.tutti.ch/images/4523452354235.jpg'。如何将 src 属性作为普通 python 字符串数据类型获取?有没有一种简单的方法可以将数据 URL 转换为普通 URL?

我从 Mozilla and GeeksforGeeks 找到了这些资源。我认为这归结为如何解码 base64 字符串的问题。

编辑:新代码片段

res = requests.get('https://www.tutti.ch/de/li/bern?q=gartenstuhl')
res.raise_for_status()
soup = bs4.BeautifulSoup(res.text, 'html.parser')

#Check for errors from here
images = soup.select('div[style] > img')

for im in images:
    imageURL = im.get('src') # get the URL of the image
    print('Downloading image %s...' % (imageURL))
    res = requests.get(imageURL) # downloads the image
    res.raise_for_status()
    imageFile = open(os.path.join('tuttiBilder', os.path.basename(imageURL)), 'wb') # creates an image file
    for chunk in res.iter_content(100000): # writes to the image file
        imageFile.write(chunk)
    imageFile.close()
print('Done.')
browser.quit()

您需要用这个 images = soup.find_all('img') 更改第 6 行并将所有这些添加到一个数组中而不是删除 data:image/gif...

import requests
from bs4 import BeautifulSoup

res = requests.get('https://www.tutti.ch/de/li/bern?q=gartenstuhl')
soup = BeautifulSoup(res.text, 'html.parser')
images = soup.find_all('img')

list = [img['src'] for img in images if img.has_attr('src')]

for element in list:
    if element[0] == 'd':
        list.remove(element)

print(list)

输出:

[
'https://c.tutti.ch/images/2506797800.jpg',
'https://c.tutti.ch/images/2542095444.jpg',
'https://c.tutti.ch/images/2525772665.jpg',
'https://c.tutti.ch/images/2544845483.jpg',
'https://c.tutti.ch/images/0256568721.jpg',
'https://c.tutti.ch/images/2466120894.jpg',
'https://c.tutti.ch/images/5890425922.jpg',
'https://c.tutti.ch/images/6251267695.jpg',
'https://c.tutti.ch/images/6201238303.jpg',
'https://c.tutti.ch/images/2277802675.jpg',
'https://c.tutti.ch/images/2273705997.jpg', 
...
]

data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7 是 one-pixel 由单个黑点组成的图像的 GIF 编码。所以你可能不需要花很多力气来解码它。

数据URLs,将一个HTTP数据object的内容直接编码成一个URL(这样就没有关联文件)有时用来嵌入很小将图像直接放入 HTML 文档中,从而节省了请求和接收小图像的成本。对于小文件,请求和回复 headers 的开销可能远大于文件本身的大小(例如 single-pixel GIF 是 42 字节),因此直接嵌入数据是有意义的,即使考虑到 Base64 编码需要四个字节来编码三个字节的数据这一事实。因此有时能够将数据 URL 转换为文件是很有用的。

数据URL以data:开头,后跟“媒体类型”、逗号和数据字符串,只能包含URL个字符。媒体类型描述了数据的格式;它由所谓的“mimetype”组成——一对由斜杠分隔的单词,如 image/gif [注 1]——可能后跟属性列表,每个属性都以分号开头。如果最后一个属性是单词 base64,则二进制数据已用 Base-64 编码。没有 base64 属性的数据 URLs 与 URLs 一样是“%-encoded”;任何特殊字符都以 % 后跟两个十六进制数字的形式发送。

所以只能处理 GIF 的 simple-minded 解码器可能看起来像:

import base64
import urllib.parse

def dataURI_decode(uri):
    if uri.startswith('data:'):
        dataFormat, data = uri.split(',', 1)
        mimetype, *attrs = dataFormat.split(';')
        if attrs and attrs[-1] == 'base64':
            return mimetype, base64.b64decode(data)
        else:
            return mimetype, urllib.parse.unquote_to_bytes(data)

# Sample use to save a GIF from a data url:
uri = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
mimetype, gif = dataURI_decode(uri)
if (mimetype == 'image/gif'):
    with open("data.gif", "wb") as f:
        f.write(gif)

备注

  1. 有一个已知 MIME 类型字符串的全局注册表;例如,image/gif 标识 GIF 文件(或数据流。这些字符串还用于标识通过 HTTP(或 e-mail 消息发送的文件的文件类型)。(您不需要发送文件时必须使用已注册的 mimetype 字符串,但接收文件的浏览器或电子邮件客户端很可能会错误地处理它。)