Практика 14
В этой лабе вы напишите простое многопоточное приложение.
Scraper
Web scraping wiki - это процесс скачивания и последующего разбора веб страниц. Ваша задача - написать свой web-scraper, который полностью скачает все гиперссылки (<a href='ADDRESS'>
элементы) в рамках этого сайта (во вне выходить нельзя, иначе мы скачаем весь интернет). То есть вам не надо качать картинки и страницы, а только сохранять сами адреса. По своей сути мы сделаем ограниченную версию wget
. Примеры использования: тут и тут.
Экспериментировать можете с сайтом ru.wikipedia.org/wiki/Заглавная_страница.
Инструкции
Рекомендуется использовать библиотеку BeautifulSoup4
. Русскоязычный туториал, официальная документация.
Поставить пакет можно командой pip install beautifulsoup4
.
Как должно всё работать
- Консольная утилита принимает как аргумент адрес сайта.
- Программа скачивает к себе в память html страницу по этому адресу.
- Программа разбирает html и ищет все
<a>
теги и извлекаетhref
параметр. - По каждому
href
идем в 1 пункт, так как внутриhref
хранится ссылка на страницу сайта. - Когда-нибудь процесс остановится и результат - список уникальных страниц разделенных новой строкой, должен лежать одном файле.
Пример файла links.csv:
https://en.wikipedia.org/wiki/Main_Page
https://en.wikipedia.org/wiki/Portal:Arts
https://en.wikipedia.org/wiki/Portal:Contents/Portals
.... и так далее ....
Однопоточная версия
Напишите в лоб решение задачи. Можете сделать рекурсивно, можете обход в ширину или глубину. Как угодно. Ограничьте работу программы 1000-ей ссылок. Это понадобится нам для замера скорости работы.
Многопоточная версия с очередями
Есть несколько вариантов ускорить работу скрейпера. Можно использовать однопоточный, но асинхронный подход, а можно синхронный, но многопоточный. Мы попробуем второй способ.
Рассмотрим обычный подход для решения подобных задач - использование очереди задач и набора(pool) работников-потоков (thread workers).
Наша задача относится к классу задач, параллельних по данным. Нам нет смысла ждать, когда будет произведен разбор предыдущей ссылки, если у нас есть еще N
штук таких же. Мы могли бы нашим работникам раздать задания - работник номер 1 возьми ссылки с 1 по 10, работник 2 - ссылки с 11 по 20 и так далее. Работники в процессе своей работы получат еще N
других ссылок, которые они положат опять в некоторое хранилище ссылок на обработку.
Реализовывать это можно следующим образом. У нас есть некоторое хранилище задач на обработку и есть конечное число работников. Каждый из них берет задачу из хранилища, что-то с ней делает и может, например, положить в это же хранилище следующие задачи. Если работник пришел к хранилищу, а в нем нет задач, то он должен заблокироваться и ждать пока не появится новая задача.
Так как в наше хранилище кто-то складывает задачи и кто-то их обрабатывает, то это очень похоже на Очередь. А условие с ожиданием новых задач - это Очередь с блокировкой.
В Python есть класс Queue и модуль threading для решение подобных задач.
Обратите внимание, что в многопоточность сама по себе очень сложная наука. В описании мы обошли понятия синхронизации потоков, гонок, дедлоков и прочего, полагая, что вы еще изучите это дальше. А в данной лабораторной мы просто показали вам паттерн Workers Pool.
Теперь сравните скорость выполнения задачи в однопоточном режиме и многопоточном.
Как вы думаете, что был “узким горлышком” в данной программе и почему распаралеливание помогло. Кстати, работа асинхронная однопоточная версия будет не медленнее многопоточной, как вы думаете - почему?
Не Python
Если вы делаете это задание на Java, то это даже интереснее. Вы скорее всего будете использовать ExecutorService и синхронизированную очередь. Попробуйте для интереса использовать потоко НЕ безопасную очередь и посмотрите, что случится.
Прочитайте статью на русском про Java Concurency.
Дополнительные материалы
- Advanced Web Scraping tutorial - интересная статья, в которой рассказывают с какими трудностями сталкиваются при web-скрейпинге.
- AsyncIO - модуль асинхронного программирования в Python. Рекуомендуется к ознакомлению.
- Thread Safe Queue
- Threading
- Многопоточность в Java
- Обзор java.util.concurrent - очень хорошая статья
- Java Concurency