Уведомления

Группа в Telegram: @pythonsu

#1 Июль 5, 2014 21:41:24

d54wvzc+i5zirs
Зарегистрирован: 2014-05-26
Сообщения: 62
Репутация: +  0  -
Профиль   Отправить e-mail  

Парсинг web страницы

Появилась нужда разобраться страничку сайта http://tourdom.tv/happy-hour/schastlivyj-chas/.
Всегда в таких задачах использовал библиотеку lxml.html и при помощи css select указывал какие нужно данные получить.
На данной странице такой вариант на сколько я вижу не пройдёт, страница сверстана так, что нет четкой структуры вложенности и нужные теги не имеют идентификатора уникального и не принадлежат ни к какому определённому классу. Из-за этого я не знаю как распарсить эту страницу. Помогите пожалуйста советом, как это можно сделать. Если я чего не знаю о lxml.html тыкните носом пожалуйста.

Офлайн

#2 Июль 5, 2014 23:26:17

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

Парсинг web страницы

d54wvzc+i5zirs
Если я чего не знаю о lxml.html

Ну да, скорее всего. Страница самая обыкновенная.



Офлайн

#3 Июль 6, 2014 11:27:07

d54wvzc+i5zirs
Зарегистрирован: 2014-05-26
Сообщения: 62
Репутация: +  0  -
Профиль   Отправить e-mail  

Парсинг web страницы

py.user.next
Раз на ваш взгляд все элементарно, покажите как вы на пример извлечёте даты только на турпакеты?

Офлайн

#4 Июль 6, 2014 13:11:17

ingfa_1981
Зарегистрирован: 2014-01-25
Сообщения: 200
Репутация: +  1  -
Профиль   Адрес электронной почты  

Парсинг web страницы

Офлайн

#5 Июль 6, 2014 13:21:15

ajib6ept
От: От: От: От: От: От: От: От:
Зарегистрирован: 2013-08-04
Сообщения: 297
Репутация: +  26  -
Профиль   Отправить e-mail  

Парсинг web страницы

Как вариант, загрузил содержимое страницы, отрезал ненужное, передал дальше в lxml.



_________________________
Python golden rule: Do not PEP 8 unto others; only PEP 8 thy self.
Don't let PEP 8 make you insanely intolerant of other people's code.

Отредактировано ajib6ept (Июль 6, 2014 13:21:27)

Офлайн

#6 Июль 6, 2014 17:30:24

john123
Зарегистрирован: 2013-12-22
Сообщения: 56
Репутация: +  7  -
Профиль   Отправить e-mail  

Парсинг web страницы

Воспользуйтесь XPath (пример кода здесь).

Путь до таблиц (<table>) турпакетов:

//*[contains(@class, 'entry-content')]/table[position() mod 2 = 0]
Получаете массив (список) и “гуляете” по нему, вытаскивая нужные Вам данные.

Отдельно путь до дат турпакетов:
//*[contains(@class, 'entry-content')]/table[position() mod 2 = 0]//td[@class='width_first_td_from_table']/b

И да, замечание: парсить регулярными выражениями SGML-документы (XML, HTML и т.д.) не комильфо.
Старайтесь избегать этого способа, если требуется извлекать больше данных, чем просто email'ы или номера телефонов.

UPD!!! Прошу прощения, немного ошибся с XPath - приведенные мной ранее пути на самом деле выделяют все таблицы, т.е. Турпакеты/Билеты/Отели.

Правильные только для турпакетов вот:
//*[contains(@class, 'entry-content')]/table[contains(., 'БИЛЕТЫ')]/preceding::table[not(contains(., 'ТУРПАКЕТЫ')) and not(@class)]
И для дат:
//*[contains(@class, 'entry-content')]/table[contains(., 'БИЛЕТЫ')]/preceding::table[not(contains(., 'ТУРПАКЕТЫ')) and not(@class)]//td[@class='width_first_td_from_table']/b
Решил не стирать прошлые, т.к. они тоже могут Вам понадобиться.

P.S. Список литературы:
http://ru.wikipedia.org/wiki/XPath
http://www.w3schools.com/xpath/xpath_syntax.asp
http://zvon.org/xxl/XPathTutorial/Output_rus/example1.html
Для удобной работы можно установить плагины к FireFox и FireBug. Например FirePath: https://addons.mozilla.org/ru/firefox/addon/firepath/

Отредактировано john123 (Июль 6, 2014 21:26:52)

Офлайн

#7 Июль 7, 2014 01:10:57

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

Парсинг web страницы

#!/usr/bin/env python3
 
import lxml.html
 
 
def parse_tables(tables):
 
    sect1, sect2, sect3 = [], [], []
 
    state = 'before'
 
    for t in tables:
 
        if state == 'before':
            if search_section_type(t) == 'ТУРПАКЕТЫ':
                state = 'section1'
 
        elif state == 'section1':
            if search_section_type(t) == 'БИЛЕТЫ':
                state = 'section2'
            elif is_valid_table1(t):
                sect1.append(get_data_table1(t))
            elif is_valid_table2(t):
                sect1.append(get_data_table2(t))
 
        elif state == 'section2':
            if search_section_type(t) == 'ОТЕЛИ':
                state = 'section3'
            elif is_valid_table1(t):
                sect2.append(get_data_table1(t))
            elif is_valid_table2(t):
                sect2.append(get_data_table2(t))
 
        elif state == 'section3':
            if is_valid_table1(t):
                sect3.append(get_data_table1(t))
            elif is_valid_table2(t):
                sect3.append(get_data_table2(t))
 
    return sect1, sect2, sect3
 
def search_section_type(t):
    lst = t.xpath('.//td[@class = "type_of_package"]/text()')
    if lst:
        return lst[0]
 
def is_valid_table1(t):
    return 'style' in t.attrib
 
def is_valid_table2(t):
    return ('class' in t.attrib and
            'hide_sc_info_table' in
                t.attrib.get('class', ''))
 
def get_data_table1(t):
    out = (
        t.xpath('.//b[@class]/text()')[0],
        t.xpath('.//strong/text()')[0],
    )
    out = tuple(map(str.strip, out))
    return out
 
def get_data_table2(t):
    out = (
        t.xpath('.//tr/td[2]/text()')[0],
    )
    out = tuple(map(str.strip, out))
    return out
 
 
url = 'http://tourdom.tv/happy-hour/schastlivyj-chas/'
 
#doc = lxml.html.parse(url)
doc = lxml.html.parse('file.html')
#print(doc)
 
alltables = doc.xpath('//table')
#print(alltables)
 
sect1, sect2, sect3 = parse_tables(alltables)
 
print(sect1)
print(len(sect1))
print(sect2)
print(len(sect2))
print(sect3)
print(len(sect3))

[('На 11.07.2014', 'Болгария, Солнечный берег,'), ('Туи',), ('На 18.07.2014', 'Кипр, Протарас,'), ('Лабиринт',), ('На 12.07.2014', 'Греция, о.Родос,'), ('Тройка',), ('На 08.07.2014', 'Испания, Коста-Брава,'), ('Лабиринт',), ('На 12.07.2014', 'Италия, о.Сардиния,'), ('Пак груп',), ('На 10.07.2014', 'Чехия, Прага,'), ('Туи',), ('На 08.07.2014', 'Турция, Алания,'), ('Санмар',), ('На 22.07.2014', 'Египет, Шарм-Эль-Шейх,'), ('Brisco',), ('На 18.07.2014', 'Хорватия, Цавтат,'), ('ПАКС',), ('На 16.07.2014', 'Черногория, Шушань,'), ('Библио Глобус',), ('На 27.07.2014', 'Тунис, Набель,'), ('Библио Глобус',)]
22
[('На 11.09.2014', 'Москва – Лондон – Москва,'), ('Москва, а/п Домодедово',), ('На 07.07.2014', 'Москва – Карловы Вары – Москва,'), ('Москва, а/п Шереметьево',)]
4
[('На 11.09.2014', 'Великобритания, Лондон,'), ('',)]
2
[guest@localhost xptours]$

d54wvzc+i5zirs
покажите как вы на пример извлечёте даты только на турпакеты?

Общий способ - использование конечного автомата. Это подходит для любых страниц (и не только страниц).


john123
Правильные только для турпакетов вот:
Не, выдаёт не всё.

#!/usr/bin/env python3
 
import lxml.html
 
url = 'http://tourdom.tv/happy-hour/schastlivyj-chas/'
 
#doc = lxml.html.parse(url)
doc = lxml.html.parse('file.html')
#print(doc)
 
tables = doc.xpath("//*[contains(@class, 'entry-content')]/table[contains(., 'БИЛЕТЫ')]/preceding::table[not(contains(., 'ТУРПАКЕТЫ')) and not(@class)]")
 
print(tables, len(tables))
 
dates = doc.xpath("//*[contains(@class, 'entry-content')]/table[contains(., 'БИЛЕТЫ')]/preceding::table[not(contains(., 'ТУРПАКЕТЫ')) and not(@class)]//td[@class='width_first_td_from_table']/b")
 
print(dates, len(dates))

[guest@localhost xptours]$ ./xptours_inc.py 
[<Element table at 0xb721eb9c>, <Element table at 0xb721ebcc>, <Element table at 0xb721ebfc>, <Element table at 0xb721ec2c>, <Element table at 0xb721ec5c>, <Element table at 0xb721ec8c>, <Element table at 0xb721ecbc>, <Element table at 0xb721ecec>, <Element table at 0xb721ed1c>, <Element table at 0xb721ed4c>] 10
[] 0
[guest@localhost xptours]$

10 элементов вместо 11



Отредактировано py.user.next (Июль 7, 2014 11:05:09)

Офлайн

#8 Июль 7, 2014 10:16:01

john123
Зарегистрирован: 2013-12-22
Сообщения: 56
Репутация: +  7  -
Профиль   Отправить e-mail  

Парсинг web страницы

Странный сайт. При перезагрузке страницы меняется верстка.

Попробуйте вот такой XPath:

//*[contains(@class, 'entry-content')]/table[contains(., 'БИЛЕТЫ')]/preceding::table[contains(@style, 'border-top')]
Сейчас перепроверил в FirePath - отрабатывает нормально.

Вот такой листинг получается (python2):
#!/usr/bin/env python
# encoding: utf-8
    
import lxml.html
    
url = 'http://tourdom.tv/happy-hour/schastlivyj-chas/'
doc = lxml.html.parse(url)
    
tables = doc.xpath(u"//*[contains(@class, 'entry-content')]/table[contains(., 'БИЛЕТЫ')]/preceding::table[contains(@style, 'border-top')]")
    
for item in tables:
    date = item.xpath(".//b[@class]/text()")[0]
    title = item.xpath(".//strong/text()")[0].strip()
    price = item.xpath(".//span[@class='title_pacag']/text()")[0]
    
    print date, title, price

Отредактировано john123 (Июль 7, 2014 10:51:41)

Офлайн

Board footer

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

Powered by DjangoBB

Lo-Fi Version