Уведомления

Группа в Telegram: @pythonsu

#1 Апрель 10, 2013 20:02:25

Viktor1703
Зарегистрирован: 2013-04-10
Сообщения: 48
Репутация: +  0  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

Имеется такой текст

<div class="b-content-item__title">
    <a href="http://pogoda.yandex.ru/nalchik/"class="b-link">Погода</a>
    <a href="http://pogoda.yandex.ru/nalchik/"title="облачно"class="b-weather__icon_link">
        <i class="b-inline b-weather__icon b-weather__icon_ovc"></i>
    </a>
    <a href="http://pogoda.yandex.ru/nalchik/"class="b-link_black_novisit">+11 °С</a>
</div>
<div class="b-weather__info">
    <a href="http://pogoda.yandex.ru/nalchik/"class="b-link_black_novisit">ночью&nbsp;+9</a>
    <a href="http://pogoda.yandex.ru/nalchik/"class="b-link_black_novisit">утром&nbsp;+7</a>
</div>

получаю я его с Yandex.ru, как проще всего вытащить значения:

облачно
+11 °С
ночью&nbsp;+9
утром&nbsp;+7

нужен только шаблон, я в Python новичёк и много не знаю, по этому решил и код показать, может поправите меня или посоветуете как лучше сделать то или иное (Python v2.7.3).

import urllib2, re, time
class Weather(object):
    def __init__(self):
        self.__weather__ = []
        self.__rgst__ = urllib2.Request('http://www.yandex.ru/')
        self.__page__ = urllib2.urlopen(self.__rgst__)
        self.__read__ = self.__page__.read()
        self.__page__.close()
        self.__main__ = re.findall(r'<div class=["|\']b-content-item__title["|\']>(.*?)</div>', self.__read__)
        self.__sts__  = re.findall(r'title=["|\'](.*?)["|\']', self.__main__[0])
        self.__now__  = re.findall(r'<a href=["|\'].*?["|\'].*class=["|\']b-link_black_novisit["|\']>(.*?)</a>', self.__main__[0])
        self.__weather__.append(self.__sts__[0].decode('utf-8'))
        self.__weather__.append(self.__now__[0].decode('utf-8'))
        self.__main__ = re.findall(r'<div class=["|\']b-weather__info["|\']>(.*?)</div>', self.__read__)
        for data in re.findall(r'<a href=["|\'].*?["|\'].*?class=["|\']b-link_black_novisit["|\']>(.*?)</a>', self.__main__[0]):
            self.__weather__.append(data.replace('&nbsp;', ' ').decode('utf-8'))
    def get(self):
        return self.__weather__
weather = Weather()
for i in weather.get():
    print(i)
time.sleep(5)

Офлайн

#2 Апрель 10, 2013 21:18:47

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  253  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

Viktor1703
как проще
Проще как у страуса, потратить день на то чтобы изучить lxml а потом за 2 минуты долететь. Регулярки дают более быстродействующий код (обычно) и проще изучаются, но не всегда удобны.

Для известного контекста и разового применения подойдет и неполный разбор (поленился знаки затаскивать)
print re.search(ur'([ \+\-]*\d+) °С',s).group(1)
print re.search(ur'ночью&nbsp;([ \+\-]*\d+)',s).group(1)
print re.search(ur'утром&nbsp;([ \+\-]*\d+)',s).group(1)
>>> 
11
9
7



Отредактировано doza_and (Апрель 10, 2013 21:59:05)

Офлайн

#3 Апрель 10, 2013 21:29:58

Viktor1703
Зарегистрирован: 2013-04-10
Сообщения: 48
Репутация: +  0  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

doza_and, Вы думаете что через lxml будет удобнее? по мне так регулярки самое то… Попробуйте посмотреть исходный код главной страницы yandex, он не структурирован а написан в одну строчу.

Отредактировано Viktor1703 (Апрель 10, 2013 21:36:52)

Офлайн

#4 Апрель 10, 2013 21:43:03

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  253  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

Если страницы разнообразные и сложные или есть повышенные требования к надежности парсинга то однозначно lxml. А тут можно даже и упростить.
Я для себя решил так - регулярки меньше 80 символов тогда регулярки если больше то парсеры
lxml если чтото специфическое то coco или pyparsing (тут выбор от требований переносить на другие языки).
Что касается советов то тут класс избыточен, достаточно функции возвращающей словарь. Ведь вам не нужны промежуточные результаты после завершения парсинга?

def get_yandex_weather():
    return [{"ночью":t_n,"днем":t_d,...},{},...]
Еще одно - двойные подчеркивания в именах с обеих сторон это зверство зарезервированное для системных нужд питона. можете наступить на грабли.



Отредактировано doza_and (Апрель 10, 2013 21:45:19)

Офлайн

#5 Апрель 10, 2013 22:26:27

Viktor1703
Зарегистрирован: 2013-04-10
Сообщения: 48
Репутация: +  0  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

doza_and, понял Вас, постараюсь разобраться с lxml, попробую разные варианты, благодарю за советы.

P.S. Не заметил что Вы добавили пример кода в первом Вашем сообщении, он как раз кстати, не знал о такой конструкции, думаю он мне подайдёт

Отредактировано Viktor1703 (Апрель 10, 2013 22:47:01)

Офлайн

#6 Апрель 11, 2013 00:20:07

Viktor1703
Зарегистрирован: 2013-04-10
Сообщения: 48
Репутация: +  0  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

Может пригодится кому, у меня получилось так:

# -*- coding: cp1251 -*-
import urllib2, re, time
def Weather():
    result = {1 : 'Unknown', 2 : 'Unknown', 3 : 'Unknown', 4 : 'Unknown', 5 : 'Unknown'}
    try:
        page = urllib2.urlopen(urllib2.Request('http://www.yandex.ru/'))
        read = page.read()
        try:
            this = re.search(r'<div class="b-content-item__title">.*?<a href="http://pogoda.yandex.ru/(.*?)[/]".*?title="(.*?)".*?>.*?</a>.*?<a.*?>(.*?)</a>.*?</div>', read)
            if (this and (len(this.groups()) == 3)):
                result[1] = this.group(1).decode('utf-8')
                result[2] = this.group(2).decode('utf-8')
                result[3] = this.group(3).decode('utf-8')
        finally:
            del this
        try:  
            time = re.search(r'<div class="b-weather__info">.*?<a.*?>(.*?)</a>.*?<a.*?>(.*?)</a>.*?</div>', read)
            if (time and (len(time.groups()) == 2)):
                result[4] = time.group(1).replace('&nbsp;', ' ').decode('utf-8')
                result[5] = time.group(2).replace('&nbsp;', ' ').decode('utf-8')
        finally:
            del time
    finally:
        page.close()
    return result
we = Weather()
print(we[1]) # город
print(we[2]) # статус погоды
print(we[3]) # температура сейчас
print(we[4]) # температура через несколько часов
print(we[5]) # температура через пол дня
#for i in we:
#    print(we[i])
time.sleep(60)

doza_and, с такой конструкцией не хочет работать

print re.search(ur'([ \+\-]*\d+) °С',s).group(1)
print re.search(ur'ночью&nbsp;([ \+\-]*\d+)',s).group(1)
print re.search(ur'утром&nbsp;([ \+\-]*\d+)',s).group(1)

дело в том что Python прикручен к MVS2010, если я ставлю # -*- coding: utf-8 -*-, то IDE начинает ругаться на строки с “u” и подчёркиваются, с cp1251 всё отлично. И ещё раз прошу взглянуть на код, может где - то, что - то лишнее, или может добавить чего.

Отредактировано Viktor1703 (Апрель 11, 2013 00:50:12)

Офлайн

#7 Апрель 11, 2013 02:13:15

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10016
Репутация: +  857  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

>>> import lxml.html
>>> 
>>> s = """
... <div class="b-content-item__title">
...     <a href="http://pogoda.yandex.ru/nalchik/"class="b-link">Погода</a>
...     <a href="http://pogoda.yandex.ru/nalchik/"title="облачно"class="b-weather__icon_link">
...         <i class="b-inline b-weather__icon b-weather__icon_ovc"></i>
...     </a>
...     <a href="http://pogoda.yandex.ru/nalchik/"class="b-link_black_novisit">+11 °С</a>
... </div>
... <div class="b-weather__info">
...     <a href="http://pogoda.yandex.ru/nalchik/"class="b-link_black_novisit">ночью&nbsp;+9</a>
...     <a href="http://pogoda.yandex.ru/nalchik/"class="b-link_black_novisit">утром&nbsp;+7</a>
... </div>
... """
>>> 
>>> html = lxml.html.fromstring(s)
>>> tup = html[0][2].text, html[1][0].text, html[1][1].text
>>> print(tup)
('+11 °С', 'ночью\xa0+9', 'утром\xa0+7')
>>> print(*tup, sep=':')
+11 °С:ночью +9:утром +7
>>>



