🐍 Самоучитель по Python для начинающих. Часть 17: Основы скрапинга и парсинга

Научимся извлекать данные из статического и динамического контента с помощью регулярных выражений, XPath, BeautifulSoup, MechanicalSoup и Selenium. В конце статьи – код 10 скриптов для скрапинга данных и изображений с Wikipedia, Habr, LiveLib, IMDb и TIOBE.
🐍 Самоучитель по Python для начинающих. Часть 17: Основы скрапинга и парсинга

Веб-скрапинг – это процесс автоматического сбора информации из онлайн-источников. Для выбора нужных сведений из массива «сырых» данных, полученных в ходе скрапинга, нужна дальнейшая обработка – парсинг. В процессе парсинга выполняются синтаксический анализ, разбор и очистка данных. Результат парсинга – очищенные, упорядоченные, структурированные данные, представленные в формате, понятном конечному пользователю (или приложению).

Скрипты для скрапинга создают определенную нагрузку на сайт, с которого они собирают данные – могут, например, посылать чрезмерное количество GET запросов к серверу. Это одна из причин, по которой скрапинг относится к спорным видам деятельности. Чтобы не выходить за рамки сетевого этикета, необходимо всегда соблюдать главные правила сбора публичной информации:

  • Если на сайте есть API, нужно запрашивать данные у него.
  • Частота и количество GET запросов должны быть разумными.
  • Следует передавать информацию о клиенте в User-Agent.
  • Если на сайте есть личные данные пользователей, необходимо учитывать настройки приватности в robots.txt.

Необходимо отметить, что универсальных рецептов скрапинга и парсинга не существует. Это связано со следующими причинами:

  • Некоторые сервисы активно блокируют скраперов. Динамическая смена прокси не всегда помогает решить эту проблему.
  • Контент многих современных сайтов генерируется динамически – результат обычного GET запроса из приложения к таким сайтам вернется практически пустым. Эта проблема решается с помощью Selenium WebDriver либо MechanicalSoup, которые имитируют действия браузера и пользователя.

Для извлечения данных со страниц с четкой, стандартной структурой эффективнее использовать язык запросов XPath. И напротив, для получения нужной информации с нестандартных страниц с произвольным синтаксисом лучше использовать средства библиотеки BeautifulSoup. Ниже мы подробно рассмотрим оба подхода.

Примеры скрапинга и парсинга на Python

Экосистема Python располагает множеством инструментов для скрапинга и парсинга. Начнем с самого простого примера – получения веб-страницы и извлечения из ее кода ссылки.

Скрапинг содержимого страницы

Воспользуемся модулем urllib.request стандартной библиотеки urllib для получения исходного кода одностраничного сайта example.com:

        from urllib.request import urlopen
url = 'http://example.com'
page = urlopen(url)
print(page.read().decode('utf-8'))

    

Результат:

        <!doctype html>
<html>
<head>
    <title>Example Domain</title>

    <meta charset="utf-8" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <style type="text/css">
    body {
        background-color: #f0f0f2;
        margin: 0;
        padding: 0;
        font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
        
    }
    div {
        width: 600px;
        margin: 5em auto;
        padding: 2em;
        background-color: #fdfdff;
        border-radius: 0.5em;
        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
    }
    a:link, a:visited {
        color: #38488f;
        text-decoration: none;
    }
    @media (max-width: 700px) {
        div {
            margin: 0 auto;
            width: auto;
        }
    }
    </style>    
</head>

<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.</p>
    <p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>

    

Точно такой же результат можно получить с помощью requests:

        from bs4 import BeautifulSoup
import requests
url = 'http://example.com'
res = requests.get(url)
soup = BeautifulSoup(res.text,'html.parser')
print(soup)

    

Этот результат – те самые сырые данные, которые нужно обработать (подвергнуть парсингу), чтобы извлечь из них нужную информацию, например, адрес указанной на странице ссылки:

🐍 Самоучитель по Python для начинающих. Часть 17: Основы скрапинга и парсинга

Парсинг полученных данных

Извлечь адрес ссылки можно 4 разными способами – с помощью:

  • Методов строк.
  • Регулярного выражения.
  • Запроса XPath.
  • Обработки BeautifulSoup.

Рассмотрим все эти способы по порядку.

