Парсинг на питоне с нуля

Парсим на Python: Pyparsing для новичков

Парсинг (синтаксический анализ) представляет собой процесс сопоставления последовательности слов или символов — так называемой формальной грамматике. Например, для строчки кода:

import matplotlib.pyplot as plt 

имеет место следующая грамматика: сначала идёт ключевое слово import, потом название модуля или цепочка имён модулей, разделённых точкой, потом ключевое слово as, а за ним — наше название импортируемому модулю.

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

Данное выражение представляет собой словарь Python, который имеет два ключа: ‘import’ и ‘as’. Значением для ключа ‘import’ является список, в котором по порядку перечислены названия импортируемых модулей.

Для парсинга как правило используют регулярные выражения. Для этого имеется модуль Python под названием re (regular expression — регулярное выражение). Если вам не доводилось работать с регулярными выражениями, их вид может вас испугать. Например, для строки кода ‘import matplotlib.pyplot as plt’ оно будет иметь вид:

К счастью, есть удобный и гибкий инструмент для парсинга, который называется Pyparsing. Главное его достоинство — он делает код более читаемым, а также позволяет проводить дополнительную обработку анализируемого текста.

В данной статье мы установим Pyparsing и создадим на нём наш первый парсер.

Вначале установим Pyparsing. Если Вы работаете в Linux, в командной строке наберите:

sudo pip install pyparsing 

В Windows Вам необходимо в командной строке, запущенной с правами администратора, предварительно зайти в каталог, где лежит файл pip.exe (например, C:\Python27\Scripts\), после чего выполнить:

Другой способ — это зайти на страницу проекта Pyparsing на SourceForge, скачать там инсталлятор для Windows и установить Pyparsing как обычную программу. Полную информацию о всевозможных способах установки Pyparsing можно получить на странице проекта.

Перейдём к парсингу. Пусть s — следующая строка:

s = 'import matplotlib.pyplot as plt' 

В результате парсинга мы хотим получить словарь:

Сначала необходимо импортировать Pyparsing. Запустите например Python IDLE и введите:

Звёздочка * выше означает импорт всех имён из pyparsing. В результате это может нарушить рабочее пространство имён, что приведёт к ошибкам в работе программы. В нашем случае * используется временно, потому что мы пока не знаем, какие классы из Pyparsing мы будем использовать. После того, как мы напишем парсер, мы заменим * на названия использованных нами классов.

При использовании pyparsing, парсер вначале пишется для отдельных ключевых слов, символов, коротких фраз, а потом из отдельных частей получается парсер для всего текста.

Читайте также:  Лабораторная работа язык разметки гипертекста html

Начнём с того, что у нас в строке есть название модуля. Формальная грамматика: в общем случае название модуля — это слово, состоящее из букв и символа нижнего подчёркивания. На pyparsing:

module_name = Word(alphas + '_') 

Word — это слово, alphas — буквы. Word(alphas + ‘_’) — слово, состоящее из букв и нижнего подчёркивания. module_name переводится как название модуля. Теперь читаем всё вместе: название модуля — это слово, состоящее из букв и символа нижнего подчёркивания. Таким образом, запись на Pyparsing очень близка к естественному языку.

Полное имя модуля — это название модуля, потом точка, потом название другого модуля, потом снова точка, потом название третьего модуля и так далее, пока по цепочке не дойдём до искомого модуля. Полное имя модуля может состоять из имени одного модуля и не иметь точек. На pyparsing:

full_module_name = module_name + ZeroOrMore('.' + module_name) 

ZeroOrMore дословно переводится как «ноль или более», а это означает, что содержимое в скобках может повторяться несколько раз или отсутствовать. В итоге читаем полностью вторую строчку парсера: полное имя модуля — это название модуля, после которого ноль и более раз идут точка и название модуля.

После полного названия модуля идёт необязательная часть ‘as plt’. Она представляет собой ключевое слово ‘as’, после которого идёт имя, которое мы сами дали импортируемому модулю. На pyparsing:

import_as = Optional('as' + module_name) 

Optional дословно переводится как «необязательный», а это означает, что содержимое в скобках может быть, а может отсутствовать. В сумме получаем: «необязательное выражение, состоящее из слова ‘as’ и названия модуля.

