Наука о данных

И. В. Щуров, НИУ ВШЭ

На странице курса находятся дополнительные материалы.

Домашнее задание №10

За разные задачи можно получить разное число баллов. Если не указано обратное, задача весит 1 балл. Максимум за ДЗ можно набрать 14 баллов.

Чтобы сдать ДЗ, его надо загрузить в nbgr-x в виде ipynb-файла. Получить ipynb-файл можно, выбрав в Jupyter пункт меню File → Download as... → IPython Notebook (.ipynb).

In [ ]:
 

Задача 1 (2 балла)

Написать функцию any_news_about_harry(url), принимающую на вход адрес веб-страницы url, загружающую эту веб-страницу и проверяющую, встречается ли в ней слово Harry (с большой буквы). Функция должна возвращать True, если встречается, и False в противном случае. Также функция должна возвращать False, если страницу не удалось открыть (например, была получена ошибка 404 Not Found.)

Подсказка. Чтобы загрузить страницу, нужно использовать библиотеку requests:

import requests
r = requests.get(url)

Содержимое страницы затем окажется в r.text. Проверить, что запрос увенчался успехом, можно так:

if r:
    # увенчался успехом

Если вы пытаетесь обратиться к несуществующему сайту, произойдёт исключение requests.RequestException. Его нужно обработать с помощью try/except. Подробнее про исключения мы поговорим во вторник, или вы можете прочитать про них здесь. Если решить задачу без обработки исключения, можно получить один балл из двух.

В этой задаче не нужно использовать BeautifulSoup.

In [ ]:
"# YOUR CODE HERE"
In [ ]:
assert any_news_about_harry(
    "https://en.wikipedia.org/w/index.php?title=J._K._Rowling&oldid=694008857"
)
assert any_news_about_harry(
    "https://en.wikipedia.org/w/index.php?title=Star_Wars&oldid=694701430"
)
assert not any_news_about_harry(
    "https://en.wikipedia.org/w/index.php?title=Darth_Vader&oldid=694617684"
)
assert not any_news_about_harry("http://math-info.hse.ru/there_is_no_Harry_here")
In [ ]:
assert not any_news_about_harry("http://nonexistent.domain.ji8bohVe/")

Задача 1 (1 балл)

Написать функцию get_strong(html), принимающую на вход html-страницу в виде длинной строки, записанной в переменную html, и возвращающую строчку, содержащуюся в первом теге strong.

Примеры см. в тестах.

In [ ]:
"# YOUR CODE HERE"
In [ ]:
assert get_strong("<html><body><p>Hello, <strong>World</strong>!") == "World"
html = """<html>
    <body>
        <p>
            Hello,
            <strong>
                World
            </strong>
        </p>
    </body>
</html>"""
assert get_strong(html).strip() == "World"
assert (
    get_strong(
        "<html><body><p>tag &lt;strong&gt; is used in HTML\n to make letters <strong>stronger</strong>"
    )
    == "stronger"
)
assert get_strong("<html><body><strong>One\nTwo</strong></body></html>") == "One\nTwo"

Задача 2 (1 балл)

Для вставки картинок в HTML используется тег <img>, содержащий атрибут src — адрес файла с картинкой. Например, <img src="https://example.com/figure.jpg"/>. Здесь значением атрибута src является строка "https://example.com/figure.jpg". Написать функцию all_images_src(html), принимающую на вход длинную строчку с HTML-документом, а возвращающую список адресов всех картинок, встречающихся в этом документе (в том порядке, в котором они встречаются в документе). Некоторые теги <img> могут не содержать атрибута src — их надо пропустить.

Подсказка. Для обращения к атрибутам тега нужно использовать квадратные скобки, как если бы тег был словарём. Например, для тега <a href="http://example.com">example site</a>

a['href']

вернёт

'http://example.com'.

Однако, у тега может не оказаться запрашиваемого атрибута, и тогда код вернет ошибку KeyError. У тега, так же, как у словаря, есть метод get(), который возвращает None, если запрашиваемого атриюута нет (или возвращает значение по умолчанию, определенное пользователем и переданное как второй аргумент):

a.get('QQQ')

вернет None.