Методы строк

Это самый трудоемкий способ – для извлечения каждого элемента нужно определить 2 индекса – начало и конец вхождения. При этом к индексу вхождения надо добавить длину стартового фрагмента:

        from urllib.request import urlopen
url = 'http://example.com/'
page = urlopen(url)
html_code = page.read().decode('utf-8')
start = html_code.find('href="') + 6
end = html_code.find('">More')
link = html_code[start:end]
print(link)

    

Результат:

        https://www.iana.org/domains/example
    

Регулярное выражение

В предыдущей главе мы подробно рассматривали способы извлечения конкретных подстрок из текста. Точно так же регулярные выражения можно использовать для поиска данных в исходном коде страниц:

        from urllib.request import urlopen
import re
url = 'http://example.com/'
page = urlopen(url)
html_code = page.read().decode('utf-8')
link = r'(https?://\S+)(?=")'
print(re.findall(link, html_code))

    

Результат:

        https://www.iana.org/domains/example

    

Запрос XPath

Язык запросов XPath (XML Path Language) позволяет извлекать данные из определенных узлов XML-документа. Для работы с HTML кодом в Python используют модуль etree:

        from urllib.request import urlopen
from lxml import etree
url = 'http://example.com/'
page = urlopen(url)
html_code = page.read().decode('utf-8')
tree = etree.HTML(html_code)
print(tree.xpath("/html/body/div/p[2]/a/@href")[0])

    

Результат:

        https://www.iana.org/domains/example

    

Чтобы узнать путь к нужному элементу страницы, в браузерах Chrome и FireFox надо кликнуть правой кнопкой по элементу и выбрать «Просмотреть код», после чего откроется консоль. В консоли по интересующему элементу нужно еще раз кликнуть правой кнопкой, выбрать «Копировать», а затем – копировать путь XPath:

Путь XPath
Путь XPath

В приведенном выше примере для извлечения ссылки к пути /html/body/div/p[2]/a/ мы добавили указание для получения значения ссылки @href, и индекс [0], поскольку результат возвращается в виде списка. Если @href заменить на text(), программа вернет текст ссылки, а не сам URL:

        print(tree.xpath("/html/body/div/p[2]/a/text()")[0])
    

Результат:

        More information...

    

Парсер BeautifulSoup

Регулярные выражения и XPath предоставляют огромные возможности для извлечения нужной информации из кода страниц, но у них есть свои недостатки: составлять Regex-шаблоны сложно, а запросы XPath хорошо работают только на страницах с безупречной, стандартной структурой. К примеру, страницы Википедии не отличаются идеальной структурой, и использование XPath для извлечения нужной информации из определенных элементов статей, таких как таблицы infobox, часто оказывается неэффективным. В этом случае оптимальным вариантом становится BeautifulSoup, специально разработанный для парсинга HTML-кода.

Библиотека BeautifulSoup не входит в стандартный набор Python, ее нужно установить самостоятельно:

        pip install beautifulsoup4
    

В приведенном ниже примере мы будем извлекать из исходного кода страницы уникальные ссылки, за исключением внутренних:

        from bs4 import BeautifulSoup
from urllib.request import urlopen
url = 'https://webscraper.io/test-sites/e-commerce/allinone/phones'
page = urlopen(url)
html = page.read().decode('utf-8')
soup = BeautifulSoup(html, 'html.parser')
links = set()
for link in soup.find_all('a'):
    l = link.get('href')
    if l != None and l.startswith('https'):
        links.add(l)
for link in links:
    print(link)
    

Результат:

        https://twitter.com/webscraperio
https://www.facebook.com/webscraperio/
https://forum.webscraper.io/
https://webscraper.io/downloads/Web_Scraper_Media_Kit.zip
https://cloud.webscraper.io/
https://chrome.google.com/webstore/detail/web-scraper/jnhgnonknehpejjnehehllkliplmbmhn?hl=en

    

При использовании XPath точно такой же результат даст следующий скрипт:

        from urllib.request import urlopen
from lxml import etree
url = 'https://webscraper.io/test-sites/e-commerce/allinone/phones'
page = urlopen(url)
html_code = page.read().decode('utf-8')
tree = etree.HTML(html_code)
sp = tree.xpath("//li/a/@href")
links = set()
for link in sp:
    if link.startswith('http'):
        links.add(link)
