我正在尝试通过编写一个非常简单的网络爬虫来教自己。
它的代码在这里:
#!/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的正则表达式,所以我很确定这就是问题所在。任何人都可以告诉我如何更好地表达我寻找的模式?谢谢!
答案 0 :(得分:1)
您的正则表达式与href
属性的所有有效值都不匹配,例如带斜杠的路径,等等。使用[^"]+
(与结束双引号不同)而不是[\w\.-]+
会有所帮助,但这并不重要,因为...... you should not parse HTML with regexps开头。
列夫已经提到过BeautifulSoup
,你也可以看一下lxml
。你可以写的任何手工制作的正则表达式都会更好。
答案 1 :(得分:1)
你可能想要这个:
raw_links = re.findall(r'<a href="(.+?)"', html)
使用括号表示您想要返回的内容,否则您将获得包括<a href=...
位在内的整个匹配项。现在你得到一切,直到结束引号,因为使用了非贪婪的+?操作
更具辨别力的过滤器可能是:
raw_links = re.findall(r'<a href="([^">]+?)"', html)
这匹配除了引号和终止括号之外的任何内容。
这些简单的RE将与已注释的URL匹配,javascript中的URL类文字字符串等。所以请小心使用结果!
答案 2 :(得分:1)
问题 与你的正则表达式。我可以用很多方法编写一个有效的HTML锚点,你的正则表达式不会匹配。例如,可能存在额外的空格或换行符,并且还有其他可能存在的属性,您尚未将其考虑在内。此外,你不考虑不同的情况。例如:
<a href="foo">foo</a>
<A HREF="foo">foo</a>
<a class="bar" href="foo">foo</a>
这些都不会与你的正则表达式匹配。
你可能想要更像这样的东西:
<a[^>]*href="(.*?)"
这将匹配锚标记start,后跟除&gt;之外的任何字符。 (这样我们仍然可以在标签内匹配)。这可能是class
或id
属性。然后,在捕获组中捕获href
属性的值,您可以通过
match.group(1)
href
值的匹配也非贪婪。这意味着它将匹配可能的最小匹配。这是因为如果你在同一行上有其他标签,那么你将超出你想要的范围。
最后,您需要以不区分大小写的方式添加re.I
标志以进行匹配。