NEWScrapingAnt MCP for Claude Code, Cursor & Windsurf — try it free →
Skip to main content

245 posts tagged with "data extraction"

View All Tags

Web Scraping with Playwright Series Part 1 - Getting Started

· 7 min read
Satyam Tripathi
Satyam is a junior data engineer and seasoned blogger. He has created several top-ranked tutorials on different topics like web scraping, automation, and scraping tools. He is always open to working with new technologies in the market and sharing his knowledge.

Web Scraping with Playwright Series Part 1 - Getting Started

Introducing the 4-Part Series on Web Scraping with Playwright! This comprehensive series will delve into web scraping using Playwright, a powerful and versatile tool for automating browser interactions.

By the end of this series, you'll have a solid understanding of web scraping with Playwright. You'll be able to build robust scrapers that can handle dynamic content, efficiently store data, and navigate through anti-scraping mechanisms.

In Part 1, you'll learn about the basics of Playwright, why it's useful, how to set up the environment, how to launch the browser using Playwright, and how to take screenshots.

Requests vs HTTPX in 2026: Measured Differences and Migration Gotchas

· 26 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

Requests vs HTTPX in 2026: Measured Differences and Migration Gotchas

Updated 2026-09-17

Every timing on this page now comes from one local server run by the evidence packet on requests 2.34.2 and httpx 0.28.1. The earlier version's benchmark table, which quoted third-party posts and unsourced figures, is gone. New: connection counting, threads vs asyncio, HTTP/2 on the same server, what "retries" means in each library, the errors a migration hits, and what the httpx 1.0 pre-release does to 0.28 code.

requests and httpx make the same request with almost the same line of code. The differences that matter are elsewhere: whether a connection is reused, how you run 200 requests at once, which protocol you speak, what happens by default on a redirect or a slow server, and what "retry" means. This page measures each of those on one server, then lists what breaks when you move a requests codebase to httpx 0.28.

The short version, from the tables below (Apple Silicon laptop, loopback, Python 3.12.11; the ratios are the claim, not the seconds):

Caserequests 2.34.2httpx 0.28.1
300 sequential GETs, one Session / Client (plain HTTP)0.150 s, 1 connection0.126 s, 1 connection
300 sequential GETs, no Session / Client (plain HTTP)0.217 s, 300 connections3.090 s, 300 connections (an SSL context is built per call)
200 GETs with a 50 ms server delay, 20 threads0.543 s (32 connections; 20 with pool_maxsize=20)0.545 s, 20 connections
Same 200 GETs, AsyncClient + asyncio.gather, Semaphore(20)not available0.558 s, 20 connections
Same 200 GETs over TLS, no semaphore (httpx caps in-flight work at 100), HTTP/1.1 vs HTTP/2HTTP/1.1 only0.476 s, 200 connections vs 0.171 s, 1 connection
50 MiB download, streamed, peak RSS35 MiB44 MiB
Retries against a 503urllib3.Retry(status_forcelist=[503]): 4 requests sentHTTPTransport(retries=3): 1 request (retries cover connection failures only)

Pick requests when the code is synchronous, the targets speak HTTP/1.1, and you want the larger ecosystem (plugins, mocks, examples). Pick httpx when you are inside asyncio (or an async framework), when you want HTTP/2, or when you want timeouts on by default. On a single reused connection the two are within a third of each other, and the one case where requests came out ahead was the loopback streaming test; nothing here says one is "faster".

BeautifulSoup Cheat Sheet (bs4 4.15): Tested Snippets, Parsers, Traps

· 20 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

BeautifulSoup Cheat Sheet (bs4 4.15): Tested Snippets, Parsers, Traps

Updated 2026-09-17

Rewritten as a tested cheat sheet. Every snippet below was run by the example package against one fixture page on beautifulsoup4 4.15.0, and the output is copied from that run. The 2024 version had no captured output (only inline # Output: comments), used the text= argument that has warned since 4.11, passed from_encoding to a str (ignored), and called pd.read_html with a string that pandas 3 now treats as a filename; all corrected. New: parsers compared on broken markup, a CPU-time table with min/median/max columns and a note on how much such timings move under load, the 4.13–4.15 deprecations captured with a type-checked example, and the errors you will actually see.

pip install beautifulsoup4 lxml # html5lib, requests and pandas only for the sections that use them
from bs4 import BeautifulSoup
with open("fixtures/page.html", encoding="utf-8") as f:
PAGE = f.read()

soup = BeautifulSoup(PAGE, "html.parser") # from a str
print(soup.title.string, "|", soup.h1.get_text(" ", strip=True))
Ant Supply — catalogue | Products (3)

That is the whole idea: parse, find, read. The sheet below is the rest of it, every line run against the same fixture page (a small catalogue with a nav, three product cards, a table, a comment and a script).

The best Python HTTP clients

· 8 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

The best Python HTTP clients

Python has emerged as a dominant language due to its simplicity and versatility. One crucial aspect of web development and scraping is making HTTP requests, and Python offers a rich ecosystem of libraries tailored for this purpose.

This report delves into the best Python HTTP clients, exploring their unique features and use cases. From the ubiquitous Requests library, known for its simplicity and ease of use, to the modern and asynchronous HTTPX, which supports the latest protocols like HTTP/2 and WebSockets, there is a tool for every need. Additionally, libraries like aiohttp offer versatile async capabilities, making them ideal for real-time data scraping tasks.

For those requiring low-level control, urllib3 stands out with its robust and flexible features. On the other hand, Uplink provides a declarative approach to API interactions, while GRequests combines the simplicity of Requests with the power of Gevent's asynchronous capabilities. This report also highlights best practices for making HTTP requests and provides a comprehensive guide to efficient web scraping using HTTPX and ScrapingAnt. By understanding the strengths and weaknesses of each library, developers can make informed decisions and choose the best tool for their web scraping and development tasks.

Python Requests: Ignore SSL Certificate Errors, and the Right Way

· 15 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

Python Requests: Ignore SSL Certificate Errors, and the Right Way

Updated 2026-09-16

Rewritten as a tested reference. Every call shown below was executed by the example package against a local HTTPS server with a self-signed certificate; the output blocks are what it printed (see "How the outputs were captured"). Four recipes from the 2024 version were wrong and are shown failing here: passing an ssl.SSLContext as verify=, REQUESTS_CA_BUNDLE="", "verify=False disables verification globally", and the "certificate pinning" example built on the same invalid verify= call.

You called requests.get() and got this:

requests.exceptions.SSLError: HTTPSConnectionPool(host='localhost', port=8443): Max retries exceeded with url: / (Caused by SSLError(SSLCertVerificationError(1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate (_ssl.c:1010)')))

The two-line way to make it go away:

import requests, urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
response = requests.get("https://localhost:8443/", verify=False)

And the one-line way to fix it instead of hiding it, when you have the server's certificate or your company's CA file:

response = requests.get("https://localhost:8443/", verify="certs/localhost.pem")

The rest of this page shows what each option does, with the real output, including the recipes you will find elsewhere that do nothing or crash in current requests.

How to Ignore SSL Certificate Errors With Wget (and Trust One Instead)

· 15 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

How to Ignore SSL Certificate Errors With Wget (and Trust One Instead)

Updated 2026-09-16

Rewritten as a tested reference. Every command below was run by the example package against three local HTTPS servers (a self-signed certificate, an expired one, and one issued by a local internal CA) and the output is copied from that run: wget 1.25.0 built with OpenSSL on macOS, plus the same core cases on Debian's GnuTLS build of wget 1.21.3 in a container. The 2024 version presented SSL_CERT_FILE as a wget feature; measured, it works on the OpenSSL build and is ignored on the GnuTLS build. New: the error messages and their causes on both builds, .wgetrc, --ca-directory, --pinnedpubkey, the internal-CA case, exit codes. Gone: a stray "Meta Description" section, unsourced breach statistics, and a wrapper script.

The flag you are looking for, run against the package's self-signed test server (substitute your URL):

wget -nv -O- --no-check-certificate https://localhost:8443/index.html
WARNING: cannot verify localhost's certificate, issued by 'CN=localhost':
Self-signed certificate encountered.
<!doctype html><title>self-signed fixture</title><p>served over HTTPS</p>
<time> URL:https://localhost:8443/index.html [74/74] -> "-" [1]
exit=0

The download proceeds and the warning stays. The one-line way to make the error go away without switching verification off, when you have the server's certificate or your CA file:

wget -nv -O- --ca-certificate=certs/localhost.pem https://localhost:8443/index.html
<!doctype html><title>self-signed fixture</title><p>served over HTTPS</p>
<time> URL:https://localhost:8443/index.html [74/74] -> "-" [1]
exit=0

The rest of this page shows what each option does with the real output, how to read the different error messages, how to make a setting permanent, and what does and does not work depending on how your wget was built. Prefer curl? See How to ignore SSL certificate errors in cURL; the same problem in Python is in Python Requests: ignore SSL certificate errors.

Scrape a Dynamic Website with C++

· 16 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

Scrape a Dynamic Website with C++

Web scraping has become an indispensable tool for acquiring data from websites, especially in the era of big data and data-driven decision-making. However, the complexity of scraping has increased with the advent of dynamic websites, which generate content on-the-fly using JavaScript and AJAX. Unlike static websites, which serve pre-built HTML pages, dynamic websites respond to user interactions and real-time data updates, making traditional scraping techniques ineffective.

To navigate this landscape, developers need to understand the intricacies of client-side and server-side rendering, the role of JavaScript frameworks such as React, Angular, and Vue.js, and the importance of AJAX for asynchronous data loading. This knowledge is crucial for choosing the right tools and techniques to effectively scrape dynamic websites. In this report, we delve into the methodologies for scraping dynamic websites using C++, exploring essential libraries like libcurl, Gumbo, and Boost, and providing a detailed, step-by-step guide to building robust web scrapers.

Scrape a Dynamic Website with C#

· 16 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

Scrape a Dynamic Website with C#

Dynamic websites have become increasingly prevalent due to their ability to deliver personalized and interactive content to users. Unlike static websites, which serve pre-built HTML pages, dynamic websites generate content on-the-fly based on user interactions, database queries, or real-time data. This dynamic nature is achieved through the use of server-side programming languages such as PHP, Ruby, and Python, as well as client-side JavaScript frameworks like React, Angular, and Vue.js.

Dynamic websites are characterized by asynchronous content loading, client-side rendering, real-time updates, personalized content, and complex DOM structures. These features enhance user experience but also introduce significant challenges for web scraping. Traditional scraping tools that rely on static HTML parsing often fall short when dealing with dynamic websites, necessitating the use of more sophisticated methods and tools.

To effectively scrape dynamic websites using C#, developers must employ specialized tools such as Selenium WebDriver and PuppeteerSharp, which can interact with web pages as if they were real users, executing JavaScript and waiting for content to load. These tools, along with proper wait mechanisms and dynamic element location strategies, enable the extraction of data from even the most complex and interactive web applications.

Scrape a Dynamic Website with Go

· 16 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

Scrape a Dynamic Website with Go

Web scraping has become an essential technique for data extraction, particularly with the rise of dynamic websites that deliver content through AJAX and JavaScript. Traditional methods of web scraping often fall short when dealing with these modern web architectures, necessitating more advanced approaches. Using the Go programming language for web scraping offers several advantages, including high performance, robust concurrency support, and a growing ecosystem of libraries specifically designed for this task.

Go, often referred to as Golang, is a statically typed, compiled language that excels in performance and efficiency. Its compilation to machine code results in faster execution times compared to interpreted languages like Python. This is particularly beneficial for large-scale web scraping projects where speed and resource utilization are critical. Additionally, Go's built-in support for concurrency through goroutines enables developers to scrape multiple web pages concurrently, making it highly scalable.

This report delves into the techniques and best practices for scraping dynamic websites using Go. It covers essential topics such as identifying and mimicking AJAX requests, utilizing headless browsers, and handling infinite scrolling. Furthermore, it provides insights into managing browser dependencies, optimizing performance, and adhering to ethical scraping practices. By the end of this report, you will have a comprehensive understanding of how to effectively scrape dynamic websites using Go, leveraging its unique features to build efficient and scalable web scraping solutions.

Scrape a Dynamic Website with PHP

· 10 min read
Oleg Kulyk
Co-Founder @ ScrapingAnt

Scrape a Dynamic Website with PHP

Dynamic websites have become the norm in modern web development, providing interactive and personalized experiences by generating content on-the-fly based on user interactions, database queries, or real-time data. Unlike static websites that serve pre-built HTML pages, dynamic sites rely heavily on server-side processing and client-side JavaScript to deliver tailored content. This dynamic nature poses significant challenges when it comes to web scraping, as traditional methods of parsing static HTML fall short.

Dynamic websites often utilize sophisticated JavaScript frameworks such as React, Angular, and Vue.js, and technologies like AJAX to update content asynchronously without refreshing the page. This complexity requires advanced scraping techniques that can handle JavaScript execution, asynchronous loading, user interaction simulation, and more. To effectively scrape dynamic websites using PHP, developers need to leverage tools such as headless browsers, API-based solutions, and JavaScript engines.

This guide offers a comprehensive overview of the challenges and techniques involved in scraping dynamic websites with PHP. It explores various tools and methods, including Puppeteer, Selenium, Symfony Panther, and WebScrapingAPI, providing practical code examples and best practices to ensure successful data extraction.