Полная инструкция импорта состоит из ключевого слова import, после которого идёт полное имя модуля, потом необязательная конструкция ‘as plt’. На pyparsing:

parse_module = 'import' + full_module_name + import_as 

В итоге имеем наш первый парсер:

module_name = Word(alphas + '_') full_module_name = module_name + ZeroOrMore('.' + module_name) import_as = Optional('as' + module_name) parse_module = 'import' + full_module_name + import_as 

Теперь надо распарсить строку s:

(['import', 'matplotlib', '.', 'pyplot', 'as', 'plt'], <>) 

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

parse_module.parseString(s).asList() 
['import', 'matplotlib', '.', 'pyplot', 'as', 'plt'] 

Теперь будем совершенствовать парсер. Прежде всего, мы бы не хотели видеть в выводе парсера слово import и точку между названиями модулей. Для подавления вывода используется Suppress(). С учётом этого наш парсер выглядит так:

module_name = Word(alphas + '_') full_module_name = module_name + ZeroOrMore(Suppress('.') + module_name) import_as = Optional(Suppress('as') + module_name) parse_module = Suppress('import') + full_module_name + import_as 

Выполнив parse_module.parseString(s).asList() , получим:

Давайте теперь сделаем так, чтобы парсер сразу возвращал нам словарь вида <'import':[модуль1, модуль2, . ], 'as':модуль>. Прежде чем сделать это, вначале нужно отдельно получить доступ к списку импортируемых модулей (full_module_name) и к нашему собственному названию модуля (import_as). Для этого pyparsing позволяет назначать имена результатам парсинга. Давайте дадим списку импортируемых модулей имя ‘modules’, а тому, как мы сами назвали модуль — имя ‘import as’:

full_module_name = (module_name + ZeroOrMore(Suppress('.') + module_name))('modules') import_as = (Optional(Suppress('as') + module_name))('import_as') 

Как видно из двух строчек выше, чтобы дать результату парсинга имя, нужно выражение парсера поставить в скобки, и после этого выражения в скобках дать название результата. Давайте посмотрим, что изменилось. Для этого выполним код:

res = parse_module.parseString(s) print(res.modules.asList()) print(res.import_as.asList()) 

Теперь мы можем отдельно извлекать цепочку модулей для импорта искомого и наше название для него. Осталось сделать так, чтобы парсер возвращал словарь. Для этого используется так называемое ParseAction — действие в процессе парсинга:

parse_module = (Suppress('import') + full_module_name).setParseAction(lambda t: ) 

lambda — это анонимная функция в Python, t — аргумент этой функции. Потом идёт двоеточие и выражение словаря Python, в который мы подставляем нужные нам данные. Когда мы вызываем asList(), мы получаем список. Имя модуля после as всегда одно, и список t.import_as.asList() всегда будет содержать только одно значение. Поэтому мы берём единственный элемент списка (он имеет индекс ноль) и пишем asList()[0].

Читайте также:  Python среднее арифметическое массив

Проверим парсер. Выполним parse_module.parseString(s).asList() и получим:

Мы почти достигли цели. Так как у полученного списка единственный аргумент, добавим [0] в конце строки для парсинга текста: parse_module.parseString(s).asList()[0]

Мы получили то, что хотели.

Достигнув цели, необходимо вернуться к ‘from pyparsing import *’ и поменять звёздочку на те классы, которые нам пригодились:

from pyparsing import Word, alphas, ZeroOrMore, Suppress, Optional 

В итоге наш код имеет следующий вид:

from pyparsing import Word, alphas, ZeroOrMore, Suppress, Optional module_name = Word(alphas + "_") full_module_name = (module_name + ZeroOrMore(Suppress('.') + module_name))('modules') import_as = (Optional(Suppress('as') + module_name))('import_as') parse_module = (Suppress('import') + full_module_name + import_as).setParseAction(lambda t: ) 

Мы рассмотрели совсем простой пример и лишь небольшую часть возможностей Pyparsing. За бортом — создание рекурсивных выражений, обработка таблиц, поиск по тексту с оптимизацией, резко ускоряющей сам поиск, и многое другое.

