Python Materials For Beginners
454 subscribers
39 photos
1 video
9 files
151 links
Download Telegram
🟣 Selectolax. Парсинг HTML

selectolax одна з найшвидших бібліотек для парсингу html. Для пошуку тегів використовується css selectors.
Вона швидша за BeautifulSoup4 і немає проблем з анотаціями типів.

Давайте розглянемо задачу коли треба з html спарсити всі фрукти з такої html сторінки
html_content = """
<html>
<body>
<section id="fruits">
<h2>Fruits</h2>
<ul>
<li class="item red-item">🍎 Apple</li>
<li class="item">🍌 Banana</li>
<li class="item"> 🍊 <span style="color: orange;">Orange</span> </li>
</ul>
</section>

<section id="vegetables">
<h2>Vegetables</h2>
<ul>
<li class="item">🥕 Carrot</li>
<li class="item" data-id="42">🥔 Potato</li>
<li class="item" data-id="69">🍆 Eggplant</li>
</ul>
</section>

<a href="https://t.me/Python_Materials_For_Beginners/170">Parsing</a>
</body>
</html>
"""


Для початку створимо об'єкт парсеру:
from selectolax.lexbor import LexborHTMLParser, LexborNode

parser = LexborHTMLParser(html_content)


Шукаєм один тег по id
container_el: LexborNode | None = parser.css_first("section#fruits")
assert container_el, "Fruits container not found!"


⚠️ Якщо тег не знайдено метод парсеру вертає None тому завжди варто перевіряти цей момент щоб отримати помилку зі зрозумілим описом. Для цього можна використовувати assert або використати if і кидати Exception.

В середині знайденого тегу шукаємо список по імені тегу:
list_el = container_el.css_first("ul")
assert list_el, "List of fruits not found!"


Використовуємо пошук по імені класу тега щоб знайти всі елементи списку
item_els: list[LexborNode] = list_el.css("li.item")
for item_el in item_els:
text = item_el.text(strip=True, separator="-").strip()
print(text)

🔺 strip=True - видаляє зайві пробіли всередині тексту дочірніх тегів
🔺 separator - задає розділювач між текстами з різних дочірніх тегів
🔺 .strip() - обрізає пробіли (та переводи рядків) з початку і кінця рядка

Вивід коду:
🍎 Apple
🍌 Banana
🍊-Orange-

#selectolax #parsing #scraping
🟠 Selectolax. Парсинг HTML. Частина 2.

🔺 Поєднанний пошук.
Пошук по CSS-селекторах дозволяє одразу поєднувати все в одному рядку запиту:
item_els = parser.css("section#fruits ul li.item")
for item_el in item_els:
text = item_el.text(strip=True, separator=" ").strip()
print(text)


❗️ Але я наполегливо не рекомендував би цим зловживати. Короткий запис — це добре, але у випадку помилки буде важко зрозуміти, з яким саме тегом проблема.

🔺 Пошук тега який має декілька класів
# <li class="item red-item"> ... </li> 

red_item_el = parser.css_first("li.item.red-item")
assert red_item_el

print(red_item_el.text()) # 🍎 Apple


🔺 Атрибути тегу
# <a href=" ... ">Parsing</a>

link_el = parser.css_first("a")
assert link_el

url = link_el.attributes["href"]

print(url) # https://t.me/Python_Materials_For_Beginners/170

Для отримання атрибуту тегу :
▪️attributes – це read-only словник. Ти можеш з нього читати, але не змінювати напряму.
▪️attrs – це mutable dict-like (звичайний словник), який можна змінювати.

🔺 Пошук по атрибуту:
data_els = parser.css("li.item[data-id]")
for data_el in data_els:
data_id = data_el.attributes["data-id"]
print(data_id) # 42 69


data_el = parser.css_first("li.item[data-id='42']")
assert data_el

print(data_el.html) # <li class="item" data-id="42"> ... Potato</li>


#selectolax #parsing #scraping
👍1
🟢 Selectolax. Парсинг HTML. Підсумки та рекомендації.

