You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask亚马逊爬虫仅显示图片URL文本而非图片,如何修复?

Flask亚马逊爬虫图片无法显示的修复方案

问题描述

我开发了一个Flask应用,可根据用户输入爬取亚马逊网站的商品名称、价格和图片URL。运行程序并输入如“Samsung galaxy s23 ultra”这类关键词时,页面仅显示图片的URL文本,无法正常展示图片。

问题截图

页面仅显示图片URL文本的问题截图

完整代码

app.py

from flask import Flask, render_template, request, session, redirect, url_for
from bs4 import BeautifulSoup
import requests
import concurrent.futures
import sqlite3

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'

header = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
    "Accept-Language": "hr-HR,hr;q=0.9,en-US;q=0.8,en;q=0.7"
}

conn = sqlite3.connect('amazon_data.db', check_same_thread=False)
cursor = conn.cursor()
cursor.execute('''
    CREATE TABLE IF NOT EXISTS products (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT,
        price TEXT,
        image_url TEXT,
        scraping_status TEXT
    )
''')
conn.commit()

def scrape_page(product_name, page_num):
    try:
        URL = f"https://www.amazon.com/s?k={product_name.replace(' ', '+')}&crid=288NMI7Z5E2WR&qid=1702226389&sprefix={product_name.replace(' ', '%2C')}%2Caps%2C572&ref=sr_pg_{page_num}"

        response = requests.get(URL, headers=header)
        response.raise_for_status()

        web_page = response.text
        soup = BeautifulSoup(web_page, 'lxml')

        result_container = soup.find("div", class_="s-main-slot")

        if result_container:
            boxes = result_container.find_all("div", class_="s-result-item")
        else:
            print(f"No search results found on page {page_num}")
            return [], [], []

        local_name_list = []
        local_price_list = []
        local_image_list = []

        for box in boxes:
            try:
                name_elem = box.find("span", class_="a-size-medium a-color-base a-text-normal")
                price_elem = box.find("span", class_="a-offscreen")
                image_elem = box.find("img", class_="s-image")

                if name_elem and price_elem and image_elem:
                    name = name_elem.getText()
                    price = price_elem.getText()
                    image_url = image_elem['src']

                    local_name_list.append(name)
                    local_price_list.append(price)
                    local_image_list.append(image_url)
                    print(f"Product: {name}\nPrice: {price}\nImage URL: {image_url}\n{'=' * 30}")

            except AttributeError as e:
                print(f"Error extracting data from box on page {page_num}: {e}")
                continue

        return local_name_list, local_price_list, local_image_list

    except requests.exceptions.RequestException as e:
        print(f"Error scraping page {page_num}: {e}")
        return [], [], []  # Return empty lists in case of an error

@app.route('/', methods=['GET', 'POST'])
def home():
    if request.method == 'POST':
        product_name = request.form.get('product_name')
        name_list = []
        price_list = []
        image_list = []

        with concurrent.futures.ThreadPoolExecutor() as executor:
            results = executor.map(scrape_page, [product_name] * 18, range(1, 19))

        for result in results:
            name_list.extend(result[0])
            price_list.extend(result[1])
            image_list.extend(result[2])

        if not name_list:
            return render_template('home.html', no_results=True, product_name=product_name)
        else:
            merged_list = list(zip(name_list, price_list, image_list))

            # Store the search results in the session
            session['results'] = merged_list
            session['product_name'] = product_name

            return render_template('home.html', results=merged_list, product_name=product_name)

    # Check if there are stored results in the session
    if 'results' in session:
        results = session['results']
        product_name = session['product_name']
        return render_template('home.html', results=results, product_name=product_name)

    return render_template('home.html')

@app.route('/delete_results', methods=['POST'])
def delete_results():
    # Clear the stored results in the session
    session.pop('results', None)
    session.pop('product_name', None)
    return redirect(url_for('home'))

if __name__ == '__main__':
    app.run(debug=True)

home.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
    <title>Amazon Scraper</title>
</head>
<body>
    <div class="container mt-5">
        <h1 class="mb-4">Amazon Scraper</h1>
        <nav class="mb-4">
            <ul class="nav">
                <li class="nav-item">
                    <a class="nav-link active" href="#">Home</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Subscription</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">About Us</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="#">Contact</a>
                </li>
            </ul>
        </nav>
        <form method="post">
            <div class="input-group mb-3">
                <input type="text" class="form-control" placeholder="Enter product name" name="product_name" required>
                <div class="input-group-append">
                    <button class="btn btn-outline-secondary" type="submit">Search</button>
                </div>
            </div>
        </form>
        {% if results %}
            <form method="post" action="{{ url_for('delete_results') }}">
                <button class="btn btn-danger mb-3" type="submit">Delete Results</button>
            </form>
            <h2>Search results for '{{ product_name }}'</h2>
            <div class="row">
                {% for result in results %}
                    <div class="col-md-4 mb-4">
                        <div class="card">
                            <img src="{{ result[2] }}" class="card-img-top" alt="Product Image">
                            <div class="card-body">
                                <h5 class="card-title">{{ result[0] }}</h5>
                                <p class="card-text">{{ result[1] }}</p>
                            </div>
                        </div>
                    </div>
                {% endfor %}
            </div>
        {% elif no_results %}
            <h2>No search results found for '{{ product_name }}'</h2>
        {% endif %}
    </div>
</body>
</html>

修复方案

核心问题

亚马逊图片URL存在防盗链机制,浏览器直接引用爬取到的URL会被拦截,导致图片无法加载;部分抓取到的URL是临时缩略图链接,也会失效。

具体修改步骤

1. 标准化爬取的图片URL

在scrape_page函数中,获取image_url后添加处理逻辑,将其转换为亚马逊通用的无限制图片地址:

# 在获取image_url后添加以下代码
image_url = image_elem['src']
# 处理相对URL或非标准域名
if image_url.startswith("//"):
    image_url = "https:" + image_url
# 提取图片ID,拼接成标准的亚马逊图片URL
import re
img_id_pattern = r"/I/([A-Za-z0-9._-]+)\.(jpg|jpeg|png)"
match = re.search(img_id_pattern, image_url)
if match:
    image_url = f"https://m.media-amazon.com/images/I/{match.group(1)}.{match.group(2)}"

2. 添加Flask图片代理(彻底解决防盗链)

如果标准化URL仍无效,可通过后端代理请求图片,避免浏览器直接访问亚马逊资源:

  • 在app.py中添加代理路由:
from flask import send_file
import io

@app.route('/proxy-image')
def proxy_image():
    img_url = request.args.get('url')
    if not img_url:
        return "", 400
    try:
        # 使用已有的header请求图片,避免被亚马逊拦截
        response = requests.get(img_url, headers=header)
        response.raise_for_status()
        # 将图片数据转为字节流返回
        img_stream = io.BytesIO(response.content)
        return send_file(img_stream, mimetype=response.headers['Content-Type'])
    except Exception as e:
        print(f"图片代理错误: {e}")
        return "", 404
  • 修改home.html中的img标签:
<img src="{{ url_for('proxy_image', url=result[2]) }}" class="card-img-top" alt="Product Image">

3. 验证URL完整性

在scrape_page函数中打印image_url,确认是完整的HTTPS链接,没有被截断。


内容的提问来源于stack exchange,提问作者SolidOpt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 08:27:02