Table of contents

How to Select Elements by CSS Selector in SwiftSoup

SwiftSoup is a pure Swift HTML parser that provides powerful CSS selector functionality, making it easy to extract specific elements from HTML documents in iOS applications. CSS selectors offer a flexible and intuitive way to navigate and parse HTML content, similar to how you would target elements in web development.

Basic Element Selection

Installing SwiftSoup

First, add SwiftSoup to your iOS project using Swift Package Manager:

// Add to Package.swift dependencies
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.6.0")

Basic Usage Pattern

Here's the fundamental approach to selecting elements with CSS selectors in SwiftSoup:

import SwiftSoup

do {
    let html = """
    <html>
        <body>
            <div class="container">
                <h1 id="title">Welcome</h1>
                <p class="intro">Introduction paragraph</p>
                <ul class="list">
                    <li class="item">Item 1</li>
                    <li class="item">Item 2</li>
                </ul>
            </div>
        </body>
    </html>
    """

    let document = try SwiftSoup.parse(html)

    // Select elements using CSS selectors
    let elements = try document.select("css-selector-here")

    for element in elements {
        print(element.text())
    }
} catch {
    print("Error: \(error)")
}

Common CSS Selector Types

1. Tag Selectors

Select elements by their HTML tag name:

// Select all paragraph elements
let paragraphs = try document.select("p")

// Select all div elements
let divs = try document.select("div")

// Select all anchor tags
let links = try document.select("a")

2. Class Selectors

Select elements by their CSS class using the dot (.) prefix:

// Select elements with class "container"
let containers = try document.select(".container")

// Select elements with class "nav-item"
let navItems = try document.select(".nav-item")

// Select multiple classes (elements must have both classes)
let specificItems = try document.select(".item.active")

3. ID Selectors