for link in links:
    print(link)
    

Имитация действий пользователя в браузере

При скрапинге сайтов очень часто требуется авторизация, нажатие кнопок «Читать дальше», переход по ссылкам, отправка форм, прокручивание ленты и так далее. Отсюда возникает необходимость имитации действий пользователя. Как правило, для этих целей используют Selenium, однако есть и более легкое решение – библиотека MechanicalSoup:

        pip install MechanicalSoup
    

По сути, MechanicalSoup исполняет роль браузера без графического интерфейса. Помимо имитации нужного взаимодействия с элементами страниц, MechanicalSoup также парсит HTML-код, используя для этого все функции BeautifulSoup.

Воспользуемся тестовым сайтом http://httpbin.org/, на котором есть возможность отправки формы заказа пиццы:

        import mechanicalsoup
browser = mechanicalsoup.StatefulBrowser()
browser.open("http://httpbin.org/")
browser.follow_link("forms")
browser.select_form('form[action="/post"]')
print(browser.form.print_summary())

    

В приведенном выше примере браузер MechanicalSoup перешел по внутренней ссылке http://httpbin.org/forms/post и вернул описание полей ввода:

        <input name="custname"/>
<input name="custtel" type="tel"/>
<input name="custemail" type="email"/>
<input name="size" type="radio" value="small"/>
<input name="size" type="radio" value="medium"/>
<input name="size" type="radio" value="large"/>
<input name="topping" type="checkbox" value="bacon"/>
<input name="topping" type="checkbox" value="cheese"/>
<input name="topping" type="checkbox" value="onion"/>
<input name="topping" type="checkbox" value="mushroom"/>
<input max="21:00" min="11:00" name="delivery" step="900" type="time"/>
<textarea name="comments"></textarea>
<button>Submit order</button>

    

Перейдем к имитации заполнения формы:

        browser["custname"] = "Best Customer"
browser["custtel"] = "+7 916 123 45 67"
browser["custemail"] = "trex@example.com"
browser["size"] = "large"
browser["topping"] = ("cheese", "mushroom")
browser["comments"] = "Add more cheese, plz. More than the last time!"

    

Теперь форму можно отправить:

        response = browser.submit_selected()

    

Результат можно вывести с помощью print(response.text):

        {
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "comments": "Add more cheese, plz. More than the last time!", 
    "custemail": "trex@example.com", 
    "custname": "Best Customer", 
    "custtel": "+7 916 123 45 67", 
    "delivery": "", 
    "size": "large", 
    "topping": [
      "cheese", 
      "mushroom"
    ]
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "191", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "Referer": "http://httpbin.org/forms/post", 
    "User-Agent": "python-requests/2.28.1 (MechanicalSoup/1.2.0)", 
    "X-Amzn-Trace-Id": "Root=1-6404d82d-2a9ae95225dddaec1968ccb8"
  }, 
  "json": null, 
  "origin": "86.55.39.89", 
  "url": "http://httpbin.org/post"
}

    

Скрапинг и парсинг динамического контента

Все примеры, которые мы рассмотрели выше, отлично работают на статических страницах. Однако на множестве платформ используется динамический подход к генерации и загрузке контента – к примеру, для просмотра всех доступных товаров в онлайн-магазине страницу нужно не только открыть, но и прокрутить до футера. Для работы с динамическим контентом в Python нужно установить:

Если установка прошла успешно, выполнение этого кода приведет к автоматическому открытию страницы:

        from selenium import webdriver
driver = webdriver.Chrome() # или webdriver.Firefox()
driver.get('https://google.com')

    

Бывает, что даже после установки оптимальной версии драйвера интерпретатор Python возвращает ошибку OSError: [WinError 216] Версия "%1" не совместима с версией Windows. В этом случае нужно воспользоваться модулем webdriver-manager, который самостоятельно установит подходящий драйвер для нужного браузера:

        from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager().install()))

    

Имитация прокрутки страницы и парсинг данных

В качестве примера скрапинга и парсинга динамического сайта мы воспользуемся разделом тестового онлайн-магазина. Здесь расположены карточки с информацией о планшетах. Карточки загружаются ряд за рядом при прокрутке страницы:

🐍 Самоучитель по Python для начинающих. Часть 17: Основы скрапинга и парсинга

Пока страница не прокручена, полный HTML-код с информацией о планшетах получить невозможно. Для имитации прокрутки мы воспользуемся скриптом 'window.scrollTo(0, document.body.scrollHeight);'. Цены планшетов находятся в тегах h4 класса pull-right price, а названия моделей – в тексте ссылок a класса title. Готовый код выглядит так:

        from selenium import webdriver
from selenium.webdriver.chrome.service import Service as ChromeService
from webdriver_manager.chrome import ChromeDriverManager
import time
from bs4 import BeautifulSoup

driver = webdriver.Chrome(service=ChromeService(ChromeDriverManager(cache_valid_range=10).install()))
url = 'https://webscraper.io/test-sites/e-commerce/scroll/computers/tablets'
driver.get(url)
driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
time.sleep(5) 
html = driver.page_source
soup = BeautifulSoup(html, 'html.parser')
prices = soup.find_all('h4', class_='pull-right price')
models = soup.find_all('a', class_='title')
for model, price in zip(models, prices):
    m = model.get_text()
    p = price.get_text()
    print(f'Планшет {m}, цена - {p}')

    

Результат:

        Планшет Lenovo IdeaTab, цена - $69.99
Планшет IdeaTab A3500L, цена - $88.99
Планшет Acer Iconia, цена - $96.99
Планшет Galaxy Tab 3, цена - $97.99
Планшет Iconia B1-730HD, цена - $99.99
Планшет Memo Pad HD 7, цена - $101.99

    

Практика

Задание 1

Напишите программу для получения названий последних статей из блога издательства O’Reilly:

🐍 Самоучитель по Python для начинающих. Часть 17: Основы скрапинга и парсинга

Пример результата:

        Sydney and the Bard
What Does Copyright Say about Generative Models?
Technology Trends for 2023
Radar Trends to Watch: February 2023

    

Решение:

        from bs4 import BeautifulSoup
import requests
url = 'https://www.oreilly.com/radar/'
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"}
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.text,'html.parser')
sp = soup.find_all('h2', class_='post-title')
for post in sp:
    print(post.get_text())

    

Задание 2

Напишите программу для определения 10 слов, которые чаще всего встречаются в тексте сказки «Колобок» (без учета регистра). Предлоги учитывать не нужно.

Ожидаемый результат:

        колобок - 32
ушел - 14
тебя - 7
коробу - 6
сусеку - 6
сметане - 5
масле - 5
покатился - 4
катится - 4
навстречу - 4

    

Решение:

        from bs4 import BeautifulSoup
import requests
import re

url = 'https://azku.ru/russkie-narodnie-skazki/kolobok.html'
res = requests.get(url)
soup = BeautifulSoup(res.text,'html.parser')
res = soup.find_all('div', class_='entry-content')
text = res[0].get_text().split('Мне нравится')[0]
sp = re.sub(r'[-,.?!"—:]', ' ', text).lower().split()
result_dict = {word: sp.count(word) for word in sp if len(word) > 3}
max_values = sorted(result_dict.items(), key=lambda x:x[1], reverse=True)[:10]
for word, number in max_values:
    print(f'{word} - {number}')

    

Задание 3

Напишите программу, которая на основе данных таблицы создает список цитат из фильмов, выпущенных после 1995 года.

Ожидаемый результат:

        Show me the money! Покажи мне деньги! Род Тидвелл Кьюба Гудинг мл. Джерри Магуайер 1996
I see dead people. Я вижу мёртвых людей. Коул Сиэр Хэйли Джоэл Осмент Шестое чувство 1999
You had me at 'hello'. Я была твоя уже на «здрасьте». Дороти Бойд Рене Зеллвегер Джерри Магуайер 1996
My precious. Моя прелесть. Голлум Энди Серкис Властелин колец: Две крепости 2002
I’m the king of the world! Я король мира! Джек Доусон Леонардо Ди Каприо Титаник 1997

    

Решение:

        from bs4 import BeautifulSoup
import requests
import re

url = 'https://ru.wikipedia.org/wiki/100_%D0%B8%D0%B7%D0%B2%D0%B5%D1%81%D1%82%D0%BD%D1%8B%D1%85_%D1%86%D0%B8%D1%82%D0%B0%D1%82_%D0%B8%D0%B7_%D0%B0%D0%BC%D0%B5%D1%80%D0%B8%D0%BA%D0%B0%D0%BD%D1%81%D0%BA%D0%B8%D1%85_%D1%84%D0%B8%D0%BB%D1%8C%D0%BC%D0%BE%D0%B2_%D0%B7%D0%B0_100_%D0%BB%D0%B5%D1%82_%D0%BF%D0%BE_%D0%B2%D0%B5%D1%80%D1%81%D0%B8%D0%B8_AFI'
res = requests.get(url).text
soup = BeautifulSoup(res,'html.parser')
table = soup.find('table', class_='wikitable')
header = table.find_all('th')
quotes = table.find_all('tr')
quotes_after_1995 = []
for i in range(1, len(quotes)):
    q = quotes[i].get_text()
    if int(q[-5:]) > 1995:
        quotes_after_1995.append(' '.join(q.split('\n\n')[1:]).replace('\n', ''))
for quote in quotes_after_1995:
    print(quote)

    

Задание 4

Напишите программу, которая извлекает данные о моделях, конфигурации и стоимости 117-ти ноутбуков, и записывает полученную информацию в csv файл.

Ожидаемый результат:

        Модель;Описание;Цена
Asus VivoBook X441NA-GA190;Asus VivoBook X441NA-GA190 Chocolate Black, 14", Celeron N3450, 4GB, 128GB SSD, Endless OS, ENG kbd;$295.99
...
Asus ROG Strix SCAR Edition GL503VM-ED115T;Asus ROG Strix SCAR Edition GL503VM-ED115T, 15.6" FHD 120Hz, Core i7-7700HQ, 16GB, 256GB SSD + 1TB SSHD, GeForce GTX 1060 6GB, Windows 10 Home;$1799.00

    

Решение:

        from bs4 import BeautifulSoup
import requests
url = 'https://webscraper.io/test-sites/e-commerce/allinone/computers/laptops'
res = requests.get(url)
soup = BeautifulSoup(res.text,'html.parser')
models = soup.find_all('a', class_='title')
description = soup.find_all('p', class_='description')
prices = soup.find_all('h4', class_='pull-right price')
with open('laptops.csv', 'w', encoding='utf-8') as file:
    file.write(f'Модель;Описание;Цена\n')
    for m, d, p in zip(models, description, prices):
        file.write(f"{m['title']};{d.get_text()};{p.get_text()}\n")

    

Задание 5

Напишите программу для скачивания полноразмерных обложек из профилей книг на LiveLib. Обложки открываются после двойного клика по миниатюре:

🐍 Самоучитель по Python для начинающих. Часть 17: Основы скрапинга и парсинга

Решение:

        from bs4 import BeautifulSoup
import requests
import re

url = 'https://www.livelib.ru/book/1002978643-ohotnik-za-tenyu-donato-karrizi'
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"}
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.text,'html.parser')
sp = soup.find('div', class_='bc-menu__image-wrapper')
img_url = re.findall(r'(?:https\:)?//.*\.(?:jpeg)', str(sp))[0]
response = requests.get(img_url, headers=headers)
if response.status_code == 200:
    file_name = url.split('-', 1)[1]
    with open(file_name + '.jpeg', 'wb') as file:
        file.write(response.content)

    

Задание 6

Напишите программу, которая составляет рейтинг топ-100 лучших триллеров на основе этого списка.

Пример результата:

        1. "Побег из Шоушенка", Стивен Кинг - 4.60
2. "Заживо в темноте", Майк Омер - 4.50
3. "Молчание ягнят", Томас Харрис - 4.47
4. "Девушка с татуировкой дракона", Стиг Ларссон - 4.42
5. "Внутри убийцы", Майк Омер - 4.38
...
98. "Абсолютная память", Дэвид Болдаччи - 4.22
99. "Сломанные девочки", Симона Сент-Джеймс - 4.11
100. "Цифровая крепость", Дэн Браун - 3.98

    

Решение:

        from bs4 import BeautifulSoup
import requests

