{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Основы программирования в Python\n", "\n", "*Алла Тамбовцева, НИУ ВШЭ*" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Web-scraping\n", "\n", "Мы уже немного познакомились со структурой html-файлов, теперь попробуем выгрузить информацию из реальной страницы, а точнее, с реального сайта [nplus1.ru](https://nplus1.ru/).\n", "\n", "**Наша задача:** выгрузить недавние новости в датафрейм `pandas`, чтобы потом сохранить все в csv-файл.\n", "\n", "Сначала сгрузим весь html-код страницы и сохраним его в отдельную переменную. Для этого нам понадобится библиотека `requests`. Импортируем её:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import requests" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Сохраним ссылку на главную страницу сайта в переменную `url` для удобства и выгрузим страницу. (Разумеется, это будет работать при подключении к интернету. Если соединение будет отключено, Python выдаст `NewConnectionError`)." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "url = 'https://nplus1.ru/' # сохраняем\n", "page = requests.get(url) # загружаем страницу по ссылке" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если мы просто посмотрим на объект, мы ничего особенного не увидим:" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "page # response 200 - страница загружена" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Импортируем функцию `BeautifulSoup` из библиотеки `bs4` (от *beautifulsoup4*) и заберём со страницы `page` код html в виде текста. " ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "from bs4 import BeautifulSoup # не спрашивайте, почему BeautifulSoup" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "soup = BeautifulSoup(page.text, 'lxml')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Если выведем `soup` на экран, мы увидим то же самое, что в режиме разработчика или в режиме происмотра исходного кода (`view-source` через *Ctrl+U* в Google Chrome)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "soup" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Для просмотра выглядит не очень удобно.  «Причешем» наш `soup` – воспользуемся методом `.prettify()` в сочетании с функцией `print()`." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print(soup.prettify())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В такой выдаче ориентироваться гораздо удобнее (но при желании, то же можно увидеть в браузере, на большом экране)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Чтобы сгрузить все новости с главной страницы сайта, нужно собрать все ссылки на страницы с этими новостями. Ссылки в html-файле всегда заключены в тэг `` и имеют атрибут `href`. Посмотрим на кусочки кода, соответствующие всем ссылкам на главной странице сайта:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "#\n", "/\n", "#\n", "#\n", "/rubric/astronomy\n", "/rubric/physics\n", "/rubric/biology\n", "/rubric/robots-drones\n", "/theme/oops\n", "/theme/economy-literature\n", "/theme/hayabusa\n", "/theme/art-of-integration\n", "/\n", "#\n", "/rubric/astronomy\n", "/rubric/physics\n", "/rubric/biology\n", "/rubric/robots-drones\n", "#\n", "/theme/oops\n", "/theme/economy-literature\n", "/theme/hayabusa\n", "/theme/art-of-integration\n", "https://nplus1.ru/blog/2019/03/27/five-years-deadline\n", "https://nplus1.ru/blog/2019/03/27/five-years-deadline\n", "https://nplus1.ru/blog/2019/03/26/limb-183\n", "https://nplus1.ru/blog/2019/03/25/ben-franklin-effect\n", "https://nplus1.ru/blog/2019/03/22/a-history-of-private-life\n", "https://nplus1.ru/blog/2019/03/22/spanish-princess\n", "https://nplus1.ru/blog/2019/03/21/climate-change-of-the-dead\n", "https://nplus1.ru/blog/2019/03/21/half-fathers\n", "https://nplus1.ru/blog/2019/03/20/coins-manufactering\n", "https://nplus1.ru/blog/2019/03/18/map-of-the-universe\n", "https://nplus1.ru/blog/2019/03/28/what-about-labioldentals\n", "/news/2019/03/31/first\n", "/news/2019/03/30/very-big-boulder-on-Bennu\n", "/news/2019/03/30/PSP-two-flyby\n", "/news/2019/03/30/functional-brain-map\n", "/news/2019/03/30/seizure-odour\n", "/news/2019/03/30/id-capsule\n", "/news/2019/03/29/mars-helicopter-fly-now\n", "/material/2019/03/29/psycho-batman\n", "/rubric/mezzanine\n", "/news/2019/03/29/ABRACADABRA\n", "/news/2019/03/29/nz-apple-picker\n", "/news/2019/03/29/i-know-kung-fu\n", "/news/2019/03/29/warming-leads-to-cruelty\n", "/news/2019/03/29/ghost-faces\n", "/news/2019/03/29/language-storage\n", "/blog/2019/03/29/the-revolutionary-genius-of-plants\n", "/blog/2019/03/28/what-about-labioldentals\n", "/material/2019/03/28/head-transplantation\n", "/rubric/medicine\n", "/material/2019/03/27/tetanus\n", "/rubric/medicine\n", "/news/2019/03/29/island-building\n", "/news/2019/03/29/deep-water-mars\n", "/news/2019/03/29/curaxins\n", "/news/2019/03/29/inflation-falsification\n", "/news/2019/03/29/sooo-tiny\n", "/news/2019/03/29/small-moons-of-Saturn\n", "/blog/2019/03/27/five-years-deadline\n", "/blog/2019/03/26/limb-183\n", "/news/2019/03/26/learning-languages\n", "/rubric/partners\n", "/material/2019/03/25/120-element\n", "/rubric/physics\n", "/theme/phys\n", "/news/2019/03/26/dark-system\n", "/news/2019/03/29/language-storage\n", "/news/2019/03/27/schizo-genes\n", "/news/2019/03/29/curaxins\n", "/news/2019/03/29/inflation-falsification\n", "/news/2019/03/26/efremovka-ancient-solar-flare\n", "/news/2019/03/29/ABRACADABRA\n", "/news/2019/03/27/exoplanet-interferometry\n", "/news/2019/03/26/quantum-radiation-pressure-noise\n", "/news/2019/03/29/ghost-faces\n", "https://nplus1.ru/blog/2019/03/27/five-years-deadline\n", "https://nplus1.ru/blog/2019/03/27/five-years-deadline\n", "https://nplus1.ru/blog/2019/03/26/limb-183\n", "https://nplus1.ru/blog/2019/03/25/ben-franklin-effect\n", "https://nplus1.ru/blog/2019/03/22/a-history-of-private-life\n", "https://nplus1.ru/blog/2019/03/22/spanish-princess\n", "https://nplus1.ru/blog/2019/03/21/climate-change-of-the-dead\n", "https://nplus1.ru/blog/2019/03/21/half-fathers\n", "https://nplus1.ru/blog/2019/03/20/coins-manufactering\n", "https://nplus1.ru/blog/2019/03/18/map-of-the-universe\n", "https://nplus1.ru/blog/2019/03/28/what-about-labioldentals\n", "/\n", "/about\n", "/adv\n", "/rules\n", "/vacancy\n", "/difficult\n", "https://nplus1.ru/personal-data-policy\n", "#\n", "https://t.me/nplusone\n", "http://vk.com/nplusone\n", "https://www.facebook.com/nplusone\n", "https://twitter.com/nplusodin\n", "https://ok.ru/nplus1\n", "https://soundcloud.com/nplus_1\n", "/rss\n" ] } ], "source": [ "for link in soup.find_all('a'):\n", " print(link.get('href'))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ссылок много. Но нам нужны только новости – ссылки, которые начинаются со слова `/news`. Добавим условие: будем выбирать только те ссылки, в которых есть `/news`. Создадим пустой список `urls` и будем добавлять в него только ссылки, которые удовлетворяют этому условию." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['/news/2019/03/31/first',\n", " '/news/2019/03/30/very-big-boulder-on-Bennu',\n", " '/news/2019/03/30/PSP-two-flyby',\n", " '/news/2019/03/30/functional-brain-map',\n", " '/news/2019/03/30/seizure-odour',\n", " '/news/2019/03/30/id-capsule',\n", " '/news/2019/03/29/mars-helicopter-fly-now',\n", " '/news/2019/03/29/ABRACADABRA',\n", " '/news/2019/03/29/nz-apple-picker',\n", " '/news/2019/03/29/i-know-kung-fu',\n", " '/news/2019/03/29/warming-leads-to-cruelty',\n", " '/news/2019/03/29/ghost-faces',\n", " '/news/2019/03/29/language-storage',\n", " '/news/2019/03/29/island-building',\n", " '/news/2019/03/29/deep-water-mars',\n", " '/news/2019/03/29/curaxins',\n", " '/news/2019/03/29/inflation-falsification',\n", " '/news/2019/03/29/sooo-tiny',\n", " '/news/2019/03/29/small-moons-of-Saturn',\n", " '/news/2019/03/26/learning-languages',\n", " '/news/2019/03/26/dark-system',\n", " '/news/2019/03/29/language-storage',\n", " '/news/2019/03/27/schizo-genes',\n", " '/news/2019/03/29/curaxins',\n", " '/news/2019/03/29/inflation-falsification',\n", " '/news/2019/03/26/efremovka-ancient-solar-flare',\n", " '/news/2019/03/29/ABRACADABRA',\n", " '/news/2019/03/27/exoplanet-interferometry',\n", " '/news/2019/03/26/quantum-radiation-pressure-noise',\n", " '/news/2019/03/29/ghost-faces']" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "urls = []\n", "\n", "for link in soup.find_all('a'):\n", " if '/news' in link.get('href'):\n", " urls.append(link.get('href'))\n", "urls" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Ссылки, которые у нас есть в списке `urls`, относительные: они неполные, начало ссылки (название сайта) отсутствует. Давайте превратим их в абсолютные ‒ склеим с ссылкой https://nplus1.ru." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['https://nplus1.ru/news/2019/03/31/first',\n", " 'https://nplus1.ru/news/2019/03/30/very-big-boulder-on-Bennu',\n", " 'https://nplus1.ru/news/2019/03/30/PSP-two-flyby',\n", " 'https://nplus1.ru/news/2019/03/30/functional-brain-map',\n", " 'https://nplus1.ru/news/2019/03/30/seizure-odour',\n", " 'https://nplus1.ru/news/2019/03/30/id-capsule',\n", " 'https://nplus1.ru/news/2019/03/29/mars-helicopter-fly-now',\n", " 'https://nplus1.ru/news/2019/03/29/ABRACADABRA',\n", " 'https://nplus1.ru/news/2019/03/29/nz-apple-picker',\n", " 'https://nplus1.ru/news/2019/03/29/i-know-kung-fu',\n", " 'https://nplus1.ru/news/2019/03/29/warming-leads-to-cruelty',\n", " 'https://nplus1.ru/news/2019/03/29/ghost-faces',\n", " 'https://nplus1.ru/news/2019/03/29/language-storage',\n", " 'https://nplus1.ru/news/2019/03/29/island-building',\n", " 'https://nplus1.ru/news/2019/03/29/deep-water-mars',\n", " 'https://nplus1.ru/news/2019/03/29/curaxins',\n", " 'https://nplus1.ru/news/2019/03/29/inflation-falsification',\n", " 'https://nplus1.ru/news/2019/03/29/sooo-tiny',\n", " 'https://nplus1.ru/news/2019/03/29/small-moons-of-Saturn',\n", " 'https://nplus1.ru/news/2019/03/26/learning-languages',\n", " 'https://nplus1.ru/news/2019/03/26/dark-system',\n", " 'https://nplus1.ru/news/2019/03/29/language-storage',\n", " 'https://nplus1.ru/news/2019/03/27/schizo-genes',\n", " 'https://nplus1.ru/news/2019/03/29/curaxins',\n", " 'https://nplus1.ru/news/2019/03/29/inflation-falsification',\n", " 'https://nplus1.ru/news/2019/03/26/efremovka-ancient-solar-flare',\n", " 'https://nplus1.ru/news/2019/03/29/ABRACADABRA',\n", " 'https://nplus1.ru/news/2019/03/27/exoplanet-interferometry',\n", " 'https://nplus1.ru/news/2019/03/26/quantum-radiation-pressure-noise',\n", " 'https://nplus1.ru/news/2019/03/29/ghost-faces']" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "full_urls = []\n", "\n", "for u in urls:\n", " res = 'https://nplus1.ru' + u\n", " full_urls.append(res) \n", "\n", "full_urls" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь наша задача сводится к следующему: изучить одну страницу с новостью, научиться из нее вытаскивать текст и всю необходимую информацию, а потом применить весь набор действий к каждой ссылке из `full_urls` в цикле. Посмотрим на новость с индексом 1, у вас может быть другая, новости обновляются." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "url0 = full_urls[1]\n", "\n", "page0 = requests.get(url0)\n", "soup0 = BeautifulSoup(page0.text, 'lxml')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "В коде каждой страницы с новостью есть часть с мета-информацией: датой, именем автора и проч. Такая информация окружена тэгом ``. Посмотрим:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ,\n", " ]" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup0.find_all('meta')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Из этого списка нам нужны части с именем автора, датой, заголовком и кратким описанием. Воспользуемся поиском по атрибуту `name`. Передадим функции `find_all()` в качестве аргумента словарь с названием и значением атрибута: " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[]" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup0.find_all('meta', {'name' : 'author'}) # например, автор" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь выберем единственный элемент полученного списка (с индексом 0):" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup0.find_all('meta', {'name' : 'author'})[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Нам нужно вытащить из этого объекта `content` – имя автора. Посмотрим на атрибуты:" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "{'name': 'author', 'content': 'Александр Войтюк'}" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup0.find_all('meta', {'name' : 'author'})[0].attrs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Как получить отсюда `content`? Очень просто, ведь это словарь! А доставать из словаря значение по ключу мы умеем." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Александр Войтюк'" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "author = soup0.find_all('meta', {'name' : 'author'})[0].attrs['content']\n", "author" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Аналогичным образом извлечем дату, заголовок и описание." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "date = soup0.find_all('meta', {'itemprop' : 'datePublished'})[0].attrs['content']\n", "title = soup0.find_all('meta', {'property' : 'og:title'})[0].attrs['content']\n", "description = soup0.find_all('meta', {'name' : 'description'})[0].attrs['content']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Осталось вытащить рубрики и сложность текста. Если мы посмотрим на исходный код страницы, мы увидим, что нужная нам информация находится в тэгах `

`:" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[

\n", " Астрономия\n", "

,

\n", " \n", " \n", " \n", "

,

\n", " \n", " Сложность\n", " 2.3\n", " \n", "

,

,

NASA/Goddard/University of Arizona/Roman Tkachenko

,

Исследователи из команды миссии OSIRIS-REx создали стереопару, благодаря которой можно ощутить в объеме 52-метровую скалу на поверхности астероида Бенну. Подобные изображения помогут в выборе места для забора пробы грунта с астероида, сообщается на сайте миссии.

,

Старт автоматической межпланетной станции OSIRIS-REx состоялся в сентябре 2016 года. Главная цель миссии — изучение 500-метрового околоземного астероида (101955) Бенну, относящегося к спектральному классу В. Станция достигла своей цели 3 декабря 2018 года, после чего начала изучение состава грунта при помощи спектрометров. Ожидается, что в начале июля 2020 года станция сблизится с поверхностью астероида и при помощи специального манипулятора соберет с нее 60 граммов пыли и фрагментов породы. После этого в марте 2021 года космический аппарат отправится к Земле и сбросит капсулу с грунтом в сентябре 2023 года.

,

Фотографии валуна на астероиде, из которых было смонтирована стереопара, были получены 1 и 2 декабря 2018 года бортовой камерой PolyCam во время фазы финального сближения OSIRIS-Rex с астероидом. Размер скалы — 52 метра, а находится она в южном полушарии астероида. Скала уходит вглубь поверхности и может быть выступающей частью одного из фрагментов родительского тела астероида. 

,

Примечательно, что в работе принял участие Брайан Мэй — гитарист рок-группы Queen и астрофизик, который в январе официально вошел в состав команды миссии и будет в дальнейшем создавать подобные изображения для помощи в выборе места забора пробы грунта с Бенну. Вторым ученым, работающим над проектом, стала астрофизик Клаудия Манцони (Claudia Manzoni).

,

,

,

NASA/Goddard/University of Arizona

,

Исследование астероида Бенну может дать важную информацию о формировании и эволюции Солнечной системы, включая и ответ на вопрос о том, какие малые тела могут быть ответственны за поставку аминокислот и воды на молодую Землю. О том, что уже узнали ученые об астероиде, читайте в нашем специальном материале «Небесное тело алмазной формы».

,

Александр Войтюк

,


,

Нашли опечатку? Выделите фрагмент и нажмите Ctrl+Enter.

,

Загляни в душу самому известному персонажу DC Comics

,

Коэффициент сложности

,

Коэффициент сложности

,

Коэффициент сложности

,

Коэффициент сложности

,

Коэффициент сложности

,

Коэффициент сложности

,

Коэффициент сложности

,

Коэффициент сложности

,

Коэффициент сложности

,

Коэффициент сложности

,

© 2019 N+1 Интернет-издание   Свидетельство о регистрации СМИ Эл № ФС77-67614

,

Использование всех текстовых материалов без изменений в некоммерческих целях разрешается со ссылкой на N+1. \n", " Все аудиовизуальные произведения являются собственностью своих авторов и правообладателей и используются \n", " только в образовательных и информационных целях. Если вы являетесь собственником того или иного произведения \n", " и не согласны с его размещением на нашем сайте, пожалуйста, напишите на kirill@nplus1.ru

,

Материалы, опубликованные в разделе «Блоги», отражают позиции их авторов, которые могут не совпадать с мнением редакции.

,

Сайт может содержать контент, не предназначенный для лиц младше 18 лет.

,

\n", " Политика обработки персональных данных пользователей сайта\n", "

,

\n", " \n", " Change privacy settings\n", "

,

\n", "

]" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup0.find_all('p')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Выберем из полученного списка первый элемент и найдем в нем все тэги ``:" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[Астрономия]" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup0.find_all('p')[0].find_all('a')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Получился список из одного элемента. Применим списковые включения – вытащим из каждого элемента (их могло бы быть больше) текст и поместим его в новый список `rubrics`." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "['Астрономия']" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rubrics = [r.text for r in soup0.find_all('p')[0].find_all('a')]\n", "rubrics" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Осталась только сложность. Возьмем соответствующий кусок кода:" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[2.3]" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ "soup0.find_all('span', {'class' : 'difficult-value'})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "И выберем оттуда текст." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'2.3'" ] }, "execution_count": 20, "metadata": {}, "output_type": "execute_result" } ], "source": [ "diff = soup0.find_all('span', {'class' : 'difficult-value'})[0].text\n", "diff" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь перейдем к тексту самой новости. Как можно заметить, текст сохранен в абзацах `

`, причем безо всяких атрибутов. Сообщим Python, что нас интересуют куски с пустым атрибутом `class`:" ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "text_list = soup0.find_all('p', {'class' : None})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "«Выцепим» все тексты (без тэгов) из полученного списка:" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [], "source": [ "text = [t.text for t in text_list]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Склеим все элементы списка `text` через пробел:" ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Исследователи из команды миссии OSIRIS-REx создали стереопару, благодаря которой можно ощутить в объеме 52-метровую скалу на поверхности астероида Бенну. Подобные изображения помогут в выборе места для забора пробы грунта с астероида, сообщается\\xa0на сайте миссии. Старт автоматической межпланетной станции OSIRIS-REx состоялся в сентябре 2016 года. Главная цель миссии — изучение 500-метрового околоземного астероида\\xa0(101955) Бенну, относящегося к спектральному классу В. Станция достигла своей цели 3 декабря 2018 года, после чего начала изучение состава грунта при помощи спектрометров. Ожидается, что в начале июля 2020 года станция сблизится с поверхностью астероида и при помощи специального манипулятора соберет с нее 60 граммов пыли и фрагментов породы. После этого в марте 2021 года космический аппарат отправится к Земле и сбросит капсулу с грунтом в сентябре 2023 года. Фотографии валуна на астероиде, из которых было смонтирована стереопара, были получены 1 и 2 декабря 2018 года бортовой камерой PolyCam во время фазы финального сближения OSIRIS-Rex с астероидом. Размер скалы — 52 метра, а находится она в южном полушарии астероида. Скала уходит вглубь поверхности и может быть выступающей частью одного из фрагментов родительского тела астероида.\\xa0 Примечательно, что в работе принял участие Брайан Мэй — гитарист рок-группы Queen и астрофизик, который в январе официально вошел в состав команды миссии и будет в дальнейшем создавать подобные изображения для помощи в выборе места забора пробы грунта с Бенну. Вторым ученым, работающим над проектом, стала астрофизик Клаудия Манцони (Claudia Manzoni). Исследование астероида Бенну может дать важную информацию о формировании и эволюции Солнечной системы, включая и ответ на вопрос о том, какие малые тела могут быть ответственны за поставку аминокислот и воды на молодую Землю. О том, что уже узнали ученые об астероиде, читайте в нашем специальном материале «Небесное тело алмазной формы». Александр Войтюк Нашли опечатку? Выделите фрагмент и нажмите Ctrl+Enter. Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности © 2019 N+1 Интернет-издание \\xa0 Свидетельство о регистрации СМИ Эл № ФС77-67614 Использование всех текстовых материалов без изменений в некоммерческих целях разрешается со ссылкой на N+1. \\n Все аудиовизуальные произведения являются собственностью своих авторов и правообладателей и используются \\n только в образовательных и информационных целях. Если вы являетесь собственником того или иного произведения \\n и не согласны с его размещением на нашем сайте, пожалуйста, напишите на kirill@nplus1.ru Материалы, опубликованные в разделе «Блоги», отражают позиции их авторов, которые могут не совпадать с мнением редакции. Сайт может содержать контент, не предназначенный для лиц младше 18 лет. \\nПолитика обработки персональных данных пользователей сайта\\n \\n\\nChange privacy settings\\n \\n'" ] }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "final_text = ' '.join(text)\n", "final_text" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Все здорово, только мешают отступы-переходы на новую строку `\\n`. Заменим их на пробелы с помощью метода `.replace`:" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [], "source": [ "final_text = final_text.replace('\\n', ' ')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Не прошло и двух пар, как мы разобрались со всем :) Теперь осталось совсем чуть-чуть. Написать готовую функцию для всех проделанных нами действий и применить ее в цикле для всех ссылок в списке `full_urls`. Напишем! Аргументом функции будет ссылка на новость, а возвращать она будет текст новости и всю необходимую информацию (дата, автор, сложность и проч.). Скопируем все строки кода выше." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [], "source": [ "def GetNews(url0):\n", " \"\"\"\n", " Returns a tuple with url0, date, author, description, title, final_text, rubrics, diff.\n", " Parameters:\n", " \n", " url0 is a link to the news (string)\n", " \"\"\"\n", " page0 = requests.get(url0)\n", " soup0 = BeautifulSoup(page0.text, 'lxml')\n", " author = soup0.find_all('meta', {'name' : 'author'})[0].attrs['content']\n", " date = soup0.find_all('meta', {'itemprop' : 'datePublished'})[0].attrs['content']\n", " title = soup0.find_all('meta', {'property' : 'og:title'})[0].attrs['content']\n", " description = soup0.find_all('meta', {'name' : 'description'})[0].attrs['content']\n", " rubrics = [r.text for r in soup0.find_all('p')[0].find_all('a')]\n", " diff = soup0.find_all('span', {'class' : 'difficult-value'})[0].text\n", " text_list = soup0.find_all('p', {'class' : None})\n", " text = [t.text for t in text_list]\n", " final_text = ' '.join(text)\n", " final_text = final_text.replace('\\n', ' ')\n", " \n", " return url0, date, author, description, title, final_text, rubrics, diff" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Уфф. Осталось применить ее в цикле. Но давайте не будем спешить: импортируем функцию `sleep` для задержки, чтобы на каждой итерации цикла, прежде чем перейти к следующей новости, Python ждал несколько секунд. Во-первых, это нужно, чтобы сайт «не понял», чтобы мы его грабим, да еще автоматически. Во-вторых, с небольшой задержкой всегда есть гарантия, что страница прогрузится (сейчас это пока не очень важно, но особенно актуально будет, когда будем обсуждать встраивание в браузер с Selenium). Приступим." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "from time import sleep" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "news = [] # это будет список из кортежей, в которых будут храниться данные по каждой новости\n", "\n", "for link in full_urls:\n", " res = GetNews(link)\n", " news.append(res)\n", " sleep(3) # задержка в 3 секунды" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Так теперь выглядит первый элемент списка:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "('https://nplus1.ru/news/2019/03/31/first',\n", " '2019-03-31',\n", " 'Илья Ферапонтов',\n", " 'Он должен заменить легкий транспортник Ан-26',\n", " 'Транспортный самолет Ил-112В совершил первый полет',\n", " 'Российский военно-транспортный самолет Ил-112В совершил первый полет, сообщает Авиационный комплекс имени Ильюшина. Испытания прошли на\\xa0аэродроме воронежского авиазавода ПАО «ВАСО», командиром экипажа был шеф-пилот ПАО «Ил» Герой России Николай Куимов. Первый полет стал завершением цикла аэродромной отработки, в\\xa0ходе которой были проведены испытания всех систем, в\\xa0том числе пробежки по\\xa0аэродрому. Полет прошел в\\xa0штатном режиме. В\\xa0воздухе Ил-112В сопровождал самолет-лаборатория Ил-114ЛЛ. Ил-112В в\\xa0перспективе должен заменить легкий транспортник Ан-26. Он\\xa0будет использоваться для перевозки и\\xa0десантирования легкой техники, грузов и\\xa0бойцов, его грузоподъемность\\xa0— около пяти тонн. Самолет сможет развивать скорость до\\xa0550 километров в\\xa0час и\\xa0совершать полеты на\\xa0расстояние до\\xa03,4 тысячи километров, а\\xa0при взлете с\\xa0грунтовых аэродромов и\\xa0максимальной загрузкой\\xa0— до 1,2 тысячи километров. Транспортник сможет взлетать с\\xa0взлетно-посадочных полос длиной не\\xa0более 900\\xa0метров. Пробег Ил-112В при посадке составит около 600\\xa0метров. Ранее Министерство обороны России объявляло о\\xa0намерении приобрести 62\\xa0самолета этого типа до\\xa02020\\xa0года, однако в августе 2018 года появилась информация о планах заказать не менее 100 машин. Сергей Кузнецов Нашли опечатку? Выделите фрагмент и нажмите Ctrl+Enter. Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности Коэффициент сложности © 2019 N+1 Интернет-издание \\xa0 Свидетельство о регистрации СМИ Эл № ФС77-67614 Использование всех текстовых материалов без изменений в некоммерческих целях разрешается со ссылкой на N+1. Все аудиовизуальные произведения являются собственностью своих авторов и правообладателей и используются только в образовательных и информационных целях. Если вы являетесь собственником того или иного произведения и не согласны с его размещением на нашем сайте, пожалуйста, напишите на kirill@nplus1.ru Материалы, опубликованные в разделе «Блоги», отражают позиции их авторов, которые могут не совпадать с мнением редакции. Сайт может содержать контент, не предназначенный для лиц младше 18 лет. Политика обработки персональных данных пользователей сайта Change privacy settings ',\n", " ['Авиация'],\n", " '1.2')" ] }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "news[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Импортируем `pandas` и создадим датафрейм из списка кортежей: " ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "import pandas as pd" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [], "source": [ "df = pd.DataFrame(news)" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
01234567
0https://nplus1.ru/news/2019/03/31/first2019-03-31Илья ФерапонтовОн должен заменить легкий транспортник Ан-26Транспортный самолет Ил-112В совершил первый п...Российский военно-транспортный самолет Ил-112В...[Авиация]1.2
1https://nplus1.ru/news/2019/03/30/very-big-bou...2019-03-30Александр ВойтюкВ создании стереопары принял участие астрофизи...OSIRIS-REx показал 52-метровую скалу на поверх...Исследователи из команды миссии OSIRIS-REx соз...[Астрономия]2.3
\n", "
" ], "text/plain": [ " 0 1 \\\n", "0 https://nplus1.ru/news/2019/03/31/first 2019-03-31 \n", "1 https://nplus1.ru/news/2019/03/30/very-big-bou... 2019-03-30 \n", "\n", " 2 3 \\\n", "0 Илья Ферапонтов Он должен заменить легкий транспортник Ан-26 \n", "1 Александр Войтюк В создании стереопары принял участие астрофизи... \n", "\n", " 4 \\\n", "0 Транспортный самолет Ил-112В совершил первый п... \n", "1 OSIRIS-REx показал 52-метровую скалу на поверх... \n", "\n", " 5 6 7 \n", "0 Российский военно-транспортный самолет Ил-112В... [Авиация] 1.2 \n", "1 Исследователи из команды миссии OSIRIS-REx соз... [Астрономия] 2.3 " ] }, "execution_count": 31, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.head(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Переименуем столбцы в базе." ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "df.columns = ['link', 'date', 'author', 'desc', 'title', 'text', 'rubric', 'diffc']" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
linkdateauthordesctitletextrubricdiffc
0https://nplus1.ru/news/2019/03/31/first2019-03-31Илья ФерапонтовОн должен заменить легкий транспортник Ан-26Транспортный самолет Ил-112В совершил первый п...Российский военно-транспортный самолет Ил-112В...[Авиация]1.2
1https://nplus1.ru/news/2019/03/30/very-big-bou...2019-03-30Александр ВойтюкВ создании стереопары принял участие астрофизи...OSIRIS-REx показал 52-метровую скалу на поверх...Исследователи из команды миссии OSIRIS-REx соз...[Астрономия]2.3
\n", "
" ], "text/plain": [ " link date \\\n", "0 https://nplus1.ru/news/2019/03/31/first 2019-03-31 \n", "1 https://nplus1.ru/news/2019/03/30/very-big-bou... 2019-03-30 \n", "\n", " author desc \\\n", "0 Илья Ферапонтов Он должен заменить легкий транспортник Ан-26 \n", "1 Александр Войтюк В создании стереопары принял участие астрофизи... \n", "\n", " title \\\n", "0 Транспортный самолет Ил-112В совершил первый п... \n", "1 OSIRIS-REx показал 52-метровую скалу на поверх... \n", "\n", " text rubric diffc \n", "0 Российский военно-транспортный самолет Ил-112В... [Авиация] 1.2 \n", "1 Исследователи из команды миссии OSIRIS-REx соз... [Астрономия] 2.3 " ] }, "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.head(2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь внесем изменения: сделаем столбец `diffc` числовым – типа *float*." ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [], "source": [ "df['diffc'] = [float(i) for i in df.diffc]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь сложность представлена в базе как количественный показатель, и описывать ее можно соответствующим образом:" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "count 30.000000\n", "mean 4.416667\n", "std 2.370521\n", "min 1.100000\n", "25% 2.125000\n", "50% 4.900000\n", "75% 6.750000\n", "max 8.200000\n", "Name: diffc, dtype: float64" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.diffc.describe()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Теперь столбец со сложностью точно числовой. Можем даже построить для него гистограмму." ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 36, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "%matplotlib inline\n", "df.diffc.plot.hist()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Объединим рубрики в *text* в одну строку через запятую:" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "df['rubric'] = [','.join(r) for r in df.rubric]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Давайте почистим текст новостей – уберем оттуда текст, не относящийся к новостям. Найдем лишнее:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "df.text[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Лишний текст находится после фразы 'Нашли опечатку?'. Так давайте будем разбивать строки по этой фразе с помощью метода `.split()` и брать все, что до нее (элемент с индексом 0)." ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [], "source": [ "df['clean_text'] = [t.split('Нашли опечатку?')[0] for t in df.text]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Осталось только заменить непонятные символы `\\xa0` на пробелы:" ] }, { "cell_type": "code", "execution_count": 39, "metadata": {}, "outputs": [], "source": [ "df['clean_text'] = [t.replace(\"\\xa0\", \" \") for t in df.clean_text]" ] }, { "cell_type": "code", "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "'Российский военно-транспортный самолет Ил-112В совершил первый полет, сообщает Авиационный комплекс имени Ильюшина. Испытания прошли на аэродроме воронежского авиазавода ПАО «ВАСО», командиром экипажа был шеф-пилот ПАО «Ил» Герой России Николай Куимов. Первый полет стал завершением цикла аэродромной отработки, в ходе которой были проведены испытания всех систем, в том числе пробежки по аэродрому. Полет прошел в штатном режиме. В воздухе Ил-112В сопровождал самолет-лаборатория Ил-114ЛЛ. Ил-112В в перспективе должен заменить легкий транспортник Ан-26. Он будет использоваться для перевозки и десантирования легкой техники, грузов и бойцов, его грузоподъемность — около пяти тонн. Самолет сможет развивать скорость до 550 километров в час и совершать полеты на расстояние до 3,4 тысячи километров, а при взлете с грунтовых аэродромов и максимальной загрузкой — до 1,2 тысячи километров. Транспортник сможет взлетать с взлетно-посадочных полос длиной не более 900 метров. Пробег Ил-112В при посадке составит около 600 метров. Ранее Министерство обороны России объявляло о намерении приобрести 62 самолета этого типа до 2020 года, однако в августе 2018 года появилась информация о планах заказать не менее 100 машин. Сергей Кузнецов '" ] }, "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df.clean_text[0]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Всё! Сохраняем датафрейм в файл. Для разнообразия сохраним в Excel:" ] }, { "cell_type": "code", "execution_count": 41, "metadata": {}, "outputs": [], "source": [ "df.to_excel('nplus-news.xlsx')" ] } ], "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.6.5" } }, "nbformat": 4, "nbformat_minor": 2 }