В заключение пару слов о себе. Я аспирант и ассистент МГТУ им. Баумана (кафедра МТ-1 „Металлорежущие станки“). Увлекаюсь Python, Linux, HTML, CSS и JS. Моё хобби — автоматизация инженерной деятельности и инженерных расчётов. Считаю, что могу быть полезным Хабру, делясь своими знаниями о работе в Pyparsing, Sage и некоторыми особенностями автоматизации инженерных расчётов. Также знаю среду SageMathCloud, которая является мощной альтернативой Wolfram Alpha. SageMathCloud заточена на проведение расчётов на Python в облаке. При этом Вам доступна консоль (Ubuntu под капотом), Sage, IPython и LaTeX. Есть возможность совместной работы. Помимо кода на Python SageMathCloud поддерживает html, css, js, coffescript, go, fortran, scilab и многое другое. В настоящее время среда бесплатна (достаточно стабильная бета-версия), потом будет будет работать по системе Freemium. На текущий момент времени эта среда не освещена на Хабре, и я хотел бы восполнить этот пробел.

Читайте также:  Обучение middle java developer

Благодарю Дарью Фролову и Никиту Коновалова за помощь в редактировании статьи.

Источник

Почему стоит научиться «парсить» сайты, или как написать свой первый парсер на Python

image

Для начала давайте разберемся, что же действительно означает на первый взгляд непонятное слово — парсинг. Прежде всего это процесс сбора данных с последующей их обработкой и анализом. К этому способу прибегают, когда предстоит обработать большой массив информации, с которым сложно справиться вручную. Понятно, что программу, которая занимается парсингом, называют — парсер. С этим вроде бы разобрались.

Перейдем к этапам парсинга.

И так, рассмотрим первый этап парсинга — Поиск данных.

Так как нужно парсить что-то полезное и интересное давайте попробуем спарсить информацию с сайта work.ua.
Для начала работы, установим 3 библиотеки Python.

pip install beautifulsoup4

Без цифры 4 вы ставите старый BS3, который работает только под Python(2.х).

pip install requests
pip install pandas

Теперь с помощью этих трех библиотек Python, можно проанализировать нашу веб-страницу.

Второй этап парсинга — Извлечение информации.

Попробуем получить структуру html-кода нашего сайта.
Давайте подключим наши новые библиотеки.

import requests from bs4 import BeautifulSoup as bs import pandas as pd 

И сделаем наш первый get-запрос.

URL_TEMPLATE = "https://www.work.ua/ru/jobs-odesa/?page=2" r = requests.get(URL_TEMPLATE) print(r.status_code) 

Статус 200 состояния HTTP — означает, что мы получили положительный ответ от сервера. Прекрасно, теперь получим код странички.

Получилось очень много, правда? Давайте попробуем получить названия вакансий на этой страничке. Для этого посмотрим в каком элементе html-кода хранится эта информация.

У нас есть тег h2 с классом «add-bottom-sm», внутри которого содержится тег a. Отлично, теперь получим title элемента a.

soup = bs(r.text, "html.parser") vacancies_names = soup.find_all('h2', class_='add-bottom-sm') for name in vacancies_names: print(name.a['title']) 

Хорошо, мы получили названия вакансий. Давайте спарсим теперь каждую ссылку на вакансию и ее описание. Описание находится в теге p с классом overflow. Ссылка находится все в том же элементе a.

Some information about vacancy.

vacancies_info = soup.find_all('p', class_='overflow') for name in vacancies_names: print('https://www.work.ua'+name.a['href']) for info in vacancies_info: print(info.text) 

И последний этап парсинга — Сохранение данных.

Давайте соберем всю полученную информацию по страничке и запишем в удобный формат — csv.

import requests from bs4 import BeautifulSoup as bs import pandas as pd URL_TEMPLATE = "https://www.work.ua/ru/jobs-odesa/?page=2" FILE_NAME = "test.csv" def parse(url = URL_TEMPLATE): result_list = r = requests.get(url) soup = bs(r.text, "html.parser") vacancies_names = soup.find_all('h2', class_='add-bottom-sm') vacancies_info = soup.find_all('p', class_='overflow') for name in vacancies_names: result_list['href'].append('https://www.work.ua'+name.a['href']) result_list['title'].append(name.a['title']) for info in vacancies_info: result_list['about'].append(info.text) return result_list df = pd.DataFrame(data=parse()) df.to_csv(FILE_NAME) 

После запуска появится файл test.csv — с результатами поиска.

Источник

Оцените статью