In [ ]:
"# YOUR CODE HERE"
In [ ]:
assert all_images_src(
    '<html><body><img src="https://upload.wikimedia.org/wikipedia/commons/b/bd/Struthio_camelus_portrait_Whipsnade_Zoo.jpg"/>'
) == [
    "https://upload.wikimedia.org/wikipedia/commons/b/bd/Struthio_camelus_portrait_Whipsnade_Zoo.jpg"
]
assert all_images_src(
    (
        '<html><body><IMG src="test.jpg">\n'
        "<p>Some text\n"
        "<img SRC='well.png'><img>\n"
        "</p></body></html>"
    )
) == ["test.jpg", "well.png"]
assert all_images_src(
    '<html><body><p><a href="link.html">'
    '<img alt="Just a test image" src="this is a test.jpg"><ul>'
    + "\n".join("<li><img src='img%04i.png'></li>" % i for i in range(1000))
    + "</ul></p></body></html>"
) == ["this is a test.jpg"] + ["img%04i.png" % i for i in range(1000)]

Задача 3 (2 балла)

Написать функцию get_all_headings(url), принимающую на вход адрес страницы в Википедии и возвращающую список, состоящий из названий разделов статьи (в порядке появления в статье). Если такой страницы не существует, функция должна вернуть список, состоящей из строки "Not found".

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

Примечание. Не на всех страницах Википедии есть оглавление. В частности, ваш код должен коорректно обрабатывать эту страницу.

Предполагается, что вы будете решать эту задачу, обрабатывая HTML-код веб-страницы, а не вики-код статей, и не будете пользоваться сторонними библиотеками (кроме urllib, requests, BeautifulSoup).

In [ ]:
"# YOUR CODE HERE"
In [ ]:
from urllib.parse import urlencode

entrypoint = "https://ru.wikipedia.org/w/index.php?"


def mkurl(title, oldid):
    return entrypoint + urlencode(dict(title=title, oldid=oldid))


assert get_all_headings(mkurl("Северовирджинская кампания", 75043192)) == [
    "Предыстория",
    "Силы сторон",
    "Сражения",
    "Последствия",
    "Примечания",
    "Литература",
    "Ссылки",
]

assert get_all_headings(mkurl("User:Ilya_Voyager/sandbox/h2test", "75055744")) == [
    "Заголовок",
    "Ещё один заголовок",
    "Третий заголовок",
]
assert get_all_headings(mkurl("User:Ilya_Voyager/This Page Will Never Exist", "")) == [
    "Not found"
]
del urlencode, mkurl

Задача 4 (4 балла)

Написать функцию city_tz(name), принимающую на вход название города и возвращающую строку, содержащую часовой пояс, действующий в этом городе (например, 'UTC+3:00'), согласно данным русской Википедии. Если такого города Википедия не знает, или если у города не указан часовой пояс None.

Предполагается, что вы будете решать эту задачу, обрабатывая HTML-код веб-страницы, а не вики-код статей, и не будете пользоваться сторонними библиотеками (кроме urllib, requests, BeautifulSoup, при необходимости, robobrowser).

Подсказка. Как сформировать адрес страницы, зная название статьи, можно подсмотреть в тесте к задаче 3. Впрочем, можно передать адрес страницы напрямую в requests.get, см. официальную документацию.

In [ ]:
"# YOUR CODE HERE"
In [ ]:
res = [
    ("Абакан", "UTC+7:00"),
    ("Анадырь", "UTC+12:00"),
    ("Киров (Кировская область)", "UTC+3:00"),
    ("Южно-Сахалинск", "UTC+11:00"),
    ("Усть-Каменоустюгск", None),
]
for city, site in res:
    assert city_tz(city) == site, (site, city_tz(city))

Задача 5 (4 балла)

Написать функцию get_languages_hse(last_name), принимающую на вход фамилию преподавателя кафедры высшей математики и возвращающую список языков, которым он или она владеет (в том же порядке, как перечислено на странице). Программа должна зайти на указанную страницу со списком сотрудников кафедры, найти там нужного преподавателя, перейти по ссылке на его личную страницу и найти на ней список языков.

In [ ]:
import werkzeug

werkzeug.cached_property = werkzeug.utils.cached_property
from robobrowser import RoboBrowser

"# YOUR CODE HERE"
In [ ]:
assert get_languages_hse("Щуров") == ["английский"]
assert get_languages_hse("Дагаев") == ["английский", "французский"]
assert get_languages_hse("Тамбовцева") == [
    "английский",
    "испанский",
    "французский",
    "словенский",
]
assert get_languages_hse("Буров") == ["английский", "французский"]