在Python中使用re.findall()进行网络爬虫

1 投票
3 回答
2203 浏览
提问于 2025-04-18 03:16

我正在尝试自学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中使用过正则表达式,所以我很确定这就是问题所在。有没有人能告诉我如何更好地表达我想要的模式?谢谢!

3 个回答

1

你可能想要的是这个:

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

使用括号来指定你想要返回的内容,否则你会得到整个匹配结果,包括 <a href=... 这一部分。现在你可以得到所有内容,直到结束的引号,这是因为使用了一个非贪婪的 +? 操作符。

一个更精确的过滤器可能是:

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

这个可以匹配任何东西,除了引号和结束的括号。

这些简单的正则表达式可以匹配被注释掉的URL、在JavaScript中类似URL的字符串等等。所以在使用这些结果时要小心哦!

1

你的正则表达式没有匹配所有有效的 href 属性值,比如带斜杠的路径等等。用 [^"]+(也就是除了结束的双引号以外的任何东西)来代替 [\w\.-]+ 会更好,但其实这也没什么关系,因为… 你根本不应该用正则表达式来解析HTML

Lev已经提到过 BeautifulSoup,你也可以看看 lxml。这些工具比你自己写的任何正则表达式都要好用。

1

你的正则表达式有问题。其实,有很多种写法可以形成有效的HTML链接,而你的正则表达式可能都无法匹配到。比如,链接中可能会有多余的空格或者换行符,还有其他一些属性你没有考虑到。此外,你的表达式也没有考虑到大小写的问题。例如:

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

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

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

这些都无法被你的正则表达式匹配到。

你可能需要一个更像这样的表达式:

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

这个表达式会匹配一个链接标签的开始部分,后面跟着任何不是“>”的字符(这样我们就能继续匹配标签内部的内容)。这些字符可能是像 classid 这样的属性。然后,href 属性的值会被捕获到一个捕获组中,你可以通过

match.group(1)

来提取这个 href 的值。这个匹配是非贪婪的,意思是它会尽量匹配最小的内容。这样做是为了避免如果同一行有其他标签时,匹配到你不想要的部分。

最后,你需要添加 re.I 这个标志,以便不区分大小写地进行匹配。

撰写回答