Неправильный анализ строки на Python - PullRequest
2 голосов
/ 08 октября 2011

Я новичок в python / django и пытаюсь найти более эффективную информацию из моего скребка.В настоящее время скребок берет список названий комиксов и правильно делит их на список CSV на три части (Дата публикации, Дата оригинала и Название).Затем я передаю текущую дату и заголовок в различные части моей базы данных, что я делаю в своем скрипте Loader (конвертирую mm / dd / yy в yyyy-mm-dd, сохраняю в столбец pub_date, title переходит в «title»колонка).

Общая строка может выглядеть следующим образом:

10/12/11|10/12/11|Stan Lee's Traveler #12 (10 Copy Incentive Cover)

Я успешно получаю дату, но заголовок хитрее.В этом случае, в идеале, я бы хотел заполнить три разных столбца информацией после второго «|».Заголовок должен идти в «заголовок», поле char.число 12 (после '#') должно идти в DecimalField "issue_num", а все, что между '()' должно идти в "Special" charfield.Я не уверен, как сделать такой строгий анализ.

Иногда существует несколько # (один комикс, в частности, описывается как пакет, «Содержащий проблемы № 90- # 95»), а у нескольких есть несколько групп «()» (например, «Предательство»).Планеты обезьян № 1 (из 4) (25 экземпляров, обложка для поощрения))

Каким будет хороший способ начать решать эту проблему? Мои знания об утверждениях If / else быстро распались наболее сложные строки. Как я могу эффективно и (если возможно) питонически анализировать эти строки и подразделять их, чтобы впоследствии я мог разместить их в нужном месте в моей базе данных?

Ответы [ 3 ]

1 голос
/ 08 октября 2011

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

Обычно я справляюсь с этим, создавая список правил, от самых специфических до общих, и пробую их по одному, пока не совпадет одно из них.

Для написания таких правил вы можете использовать модуль re или даже pyparsing .

Общая идея выглядит так:

class CantParse(Exception): 
    pass

# one rule to parse one kind of title
import re
def title_with_special( title ):
    """ accepts only a title of the form
     <text> #<issue> (<special>) """
    m = re.match(r"[^#]*#(\d+) \(([^)]+)\)", title)
    if m:
        return m.group(1), m.group(2)
    else:
        raise CantParse(title)


def parse_extra(title, rules):
    """ tries to parse extra information from a title using the rules """
    for rule in rules:
        try:
            return rule(title)
        except CantParse:
            pass

    # nothing matched
    raise CantParse(title)


# lets try this out
rules = [title_with_special] # list of rules to apply, add more functions here
titles = ["Stan Lee's Traveler #12 (10 Copy Incentive Cover)",
          "Betrayal Of The Planet Of The Apes #1 (Of 4)(25 Copy Incentive Cover) )"]

for title in titles:
    try:
        issue, special = parse_extra(title, rules)
        print "Parsed", title, "to issue=%s special='%s'" % (issue, special)
    except CantParse:
        print "No matching rule for", title

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

1 голос
/ 09 октября 2011

Регулярное выражение - путь.Но если вам неудобно писать их, вы можете попробовать небольшой синтаксический анализатор, который я написал (https://github.com/hgrecco/stringparser). Он переводит строковый формат (PEP 3101) в регулярное выражение. В вашем случае вы будете делать следующее:

>>> from stringparser import Parser
>>> p = Parser(r"{date:s}\|{date2:s}\|{title:s}#{issue:d} \({special:s}\)")
>>> x = p("10/12/11|10/12/11|Stan Lee's Traveler #12 (10 Copy Incentive Cover)")
OrderedDict([('date', '10/12/11'), ('date2', '10/12/11'), ('title', "Stan Lee's Traveler "), ('issue', 12), ('special', '10 Copy Incentive Cover')])
>>> x.issue
12

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

Еще одна вещь: обратите внимание, что вв текущей версии вам нужно вручную экранировать символы регулярного выражения (т. е. если вы хотите найти |, вам нужно набрать \ |). Я планирую изменить это в ближайшее время.

1 голос
/ 08 октября 2011

Используйте регулярное выражение , модуль , re.Например, если у вас есть третье | -ограниченное поле вашей записи выборки в переменной s, тогда вы можете сделать

match = re.match(r"^(?P<title>[^#]*) #(?P<num>[0-9]+) \((?P<special>.*)\)$", s)
title = match.groups('title')
issue = match.groups('num')
special = match.groups('special')

. Вы получите IndexError в последних трехстроки для пропущенного поля.Адаптируйте RE, пока он не проанализирует все, что вы хотите.

...