"""Gradio version of the Flipkart ISBN price scraper. Run with: python "Gradio version.py" """ from __future__ import annotations import random import tempfile import time from pathlib import Path from typing import Optional import gradio as gr import pandas as pd import requests from bs4 import BeautifulSoup USER_AGENTS = ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) " "Gecko/20100101 Firefox/141.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_5) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/605.1.15 " "(KHTML, like Gecko) Version/18.5 Safari/605.1.15", "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_5; rv:141.0) " "Gecko/20100101 Firefox/141.0", "Mozilla/5.0 (X11; Linux x86_64; rv:141.0) Gecko/20100101 Firefox/141.0", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Edge/138.0.0.0", "Mozilla/5.0 (Macintosh; Intel Mac OS X 15_5) AppleWebKit/537.36 " "(KHTML, like Gecko) Edge/138.0.0.0", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36", ) REQUEST_TIMEOUT_SECONDS = 20 REQUEST_DELAY_SECONDS = 0.01 # Retains the small delay used by the Streamlit app. def load_workbook(file_path: Optional[str]): """Read the ISBN column once, when the user uploads a workbook.""" if not file_path: return "Upload an Excel file to begin.", None try: source = pd.read_excel( file_path, header=None, dtype={0: "string"}, keep_default_na=False, ) except Exception as exc: return f"Could not read the workbook: {exc}", None if source.empty: return "The workbook does not contain any ISBN rows.", None # The original app expects one input column. Ignore accidental extra columns # rather than letting them overwrite the position reserved for Price. dataframe = source.iloc[:, [0]].copy() dataframe.columns = ["ISBN"] return f"File loaded successfully ({len(dataframe)} rows). Click Start processing.", dataframe def get_flipkart_price(session: requests.Session, isbn: str) -> Optional[str]: """Return the last price found by the selector used in the original app.""" response = session.get( "https://www.flipkart.com/books/pr", params={ "sid": "bks", "q": isbn, "otracker": "categorytree", }, headers={"User-Agent": random.choice(USER_AGENTS)}, timeout=REQUEST_TIMEOUT_SECONDS, ) response.raise_for_status() prices = [ price.get_text(strip=True).lstrip("₹") for price in BeautifulSoup(response.text, "lxml").select("div.hZ3P6w") ] return prices[-1] if prices else None def process_isbns(dataframe, progress=gr.Progress(track_tqdm=False)): """Fetch prices and produce an ISBN/Price CSV file.""" if dataframe is None or len(dataframe) == 0: return "Please upload a non-empty Excel file first.", None, gr.DownloadButton(visible=False) result = dataframe.copy() result["Price"] = pd.NA failures = 0 total_rows = len(result) with requests.Session() as session: for row_number, (index, isbn) in enumerate(result["ISBN"].items(), start=1): isbn_text = str(isbn).strip() if isbn_text: try: result.at[index, "Price"] = get_flipkart_price(session, isbn_text) except requests.RequestException: failures += 1 progress( (row_number, total_rows), desc=f"Processing row {row_number} of {total_rows}", ) time.sleep(REQUEST_DELAY_SECONDS) with tempfile.NamedTemporaryFile( mode="w", suffix=".csv", prefix="flipkart_prices_", encoding="utf-8-sig", newline="", delete=False, ) as output_file: result.to_csv(output_file, index=False) output_path = Path(output_file.name) message = "Processing complete!" if failures: message += f" {failures} request(s) could not be completed; their prices are blank." return message, result, gr.DownloadButton( value=str(output_path), label="Download processed file", visible=True, ) with gr.Blocks(title="Flipkart Scraper") as app: gr.Markdown( """ # Flipkart Scraper ### Instructions - Upload an **Excel (.xlsx, .xls, or .xlsm)** file. - The workbook should have ISBNs in the first column, with no header row. - Use one ISBN per row. - Click **Start processing**, then download the resulting CSV. - Uploaded data is used only for the current session. """ ) workbook_state = gr.State(value=None) workbook = gr.File( label="Upload an Excel file", file_types=[".xlsx", ".xls", ".xlsm"], type="filepath", ) status = gr.Markdown("Upload an Excel file to begin.") start_button = gr.Button("Start processing", variant="primary") preview = gr.Dataframe(label="Processed data", interactive=False) download = gr.DownloadButton("Download processed file", visible=False) workbook.change( load_workbook, inputs=workbook, outputs=[status, workbook_state], ) start_button.click( process_isbns, inputs=workbook_state, outputs=[status, preview, download], ) if __name__ == "__main__": app.queue().launch()