Использование re.findall() в Python для веб-сканирования

Я пытаюсь научить себя Python, написав на нем очень простой веб-краулер.

Код для него здесь:

#!/usr/bin/python

import sys, getopt, time, urllib, re

LINK_INDEX = 1
links = [sys.argv[len(sys.argv) - 1]]
visited = []
politeness = 10
maxpages = 20

def print_usage():
    print "USAGE:\n./crawl [-politeness <seconds>] [-maxpages <pages>] seed_url"

def parse_args():
    #code for parsing arguments (works fine so didnt need to be included here)

def crawl():
    global links, visited
    url = links.pop()    
    visited.append(url)

    print "\ncurrent url: %s" % url

    response = urllib.urlopen(url)
    html = response.read()

    html = html.lower()

    raw_links = re.findall(r'<a href="[\w\.-]+"', html)

    print "found: %d" % len(raw_links)

    for raw_link in raw_links:
        temp = raw_link.split('"')
        if temp[LINK_INDEX] not in visited and temp[LINK_INDEX] not in links:
            links.append(temp[LINK_INDEX])

    print "\nunvisited:"
    for link in links:
        print link

    print "\nvisited:"
    for link in visited:
        print link

parse_args()

while len(visited) < maxpages and len(links) > 0:
    crawl()
    time.sleep(politeness)

print "politeness = %d, maxpages = %d" % (politeness, maxpages)

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

Он может нормально получить html-код, потому что я могу его распечатать, но кажется, что часть re.findall() не делает то, что должна, потому что список ссылок никогда не заполняется. Возможно, я неправильно написал свое регулярное выражение? Он отлично работал, чтобы найти такие строки, как <a href="test02.html", а затем проанализировать ссылку, но по какой-то причине это не работает для реальных веб-страниц. Возможно, это часть http, которая отбрасывает его?

Я никогда раньше не использовал регулярное выражение с Python, поэтому я уверен, что это проблема. Может ли кто-нибудь дать мне представление о том, как лучше выразить шаблон, который я ищу? Спасибо!


person guskenny83    schedule 18.04.2014    source источник
comment
Не анализируйте HTML с помощью регулярных выражений. Используйте настоящий парсер HTML. Я предлагаю BeautifulSoup.   -  person Lev Levitsky    schedule 18.04.2014
comment
Ваше регулярное выражение верно, вы уверены, что html не имеет символов Unicode?   -  person Pedro Lobito    schedule 18.04.2014
comment
одна из ссылок в html - http://www.theage.com.au/digital-life/mobiles/Mobiles (я только что запустил ее на возрастном веб-сайте, чтобы проверить), и она должна была получить эту ссылку, не так ли? я не думаю, что там есть какие-либо символы юникода ..   -  person guskenny83    schedule 18.04.2014
comment
на самом деле, прочитав немного больше, кажется, что оператор w в регулярном выражении означает только символ слова, т.е. az, AZ, 0-9 и _. так что, вероятно, это : и //, которые сбивают его с толку .. есть идеи, что использовать вместо этого?   -  person guskenny83    schedule 18.04.2014


Ответы (3)


Проблема в вашем регулярном выражении. Есть целая куча способов, которыми я мог бы написать действительный HTML-якорь, который не будет соответствовать вашему регулярному выражению. Например, в нем могут быть лишние пробелы или разрывы строк, а также могут существовать другие атрибуты, которые вы не учли. Кроме того, вы не принимаете во внимание другой случай. Например:

<a  href="foo">foo</a>

<A HREF="foo">foo</a>

<a class="bar" href="foo">foo</a>

Ни одно из них не будет соответствовать вашему регулярному выражению.

Вы, вероятно, хотите что-то еще вроде этого:

<a[^>]*href="(.*?)"

Это будет соответствовать началу тега привязки, за которым следуют любые символы, кроме > (так что мы все еще совпадаем внутри тега). Это могут быть атрибуты class или id. Затем значение атрибута href фиксируется в группе захвата, которую можно извлечь,

match.group(1)

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

Наконец, вам нужно добавить флаг re.I для соответствия без учета регистра.

person SpoonMeiser    schedule 18.04.2014

Ваше регулярное выражение не соответствует всем допустимым значениям атрибутов href, таких как путь с косой чертой и т. д. Использование [^"]+ (все, что отличается от закрывающей двойной кавычки) вместо [\w\.-]+ помогло бы, но это не имеет значения, потому что… для начала не следует анализировать HTML с помощью регулярных выражений.

Лев уже упомянул BeautifulSoup, можно еще посмотреть lxml. Это будет работать лучше, чем любое регулярное выражение, которое вы могли бы написать вручную.

person Arkanosis    schedule 18.04.2014

Вы, вероятно, хотите этого:

raw_links = re.findall(r'<a href="(.+?)"', html)

Используйте скобки, чтобы указать, что вы хотите вернуть, иначе вы получите полное совпадение, включая бит <a href=.... Теперь вы получаете все до закрывающей кавычки из-за использования нежадного +? оператор.

Более разборчивым фильтром может быть:

raw_links = re.findall(r'<a href="([^">]+?)"', html)

это соответствует чему угодно, кроме кавычки и закрывающей скобки.

Эти простые RE будут соответствовать URL-адресам, которые были прокомментированы, URL-подобным литеральным строкам внутри битов javascript и т. д. Так что будьте осторожны при использовании результатов!

person EvertW    schedule 18.04.2014