Selectolax is a fast HTML parser that uses CSS selectors to locate and extract data from HTML.
It uses the Modest and Lexbor libraries to parse HTML, but this is an implementation detail and you don’t need to know anything about them to use Selectolax.
In this document, I will try to illustrate the main features of Selectolax using examples.
Selectolax is on PyPI, so you can install it with pip:
$ python3 -m pip install selectolax
or
$ pip3 install selectolax
It can work inside a virtual environment or globally.
In [2]:
import selectolax
example_html = “”“
The Dormouse's story
Once upon a time there were three little sisters; and their names were Elsie, Lacie and Tillie; and they lived at the bottom of a well.
...
"""tree = selectolax.parser.HTMLParser(example_html)
tree.css_first(‘title’).text()
Out [2]:
“The Dormouse’s story”
Let’s delve deeper into the features of Selectolax by looking at some examples with real HTML.
In the examples, we will use requests to fetch HTML from the web, and selectolax to parse it.
Let’s get some HTML from my blog as a quick example. To turn the HTML from a string into a tree, you can use the selectolax.parser.HTMLParser class.
You can pass this class a string, or just pass bytes and let it figure out the encoding.
In [3]:
html = requests.get(‘https://www.gkbrk.com’).text tree = selectolax.parser.HTMLParser(html)
Let’s start with a simple example, and extract the title of the page.
In [4]:
title = tree.css_first(‘title’).text() title
Out [4]:
‘Gokberk Yaltirakli’
That wasn’t too hard, that is indeed the title of my website. Let’s try something a little more complicated.
My home page has a list of recent posts, and it’s not too far fetched to imagine that we might want to extract the title and URL of each post.
Let’s try that.
In [5]:
articles = tree.css(‘article > ul > li’)
titles = [] dates = []
for article in articles: title = article.css_first(‘b’).text() url = article.css_first(‘a’).attrs[‘href’] date = article.css_first(‘time’).text()
titles.append(title)
dates.append(date)
pd.DataFrame({‘title’: titles, ‘date’: dates}).head()
Out [5]:
| title | date | |
|---|---|---|
| 0 | Shell scripts as a poor man’s AppImage | 2023-04-28 |
| 1 | Earthquake data for Turkey | 2022-11-26 |
| 2 | A Brief Overview of Mastodon | 2022-11-19 |
| 3 | Memorable Unique Identifiers (MUIDs) | 2022-10-10 |
| 4 | Status Update, May 2022 | 2022-05-15 |
Pretty easy, right? Let’s step it up a notch and try a website that I don’t control.
In [6]:
html = requests.get(“https://encode.su/forums/2-Data-Compression”).text tree = selectolax.parser.HTMLParser(html)
In [7]:
threads = tree.css(‘ol > li.threadbit’)
titles = [] dates = []
for thread in threads: title = thread.css_first(‘a.title’).text() date = thread.css(‘dl.threadlastpost > dd’)[1].text().strip()
titles.append(title)
dates.append(date)
pd.DataFrame({‘title’: titles, ‘date’: dates}).head()
Out [7]:
| title | date | |
|---|---|---|
| 0 | GDC Competition: Discussions | Today, 00:53 |
| 1 | List of Asymmetric Numeral Systems implementat… | 25th October 2023, 06:01 |
| 2 | New saca and bwt library (libsais) | 12th July 2023, 22:21 |
| 3 | RAZOR - strong LZ-based archiver | 18th April 2023, 18:11 |
| 4 | Is encode.su community interested in a new fre… | Yesterday, 22:33 |
How about something more familiar, like Hacker News?
In [8]:
html = requests.get(“https://news.ycombinator.com/”).text tree = selectolax.parser.HTMLParser(html)
In [9]:
titles = []
for post in tree.css(‘td.title > span.titleline’): title = post.text() titles.append(title)
pd.DataFrame({‘title’: titles}).head()
Out [9]:
| title | |
|---|---|
| 0 | Not a real engineer (2019) (twitchard.github.io) |
| 1 | Open-source drawing tool – Excalidraw (github…. |
| 2 | Tiny volumetric display (mitxela.com) |
| 3 | Scientists discover retinal cells that help st… |
| 4 | Roar of cicadas was so loud, it was picked up … |
The Node class represents a node in the HTML tree. Each element and text node in the tree is represented by a Node object.
In [10]:
tree = selectolax.parser.HTMLParser(example_html)
node = tree.css_first(‘a.sister’)
type(node)
Out [10]:
selectolax.parser.Node
If you have a text node, you can get the text with the .text_content property. If your node was not a text node, this will return None.
You can also call .text() on any node, and it will return the text content of the node and all its children.
In [18]:
node.text()
Out [18]:
‘Elsie’
In [11]:
node.attributes
Out [11]:
{‘href’: ‘http://example.com/elsie’, ‘class’: ‘sister’, ‘id’: ‘link1’}
In [12]:
node.attributes[‘href’] node.attrs.get(‘href’)
Out [12]:
‘http://example.com/elsie’
Out [12]:
‘http://example.com/elsie’