【问题标题】:Using re.findall() in Python for Web Crawling在 Python 中使用 re.findall() 进行网络爬取
【发布时间】:2014-04-18 14:21:19
【问题描述】:

我正在尝试通过编写一个非常简单的网络爬虫来自学 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() 部分没有做它应该做的事情,因为链接列表永远不会被填充。我可能写错了我的正则表达式吗?找到&lt;a href="test02.html" 之类的字符串并从中解析链接效果很好,但由于某种原因,它不适用于实际网页。可能是 http 部分将其扔掉了?

我以前从未在 Python 中使用过正则表达式,所以我很确定这就是问题所在。谁能告诉我如何更好地表达我正在寻找的模式?谢谢!

【问题讨论】:

  • 不要使用正则表达式解析 HTML。使用实际的 HTML 解析器。我建议BeautifulSoup
  • 你的正则表达式是正确的,你确定html没有Unicode字符吗?
  • html 中的一个链接是http://www.theage.com.au/digital-life/mobiles/Mobiles(我只是在age 网站上运行它进行测试),它应该得到那个链接吗?我不认为那里有任何 unicode 字符..
  • 实际上,在阅读了更多内容之后,正则表达式中的“w”运算符似乎只代表“单词”字符,即。 a-z、A-Z、0-9 和 _。所以它可能是“:”和“//”导致它出错..知道用什么代替吗?

标签: python regex web-crawler findall


【解决方案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 标志以不区分大小写。

【讨论】:

    【解决方案2】:

    您的正则表达式与href 属性的所有有效值不匹配,例如带有斜杠的路径等。使用[^"]+(与结束双引号不同的任何东西)而不是[\w\.-]+ 会有所帮助,但这并不重要,因为……首先是you should not parse HTML with regexps

    Lev 已经提到了BeautifulSoup,你也可以看看lxml。它会比你可以编写的任何手工制作的正则表达式更好。

    【讨论】:

      【解决方案3】:

      你可能想要这个:

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

      使用方括号表示您想要返回的内容,否则您将得到包括&lt;a href=... 位在内的整个匹配项。由于使用了非贪婪的 +,现在你得到了所有的东西,直到结束引号运算符。

      更具辨别力的过滤器可能是:

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

      这匹配除引号和终止括号之外的任何内容。

      这些简单的 RE 将匹配已注释的 URL、javascript 位中的类似 URL 的文字字符串等。因此使用结果时要小心!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多