Для пошуку тегів ми можемо використовувати:
▪️ id тега
▪️ назву тега
▪️ клас тега
▪️ атрибути тега

❗️ У коректному HTML id завжди повинен бути унікальним, тому пошук по ньому — найнадійніший.

Якщо ж шукати по назві/класу/атрибутам, результатів може бути кілька, і серед них можуть траплятися елементи, які ви не очікували знайти.

У нашому прикладі, якщо ми хочемо тільки фрукти й напишемо:
item_els = parser.css("li.item") 


То отримаємо не лише фрукти, а й овочі — це видно одразу у нашому простому HTML-коду. Реальні html-сторінки по розміру значно більші і можна не помітити десь ще на сторінці використовуються схожі теги але у зовсім іншому контексті, як наслідок — незрозумілі помилки у коді.

👉 Тому рекомендую завжди звужувати пошук: спершу знайдіть найближчий батьківський елемент із логічним id, класом чи атрибутом, а вже потім отримуйте потрібні дочірні теги.

👉 Важливо, щоб назви класів, id чи атрибутів, до яких ви прив’язуєтеся, були логічними та «говорили самі за себе». Так менша ймовірність отримати помилкові результати, а код буде легше читати й підтримувати.

👉 Є певна термінологія в HTML: тег зазвичай називають елементом (DOM element) або нодою (Node). Я відповідно додаю суфікс _el або _els до змінних, які містять об’єкти тегів. Це не принципово, але, на мою думку, так код читається краще й спрощується іменування змінних.

#selectolax #parsing #scraping
👍1
💎 httpx+selectolax. Data scraping.

Розглянимо приклад коду який наводив у чаті . Код достатньо довгий тому Git-Gist

1️⃣ Часті запити на сайт можуть призвести до:
▪️ відповіді HTTP 429 Too Many Requests
▪️ обриву з'єднання сервером
▪️ появу капчі
▪️ блокуванню по IP

Тому варто обмежувати кількість запитів у секунду. Для цього існує бібліотека asynciolimiter яка дає можливість це зробити за допомогою Limiter: перед кожним запитом варто викликати його метод limiter.wait()

2️⃣ При скрапінгу даних використовуємо сесії. Це пришвидшує виконання запитів і дає підтримку cookie.

3️⃣ Щоб згрупувати дані разом варто використовувати класи, dataclasses, NamedTuple або як я моделі pydantic

4️⃣ Код варто розбивати на функції, класи, модулі і пакети уникаючи награмодження коду. В даному прикладі я показую розбиття на ф-ції але можна було всю роботу з сайтом винести у модуль або пакет. Також можна було зробити клас QuoteScraper з відповідними методами (це дозволило б не передавати постійно session та limiter у параметрах).

📦 quote_scraper
┣━━ 📜 __init__.py
┣━━ ⚙️ config.py - константи, наприклад BASE_URL
┣━━ 🧾 schemas.py - моделі даних
┣━━ 🔎 parsers.py - функції для парсингу html
┗━━ 🕷 scrapers.py - функції для запит+парсинг
📜 main.py

Структура має бути не обов'язково така сама, але вона має
бути. Коли весь проєкт у одному файлі — це не норм.

5️⃣ Особисто я використовую такий неймінг:
5.1. Функції які займаютья парсингом HTML назвиваю як
parse_ *
5.2. Функціїї які роблять запити: scrape_*
також можна назвивати get_* або fetch_*

6️⃣
Для перевірки наявності потрібних використовую assert але можна також кидати Exception/RuntimeError

7️⃣ Використання logging дає змогу налаштовувати кількість/рівень повідомлень у консоль як власного коду так і коду використаних бібліотек.

8️⃣ Використання генераторів та ітераторів дає можливість більш гнучко використовувати написаний код. (наприклад зупинити ітерацію по сторінкам коли знайдена потрібна цитата)

#httpx #selectolax #parsing #scraping
👍1