MCPcopy Index your code
hub / github.com/James-LG/Skyscraper

github.com/James-LG/Skyscraper @v0.7.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.7.0 ↗ · + Follow
2,660 symbols 10,806 edges 152 files 369 documented · 14%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Skyscraper - HTML scraping with XPath

Dependency Status License MIT Crates.io doc.rs

Rust library to scrape HTML documents with XPath expressions.

This library is major-version 0 as the API is still evolving. See the Supported XPath Features section for details.

HTML Parsing

Skyscraper has its own HTML parser implementation. The parser outputs a tree structure that can be traversed manually with parent/child relationships.

Example: Simple HTML Parsing

use skyscraper::html::{self, parse::ParseError};
let html_text = r##"
<html>
    <body>


Hello world


    </body>
</html>"##;

let document = html::parse(html_text)?;

Example: Traversing Parent/Child Relationships

// Parse the HTML text into a document
let text = r#"<parent><child/><child/></parent>"#;
let document = html::parse(text)?;

// Get the children of the root node
let parent_node: DocumentNode = document.root_node;
let children: Vec<DocumentNode> = parent_node.children(&document).collect();
assert_eq!(2, children.len());

// Get the parent of both child nodes
let parent_of_child0: DocumentNode = children[0].parent(&document).expect("parent of child 0 missing");
let parent_of_child1: DocumentNode = children[1].parent(&document).expect("parent of child 1 missing");

assert_eq!(parent_node, parent_of_child0);
assert_eq!(parent_node, parent_of_child1);

WHATWG Compliance Note

Skyscraper's HTML parser follows the WHATWG parsing specification. One notable consequence is implicit <tbody> insertion: when <tr>, <td>, or <th> elements appear as direct children of <table>, the parser automatically wraps them in a <tbody> element (per WHATWG §13.2.6.4.9). This matches browser behavior but differs from parsers like Python's lxml, which does not insert <tbody>. As a result, XPath expressions like //table/* or //table//* may return different results than lxml for the same input HTML. To avoid this discrepancy, use explicit <tbody> tags in your HTML or account for the implicit element in your XPath expressions.

XPath Expressions

Skyscraper is capable of parsing XPath strings and applying them to HTML documents.

Below is a basic xpath example. Please see the docs for more examples.

use skyscraper::html;
use skyscraper::xpath::{self, XpathItemTree, grammar::{XpathItemTreeNodeData, data_model::{Node, XpathItem}}};
use std::error::Error;

fn main() -> Result<(), Box<dyn Error>> {
    let html_text = r##"
    <html>
        <body>


Hello world


        </body>
    </html>"##;

    let document = html::parse(html_text)?;
    let xpath_item_tree = XpathItemTree::from(&document);
    let xpath = xpath::parse("//div")?;

    let item_set = xpath.apply(&xpath_item_tree)?;

    assert_eq!(item_set.len(), 1);

    let mut items = item_set.into_iter();

    let item = items
        .next()
        .unwrap();

    let element = item
        .as_node()?
        .as_tree_node()?
        .data
        .as_element_node()?;

    assert_eq!(element.name, "div");
    Ok(())
}

Supported XPath Features

Below is a non-exhaustive list of all the features that are currently supported.

  1. Basic xpath steps: /html/body/div, //div/table//span
  2. Attribute selection: //div/@class
  3. Text selection: //div/text()
  4. Wildcard node selection: //body/*
  5. Predicates:
    1. Attributes: //div[@class='hi']
    2. Indexing: //div[1]
    3. Arbitrary expressions: //div[contains(@class, 'hi')]
  6. Forward axes: child::, descendant::, attribute::, self::, descendant-or-self::, following-sibling::, following::, namespace::
  7. Reverse axes: parent::, ancestor::, preceding-sibling::, preceding::, ancestor-or-self::
  8. Operators:
    1. Logical: and, or
    2. Comparison: =, !=, <, >, <=, >=, eq, ne, lt, gt, le, ge
    3. Arithmetic: +, -, *, div, idiv, mod
    4. String concatenation: ||
    5. Sequence: union/|, intersect, except, to
    6. Simple map: !
    7. Arrow: =>
    8. Node comparison: is, <<, >>
  9. Expressions: if/then/else, for, let, some/every (quantified)
  10. Type expressions: instance of, cast as, castable as, treat as
  11. Functions (100+): string (contains, starts-with, ends-with, substring, concat, normalize-space, upper-case, lower-case, translate, matches, replace, tokenize, ...), numeric (round, floor, ceiling, abs, sum, avg, min, max, ...), boolean (not, true, false, boolean), sequence (count, empty, exists, reverse, sort, distinct-values, head, tail, subsequence, ...), node (name, local-name, root, path, has-children, data, ...), higher-order (for-each, filter, fold-left, fold-right, ...), and more
  12. Maps and arrays construction and access

If your use case requires an unimplemented feature, please open an issue on GitHub.

See docs/features-backlog.md for a detailed list of spec gaps, known limitations, and design decisions.

Extension points exported contracts — how you extend this code

Whitespace (Interface)
(no doc) [6 implementers]
src/xpath/grammar/whitespace_recipes.rs
ParseErrorHandler (Interface)
Handler for parse errors emitted during HTML tokenization and tree construction. Implement this trait to customize how [1 …
src/html/grammar/mod.rs
Sep (Interface)
(no doc) [4 implementers]
src/xpath/grammar/whitespace_recipes.rs
TokenizerErrorHandler (Interface)
(no doc) [1 implementers]
src/html/grammar/tokenizer/mod.rs
Max (Interface)
(no doc) [3 implementers]
src/xpath/grammar/recipes.rs
Parser (Interface)
(no doc) [1 implementers]
src/html/grammar/tokenizer/mod.rs

Core symbols most depended-on inside this repo

insert
called by 2272
src/html/grammar/mod.rs
parse
called by 1986
src/xpath/mod.rs
add_element
called by 858
src/html/grammar/document_builder.rs
apply
called by 796
src/xpath/grammar/expressions/mod.rs
handle_error
called by 223
src/html/grammar/tokenizer/mod.rs
build
called by 192
src/html/grammar/document_builder.rs
len
called by 187
src/xpath/xpath_item_set.rs
add_text
called by 177
src/html/grammar/document_builder.rs

Shape

Function 2,024
Method 440
Class 122
Enum 68
Interface 6

Languages

Rust100%
Python1%

Modules by API surface

tests/xpath_tests/lxml_tests.rs213 symbols
tests/xpath_tests/regression_tests.rs212 symbols
tests/xpath_tests/builtin_function_tests.rs159 symbols
src/html/grammar/mod.rs132 symbols
tests/html_tests/html_tokenizer_tests.rs112 symbols
tests/html_tests/html_regression_tests.rs93 symbols
src/html/grammar/tokenizer/state_impls.rs89 symbols
tests/html_tests/html_in_body_tests.rs56 symbols
src/xpath/grammar/expressions/primary_expressions/static_function_calls.rs54 symbols
src/html/grammar/tokenizer/mod.rs43 symbols
src/xpath/grammar/data_model/mod.rs41 symbols
src/xpath/grammar/types/sequence_type.rs40 symbols

For agents

$ claude mcp add Skyscraper \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact