четверг, 13 сентября 2012 г.

Python Парсинг сайта

В данной теме парсинга сайтов на Python будут освещены следующие возможности языка Python:
- парсинг страницы сайта с помощью простого регулярного выражения;
- скачивание файла с web-страницы;
- отправка скаченного файла через smtp-сервер;
- написание небольшого обобщающего скрипта.

Python Парсинг сайта.

Для работы с вебом используется модуль urllib2. Данный модуль позволяет получать содержимое страниц.

Воспользуемся следующим кодом для получения содержимого страницы:

def download_by_link(self, link):
        content = urllib2.urlopen(link).read() # считываем HTML-код страницы, расположенный по адресу link
        link_on_file, filename = self.get_filename(content) # получаем из HTML-кода ссылку для скачивания
        fullname = self.get_dir_name() + filename
        if self.is_new_version(fullname):
            with open(fullname, 'w') as fd:                               
                content = urllib2.urlopen(link_on_file).read() # скачиваем файл
                fd.write(content)                                                   
        return self.get_prepared_files(fullname)

with - гарантирует, что ресурсы, захваченные в данном блоке будут освобождены, как только мы выйдем за его пределы (в случае, обычного выхода или, например, исключения).

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

   def get_filename(self, text):
         pattern = r'(?P<link>http:\/\/archive\.sport\-express\.ru\/pdf\/(?P<filename>[0-9]+\.zip))'
         match = re.search(pattern, text)
         return match.group('link'), match.group('filename')

(?P ) - так задается именованная группа в регулярном выражении. Таким образом мы получает линк и имя файла.

class SportExpress():
    def get_from(self):
        return 'username@mail.ru'

    def get_title(self):
        return 'Sport Express subscribe'

    def get_text(self):
        return "Good morning! Don't be lazy - read newspaper!"

    def get_prepared_files(self, archive):                                     
        directory_to_extract = archive[:archive.rfind('.')]              
        zipfile.ZipFile(archive).extractall(directory_to_extract) # опасно использовать на произвольном содержимом
        res = glob.glob(directory_to_extract+'/*.*')
        return res

    def get_dir_name(self):
        dir_name = 'archive/sport-express/'
        if not os.path.exists(dir_name):
            os.mkdir(dir_name)
        return dir_name

    def is_new_version(self, filename):
        return os.path.exists(self.get_dir_name() + filename)

Создаем письмо.

Теперь, когда у нас есть содержимое, которое мы планируем высылать, неплохо было бы создать для него конверт.
Для работы с письмами в Python используется модуль email:

import email, email. encoders, email.mime.text, email.mime.base

class MessageBase:
    def __init__(self, subscriber):
        self.__to_addresses = subscriber.get_subscribers()
        self.__from_address = subscriber.get_from()
        self.__message = self.__create_message(subscriber.get_title(), subscriber.get_text())

Для начала создадим простое текстовое письмо.

 def __create_message(self, subject, text):
        email_msg = email.MIMEMultipart.MIMEMultipart('mixed')
        email_msg['Subject'] = subject
        email_msg['From'] = self.get_from()
        email_msg['To'] = ', '.join(self.get_to())
        email_msg.attach(email.mime.text.MIMEText(text,'text'))
        return email_msg

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

    def attach_file(self, filename, file_link, file_type = 'unknown'):
        file_message = email.mime.base.MIMEBase('application', file_type)
        file_message.set_payload(file(file_link).read())
        email.encoders.encode_base64(file_message)
        file_message.add_header('Content-Disposition','attachment;filename='+filename)
        self.__message.attach(file_message)
        return True

Для того, чтобы получить из сообщения сообщение для отправки нужно вызвать метод as_string().

    def get_text(self):
        return self.__message.as_string()

Отправляем письмо.

Теперь когда есть письмо, нужно уметь его отправлять. За отправку писем в Python отвечает smtplib.

import smtplib

from message import MessageBase

class SmtpBase:
    def __init__(self, serverhost):
        self.__smtp_server = serverhost

    def open(self):
        self.__server = smtplib.SMTP(self.__smtp_server)
        self.__server.login('<enter your smtp login>', '<enter your smtp password>')

    def close(self):
        self.__server.quit()

Представим что у нас есть класс Message с методами get_from(), get_to(), get_text().

    def send_mail(self, message):
        for message_to in message.get_to():
            self.__server.sendmail(message.get_from(), message_to, message.get_text())

Чтобы поддерживать with необходимо добавить пару методов:

    def __enter__(self):
        self.open()
        return self

    def __exit__(self, type, value, traceback):
        if type:
            print '%s: %s %s' % (type, value, traceback)
        self.close()

Обобщаем сценарий.

Итоговый код выглядит так:

from subscriber_sex import SportExpress
from smtp import SmtpBase
from message import MessageBase

b = SportExpress()
filenames = b.download_by_link('http://www.sport-express.ru')

msg = MessageBase(b)
for file in filenames:
    msg.attach_file(file[file.rfind('/')+1:], file, 'pdf')

with SmtpBase('smtp.yandex.ru') as s:
    s.send_mail(msg)

После каждого запуска данного кода Python и парсинга сайта свежий выпуск "Спорт-Экспресс" посылается прямо в ваш почтовый ящик!

Комментариев нет:

Отправить комментарий