在Python中使用re.findall()进行网络爬虫
我正在尝试自学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 个回答
你可能想要的是这个:
raw_links = re.findall(r'<a href="(.+?)"', html)
使用括号来指定你想要返回的内容,否则你会得到整个匹配结果,包括 <a href=...
这一部分。现在你可以得到所有内容,直到结束的引号,这是因为使用了一个非贪婪的 +? 操作符。
一个更精确的过滤器可能是:
raw_links = re.findall(r'<a href="([^">]+?)"', html)
这个可以匹配任何东西,除了引号和结束的括号。
这些简单的正则表达式可以匹配被注释掉的URL、在JavaScript中类似URL的字符串等等。所以在使用这些结果时要小心哦!
你的正则表达式没有匹配所有有效的 href
属性值,比如带斜杠的路径等等。用 [^"]+
(也就是除了结束的双引号以外的任何东西)来代替 [\w\.-]+
会更好,但其实这也没什么关系,因为… 你根本不应该用正则表达式来解析HTML。
Lev已经提到过 BeautifulSoup
,你也可以看看 lxml
。这些工具比你自己写的任何正则表达式都要好用。
你的正则表达式有问题。其实,有很多种写法可以形成有效的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
这个标志,以便不区分大小写地进行匹配。