Select elements by their ID using the hash (#) prefix:

// Select element with ID "header"
let header = try document.select("#header")

// Select element with ID "main-content"
let mainContent = try document.select("#main-content")

4. Attribute Selectors

Select elements based on their attributes:

// Elements with specific attribute
let elementsWithHref = try document.select("[href]")

// Elements with specific attribute value
let externalLinks = try document.select("[target='_blank']")

// Elements with attribute containing specific text
let mailtoLinks = try document.select("[href*='mailto:']")

// Elements with attribute starting with specific text
let httpsLinks = try document.select("[href^='https://']")

// Elements with attribute ending with specific text
let pdfLinks = try document.select("[href$='.pdf']")

Advanced CSS Selectors

Combinators and Hierarchical Selection

// Descendant combinator (space) - select nested elements
let nestedItems = try document.select("ul li")

// Child combinator (>) - select direct children only
let directChildren = try document.select("ul > li")

// Adjacent sibling combinator (+) - select immediately following sibling
let nextSibling = try document.select("h1 + p")

// General sibling combinator (~) - select all following siblings
let allSiblings = try document.select("h1 ~ p")

Pseudo-selectors

SwiftSoup supports various pseudo-selectors for more precise targeting:

// First child element
let firstItem = try document.select("li:first-child")

// Last child element
let lastItem = try document.select("li:last-child")

// Nth child (1-based indexing)
let thirdItem = try document.select("li:nth-child(3)")

// Even/odd children
let evenItems = try document.select("li:nth-child(even)")
let oddItems = try document.select("li:nth-child(odd)")

// Elements containing specific text
let elementsWithText = try document.select(":contains(Welcome)")

// Empty elements
let emptyElements = try document.select(":empty")

Practical Examples

Extracting Article Information

let html = """
<article class="blog-post">
    <header>
        <h1 class="title">Understanding SwiftSoup</h1>
        <div class="meta">
            <span class="author">John Doe</span>
            <span class="date">2024-01-15</span>
        </div>
    </header>
    <div class="content">
        <p class="intro">This article covers SwiftSoup basics...</p>
        <p>More content here...</p>
    </div>
    <footer>
        <div class="tags">
            <span class="tag">swift</span>
            <span class="tag">html</span>
        </div>
    </footer>
</article>
"""

do {
    let document = try SwiftSoup.parse(html)

    // Extract article title
    let title = try document.select(".blog-post .title").first()?.text() ?? ""

    // Extract author
    let author = try document.select(".meta .author").first()?.text() ?? ""

    // Extract publication date
    let date = try document.select(".meta .date").first()?.text() ?? ""

    // Extract all paragraphs from content
    let contentParagraphs = try document.select(".content p")

    // Extract all tags
    let tags = try document.select(".tags .tag").map { try $0.text() }

    print("Title: \(title)")
    print("Author: \(author)")
    print("Date: \(date)")
    print("Tags: \(tags.joined(separator: ", "))")

} catch {
    print("Parsing error: \(error)")
}

Working with Tables

let tableHTML = """
<table class="data-table">
    <thead>
        <tr>
            <th>Name</th>
            <th>Age</th>
            <th>City</th>
        </tr>
    </thead>
    <tbody>
        <tr class="person" data-id="1">
            <td class="name">Alice</td>
            <td class="age">25</td>
            <td class="city">New York</td>
        </tr>
        <tr class="person" data-id="2">
            <td class="name">Bob</td>
            <td class="age">30</td>
            <td class="city">London</td>
        </tr>
    </tbody>
</table>
"""

do {
    let document = try SwiftSoup.parse(tableHTML)

    // Extract table headers
    let headers = try document.select("thead th").map { try $0.text() }

    // Extract all data rows
    let rows = try document.select("tbody tr.person")

    for row in rows {
        let id = try row.attr("data-id")
        let name = try row.select(".name").first()?.text() ?? ""
        let age = try row.select(".age").first()?.text() ?? ""
        let city = try row.select(".city").first()?.text() ?? ""

        print("ID: \(id), Name: \(name), Age: \(age), City: \(city)")
    }

} catch {
    print("Table parsing error: \(error)")
}

Error Handling and Best Practices

Robust Element Selection

extension Document {
    func safeSelect(_ selector: String) -> Elements? {
        do {
            let elements = try self.select(selector)
            return elements.isEmpty() ? nil : elements
        } catch {
            print("Selector error for '\(selector)': \(error)")
            return nil
        }
    }
}

// Usage
if let elements = document.safeSelect(".content p") {
    for element in elements {
        print(try? element.text() ?? "")
    }
}

Chaining Selectors Safely

func extractNestedContent(from document: Document, selector: String) -> [String] {
    do {
        return try document.select(selector).compactMap { element in
            try element.text().isEmpty ? nil : element.text()
        }
    } catch {
        print("Error extracting content with selector '\(selector)': \(error)")
        return []
    }
}

// Usage
let contentItems = extractNestedContent(from: document, selector: ".article .content p")

Performance Considerations

Efficient Selector Usage

// Prefer specific selectors over broad ones
// Good: Specific path
let specificElements = try document.select("article.blog-post .content p.intro")

// Less efficient: Broad selector
let broadElements = try document.select("p")

// Use parent context when selecting multiple related elements
let article = try document.select("article.blog-post").first()
if let articleElement = article {
    let title = try articleElement.select(".title").first()?.text()
    let content = try articleElement.select(".content p")
}

Caching Selected Elements

struct ArticleParser {
    private let document: Document
    private lazy var articleElement: Element? = {
        try? document.select("article.blog-post").first()
    }()

    init(html: String) throws {
        self.document = try SwiftSoup.parse(html)
    }

    func getTitle() -> String? {
        try? articleElement?.select(".title").first()?.text()
    }

    func getContent() -> [String] {
        guard let article = articleElement else { return [] }
        return (try? article.select(".content p").map { try $0.text() }) ?? []
    }
}

Integration with Web Scraping Workflows

When building comprehensive web scraping solutions, SwiftSoup's CSS selectors can be particularly powerful when combined with proper HTML parsing strategies. For complex dynamic content that requires JavaScript execution, you might need to consider server-side solutions or web scraping APIs that can handle JavaScript-rendered content, similar to how modern browser automation tools handle dynamic pages.

SwiftSoup excels at parsing static HTML content efficiently, making it ideal for scenarios where you need fast, reliable element extraction without the overhead of a full browser engine. This approach is particularly valuable when handling multiple pages or implementing systematic data extraction workflows in iOS applications.

Conclusion

CSS selectors in SwiftSoup provide a powerful and familiar way to extract data from HTML documents in Swift applications. By mastering the various selector types—from basic tag and class selectors to advanced pseudo-selectors and combinators—you can efficiently parse and extract the exact content you need from web pages.

The key to successful HTML parsing with SwiftSoup lies in understanding your target HTML structure, using specific selectors for better performance, implementing proper error handling, and following Swift best practices for safe unwrapping and error management. With these techniques, you can build robust web scraping and HTML parsing functionality directly into your iOS applications.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon