`'а."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"#cite_ref-.D0.9B.D0.B5.D1.81.D1.81.D0.BA.D0.B8.D1.81.E2.80.941999.E2.80.94.E2.80.94213_1-0\n",
"#CITEREF.D0.9B.D0.B5.D1.81.D1.81.D0.BA.D0.B8.D1.811999\n",
"#cite_ref-.D0.9B.D0.BE.D1.81.D0.B5.D0.B2.E2.80.941993.E2.80.94.E2.80.94407_2-0\n",
"#cite_ref-.D0.9B.D0.BE.D1.81.D0.B5.D0.B2.E2.80.941993.E2.80.94.E2.80.94407_2-1\n",
"#CITEREF.D0.9B.D0.BE.D1.81.D0.B5.D0.B21993\n",
"#cite_ref-.D0.9B.D0.B5.D1.81.D1.81.D0.BA.D0.B8.D1.81.E2.80.941999.E2.80.94.E2.80.94214_3-0\n",
"#CITEREF.D0.9B.D0.B5.D1.81.D1.81.D0.BA.D0.B8.D1.811999\n",
"#cite_ref-.D0.A7.D1.83.D0.B4.D0.B0.D0.BA.D0.BE.D0.B2.D0.B0.E2.80.941988.E2.80.94.E2.80.94300_4-0\n",
"#CITEREF.D0.A7.D1.83.D0.B4.D0.B0.D0.BA.D0.BE.D0.B2.D0.B01988\n",
"#cite_ref-.D0.A7.D1.83.D0.B4.D0.B0.D0.BA.D0.BE.D0.B2.D0.B0.E2.80.941988.E2.80.94.E2.80.94301_5-0\n"
]
}
],
"source": [
"div = page.findAll('div', class_='references-small')[0]\n",
"for link in div(\"a\")[0:10]:\n",
" print(link['href'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Для экономии места я вывел только первые 10 ссылок. Это внутренние ссылки на другие фрагменты страницы, поэтому они начинаются с символа `#`. Легко увидеть, что мы получили то, что требовалось.\n",
"\n",
"### Некоторые итоги\n",
"Подведём некоторые итоги по поводу поиска информации в HTML-файлах:\n",
"\n",
"- Это всегда творческий процесс: все сайты разные и нет единого рецепта, как извлекать из них нужную информацию.\n",
"- В первую очередь нужно посмотреть в исходник интересующей вас странички. Проще всего это делать с помощью инструментария веб-разработчика типа Firebug или встроенного инспектора кода в Firefox или аналогичных инструментов для других браузеров.\n",
"- В HTML-дереве можно ориентироваться по названиям тегов, их классам, id'ам и другим свойствам.\n",
"- Можно искать нужный элемент итеративно — сначала найти «большой» тег, который включает наш элемент, потом найти в нём элемент поменьше и т.д."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### API и XML\n",
"Анализируя веб-страницы и извлекая из них информацию мы пытаемся написать программу, которая бы действовала как человек. Это бывает непросто. К счастью, всё чаще разнообразные сайты предлагают информацию, которую может легко обрабатывать не только человек, но и другая программа. Это называется API — application program interface. Обычный интерфейс — это способ взаимодействия человека с программой, а API — одной программы с другой. Например, вашего скрипта на Python с удалённым веб-сервером.\n",
"\n",
"Для хранения веб-страниц, которые читают люди, используется язык HTML. Для хранения произвольных структурированных данных, которыми обмениваются между собой программы, используются другие языки — в частности, язык XML, похожий на HTML. Вернее было бы сказать, что XML это *метаязык*, то есть способ описания языков. В отличие от HTML, набор тегов в XML-документе может быть произвольным (и определяется разработчиком конкретного диалекта XML). Например, если бы мы хотели описать в виде XML некоторую студенческую группу, это могло бы выглядеть так:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```xml\n",
"
\n",
" 134\n",
" \n",
" Виталий\n",
" Иванов\n",
" \n",
" \n",
" Мария\n",
" Петрова\n",
" \n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Для обработки XML-файлов можно использовать тот же пакет *Beautiful Soup*, который мы уже использовали для работы с HTML. Единственное различие — нужно указать дополнительный параметр `feautres=\"xml\"` при вызове функции `BeautifulSoup` — чтобы он не искал в документе HTML-теги."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"group = \"\"\"
\n",
"134\n",
"\n",
"Виталий\n",
"Иванов\n",
"\n",
"\n",
"Мария\n",
"Петрова\n",
"\n",
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"
\n",
" \n",
" 134\n",
" \n",
" \n",
" \n",
" Виталий\n",
" \n",
" \n",
" Иванов\n",
" \n",
" \n",
" \n",
" \n",
" Мария\n",
" \n",
" \n",
" Петрова\n",
" \n",
" \n",
"\n"
]
}
],
"source": [
"obj = BeautifulSoup(group, features=\"xml\")\n",
"print(obj.prettify())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Вот так мы можем найти в нашем XML-документе номер группы:"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'134'"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"obj.group.number.string"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Это значит «в объекте `obj` найти тег `group` в нём найти тег `number` и выдать в виде строки то, что в нём содержится.\n",
"\n",
"А вот так можно перечислить всех студентов:"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Иванов Виталий\n",
"Петрова Мария\n"
]
}
],
"source": [
"for student in obj.group.findAll('student'):\n",
" print(student.lastname.string, student.firstname.string)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Получаем список статей из категории в Википедии"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Допустим, нам потребовалось получить список всех статей из некоторой категории в Википедии. Мы могли бы открыть эту категорию в браузере и дальше действовать теми методами, которые обсуждались выше. Однако, на наше счастье разработчики Википедии сделали удобное API. Чтобы научиться с ним работать, придётся познакомиться с [документацией](https://www.mediawiki.org/wiki/API:Main_page) (так будет с любым API), но это кажется сложным только в первый раз. Ну хорошо, в первые 10 раз. Или 20. Потом будет проще.\n",
"\n",
"Итак, приступим. Взаимодействие с сервером при помощи API происходит с помощью отправки специальным образом сформированных запросов и получения ответа в одном из машинночитаемых форматов. Нас будет интересовать формат XML, хотя бывают и другие (позже мы познакомимся с JSONN). А вот такой запрос мы можем отправить:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"https://en.wikipedia.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:Physics&cmsort=timestamp&cmdir=desc&format=xmlfm"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Строка `https://en.wikipedia.org/w/api.php` (до знака вопроса) — это *точка входа* в API. Всё, что идёт после знака вопроса — это, собственно, запрос. Он представляет собой что-то вроде словаря и состоит из пар «ключ=значение», разделяемых амперсандом `&`. Некоторые символы приходится кодировать специальным образом.\n",
"\n",
"Например, в адресе выше сказано, что мы хотим сделать запрос (`action=query`), перечислить элементы категории `list=categorymembers`, в качестве категории, которая нас интересует, указана `Category:Physics` (`cmtitle=Category:Physics`) и указаны некоторые другие параметры. Если кликнуть по этой ссылке, откроется примерно такая штука:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```xml\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
"\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы видим здесь разные теги, и видим, что нас интересуют теги `
`, находящиеся внутри тега ``.\n",
"\n",
"Давайте сделаем соответствующий запрос с помощью Python. Для этого нам понадобится уже знакомый модуль `requests`."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"url = \"https://en.wikipedia.org/w/api.php\"\n",
"params = {\n",
" 'action':'query',\n",
" 'list':'categorymembers',\n",
" 'cmtitle': 'Category:Physics',\n",
" 'format': 'xml'\n",
"}\n",
"\n",
"g = requests.get(url, params=params)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Как видно, список параметров мы передаем в виде обычного словаря. Посмотрим, что получилось."
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"True"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"g.ok"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Всё хорошо. Теперь используем *Beautiful Soup* для обработки этого XML."
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"data = BeautifulSoup(g.text, features='xml')"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
"\n"
]
}
],
"source": [
"print(data.prettify())"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Найдём все вхождения тега `` и выведем их атрибут `title`:"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Physics\n",
"Branches of physics\n",
"Glossary of classical physics\n",
"Outline of physics\n",
"Portal:Physics\n",
"Classical physics\n",
"Epicatalysis\n",
"Experimental physics\n",
"Hume Feldman\n",
"Microphysics\n"
]
}
],
"source": [
"for cm in data.api.query.categorymembers(\"cm\"):\n",
" print(cm['title'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Можно было упростить поиск ``, не указывая «полный путь» к ним:"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Physics\n",
"Branches of physics\n",
"Glossary of classical physics\n",
"Outline of physics\n",
"Portal:Physics\n",
"Classical physics\n",
"Epicatalysis\n",
"Experimental physics\n",
"Hume Feldman\n",
"Microphysics\n"
]
}
],
"source": [
"for cm in data(\"cm\"):\n",
" print(cm['title'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"По умолчанию сервер вернул нам список из 10 элементов. Если мы хотим больше, нужно воспользоваться элементом `continue` — это своего рода гиперссылка на следующие 10 элементов."
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'page|4d4f4445524e20504859534943530a4d4f4445524e2050485953494353|844186'"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data.find(\"continue\")['cmcontinue']"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мне пришлось использовать метод `find()` вместо того, чтобы просто написать `data.continue`, потому что `continue` в Python имеет специальный смысл.\n",
"\n",
"Теперь добавим `cmcontinue` в наш запрос и выполним его ещё раз:"
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"collapsed": true
},
"outputs": [],
"source": [
"params['cmcontinue'] = data.api(\"continue\")[0]['cmcontinue']"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Modern physics\n",
"Northwest Nuclear Consortium\n",
"Phase Stretch Transform\n",
"Statistical mechanics\n",
"Surface science\n",
"Wigner rotation\n",
"Category:Concepts in physics\n",
"Category:Physicists\n",
"Category:Applied and interdisciplinary physics\n",
"Category:Atomic, molecular, and optical physics\n"
]
}
],
"source": [
"g = requests.get(url, params=params)\n",
"data = BeautifulSoup(g.text, features='xml')\n",
"for cm in data.api.query.categorymembers(\"cm\"):\n",
" print(cm['title'])"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Мы получили следующие 10 элементов из категории. Продолжая таким образом, можно выкачать её даже целиком (правда, для этого потребуется много времени).\n",
"\n",
"Аналогичным образом реализована работа с разнообразными другими API, имеющимися на разных сайтах. Где-то API является полностью открытым (как в Википедии), где-то вам потребуется зарегистрироваться и получить application id и какой-нибудь ключ для доступа к API, где-то попросят даже заплатить (например, автоматический поиск в Google стоит что-то вроде 5 долларов за 100 запросов). Есть API, которые позволяют только читать информацию, а бывают и такие, которые позволяют её править. Например, можно написать скрипт, который будет автоматически сохранять какую-то информацию в Google Spreadsheets. Всякий раз при использовании API вам придётся изучить его документацию, но это в любом случае проще, чем обрабатывать HTML-код. Иногда удаётся упростить доступ к API, используя специальные библиотеки."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.0"
}
},
"nbformat": 4,
"nbformat_minor": 0
}