Офлайн

#8 Апрель 11, 2013 09:04:00

Viktor1703
Зарегистрирован: 2013-04-10
Сообщения: 48
Репутация: +  0  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

py.user.next, не плохо, только я получаю не этот кусочек кода, а весь код страницы, в ней всё в одну строку, для примера:

<html><head></head><script></script><body><div></div></body></html>

мне трудно будет просто найти уровень вложенности

<div class="b-content-item__title">
</div>

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

Отредактировано Viktor1703 (Апрель 11, 2013 09:05:56)

Офлайн

#9 Апрель 11, 2013 11:22:19

py.user.next
От:
Зарегистрирован: 2010-04-29
Сообщения: 10016
Репутация: +  857  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

Viktor1703
только я получаю не этот кусочек кода, а весь код страницы, в ней всё в одну строку
lxml не опирается на то, одна там строка или много строк, она проводит синтаксический анализ языка html, используя его грамматику (браузер точно так же делает)

Viktor1703
хотя, можно с помощью регулярки извлечь нужный мне кусок кода
а вот регулярные выражения работают просто с текстом, поэтому они не отличают теги от нетегов
оно не отличит строку от тега
<div class="b-content-item__title">
alert('<div class="b-content-item__title">');
поэтому их не используют для разбора

Viktor1703
мне трудно будет просто найти уровень вложенности
эти теги div легко найти; обычно ищешь родительские теги, которые содержат нужные, а потом берёшь их дочерние элементы
там есть методы разные для поиска: есть, к примеру, итератор по узлам, ты можешь брать узел, рассматривать его как корневой узел и брать уже его итератор по узлам
вариантов много
.xpath() поддерживает широкие возможности просмотра атрибутов (может выполнять функции для анализа атрибутов, например, отыскивать имена классов, начинающиеся на такую-то букву или слово)



Отредактировано py.user.next (Апрель 11, 2013 11:24:22)

Офлайн

#10 Апрель 11, 2013 18:46:34

doza_and
От:
Зарегистрирован: 2010-08-15
Сообщения: 4138
Репутация: +  253  -
Профиль   Отправить e-mail  

Распарсить текст с помощью рег. выражений

# -*- coding: cp1251 -*-
import urllib2, re, time
def Weather():
#    result = {1 : 'Unknown', 2 : 'Unknown', 3 : 'Unknown', 4 : 'Unknown', 5 : 'Unknown'}
-> result = {}
    try:
        page = urllib2.urlopen(urllib2.Request('http://www.yandex.ru/'))
        read = page.read()
        try:
            this = re.search(r'<div class="b-content-item__title">.*?<a href="http://pogoda.yandex.ru/(.*?)[/]".*?title="(.*?)".*?>.*?</a>.*?<a.*?>(.*?)</a>.*?</div>', read)
-> спорный вопрос - но можно множество проверок убрать и просто ловить Эксепшены.
            if (this and (len(this.groups()) == 3)):
                result[1] = this.group(1).decode('utf-8')
                result[2] = this.group(2).decode('utf-8')
                result[3] = this.group(3).decode('utf-8')
        finally:
            del this
-> работает сборщик мусора del не обязателен а иногда и вреден
        try:  
            time = re.search(r'<div class="b-weather__info">.*?<a.*?>(.*?)</a>.*?<a.*?>(.*?)</a>.*?</div>', read)
            if (time and (len(time.groups()) == 2)):
                result[4] = time.group(1).replace('&nbsp;', ' ').decode('utf-8')
                result[5] = time.group(2).replace('&nbsp;', ' ').decode('utf-8')
        finally:
            del time
    finally:
        page.close()
    return result
we = Weather()
-> вместо кучи принтов пойдет pprint we
print(we[1]) # город
print(we[2]) # статус погоды
print(we[3]) # температура сейчас
print(we[4]) # температура через несколько часов
print(we[5]) # температура через пол дня
#for i in we:
#    print(we[i])
time.sleep(60)-> это так понимаю для вижуала  в c++ можно жать не f5 а чтото типа ctrl f5 или shift точно не помню - и консоль не закроется.

Если вы хотите тщательно следить за освобождением ресурсов используйте конструкцию with



Отредактировано doza_and (Апрель 11, 2013 18:47:58)

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version