上一篇大致分析了Spaghetti的指纹识别部分,这篇来大致分析下它的爬虫部分。
先来看下extractor目录下的urlextract.py:
class UrlExtract:
@staticmethod
def run(content):
try:
urls = re.findall(r'href=[\'"]?([^\'" >]+)|Allow: (\/.*)|Disallow: (\/.*)|<loc>(.+?)</loc>',content)
return urls
except Exception,e:
pass
这个文件定义的类主要是解析response返回中的url连接和固有文件robots.txt、sitemap.xml中的指定路径。
forms.py中的源码主要集中在对表单的处理。表单处理用到了BeautifulSoup。
try:
params = zip(*[iter(params)]*2)
data = urllib.unquote(urllib.urlencode(params))
if method == []:
method = ['get']
method = method[0]
if method.upper() == "GET":
return data
except Exception,e:
pass
上面的代码中那个zip和iter组合使用的技巧值得知晓,可以参照这里。后面的urlencode相当于对dict进行编码成url那种key=value&key=value的形式。其实只要是由两个元素组成元组队列也可以编码成功。
下面,来分析爬虫的功能实现文件crawler.py。看下函数run的代码:
def run(self):
links_list = []
try:
for path in ('','robots.txt','sitemap.xml','spaghetti'):
url = self.ucheck.path(self.url,path)
resp = self.request.send(url,cookies=self.cookie)
links = self.extract.run(resp.content)
if links == None: link=[]
forms = self.forms.run(resp.content,self.url)
if forms == None: forms=[]
links_list += links
links_list += forms
return self.get(self.parse(links_list))
except Exception,e:
pass
这个函数主要是对制定的url,这个url就是目标地址,然后尝试找根目录下的robots.txt和sitemap.xml文件,形成一个需要爬取的url地址列表。
看下get函数:
def get(self,lista1):
lista = []
for i in lista1:
if re.search('=',i,re.I):
lista.append(i)
return lista
这个函数是寻找有等号的url,这个难道是为了后续XSS、SQL等的测试么?寻找有等号的url,相当于寻找key=value这样的url,也就是找有输入点的地方。
看下parse函数,这里分几块来看:
for link in links:
for i in link:
if i == '':
pass
else:
if i not in flinks:
tlinks.append(i)
blacklist = ['.png', '.jpg', '.jpeg', '.mp3', '.mp4', '.avi', '.gif', '.svg','.pdf','.js','.zip','.css','.doc','mailto']
for link in tlinks:
for bl in blacklist:
if bl in link:
pblacklist.append(link)
for link in tlinks:
for bl in pblacklist:
if bl == link:
index = tlinks.index(bl)
del tlinks[index]
这一块主要是过滤掉一些图片、视频、css、js等资源链接。我咋觉得这块写的不好,作者的思路是先找到所以符合黑名单的链接,存到pblacklist里面,然后双循环遍历tlinks和pblacklist,剔除掉tlinks里面符合pblacklist的链接。可是在前面筛选pblacklist的时候,就可以直接剔除掉不符合要求的链接,没有必要再遍历一次。要是我来写的话,直接过滤掉包含黑名单的链接,开一个列表存符合要求的链接即可。
继续看:
for link in tlinks:
if link.startswith('./'):
link = link.split('.')[1]
if link.startswith('http://')or link.startswith('https://'):
if link not in dlinks:
dlinks.append(link)
elif link.startswith('www.'):
link = 'http://'+link
if link not in dlinks:
dlinks.append(link)
elif link.startswith('/'):
link = self.ucheck.path(self.url,link)
if link not in dlinks:
dlinks.append(link)
else:
link = self.ucheck.path(self.url,link)
if link not in dlinks:
dlinks.append(link)
for link in dlinks:
if not link.startswith('http'):
pass
elif self.parser.host() not in link:
pass
elif link.startswith('http://http://') or link.startswith('https://http://'):
link = 'http'+link.split('http')[2]
complete.append(link)
else:
complete.append(link)
for i in complete:
i = urllib.unquote(i)
i = i.replace('&','&')
if i not in deflinks:
deflinks.append(i)
return deflinks
这一块就是拼接url了,让其成为一个符合标准的url链接。之前得到的url很多是相对地址或者是非目标主机的url,在这里都会被过滤掉。
thread_run函数:
def thread_run(self):
links = self.run()
links_list = []
if '--crawler' in sys.argv:
self.output.info('Starting deep crawler for %s'%self.parser.host())
try:
for link in links:
resp = self.request.send(link,cookies=self.cookie)
links_extract = self.extract.run(resp.content)
if links_extract == None: links_extract=[]
forms_extract = self.forms.run(resp.content,self.url)
if forms_extract == None: forms_extract=[]
links_list += links_extract
links_list += forms_extract
links_list = self.get(self.parse(links_list))
links_list = links_list+links
return links_list
except Exception,e:
pass
return links
可以看到就是组合上面几个函数,然后得到最终符合要求的链接。
整体看,这个爬虫没有处理Ajax技术相关的动态页面。从源码上看,这个爬虫就爬取了第一层的链接。然后url去重也木有实现。
大概就是这些了。