Обкачка сайтов своими руками: разбираемся с HTML |
||
МЕНЮ Искусственный интеллект Поиск Регистрация на сайте Помощь проекту ТЕМЫ Новости ИИ Искусственный интеллект Разработка ИИГолосовой помощник Городские сумасшедшие ИИ в медицине ИИ проекты Искусственные нейросети Слежка за людьми Угроза ИИ ИИ теория Внедрение ИИКомпьютерные науки Машинное обуч. (Ошибки) Машинное обучение Машинный перевод Реализация ИИ Реализация нейросетей Создание беспилотных авто Трезво про ИИ Философия ИИ Big data Работа разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика
Генетические алгоритмы Капсульные нейросети Основы нейронных сетей Распознавание лиц Распознавание образов Распознавание речи Техническое зрение Чат-боты Авторизация |
2019-04-19 01:38 Введение В прошлых материалах мы рассказывали, как массово скачивать тексты из Твиттера и из «Вконтакте» Уметь выкачивать данные социальных сетей бывает полезно, но далеко не всегда там можно найти нужную для конкретных исследовательских целей информацию. Что делать в таком случае? Можно пойти по протоптанной дорожке и найти подготовленные кем-то датасеты. Однако это не всегда работает: далеко не факт ,что кто-то озаботился собрать данные, нужные именно вам. Зато если в поле зрения есть сайт с нужными данными, то можно скачать их собственноручно! В этом туториале мы разберем, как это сделать с помощью Python. Парочка замечаний касательно туториала. Структура HTML-страницы Перед тем как переходить непосредственно к парсингу (автоматическому считыванию данных), надо иметь представление о том, что скрывается за привычными нам красивыми сайтами с картиночками, текстом, ссылками и т.д. Большинство веб-страниц пишутся на языке разметки HTML и структурно представляют собой дерево из HTML-элементов, которые интерпретируются браузерами и превращаются в знакомые всем странички. HTML-элементы кодируют заголовки, текстовые поля, картиночки, кнопочки и т. д. Разберем на простом примере, из чего же такие элементы состоят: Каждый элемент неизбежно включает в себя тег (закрывающий и открывающий), и именно тег отвечает за тип информации, который передан в элементе. В примере на картинке представлен тег <a>, который отвечает за создание ссылок*. Открывающий тег может содержать дополнительную информацию: атрибуты и их значения. Атрибут показывает тип того, что передано в элементе, то есть настраивает его. Значение же атрибута — это то, что непосредственно в него передается. Вернемся к картинке: тег <a> имеет атрибут href, который говорит нам о том, что элемент содержит URL-ссылку на внешний ресурс, а сама ссылка (в нашем случае — https://sysblok.ru) прописывается в значении атрибута внутри кавычек**. Последняя составляющая элемента — содержание, которое отвечает за то, как элемент будет выглядеть на странице. То есть, в нашем примере картинки надпись Системный Блокъ будет интерактивной, при нажатии на нее будет открываться ссылка, которая передана в значении атрибута href. Зачем нам это знать? Затем, что когда мы вытаскиваем с сайта какую-то информацию, мы делаем это как раз через обращение к определенному элементу внутри страницы. Теперь, когда мы немного разобрались в структуре сайтов, можно переходить к практике. *Назначения различных тегов можно посмотреть, например, в Справочнике HTML. Вытаскиваем данные простого сайта Сначала подключаем нужные модули из библиотек. Мы будем пользоваться библиотекой requests, которая позволяет отправлять http-запросы на сервер и получать ответы в различных форматах. Также нам понадобится библиотека pyquery, использующаяся как раз для того, чтобы доставать элементы из html-документов. Последняя библиотека, которую мы используем, — tqdm. Она создана для удобства отслеживания количества записываемых или скачиваемых файликов и скорости передачи данных. Можно обойтись и без tqdm. Если вы не знаете, как устанавливать библиотеки в Python, можете воспользоваться этим гайдом. import requests from pyquery import PyQuery as pq from tqdm import tqdm_notebook Попробуем в качестве примера выкачать эзотерические стихи с сайта Стихи.ру. Сначала попробуем выгрузить только первое стихотворение из списка. Откроем ссылку на стихотворение в браузере, чтобы понять, что и где искать. Допустим, мы хотим вытащить со странички имя автора, название и текст. Начнем с автора. Чтобы понять, какой элемент хранит в себе его имя и фамилию, мы нажимаем правой кнопкой мыши на автора и выбираем «Показать код» (варианты: Inspect, View Source Code и т.п.). Справа откроется окно с тем самым деревом html-элементов, о котором мы говорили ранее, где элемент, содержащий автора, будет подсвечен. Что же мы видим: автор находится в элементе с тегом <a>, который передает ссылки. Действительно, если нажать на автора, то мы окажемся на страничке с его произведениями (ссылка на страницу передана в атрибуте href). Но так как нам нужна не ссылка, а просто имя, то, чтобы достать его, надо отступить по дереву элементов на шаг назад до тега <div>, в котором содержится атрибут class со значением titleauthor. Напишем для этого код, в котором сначала мы сохраняем страничку в качестве html-документа (она сохранится как дерево из элементов), а из дерева достаем имя и фамилию автора и записываем их в отдельную переменную author. response_poems = requests.get("https://www.stihi.ru/2019/03/28/5717") # response_poems.text[:500] author = pq(response_poems.text).find("div.titleauthor").text() Теперь достанем название стихотворения, также нажав по нему правой кнопкой на страничке. Название стихотворения здесь находится в элементе с тегом <h1>. Напишем еще одну строчку кода: title = pq(response_poems.text).find("h1").text() Ну и наконец вытащим сам текст стихотворения. Подсветив элемент в окне с деревом, видим, что текст лежит в теге <div> с атрибутом class со значением text. Вот такая строчка кода вытащит текст стихотворения: text = pq(response_poems.text).find("div.text").text() А теперь попробуем выкачать все стихи с первой страницы раздела «мистика и эзотерика». Нам надо сначала собрать ссылки на стихи, а потом по каждой ссылке собирать содержимое — и делать это мы уже научились выше. Сначала посмотрим, в каких элементах прячутся ссылки на стихи, подсветив их в окне кода страницы. Мы видим, что ссылки скрываются в теге <a> сразу с двумя атрибутами: hrefсо значением самой URL-ссылки на стих и class со значением poemlink. В таком случае нам надо указать, что сначала мы достаем все теги <a>, которые содержат в себе атрибут со значением poemlink, а уже потом из этого элемента вытаскиваем значение атрибута href, то есть, саму ссылку. Посмотрим, как это будет выглядеть в коде: response_poems_full = requests.get("https://www.stihi.ru/poems/list.html?topic=13") poems_urls = [] for poem in pq(response_poems_full.text).find("a.poemlink"): url = pq(poem).attr("href") poems_urls.append("https://www.stihi.ru" + url) Разберем код, написанный выше, построчно. В первой строке мы сохраняем страничку со ссылками на стихи как html-документ. Во второй строчке создаем пустой список, в который мы будет записывать наши ссылки. В третьей строке начинаем цикл, где указываем, что нам надо у всех элементов с тегом <a> со значением poemlink вытащить значение атрибута href. Это значение записывается в переменную url. Каждый такой url добавляется в созданный ранее список при помощи метода append. К каждой ссылке добавляется адрес сайта (ссылки внутри сайта относительные, а не полные, и выглядят вот так: /2019/03/28/7891. Поэтому их надо модифицировать). Наконец напишем цикл, который соберет в себе все предыдущие этапы и для каждого стиха со странички скачает автора, название и текст в отдельный файл на компьютере (для этого в вашей рабочей папке надо заранее создать папку, куда будут скачиваться файлы). for num, poem_url in tqdm_notebook(enumerate(poems_urls)): res = requests.get(poem_url) title = pq(res.text).find("h1").text() author = pq(res.text).find("div.titleauthor").text() text = "" for poem in pq(res.text).find("div.text"): text += pq(poem).text().replace(" ", " ") try: with open(f"stihi/{num}.txt", "wt", encoding="utf-8") as f: f.write(title + " " + author + " " + text) except UnicodeEncodeError as err: print(err, poem_url) Разберем код детально. Для каждой пронумерованной ссылки (обходим и нумеруем ссылки встроенной функцией enumerate) мы сначала записываем веб-страничку в формате html-кода, откуда потом вытаскиваем название, автора и сам текст. В тексте заменяем все символы *** на перенос строки, и заносим его в переменную типа строка. После этого мы записываем каждое собранное стихотворение с метаинформацией (сначала название, на следующей строке автор, на следующей — сам текст) в отдельный файл формата txt с кодировкой utf-8. При этом мы говорим, что если какой-то текст содержит в себе непонятные символы (которые не поддерживаются в utf-8), то программе не надо останавливаться, а стоит пропустить файл и переходить к следующему (если этого не сделать, цикл при ошибке прервется). *** Тут, вероятно, требуется пояснение. сам по себе означает перенос строки, однако, когда мы выкачиваем текст с html-кода, этот символ записывается как обычный набор знаков в тексте, поэтому его надо заменить на нормальный разделитель строки, который будет читаться в программе. Собираем динамические сайты Бывают ситуации, когда код для парсинга написан верно, но ничего не выкачивается, либо выдача никак не соответствует тому, что нужно было собрать. В таком случае, скорее всего, сайт динамический, и к нему нужен другой подход. Динамические сайты не хранят всю информацию прямо у себя на страницах, а подгружают её со своего сервера с помощью API (мы рассказывали про общую идею API в прошлых постах по обкачиванию) при возникновении потребности. Например, когда пользователь скроллит вниз ленту, чтобы открыть доступ к более старым постам. В таком случае надо сначала найти ссылку, с помощью которой сайт подгружает информацию. Посмотрим, где искать эту ссылку и что с ней делать, на примере новостного сайта Meduza. На главной странице с новостями в правом верхнем углу выбираем опцию «Показывать по порядку», чтобы мы могли получить доступ к более старым новостям, проскроллив до конца страницы. Выводим код страницы (как в предыдущем примере или с помощью Ctrl + Shift + I) и переходим во вкладку Network, после чего должно открыться пустое окно. Прокручиваем ленту вниз и нажимаем «Показать ещё», чтобы открыть новости со следующей страницы. Теперь во вкладке справа появились ссылки разных типов (в основном, это ссылки на jpeg и gif). Нам нужна ссылка с типом xhr, которая отвечает, как правило, за отправку запроса к серверу через API. Нажимаем на неё и видим, что это действительно запрос на API сайта. Теперь, после того как мы поняли, откуда подгружаются статьи, процесс парсинга очень похож на тот, которым выкачивают информацию с не-динамических сайтов. url_t = "https://meduza.io/api/w4/search?chrono=news&page={page}&per_page=24&locale=ru" articles = [] for page in range(1, 11): url = url_t.format(page = page) print(url) res = requests.get(url) articles.extend(res.json()["collection"]) Откуда появился раздел collection? Вернемся к окошку, откуда мы копировали ссылку на запрос к API. Переходим в раздел Preview, где находится, по сути, содержание сайта. Открываем вкладку collection, где видим 24 ссылки на статьи на странице — то, что нам и надо. В данном случае доставать ссылки через дерево элементов не требуется. В целом у нас должно получиться 240 статей для выкачки. Начнем их собирать: for num, article_url in tqdm_notebook(enumerate(articles)): res = requests.get("https://meduza.io/" + article_url) title = "" for title in pq(res.text).find("h1.SimpleTitle-root"): title = pq(title).text().replace("xa0", " ") text = "" for paragraph in pq(res.text).find("div.GeneralMaterial-article p"): text += pq(paragraph).text().replace("xa0", " ") try: with open(f"texts_meduza/{num}.txt", "wt", encoding="utf-8") as f: f.write(title + " " + text) except UnicodeEncodeError as err: print(err, article_url) Разберем пошагово код выше. Каждую ссылку на статью мы сначала прикрепляем к ссылке на сайт и скачиваем статью как дерево элементов. После выкачиваем название статьи (находится в элементе с тегом <h1>со значением атрибута SimpleTitle-root), в которой заменяем не нужный нам символ xa0 на пустое место. Далее делаем то же самое с текстом статьи (находится в элементе с тегом <div> с значением атрибута GeneralMaterial-article p). Название и текст мы записываем в отдельный файл с кодировкой utf-8 в заранее созданной папке texts_meduza в нашей рабочей папке Вновь говорим, что если в статье встречаются непонятные кодировке utf-8 символы, то эту статью мы игнорируем и качаем следующую, чтобы не убить весь цикл. Успех, мы получили названия и тексты статей! Теперь докачаем к ним метаинформацию, например, дату публикации. Она содержится в элементе с тегом time со значением атрибута Timestamp-root. Скачаем название статьи, дату и время публикации в отдельные файлы в новую папку texts_meduza_meta (опять же создаем её в нашей рабочей папке). for num, article_url in tqdm_notebook(enumerate(articles)): res = requests.get("https://meduza.io/" + article_url) title = "" for title in pq(res.text).find("h1.SimpleTitle-root"): title = pq(title).text().replace("xa0", " ") time = pq(res.text).find("time.Timestamp-root").text() try: with open(f"texts_meduza_meta/{num}.txt", "wt", encoding="utf-8") as f: f.write(title + " " + time) except UnicodeEncodeError as err: print(err, article_url) Поздравляю, мы успешно собрали корпус из статей и метаданные к ним! В заключение следует заметить, что каждый сайт по структуре различается, поэтому каждый раз надо тратить некоторое время, чтобы найти нужные элементы в документе. Дарья Чуприна Источник: m.vk.com Комментарии: |
|