Уведомления

Группа в Telegram: @pythonsu

#1 Март 28, 2017 22:27:07

Romissevd
От: Счастье
Зарегистрирован: 2015-03-01
Сообщения: 533
Репутация: +  76  -
Профиль   Отправить e-mail  

Регулярные выражения

Доброй ночи, форумчане!
Прошу помощи в написании регулярного выражения для отбора ссылок.
Выражение должно начинаться на /catalog/, далее может следовать последовательность буквенных символов и /, но если в конце встречается запись ?page=12, ссылку нужно игнорировать.
Например, ссылки удовлетворяющие условию:
/catalog/
/catalog/fdff/df
и не удовлетворяющие:
/catalog/fdff/df/?page=3
google.com/dfdf/

Вот что я пытался сделать, но результат не тот

 '(^(\/catalog\/)[a-z\/]*)(?!\?page=\d+)'
Заранее благодарен.

Отредактировано Romissevd (Март 28, 2017 22:27:31)

Офлайн

#2 Март 28, 2017 23:32:18

wallet
Зарегистрирован: 2016-02-21
Сообщения: 50
Репутация: +  2  -
Профиль  

Регулярные выражения

Попробуйте так

 '\/catalog\/[a-z\/]*[^\?page=?\d+]$'

Офлайн

#3 Март 29, 2017 03:20:31

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

Регулярные выражения

  
>>> import re
>>> 
>>> pat = r'^/catalog/[a-zA-Z/]*(?!\?page=\d+)$'
>>> 
>>> lst = ["/catalog/",
...        "/catalog/fdff/df",
...        "/catalog/fdff/df/?page=3",
...        "google.com/dfdf/"]
>>> 
>>> list(filter(re.compile(pat).search, lst))
['/catalog/', '/catalog/fdff/df']
>>>



Офлайн

#4 Март 29, 2017 05:03:04

scidam
Зарегистрирован: 2016-06-15
Сообщения: 288
Репутация: +  35  -
Профиль   Отправить e-mail  

Регулярные выражения

Можно также использовать функцию urllib.parse.urlparse (встроенный модуль python), она позволяет разбить url на компоненты, а потом уже можно и условия проверять.

Офлайн

#5 Март 29, 2017 17:21:22

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

Регулярные выражения

scidam
она позволяет разбить url на компоненты, а потом уже можно и условия проверять
А потом склеивать обратно, если подошло. Лучше с текстом работать. Максимум, что можно посоветовать при подобных проблемах (когда сложно составить регулярку), - разделить задачу на этапы.

Для данной задачи будет:
1) Выбрать всё, что начинается на /catalog/.
2) Из полученного выбрать всё, что не заканчивается на ?page=число .

Декомпозиция, короче.

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



Отредактировано py.user.next (Март 29, 2017 17:21:57)

Офлайн

#6 Март 29, 2017 22:06:05

Romissevd
От: Счастье
Зарегистрирован: 2015-03-01
Сообщения: 533
Репутация: +  76  -
Профиль   Отправить e-mail  

Регулярные выражения

py.user.next
Спасибо большое за помощь. Понимал, что кручусь где-то возле, но каша в голове не дала дойти до результата.

py.user.next
Для данной задачи будет:
1) Выбрать всё, что начинается на /catalog/.
2) Из полученного выбрать всё, что не заканчивается на ?page=число .
так и поступил приблизительно, до момента получения подходящего паттерна.

Офлайн

#7 Март 30, 2017 02:51:41

scidam
Зарегистрирован: 2016-06-15
Сообщения: 288
Репутация: +  35  -
Профиль   Отправить e-mail  

Регулярные выражения

py.user.next
А потом склеивать обратно, если подошло
Склеить там не сложно – для этого специальный метод есть – parsed_instance.geturl().

Точно, однако, в конкретном случае regexp – лучше, пожалуй.

Но вот вдруг кроме page, будет какой-нибудь еще GET параметер, т.е.
?page=10&theme='blue' или еще что, а еще после catalog/ могут быть отличные от a-zA-Z символы – их придется добавить в re…

Все-таки urlparse специальный для этих целей инструмент… Вот такое будет решение с urlparse:

 from urllib import parse
list(map(lambda x: x.geturl(), filter(lambda x: x.path.startswith('/catalog/') and 'page' not in parse.parse_qs(x.query), map(parse.urlparse, lst))))

Извиняюсь, что может быть не совсем читабельно, однако, все же решает более гибко данную задачу:
например, всякие такие url ‘/catalog/some-path-to/another_item/df/df?page=1&theme=“colored”’ обработаются правильно, а regexp – подстраивать нужно…







Отредактировано scidam (Март 30, 2017 04:48:41)

Офлайн

#8 Март 30, 2017 05:07:15

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

Регулярные выражения

scidam
Извиняюсь, что может быть не совсем читабельно
Да совсем не читабельно, что уж там говорить. А по операциям - тем более завал, представь просто миллион ссылок так обработать (разрезать-искать-искать-искать-склеить). Регулярка моментально найдёт, так как оптимизирована на базовом уровне.

scidam
Но вот вдруг кроме page, будет какой-нибудь еще GET параметер, т.е.
?page=10&theme='blue' или еще что, а еще после catalog/ могут быть отличные от a-zA-Z символы – их придется добавить в re…
Если у тебя варианты появляются, ты можешь просто добавить вторую регулярку. А потом с ней можно что сделать? Правильно, объединить с первой через дизъюнкцию | , получится снова одна регулярка, которая будет быстро проверяться. Да и регулярки можно вынести в такую как бы базу данных (в таблицу), которую пополнять потом. Если Dive Into Python 3 почитаешь, там увидишь пример на языковых правилах, как это делается, - правила заносятся в список, а потом в коде подцепляются из него постепенно.



Отредактировано py.user.next (Март 30, 2017 05:12:18)

Офлайн

#9 Март 30, 2017 09:03:21

scidam
Зарегистрирован: 2016-06-15
Сообщения: 288
Репутация: +  35  -
Профиль   Отправить e-mail  

Регулярные выражения

py.user.next
Регулярка моментально найдёт, так как оптимизирована на базовом уровне.
Да, регулярка быстрей, конечно…

Вот, на скорую руку, подтверждение:

 import random, string, re
import timeit 
from urllib import parse
def gen_urls(n=1000):
    choices= ['/catalog/%s/?page=12',
              '/catalog/%s/someurl']
    res = []
    for i in range(n):
        m =  random.randrange(20,30)
        intermediate = ''.join(random.choice(string.ascii_uppercase + '/') for _ in range(m))
        res.append(random.choice(choices)%intermediate)
    return res
data = gen_urls(n=10000)
pat = re.compile(r'^/catalog/[a-zA-Z/]*(?!\?page=\d+)$')
def code1():
    return list(filter(pat.search, data))
    
def code2():
    return list(map(lambda x: x.geturl(), filter(lambda x: x.path.startswith('/catalog/') and 'page' not in parse.parse_qs(x.query), map(parse.urlparse, data))))
print(timeit.timeit("code1()", setup="from __main__ import code1", number=100)/100)
#0,01.
print(timeit.timeit("code2()", setup="from __main__ import code2", number=100)/100)
#0.16

Здесь, как минимум в 10 раз эффективней подход на регулярке…

Офлайн

#10 Март 30, 2017 11:49:52

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

Регулярные выражения

scidam
Вот, на скорую руку, подтверждение:
Что-то ты намастрячил фикстуру, можно просто десять ссылок взять и результат тот же будет. Главное, чтобы разные коды получали одинаковые данные.



Офлайн

Board footer

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

Powered by DjangoBB

Lo-Fi Version