Use PuppeteerSharp to load the page, wait until the span exists, select it with a CSS selector, read its innerText property, and convert the result to a C# string. The essential extraction is GetPropertyAsync("innerText") followed by JsonValueAsync<string>(). If QuerySelectorAsync finds no match, it returns null, so production code should check the handle before reading it.
Minimal working example
This console-style example launches a headless browser, opens a page, finds span.price, and prints the rendered text. Replace the URL and selector with those used by your page.
using PuppeteerSharp;
await new BrowserFetcher().DownloadAsync();
await using var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true });
await using var page = await browser.NewPageAsync();
await page.GoToAsync("https://example.com");
var span = await page.QuerySelectorAsync("span.price");
if (span is null)
{
throw new InvalidOperationException("The span selector did not match an element.");
}
var textHandle = await span.GetPropertyAsync("innerText");
var text = await textHandle.JsonValueAsync<string>();
Console.WriteLine(text);
QuerySelectorAsync performs a selector lookup in the current page and returns an element handle for the first match. The handle represents an in-page DOM element. When there is no match, the result is null; it is not an empty element that can safely be dereferenced.
Prepare a selector that identifies the right span
The selector is the part most likely to break when a site’s markup changes. Prefer a stable class, an attribute intended for testing, or a meaningful component hook. For example, span.price is clearer than a positional expression such as “the third span.” If several spans share a class, decide whether you need the first match or every match before choosing between the single-element and collection APIs.
#1 Best Overall
- Read Before You Buy — No Video Output: These adapters support charging and USB 2.0 data transfer, but cannot transmit video signals. Except for standard USB webcams (which use USB data only), they are not compatible with HDMI/DisplayPort cables, video-capable USB-C hubs, or docking stations with video output.
- Convert USB-A Ports to USB-C: Designed to connect USB-C earphones, cables, flash drives, card readers, and other USB-C accessories to standard USB-A ports. Plug-and-play with no drivers or software required.
- Aluminum Alloy Housing: Built with a sturdy aluminum alloy shell that aids in heat dissipation and protects against daily wear and scratches. Designed to maintain a stable and secure connection.
- Compact & Travel-Friendly: The ultra-compact design allows the adapter to stay plugged into your device without blocking adjacent ports or adding bulk, reducing wear and tear on your original USB ports.
- 12-Month Warranty: Backed by a 12-month manufacturer warranty for peace of mind. Designed to meet strict quality control standards for reliable everyday performance.
- Single known value: use
QuerySelectorAsync("span.price"). - Repeated values: use
QuerySelectorAllAsync("span.result"). - Markup added by JavaScript: wait for the selector before querying it.
- Custom traversal or fallback behavior: evaluate a function in the page context.
Scrape a span that is rendered after navigation
Navigation can finish before client-side code inserts the target span. Call WaitForSelectorAsync after navigation and before the query. The PuppeteerSharp API describes this operation as waiting for a selector to be added to the DOM.
await page.GoToAsync("https://example.com/product");
await page.WaitForSelectorAsync("span.price");
var span = await page.QuerySelectorAsync("span.price");
if (span is null)
{
throw new InvalidOperationException("The price span was not rendered.");
}
var text = await (await span.GetPropertyAsync("innerText"))
.JsonValueAsync<string>();
Console.WriteLine(text);
Keep the selector used for waiting identical to the selector used for extraction. If the wait completes but the subsequent query is still null, check whether the page changed its markup, navigated again, or placed the content inside a frame.
Extract text from several spans
QuerySelectorAllAsync returns the collection form of selector lookup. Iterate over the handles and apply the same property-extraction pattern to each one. The order is the order returned by the page’s selector query.
var spans = await page.QuerySelectorAllAsync("span.result");
var values = new List<string>();
foreach (var item in spans)
{
var value = await (await item.GetPropertyAsync("innerText"))
.JsonValueAsync<string>();
values.Add(value);
}
foreach (var value in values)
{
Console.WriteLine(value);
}
An empty collection is different from a null single-element handle: it means the collection query matched no elements. Treat that as an expected condition when a result list can legitimately be empty, or raise an error when at least one value is required.
Recommended Free Tools
Rank #2
- 5-in-1 USB-C Hub: Experience comprehensive connectivity featuring a Power Delivery input, two USB-A 2.0 ports, a USB-A 3.0 port, and an HDMI port. (Note: The USB-C power delivery input port is only for connecting an external wall charger to power your laptop and cannot power peripheral devices.)
- 90W Pass-Through Charging: Achieve optimal charging with 90W pass-through power to your laptop, supported by a total input of 100W, with the hub reserving 10W for operational efficiency. (Note: Wall charger not included.)
- Quick Data Transfers: Accelerate your productivity with rapid data transfers using a high-speed 5Gbps USB 3.0 port and two 480Mbps USB 2.0 ports.
- 4K HDMI Display: Enhance your visual experience with a hub capable of delivering 4K resolution at 30Hz in both mirror and extend modes. Please note that this hub is compatible with MacBook (macOS 12 and newer), Windows 10 and 11, ChromeOS, and laptops equipped with DP Alt Mode and Power Delivery. Note: This device is not compatible with Linux.
- What You Get: Anker USB-C Hub (5-in-1, 4K HDMI), welcome guide, 18-month warranty, and our friendly customer service.
Use page-side evaluation for custom extraction
When you need a fallback, nested lookup, or a small piece of JavaScript logic, evaluate a function in the page context. This example returns an empty string when the selector does not match.
var text = await page.EvaluateFunctionAsync<string>(
"selector => document.querySelector(selector)?.innerText ?? ''",
"span.price");
Console.WriteLine(text);
EvaluateFunctionAsync is available on the Page and Frame APIs. Use it when the extraction rule is easier to express in JavaScript than as a sequence of element-handle operations. For strict data pipelines, the explicit null check shown earlier is usually preferable because it reports a missing selector instead of silently producing an empty value.
Choose the right extraction pattern
| Situation | API pattern | Result to handle |
|---|---|---|
| One span in already-loaded markup | QuerySelectorAsync then GetPropertyAsync("innerText") |
A single element handle, or null when there is no match |
| Span inserted after navigation | WaitForSelectorAsync, then the single-element pattern |
The wait must target the same selector you intend to read |
| Several matching spans | QuerySelectorAllAsync and a loop |
A collection that can contain zero or more handles |
| Custom JavaScript or nullable fallback | EvaluateFunctionAsync<string> |
The function’s returned string, including an intentionally chosen empty fallback |
Handle missing spans deliberately
A null handle normally means one of three things: the selector is wrong, the content has not been inserted yet, or the query is running in the wrong document context. Include both the URL and selector in your exception so a failed job can be diagnosed without reproducing it interactively.
var url = "https://example.com/product";
var selector = "span.price";
await page.GoToAsync(url);
await page.WaitForSelectorAsync(selector);
var span = await page.QuerySelectorAsync(selector);
if (span is null)
{
throw new InvalidOperationException(
$"No element matched '{selector}' at {url}.");
}
var value = await (await span.GetPropertyAsync("innerText"))
.JsonValueAsync<string>();
If an absent span is valid for a particular page, use the evaluation fallback or return a nullable result from your own method instead of throwing. The important choice is to make the missing-data policy explicit.
Rank #3
- Sleek 7-in-1 USB-C Hub: Features an HDMI port, two USB-A 3.0 ports, and a USB-C data port, each providing 5Gbps transfer speeds. It also includes a USB-C PD input port for charging up to 100W and dual SD and TF card slots, all in a compact design.
- Flawless 4K@60Hz Video with HDMI: Delivers exceptional clarity and smoothness with its 4K@60Hz HDMI port, making it ideal for high-definition presentations and entertainment. (Note: Only the HDMI port supports video projection; the USB-C port is for data transfer only.)
- Double Up on Efficiency: The two USB-A 3.0 ports and a USB-C port support a fast 5Gbps data rate, significantly boosting your transfer speeds and improving productivity.
- Fast and Reliable 85W Charging: Offers high-capacity, speedy charging for laptops up to 85W, so you spend less time tethered to an outlet and more time being productive.
- What You Get: Anker USB-C Hub (7-in-1), welcome guide, 18-month warranty, and our friendly customer service.
When the span is inside an iframe
Selector queries run against the current page or frame. If the visible span belongs to an iframe, obtain that frame first and perform the same WaitForSelectorAsync, QuerySelectorAsync, and property-extraction operations against the frame’s page context. Querying the top-level page will not find an element that exists only inside the embedded document.
After switching frames, keep the selector specific to that frame and include the frame URL or identifying attribute in diagnostic messages. This prevents a valid selector from being mistaken for a missing element simply because it was evaluated in the wrong context.
Reliability and performance practices
- Reuse a page when appropriate. If a job visits many URLs, avoid launching a new browser for every span. Navigate the existing page, extract the value, and then continue.
- Wait narrowly. Wait for the target selector rather than relying only on navigation completion; this reduces races with client-side rendering.
- Batch repeated values. Use
QuerySelectorAllAsynconce and iterate over the returned handles instead of issuing a separate selector query for each position. - Keep selectors stable. Prefer semantic classes or data attributes over positional selectors that change when unrelated markup is inserted.
- Dispose resources. Use
await usingfor browser and page resources in long-running jobs so processes do not accumulate open browser instances. - Do not claim a benchmark that does not exist. The official API material for these operations provides behavior and signatures, not a published extraction-speed figure. Measure your own workload if throughput matters.
Troubleshooting common failures
QuerySelectorAsync returns null
Verify the selector in the page’s final DOM, then add WaitForSelectorAsync if JavaScript inserts the span after navigation. Also verify that you are querying the correct frame.
The wait never reaches the extraction code
The selector may never be added, may differ between states, or may be rendered only after an action. Confirm the exact selector and the page state required to produce it. If the element is optional, use a nullable or page-side fallback rather than waiting for a required element.
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Rank #4
- Dual Converters, Infinite Potential:Includes 2× USB C male to USB A female adapters and 2× USB A male to USB C female adapters. Perfect for a wide range of uses—tablets with Bluetooth keyboards, expand USB ports on macbook, and more. Two different converters for all your daily needs
- Next-Level 10Gbps & 3A Charging: No more slow 480Mbps, this usb to usb c adapter has a transfer speed of up to 10Gbps, allowing you to do more transferring in less time. This usb adapter fits both USB A and USB C charger, supporting up to 3A fast charging
- Upgraded Exquisite Craftsmanship: With an aluminum alloy housing and metal connector, the usbc to usb adapter is extremely durable and sturdy. Rigorously tested to withstand more than 10,000 times of plugging and unplugging, ensuring long-lasting performance
- Broad Compatible: The usb c to usb adapter widely supports all USB C/ USB A devices like laptops, tablets, cellphones, car chargers, and phone chargers. Such as compatible with MacBook Pro/Air 2023/2022, Thunderbolt 4/3 Devices,Apple MagSafe Watch 9/8/7/SE/Ultra, iPad Pro 2022/2021, Samsung Galaxy S23/S20/S10, and iPhone 17/16/15 Pro. Plug and play
- Please Note: To reach 10Gbps speed, keep the cable under 3.3 ft. For USB A Male to USB C adapters, try flipping the USB C connector. USB C Male to USB A adapters support bidirectional 10Gbps transfer within 3.3 ft
The returned string is empty
Check that the matched node is the intended span and that the page has reached the state containing its text. For custom rules, inspect the function passed to EvaluateFunctionAsync and decide whether an empty fallback is masking a selector mismatch.
The script works once but fails in a long-running process
Ensure browser and page objects are disposed with await using. Keep failure messages tied to the URL and selector so intermittent navigation or markup changes can be identified from logs.
The selector finds a different value after a redesign
Replace positional selectors with a stable class or data attribute, and add a test that asserts the extracted value is present before saving it.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Or skip the browser setup
If your goal is a visual capture of a page rather than reading a span’s text, ScreenshotNeo provides a website screenshot API and MCP server. A GET request returns a PNG, JPEG, WebP, or PDF. It is not a replacement for PuppeteerSharp when you need structured text, but it can remove the browser-installation and rendering code from screenshot workflows.
Best Value
- 5-in-1 Connectivity: Equipped with a 4K HDMI port, a 5 Gbps USB-C data port, two 5 Gbps USB-A ports, and a USB C 100W PD-IN port. Note: The USB C 100W PD-IN port supports only charging and does not support data transfer devices such as headphones or speakers.
- Powerful Pass-Through Charging: Supports up to 85W pass-through charging so you can power up your laptop while you use the hub. Note: Pass-through charging requires a charger (not included). Note: To achieve full power for iPad, we recommend using a 45W wall charger.
- Transfer Files in Seconds: Move files to and from your laptop at speeds of up to 5 Gbps via the USB-C and USB-A data ports. Note: The USB C 5Gbps Data port does not support video output.
- HD Display: Connect to the HDMI port to stream or mirror content to an external monitor in resolutions of up to 4K@30Hz. Note: The USB-C ports do not support video output.
- What You Get: Anker 332 USB-C Hub (5-in-1), welcome guide, our worry-free 18-month warranty, and friendly customer service.
ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers. Its MCP server exposes take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients.
The service has 63 capture options, including full-page screenshots with lazy images loaded, CSS-selector element capture, dark mode, device and viewport settings, retina scale, PDF paper and margin controls, custom CSS and JavaScript, click-before-capture actions, selector or network-idle waits, request and resource blocking, custom headers, cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable caching, signed image links, asynchronous webhooks, bulk capture of up to 100 URLs per call, a usage API, and an OpenAPI specification. Parameter names used by other screenshot APIs are accepted to ease migration.
Use the API documentation at https://screenshotneo.com/docs/ for authentication and options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/product -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://example.com/product"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://example.com/product'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
The Free plan includes 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing provides two months free, and every feature is available on every plan. Sign up for the free ScreenshotNeo plan to try it without entering a card.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

