在使用爬虫爬取多个页面时(比如爬取邮箱,手机号等),一般层级越高与我们原始目标数据之间准确率越低,所以很有必要控制爬取层级达到有效爬取
无论是深度还是广度爬取,都需要以下变量和方法
- #链接的正则表达式,注意是在标签中的href属性里的才是真正的链接
- PATTERN_URl = "<a.*href=\"(https?://.*?)[\"|\'].*"
-
- #获取网页源代码,注意使用requests时访问https会有SSL验证,需要在get方法时关闭验证
- def getHtml(url):
- res = requests.get(url,verify=False)
- text = res.text
- return text
- #有时还是会有警告,可以采用以下方式禁用警告
- #import urllib3
- #urllib3.disable_warnings()
-
- #获取指定页面中含有的url
- def getPageUrl(url,html=None):
- if html == None:
- html = getHtml(url)
- uList = re.findall(PATTERN_URl, html)
- return uList
深度:从首页爬取到所有url中,进入到第一个url(第1级)中再爬取所有url,进入第2级中的第一个url中继续操作,所以应该通过递归控制层级。
- #深度字典,{url:层级,...},用来去重
- depthDict = {}
-
- def getUrlsDeep(url,depth = 3):
- try:
- if(depthDict[url]>=depth):
- return
-
- #避免碰到了下载链接,可见下文
- # if 'download' in str(url):
- # return
-
- #获取此页中的所有连接
- clist = getPageUrl(url)
- print("\t\t"*depthDict[url],"#%d:%s"%(depthDict[url],url))
- for c in clist:
- #判断深度字典有有无此键,达到去重目的
- if c not in depthDict:
- depthDict[c]=depthDict[url]+1
- getUrlsDeep(c)
-
- except Exception as e:
- pass
-
- if __name__ == '__main__':
- startUrl = 'https://gsh.cdsy.xyz'
- #爬取页面设置有第0级
- depthDict[startUrl] = 0
- #一共爬取2级
- getUrlsDeep(startUrl,depth=3)
关于防止爬到下载链接从而一直在下载,对于html页面类型Content Type的内容为"text/html",可以获取页面消息头信息来实现。
由于requests模块中的headers属性是要get完响应页面才能获取到,也就是说要等下载完才能知道是否为下载链接。所以可以使用urllib.request来获取消息头,参考https://www.cdsy.xyz/computer/programme/Python/230823/cd45622.html
运行结果
...
广度:在首页中爬取所有url,然后进入第1级中的第1个url中爬取url,再进入第1级中第2个页面爬取url。可以将未爬取的页面存放在一个临时列表中,每爬取一个页面则出队,将爬取到的进队,所以是通过循环控制层级
- #临时存放url的列表,作为待爬列表,通过队列FIFO特性遍历url
- tmplist = []
- #存放爬取到的所有url
- urlList = []
- #同为深度字典
- depthDict = {}
-
-
- def getUrls(depth):
- while (len(tmplist)>0):
- #从临时列表中移除并获取对首元素链接
- url = tmplist.pop(0)
- #添加到链接列表
- urlList.append(url)
- if depthDict[url] < depth:
- #获取子页面url列表
- subList = getPageUrl(url)
- for u in subList:
- if u not in depthDict:
- depthDict[u] = depthDict[url]+1
- tmplist.append(u)
-
- if __name__ == '__main__':
- startUrl = 'https://gsh.cdsy.xyz'
- depthDict[startUrl] = 0
- tmplist.append(startUrl)
- getUrls(3)
- for url in urlList:
- print("\t"*depthDict[url],"#%d:%s"%(depthDict[url],url))
运行结果
···
...
分析两者方式运行结果,深度与广度之间的逻辑区别清晰可见:)
可以发现通过以上的单线程广度爬取速度很慢,可以改进成多进程的:
- tmplist = []
- depthDict = {}
- urlList = []
- #线程列表
- tlist = []
-
- #获取指定url中的所有url,并加入到待爬列表中,作为线程的target
- def getSonPageUrl(url):
- subList = getPageUrl(url)
- for u in subList:
- if u not in depthDict:
- depthDict[u] = depthDict[url] + 1
- tmplist.append(u)
-
- def getUrls(depth):
- #当还有待爬网页或者还有运行中的线程
- while ((len(tmplist)>0) or (threading.activeCount()>1)):
- while len(tmplist) == 0:
- continue
- url = tmplist.pop(0)
- urlList.append(url)
- #print(threading.activeCount(),"\t" * depthDict[url], "#%d:%s" % (depthDict[url], url))
- if depthDict[url] <depth:
- t = threading.Thread(target=getSonPageUrl,args=(url,))
- tlist.append(t)
- t.start()
-
-
- startUrl = 'https://gsh.cdsy.xyz'
- if __name__ == '__main__':
- depthDict[startUrl] = 0
- tmplist.append(startUrl)
- getUrls(3)
-