Немного про парсинг web-страниц. (Python)
Тема не раз обсуждалась на просторах рунета. Соберу в одном топике наиболее важные моменты, применяемые в практике.
Парсить удобно двумя способами: по тегам с классами, по XPath и по микроразметке Schema.org. При этом последний вариант наиболее универсален для тематических сайтов.
Общим моментом для всех способов является загрузка (получение) web-страницы.
Для работы потребуется подключить модуль requests.
Для получения содержимого веб-страницы используем метод get, т.е.
requests.get('http://site.com')или
s.get('http://site.com')Для удобства работы создаем функцию:
На этом первая основная часть заканчивается, у нас есть копия страницы в объекте page, которую можно далее обрабатывать удобным способом.
Приступаем к обработке.
Для парсинга данных по тегам, необходимо подключить модуль bs4, точнее - его компонент BeautifulSoup:
Рассмотрим один из примеров использования. Например, нам надо получить названия (заголовки) объявления, используя классы CSS при помощи lxml. Для этого подготовим страницу к парсингу:
Затем смотрим, как представлены заголовки объявления в исходнике страницы, а представлены они в таком виде:
<div class="product_item__title">ЗАГОЛОВОК</div>
Ищем все элементы с классом product_item__title, делается это следующим образом:
soup.find_all('TAG', {'class': ['CLASS_NAME']})
Тэг у нас - div, а имя класса - product_item__title. и присваиваем все новой переменной.
Хорошо, текст заголовка есть, неплохо бы получить и ссылку. Как известно, ссылка указывается в тэге 'a' в атрибуте 'href', чтоб получить содержимое атрибута, используем метод 'get("имя атрибута")'. Хорошо, пробуем.
Ищем все элементы с тэгом 'a':
Ок, приступаем. Ищем родительские элементы для объявлений. А родительским элементом является <li class="product_item"......></li>, получаем их список:
Теперь мы получили заголовки и url-ы к каждому объявлению. Так же легко и просто получить и цену для каждого объявления, для этого всего лишь надо получить текст для тэга 'div' с CSS классом 'product_item__description'. Можно заметить, что в случае для выбранного ресурса с текстом цены будут присутствовать символы табуляции и прочие "прелести", но это все легко отсекается простой обработкой строк. Все полученные данные можно сохранять в списки, массивы, объекты классов, базу данных и т.д. - куда вздумается, а далее не проблема переходить уже по полученным ссылкам в зависимости от заголовка объявления и уже внутри текста объявления искать что интересно и делать что потребуется, например, если текст интересен - сохранять объявление куда-либо или отсылать сообщение, или еще что.
Удобно использовать с любыми сайтами, где применяются стили, с некоторыми оговорками, как, например, в случае с нижеизложенным XPath.
Переходим к парсингу с помощью микроразметки.
Многие сайты в угоду поисковым системам, да и просто для нормальной организации структуры сайта, используют микроразметки, одной из самых популярных является Schema.org. Ее и рассмотрим.
Работать с микроразметкой крайне просто. Для этого используется все тот же модуль BeautifulSoup и метод "find". Но... на сайте Юла в обычном ее представлении она отсутствует, возьмем другой сайт, например, сайт комус, и перейдем на страницу товара (любого, ссылки приводить не буду). Отыщем картинки товара, согласно разметке свойство у них - "image", т.е. ищем itemprop="image", ищем все изображения, проверяем, есть ли они вообще, т.е. объект не пустой, и выводим на печать:
Парсинг по тэгам и CSS-классам, и микроразметке рассмотрели. Переходим к парсингу по XPath.
Парсинг с помощью XPath.
Первым делом подключаем html модуль:
Ищем XPath пусть нужного элемента, для этого в исходном коде страницы выбираем нужный элемент, ПКМ - Copy - Copy XPath. В браузере Chrome удобно использовать расширение Toggle Xpather, запускаем его и вставляем скопированный XPath, например, что-то вроде: //*[@id="app"]/div[1]/div/div/div/main/section[2]/div/ul/li[14]/a/figure/figcaption/div[2]. И видим текст для объявления (его заголовок). Чтобы увидеть все тексты, немножко отредактируем: //*[@id="app"]/div[1]/div/div/div/main/section[2]/div/ul/li/a/figure/figcaption/div[2] (убрали [14]). Получаем список заголовков. Делаем аналогично для цен и ссылок, получаем (текст уже для Python):
Теперь формируем список заголовков в Python:
Удобно работать с большинством сайтов, но надо быть предельно внимательным, т.е. может попасться что-то вроде "чай черный", где слово "чай" будет анкорной ссылкой, а "черный" - текстом, который ищется парсером. В таком случае будет найдено только слово "черный", а слово "чай" будет опущено.
Многостраничный проход легко реализовывается по собранным ссылкам: в одном методе формируем список ссылок, в другой - проходим по ним, попутно так же формируя список новых ссылок, и т.д.
Пока вроде все написал, что хотел, с этой краткой теорией можно работать с абсолютным большинством ресурсов и искать необходимые данные. При использовании многостраничного парсинга стоит использовать паузы, задержки, дабы не создавать лишнюю нагрузку на сайт, да и в зависимости от настроек интернет-ресурса можно и в блок попасть.
Статья написана исключительно в ознакомительных целях при закреплении материала. Ни один сайт не пострадал xD
Парсить удобно двумя способами: по тегам с классами, по XPath и по микроразметке Schema.org. При этом последний вариант наиболее универсален для тематических сайтов.
Общим моментом для всех способов является загрузка (получение) web-страницы.
Для работы потребуется подключить модуль requests.
import requestsЗатем создать сессию и указать юзер-агента:
s = requests.Session()
s.headers.update({
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:45.0) Gecko/20100101 Firefox/45.0'
})
Для получения содержимого веб-страницы используем метод get, т.е.
requests.get('http://site.com')или
s.get('http://site.com')Для удобства работы создаем функцию:
def get_page(url):Например, нам надо получить страницу сайта объявлений юла, тогда код будет выглядеть следующим образом:
return s.get(url)
link = 'https://youla.ru/'Чтобы распечатать текстовое представление (html), используем атрибут text:
page = get_page(link)
print(page.text)После чего на экране увидим страницу в виде текста html.
На этом первая основная часть заканчивается, у нас есть копия страницы в объекте page, которую можно далее обрабатывать удобным способом.
Приступаем к обработке.
Для парсинга данных по тегам, необходимо подключить модуль bs4, точнее - его компонент BeautifulSoup:
from bs4 import BeautifulSoupBeautifulSoup - это парсер для синтаксического разбора файлов HTML/XML (http://wiki.python.su/%D0%94%D0%BE%D0%BA%D1%83%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%86%D0%B8%D0%B8/BeautifulSoup). В вики много полезной инфы, рекомендуется к прочтению.
Рассмотрим один из примеров использования. Например, нам надо получить названия (заголовки) объявления, используя классы CSS при помощи lxml. Для этого подготовим страницу к парсингу:
soup = BeautifulSoup(page.text, "lxml")Примечание: в качестве параметра передается текстовое содержание страницы.
Затем смотрим, как представлены заголовки объявления в исходнике страницы, а представлены они в таком виде:
<div class="product_item__title">ЗАГОЛОВОК</div>
Ищем все элементы с классом product_item__title, делается это следующим образом:
soup.find_all('TAG', {'class': ['CLASS_NAME']})
Тэг у нас - div, а имя класса - product_item__title. и присваиваем все новой переменной.
names = soup.find_all('div', {'class': ['product_item__title']})Метод find_all возвращает массив объектов, поэтому для просмотра, что мы получили в итоге, необходимо обратиться к каждому из них, а для просмотра текста заголовка - использовать атрибут 'text' для каждого элемента, выглядеть это будет так:
for item in names:В итоге на экране мы увидим заголовки объявлений.
print(item.text)
Хорошо, текст заголовка есть, неплохо бы получить и ссылку. Как известно, ссылка указывается в тэге 'a' в атрибуте 'href', чтоб получить содержимое атрибута, используем метод 'get("имя атрибута")'. Хорошо, пробуем.
Ищем все элементы с тэгом 'a':
for item in all_links:Вроде получилось, получили много ссылок, но слишком много, больше, чем заголовков, да и ссылки порой к заголовкам не относятся. А все потому, что были спарсены абсолютно все тэги "а". Чтобы все упорядочить, надо проводить парсинг поблочно, поэлементно, то есть, вся страница представлена в виде иерархии элементов, один вложен в другой, в одном элементе может быть куча других, так и ссылка и заголовок находятся внутри какого-то другого элемента, где так же находится и цена. Поэтому надо найти сначала все эти "родительские" элементы, а уже внутри них искать необходимую информацию.
link = item.get('href')
print(link)
Ок, приступаем. Ищем родительские элементы для объявлений. А родительским элементом является <li class="product_item"......></li>, получаем их список:
parent_blocks = soup.find_all('li', {'class': ['product_item']})Далее уже внутри каждого элемента ищем заголовки и ссылки точно так же, как делали это раньше, НО уже не в тексте всей страницы, а в тексте только отдельно взятого блока, т.е.:
for parent_block in parent_blocks:Здесь уже используем не 'find_all', а просто 'find', т.к. у нас всего один элемент с таким тэгом и классом. Но если будет несколько, то надо использовать 'find_all', а далее с полученными данными работать как с массивом.
name = parent_block.find('div', {'class': ['product_item__title']})
print(name.text)
link = parent_block.find('a')
print(link.get('href'))
Теперь мы получили заголовки и url-ы к каждому объявлению. Так же легко и просто получить и цену для каждого объявления, для этого всего лишь надо получить текст для тэга 'div' с CSS классом 'product_item__description'. Можно заметить, что в случае для выбранного ресурса с текстом цены будут присутствовать символы табуляции и прочие "прелести", но это все легко отсекается простой обработкой строк. Все полученные данные можно сохранять в списки, массивы, объекты классов, базу данных и т.д. - куда вздумается, а далее не проблема переходить уже по полученным ссылкам в зависимости от заголовка объявления и уже внутри текста объявления искать что интересно и делать что потребуется, например, если текст интересен - сохранять объявление куда-либо или отсылать сообщение, или еще что.
Удобно использовать с любыми сайтами, где применяются стили, с некоторыми оговорками, как, например, в случае с нижеизложенным XPath.
Переходим к парсингу с помощью микроразметки.
Многие сайты в угоду поисковым системам, да и просто для нормальной организации структуры сайта, используют микроразметки, одной из самых популярных является Schema.org. Ее и рассмотрим.
Работать с микроразметкой крайне просто. Для этого используется все тот же модуль BeautifulSoup и метод "find". Но... на сайте Юла в обычном ее представлении она отсутствует, возьмем другой сайт, например, сайт комус, и перейдем на страницу товара (любого, ссылки приводить не буду). Отыщем картинки товара, согласно разметке свойство у них - "image", т.е. ищем itemprop="image", ищем все изображения, проверяем, есть ли они вообще, т.е. объект не пустой, и выводим на печать:
images = soup.find_all(itemprop="image")Удобно использовать для работы с каталогами, интернет-магазинами и прочими структурированными сайтами.
if(images != None):
for image in images:
pict = image.get('src')
print(pict)
Парсинг по тэгам и CSS-классам, и микроразметке рассмотрели. Переходим к парсингу по XPath.
Парсинг с помощью XPath.
Первым делом подключаем html модуль:
from lxml import htmlЗатем подготавливаем страницу для парсинга с помощью XPath:
tree = html.fromstring(page.content)Примечание: в отличие от парсинга по классам CSS, используется атрибут не text, а content.
Ищем XPath пусть нужного элемента, для этого в исходном коде страницы выбираем нужный элемент, ПКМ - Copy - Copy XPath. В браузере Chrome удобно использовать расширение Toggle Xpather, запускаем его и вставляем скопированный XPath, например, что-то вроде: //*[@id="app"]/div[1]/div/div/div/main/section[2]/div/ul/li[14]/a/figure/figcaption/div[2]. И видим текст для объявления (его заголовок). Чтобы увидеть все тексты, немножко отредактируем: //*[@id="app"]/div[1]/div/div/div/main/section[2]/div/ul/li/a/figure/figcaption/div[2] (убрали [14]). Получаем список заголовков. Делаем аналогично для цен и ссылок, получаем (текст уже для Python):
xpnames = '//*[@id="app"]/div[1]/div/div/div/main/section[2]/div/ul/li/a/figure/figcaption/div[2]'@href' - добавляется самостоятельно на конце и указывает на атрибут.
xpprices = '//*[@id="app"]/div[1]/div/div/div/main/section[2]/div/ul/li/a/figure/figcaption/div[1]/text()'
xplinks = '//*[@id="app"]/div[1]/div/div/div/main/section[2]/div/ul/li/a/@href'
Теперь формируем список заголовков в Python:
names = tree.xpath(xpnames)И выводим на печать:
for name in names:Повторяем для цены и ссылок:
print(name.text)
prices = tree.xpath(xpprices)Дальнейшая обработка (сопоставление заголовка с ценой и ссылкой и т.д.) - уже обычная работа со строками и списками (массивами). Можно проверять количество объектов, пустой ли объект и т.д., все зависит от конкретного случая, удобства и привычки. Как видно, ничего сложного в этом нет, а для проверки корректности XPath удобно пользоваться расширениями для браузеров. Еще момент: надо быть внимательным и следить, нужно ли использовать атрибут .text или нет, чтоб не заморачиваться и было наверняка - можно написать и запустить 1 раз, тогда станет ясно.
for price in prices:
print(price)
links = tree.xpath(xplinks)
for link in links:
print(link)
Удобно работать с большинством сайтов, но надо быть предельно внимательным, т.е. может попасться что-то вроде "чай черный", где слово "чай" будет анкорной ссылкой, а "черный" - текстом, который ищется парсером. В таком случае будет найдено только слово "черный", а слово "чай" будет опущено.
Многостраничный проход легко реализовывается по собранным ссылкам: в одном методе формируем список ссылок, в другой - проходим по ним, попутно так же формируя список новых ссылок, и т.д.
Пока вроде все написал, что хотел, с этой краткой теорией можно работать с абсолютным большинством ресурсов и искать необходимые данные. При использовании многостраничного парсинга стоит использовать паузы, задержки, дабы не создавать лишнюю нагрузку на сайт, да и в зависимости от настроек интернет-ресурса можно и в блок попасть.
Статья написана исключительно в ознакомительных целях при закреплении материала. Ни один сайт не пострадал xD
Комментарии
Отправить комментарий