url = 'https://www.livelib.ru/genre/%D0%A2%D1%80%D0%B8%D0%BB%D0%BB%D0%B5%D1%80%D1%8B/top'
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"}
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.content,'html.parser')
titles = soup.find_all('a', class_='brow-book-name with-cycle')
authors = soup.find_all('a', class_='brow-book-author')
rating = soup.find_all('span', class_='rating-value stars-color-orange')
i = 1
for t, a, r in zip(titles, authors, rating):
    print(f'{i}. "{t.get_text()}", {a.get_text()} - {r.get_text()}')
    i += 1
    

Задание 7

Напишите программу, которая составляет топ-20 языков программирования на основе рейтинга популярности TIOBE.

Пример результата:

        1. Python: 14.83%
2. C: 14.73%
3. Java: 13.56%
4. C++: 13.29%
5. C#: 7.17%
6. Visual Basic: 4.75%
7. JavaScript: 2.17%
8. SQL: 1.95%
9. PHP: 1.61%
10. Go: 1.24%
11. Assembly language: 1.11%
12. MATLAB: 1.08%
13. Delphi/Object Pascal: 1.06%
14. Scratch: 1.00%
15. Classic Visual Basic: 0.98%
16. R: 0.93%
17. Fortran: 0.79%
18. Ruby: 0.76%
19. Rust: 0.73%
20. Swift: 0.71%

    

Решение:

        import requests
from lxml import html

url = 'https://www.tiobe.com/tiobe-index/'
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36"}
page = requests.get(url, headers=headers)
tree = html.fromstring(page.content)
languages, rating = [], []
for i in range(1, 21):
    languages.append(tree.xpath(f'//*[@id="top20"]/tbody/tr[{i}]/td[5]/text()')[0])
    rating.append(tree.xpath(f'//*[@id="top20"]/tbody/tr[{i}]/td[6]/text()')[0])
i = 1
for l, r in zip(languages, rating):
    print(f'{i}. {l}: {r}')
    i += 1
    

Задание 8

Напишите программу для получения рейтинга 250 лучших фильмов по версии IMDb. Названия должны быть на русском языке.

Пример результата:

        1. Побег из Шоушенка, (1994), 9,2
2. Крестный отец, (1972), 9,2
3. Темный рыцарь, (2008), 9,0
...
248. Аладдин, (1992), 8,0
249. Ганди, (1982), 8,0
250. Танцующий с волками, (1990), 8,0

    

Решение:

        import requests
from lxml import html

url = 'https://www.imdb.com/chart/top/'
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36",
           "Accept-Language": "ru-RU"}
page = requests.get(url, headers=headers)
tree = html.fromstring(page.content)
movies, year, rating = [], [], []
for i in range(1, 251):
    movies.append(tree.xpath(f'//*[@id="main"]/div/span/div/div/div[3]/table/tbody/tr[{i}]/td[2]/a/text()')[0])
    year.append(tree.xpath(f'//*[@id="main"]/div/span/div/div/div[3]/table/tbody/tr[{i}]/td[2]/span/text()')[0])
    rating.append(tree.xpath(f'//*[@id="main"]/div/span/div/div/div[3]/table/tbody/tr[{i}]/td[3]/strong/text()')[0])
i = 1
for m, y, r in zip(movies, year, rating):
    print(f'{i}. {m}, {y}, {r}')
    i += 1
    

Задание 9

Напишите программу, которая сохраняет в текстовый файл данные о фэнтези фильмах с 10 первых страниц соответствующего раздела IMDb. Если у фильма/сериала еще нет рейтинга, следует указать N/A.

Ожидаемый результат в файле fantasy.txt – 500 записей:

        Мандалорец, (2019– ), 8,7
Всё везде и сразу, (2022), 8,0
Атака титанов, (2013–2023), 9,0
Peter Pan & Wendy, (2023), N/A
Игра престолов, (2011–2019), 9,2
...
Шрэк 3, (2007), 6,1
Кунг-фу Панда 3, (2016), 7,1
Смерть ей к лицу, (1992), 6,6
Исход: Цари и боги, (2014), 6,0
Кошмар на улице Вязов 3: Воины сна, (1987), 6,6

    

Решение:

        import requests
import mechanicalsoup
from lxml import html
import time
url = 'https://www.imdb.com/search/title/?genres=fantasy'
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36",
           "Accept-Language": "ru-RU"}

browser = mechanicalsoup.StatefulBrowser()
j = 51

