Яндекс.Метрика

    Песочница

    Мини web-краулер. Качаем книгу из интернета

    Преамбула

    Поскольку большинство книг я читаю с наладонника, книги нужно где-то качать. Большинство интересующих меня книг можно найти в сетевых библиотеках в формате txt, html, fb2 или другом текстовом. Бывают случаи, когда книга разделена на несколько страниц и лежит на каком-нибудь сайте, где возможность скачать целиком не предусмотрена. В этом случае можно сохранить каждую html страницу вручную, но такой способ имеет два важных недостатка. Во-первых, если книга разделена на 15-20 страниц, сохранение вручную займёт много времени и будет раздражать. Во-вторых, вместе с текстом книги мы получим кучу всякого мусора — текст, не относящийся к книге, таблицы, скрипты, ссылки на другие сайты и прочую муть.

    Чтобы упростить себе жизнь, мы напишем программу, которая скачает книгу для нас. Из сказанного выше следует, что нам нужна программа, которая: а) скачает в нужном порядке все страницы, по которым разбросана книга; б) с каждой страницы возьмёт только тест и ничего лишнего и в) сохранит весь текст книги в одном html-файле.

    В качестве примера мы возьмём книгу Владимира Плунгяна «Почему языки такие разные. Популярная лингвистика». Я нашёл только одну сетевую бибилотеку, где её можно скачать в текстовом формате, да и там требуется регистрация, поэтому мы скачаем её по указанной ссылке с помощью программы, о которой речь пойдёт дальше. Для написания программы мы используем язык программирования Python. Я использовал Python версии 2.6. Эту или более новую версию можно скачать бесплатно на официальном сайте.
    Скачиваем страницы

    Чтобы скачать страницы, нам понадобятся их адреса. Как мы видим, адреса всех страниц кроме первой одинаковы и различаются только номером:

    http://profismart.ru/web/bookreader-115980-номер_страницы.php

    Благодаря этому, мы сможем не писать их вручную, а сгенерировать с помощью следующего кода:

    for i in range(2,29):
            url = "http://profismart.ru/web/bookreader-115980-%i.php" % i


    Это даст нам адреса всех страниц со второй по 28-ю. Адрес первой страницы отличается от остальных (http://profismart.ru/web/bookreader-115980.php), поэтому его мы вставим в программу вручную.

    Далее каждую из этих страниц мы скачаем, а сам html-код запишем в переменную html:

    for i in range(2,29):
            # Генерируем гиперссылку
            url = "http://profismart.ru/web/bookreader-115980-%i.php" % i

            # Качаем страницу
            html = ""
            sock = urllib.urlopen(url)
            html = sock.read()
            sock.close()



    Извлекаем из страницы нужный текст.

    Html-код, содержащий текст книги, мы извлечём из кода страницы с помощью регулярных выражений (regular expressions). В нашем случае текст находится внутри страницы между тегами <td style="padding:7px" class="ps24 ps37"> и </td>. Каждый параграф текста находится между тегами <div> и </div>, а сами теги <div> без дополнительных параметров используются только в тексте книги. Таким образом, чтобы извлечь текст, нам достаточно найти первый тег <div> и взять весь текст до первого тега </td>. Регулярное выражение будет выглядеть так:

    (<div>.+?</div>)</td>

    Точка обозначает любой символ, а знак «плюс» говорит, что символ должен быть как минимум один. Знак плюс после комбинации .+ говорит, что мы хотим взять минимальное количество символов, которые удовлетворяют нашему запросу. Таким образом, мы ищем весь текст от первого найденного в html-коде тега <div> до первой (благодаря знаку вопроса) комбинации </div></td>. Скобки указывают на ту часть найденного текста, которую мы возьмём. В данном случае мы берём весь найденный текст без тега </td>, который нам не нужен. Чтобы использовать регулярное выражение в нашей программе, добавим в её начало следующую строку:

    text_regex = re.compile(u"(<div>.+?</div>)</td>", re.IGNORECASE | re.DOTALL | re.UNICODE)

    Обращаться к выражению мы будем по имени text_regex. Текст книги мы сохраним в переменной book. С добавкой регулярного выражения код, который обрабатывает страницы 2-28, будет выглядеть так:

    for i in range(2,29):
            # Генерируем гиперссылку
            url = "http://profismart.ru/web/bookreader-115980-%i.php" % i

            # Качаем страницу
            html = ""
            sock = urllib.urlopen(url)
            html = sock.read()
            sock.close()

            # Извлекаем из страницы нужный текст и добавляем его к уже имеющемуся
            book = book + text_regex.search(html).group(1)


    Первую страницу мы обработаем отдельно. Из неё кроме текста книги мы возьмём ещё и заголовок для нового html-файла. Его мы извлечём также с помощью регулярного выражения. Само выражение будет выглядеть так:

    (<html.+?<body>)

    То есть мы берём весь код от <html> и до <body>. В нём тоже есть немного мусора, но удалять его нашей программой мы не будем. Если захотим, позже удалим вручную в каком-нибудь редакторе. Как и для предыдущего выражения, добавим в начало программы строку:

    head_regex = re.compile(u"(<html.+?<body>)", re.IGNORECASE | re.DOTALL | re.UNICODE)

    Код, обрабатывающий первую страницу будет выглядеть так:

    # Качаем первую страницу
    url = "http://profismart.ru/web/bookreader-115980.php"
    html = ""
    sock = urllib.urlopen(url)
    html = sock.read()
    sock.close()

    # Берём заголовок
    head = head_regex.search(html).group(1)
    book = book + head
    # Извлекаем и сохраняем текст книги
    book = book + text_regex.search(html).group(1)



    Сохраняем текст книги в файл

    Здесь всё просто. К содержимому переменной book нужно добавить закрывающие теги для <html> и <body> и записать его в файл. Это делает следующий код:

    # Открываем файл для записи
    file_out = open('book.html', 'w')

    # Записываем содержимое переменной
    file_out.write(book)
    # Записываем закрывающие теги
    file_out.write("\n</body></html>")
    # Закрываем файл
    file_out.close()


    Собираем всю программу вместе

    В конечную версию программы я добавил замену тегов <div>...</div> на <p align=justify>...</p> и вывод информации о странице, которая обрабатывается в данный момент. Первое — потому что мне нравится, когда текст выровнен по ширине страницы. Второе — чтобы программа во время работы подавала хоть какие-нибудь признаки жизни.

    Полный текст программы можно сохранить в файле с расширением .py и запустить. Вот он:

    import urllib
    import re

    text_regex = re.compile(u"(<div>.+?</div>)</td>", re.IGNORECASE | re.DOTALL | re.UNICODE)
    head_regex = re.compile(u"(<html.+?<body>)", re.IGNORECASE | re.DOTALL | re.UNICODE)

    book = ""

    # Download first page
    url = "http://profismart.ru/web/bookreader-115980.php"
    print "Page 1"
    print url
    html = ""
    sock = urllib.urlopen(url)
    html = sock.read()
    sock.close()
    print "Page downloaded."

    # Extract page head and book text
    book = book +
    head_regex.search(html).group(1)
    book = book + text_regex.search(html).group(1)

    for i in range(2,29):
            # Generate page url
            url = "http://profismart.ru/web/bookreader-115980-%i.php" % i
            print "Page %i" % i
            print url

            # Download the page
            html = ""
            sock = urllib.urlopen(url)
            html = sock.read()
            sock.close()
            print "Page downloaded."

            # Extract required text
            book = book + text_regex.search(html).group(1)

    # Replace <div> with <p align=justify>
    book = book.replace("<div>", "\n<p align=justify>")
    book = book.replace("</div>", "</p>")

    # Write to file
    file_out = open('book.html', 'w')
    file_out.write(book)
    file_out.write("\n</body></html>")
    file_out.close()