I want to scrap the lululemon website so that it takes the first six products that are displayed, and turns them into buttons that display the product's name, Image, and each link to the url of the item. Here is the URL that I am trying to scrape from as scene in the code below: https://shop.lululemon.com/search?Ntt=mens%20shirts
//
// LuluProducts.swift
import SwiftUI
import UIKit
import WebKit
struct LuluProducts: View {
@State private var productData: [ProductInfo] = []
@State var gender: String = "Male"
@State var clothes: String = "Shirts"
@State private var webView = WKWebView()
@State private var isLoaded = false
@State private var isLoading = true
struct ProductInfo: Identifiable {
let id = UUID()
let title: String
let productURL: String
let imageLink: String
}
var body: some View {
let LulURL = createLuLURL()
ZStack {
if isLoading {
VStack{
ProgressView()
Text("Fetching some recommended products!")
}
} else {
List(productData) { info in
Button(action: {
openURLInSafari(urlString: info.productURL)
}) {
HStack {
CachedImage(urlString: info.imageLink)
.aspectRatio(contentMode: .fit)
.frame(width: 100, height: 100)
VStack(alignment: .leading) {
Text(info.title)
.foregroundColor(.gray)
}
}
}
}
}
WebView(webView: $webView, url: URL(string: LulURL), isLoaded: $isLoaded, onDidFinish: { webView in
self.isLoading = false
let script = """
var products = [];
var productElements = document.querySelectorAll('div.product-tile');
for (var i = 0; i < productElements.length && i < 6; i++) {
var productElement = productElements[i];
var title = productElement.querySelector('.product-card__title').innerText;
var link = productElement.querySelector('.product-card__link').getAttribute('href');
var img = productElement.querySelector('.product-card__image').getAttribute('srcset');
if (link && img) {
products.push({
title: title,
productURL: link,
imageLink: img,
});
}
}
products;
"""
webView.evaluateJavaScript(script) { result, error in
if let productInfoArray = result as? [[String: String]], !productInfoArray.isEmpty {
self.productData = productInfoArray.map { dict in
ProductInfo(title: dict["title"] ?? "",
productURL: dict["productURL"] ?? "",
imageLink: dict["imageLink"] ?? "" )
}
} else if let error = error {
print("JavaScript execution failed: \(error)")
}
print("JavaScript result: \(String(describing: result))")
}
})
.frame(width: 0, height: 0)
.opacity(0)
}
}
func createLuLURL() -> String {
var LulURL = ""
if gender == "Male" {
if clothes == "Shirts" {
LulURL = "https://shop.lululemon.com/search?Ntt=mens%20shirts"
}
if clothes == "Hoodies" {
LulURL = "https://shop.lululemon.com/search?Ntt=mens%20hoodies"
}
if clothes == "Pants" {
LulURL = "https://shop.lululemon.com/search?Ntt=mens%20pants"
}
}
else {
if clothes == "Shirts" {
LulURL = "https://shop.lululemon.com/search?Ntt=womens%20shirts"
}
if clothes == "Hoodies" {
LulURL = "https://shop.lululemon.com/search?Ntt=womens%20hoodies"
}
if clothes == "Pants" {
LulURL = "https://shop.lululemon.com/search?Ntt=womens%20pants"
}
}
return LulURL
}
func openURLInSafari(urlString: String) {
if let url = URL(string: urlString) {
UIApplication.shared.open(url)
}
}
struct WebView: UIViewRepresentable {
@Binding var webView: WKWebView
let url: URL?
@Binding var isLoaded: Bool
let onDidFinish: ((WKWebView) -> Void)?
func makeUIView(context: Context) -> WKWebView {
webView.navigationDelegate = context.coordinator
return webView
}
func updateUIView(_ uiView: WKWebView, context: Context) {
guard !isLoaded, let url = url else { return }
let request = URLRequest(url: url)
uiView.load(request)
isLoaded = true
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, WKNavigationDelegate {
var parent: WebView
init(_ parent: WebView) {
self.parent = parent
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
parent.onDidFinish?(webView)
}
}
}
class ImageCache {
private var cache = NSCache<NSString, UIImage>()
static let shared = ImageCache()
func getImage(urlString: String) -> UIImage? {
return cache.object(forKey: urlString as NSString)
}
func setImage(image: UIImage, urlString: String) {
cache.setObject(image, forKey: urlString as NSString)
}
}
class ImageLoader: ObservableObject {
@Published var image: UIImage?
private var urlString: String
init(urlString: String) {
self.urlString = urlString
loadImage()
}
private func loadImage() {
// Check if the image is already cached
if let cachedImage = ImageCache.shared.getImage(urlString: urlString) {
self.image = cachedImage
return
}
// Image not cached, download it
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, let loadedImage = UIImage(data: data) else { return }
DispatchQueue.main.async {
// Cache the downloaded image
ImageCache.shared.setImage(image: loadedImage, urlString: self.urlString)
self.image = loadedImage
}
}
task.resume()
}
}
struct CachedImage: View {
@ObservedObject private var loader: ImageLoader
init(urlString: String) {
loader = ImageLoader(urlString: urlString)
}
var body: some View {
if let image = loader.image {
Image(uiImage: image)
.resizable()
} else {
ProgressView()
}
}
}
}
struct LuluProducts_Preview: PreviewProvider {
static var previews: some View {
LuluProducts()
}
}
I have tried manually changing the parameters of the script using the html of the page, but no matter what I do the page still comes out as blank. I just want 6 buttons that pull the very first 6 items for each category that link to the item's safari page when pressed