for _ in range(10):
    browser.open(url)
    page = requests.get(url, headers=headers)
    tree = html.fromstring(page.content)
    titles, year, rating = [], [], []
    for i in range(1, 51):
        if tree.xpath(f'//*[@id="main"]/div/div[3]/div/div[{i}]/div[3]/p[1]/b/text()') != []:
            titles.append(tree.xpath(f'//*[@id="main"]/div/div[3]/div/div[{i}]/div[3]/h3/a/text()')[0])
            year.append(tree.xpath(f'//*[@id="main"]/div/div[3]/div/div[{i}]/div[3]/h3/span[2]/text()')[0])
            rating.append('N/A')
        else:
            titles.append(tree.xpath(f'//*[@id="main"]/div/div[3]/div/div[{i}]/div[3]/h3/a/text()')[0])
            year.append(tree.xpath(f'//*[@id="main"]/div/div[3]/div/div[{i}]/div[3]/h3/span[2]/text()')[0])
            rating.append(tree.xpath(f'//*[@id="main"]/div/div[3]/div/div[{i}]/div[3]/div/div[1]/strong/text()')[0])
    with open('fantasy.txt', 'a', encoding='utf-8') as file:
        for t, y, r in zip(titles, year, rating):
            file.write(f'{t}, {y}, {r}\n')

    time.sleep(2)    
    lnk = browser.follow_link('start=' + str(j))
    url = browser.url
    j += 50

    

Задание 10

Напишите программу для получения главных новостей (на русском) с портала Habr. Каждый заголовок должен сопровождаться ссылкой на полный текст новости.

Пример вывода:

        Bethesda назвала дату релиза Starfield на ПК, Xbox Series и Xbox Game Pass — 6 сентября 2023 года
https://habr.com/ru/news/t/721148/
Honda запатентовала съёмные подушки безопасности для мотоциклистов
https://habr.com/ru/news/t/721142/
...
Microsoft увольняет 689 сотрудников из своих офисов в Сиэтле
https://habr.com/ru/news/t/721010/
«Ъ»: в России образовались большие запасы бытовой техники из-за низкого спроса
https://habr.com/ru/news/t/721006/

    

Решение:

        from bs4 import BeautifulSoup
import requests

url = 'https://habr.com/ru/news/'
headers = {"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.96 Safari/537.36",
           "Accept-Language": "ru-RU"}
res = requests.get(url, headers=headers)
soup = BeautifulSoup(res.content,'html.parser')
articles = soup.find_all('a', class_='tm-article-snippet__title-link')
for a in articles:
    print(f'{a.get_text()}\nhttps://habr.com{a.get("href")}')

    

Заключение

Мы рассмотрели основные приемы работы с главными Python-инструментами для скрапинга и парсинга. Способы извлечения и обработки данных варьируются от сайта к сайту – в некоторых случаях эффективнее использование XPath, в других – разбор с BeautifulSoup, а иногда может потребоваться применение регулярных выражений.

В следующей главе приступим к изучению основ ООП (объектно-ориентированного программирования).

***

Содержание самоучителя

  1. Особенности, сферы применения, установка, онлайн IDE
  2. Все, что нужно для изучения Python с нуля – книги, сайты, каналы и курсы
  3. Типы данных: преобразование и базовые операции
  4. Методы работы со строками
  5. Методы работы со списками и списковыми включениями
  6. Методы работы со словарями и генераторами словарей
  7. Методы работы с кортежами
  8. Методы работы со множествами
  9. Особенности цикла for
  10. Условный цикл while
  11. Функции с позиционными и именованными аргументами
  12. Анонимные функции
  13. Рекурсивные функции
  14. Функции высшего порядка, замыкания и декораторы
  15. Методы работы с файлами и файловой системой
  16. Регулярные выражения
  17. Основы скрапинга и парсинга
  18. Основы ООП – инкапсуляция и наследование
  19. Основы ООП – абстракция и полиморфизм
  20. Графический интерфейс на Tkinter
  21. Основы разработки игр на Pygame
  22. Основы работы с SQLite
  23. Основы веб-разработки на Flask
  24. Основы работы с NumPy
  25. Основы анализа данных с Pandas
***
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Backend Lead (Python, Django)
по итогам собеседования

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