学习爬取

 最简单的单页面抓取 
思路: 
获取页面所有url 
对获取的所有url进行分类 
A 获取属于本域名下的url 
B 获取属于其他url

2 用到的模块 
urllib 
详细介绍见链接 
http://blog.csdn.net/dolphin_h/article/details/45296353

bs4 
详细介绍见链接 
http://blog.csdn.net/winterto1990/article/details/47624167

re正则表达式 
详细介绍见链接 
http://blog.csdn.net/winterto1990/article/details/47624167

3 一下代码出自freebuf文章,链接 
http://www.freebuf.com/news/topnews/96821.html

代码说明:

import urllib
from bs4 import BeautifulSoup
import re

def get_all_url(url):
   urls = []
   web = urllib.urlopen(url)#使用urllib模块的urlopen函数打开url,复制给web
soup =BeautifulSoup(web.read())#将web内容转化为beautigulsoup格式的数据。

#通过正则过滤合理的url(针对与freebuf.com来讲)
   tags_a =soup.findAll(name='a',attrs={'href':re.compile("^https?://")})
   #soup.findall函数的运用,配合正则表达式来过滤url
    try :
       for tag_a in tags_a:
#re:^ 表示匹配字符串开头例如,^ abc 匹配 abc
    ? 表示匹配前一个字符串0次或1次,例如 abc? 匹配 ab 和 abc
       #return urls
   except:
       pass
   return  urls


#得到所有freebuf.com下的url
def get_local_urls(url):
   local_urls = []
   urls = get_all_url(url)
   for _url in urls:
       ret = _url
       if 'freebuf.com' in ret.replace('//','').split('/')[0]:
           local_urls.append(_url)
   return  local_urls
 #if 'freebuf.com' in ret.replace('//','').split('/')[0]:这个if语句不是很明白,通过split()函数,把域名分割,获取分割后组成的类表的第一个字符串。但是在分割前为什么要把//替换成空格???


#得到所有的不是freebuf.com域名的url
def get_remote_urls(url):
   remote_urls = []
   urls = get_all_url(url)
   for _url in urls:
       ret = _url
       if "freebuf.com" not in ret.replace('//','').split('/')[0]:
           remote_urls.append(_url)
   return  remote_urls


 #主函数
def __main__():
   url = 'http://freebuf.com/'
   rurls = get_remote_urls(url)
   print "--------------------remote urls-----------------------"
   for ret in rurls:
       print ret
   print "---------------------localurls-----------------------"   
   lurls = get_local_urls(url)
   for ret in lurls:
       print ret


if __name__ == '__main__':
__main__()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60

上面是单独对一个页面抓取。如果对整个站点抓取的话,还设计到url的处理,用作者的原话: 
我们可以把整站当成一个错综复杂的图结构,有一些算法基础的读者都会知道图的简单遍历方法:dfs和bfs(深度优先和广度优先)。如果这里读者有问题的话建议先去学习一下这两种算法。大体的算法结构我们清楚了,但是在实现中我们显然需要特殊处理url,需要可以区分当前目标站点域名下的网站和其他域名的网站,除此之外,在href的值中经常会出现相对url,这里也要特别处理。 
下面是代码:

import urllib
from bs4 import BeautifulSoup
import urlparse
import time
import urllib2
 #urllib 和 urllib2的区别http://blog.csdn.net/dolphin_h/article/details/45296353

url = "http://xxxx.xx/"
domain = "xxxx.xx"
deep = 0
tmp = ""
sites = set()
visited = set()
#local = set()
#集合:
python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和sysmmetric difference(对称差集)等数学运算.  

def get_local_pages(url,domain):
   global deep
   global sites
   global tmp
   repeat_time = 0
   pages = set()


    #防止url读取卡住
   while True:
       try:
           print "Ready to Open the web!"
           time.sleep(1)
           #time.sleep()函数,
           Python time sleep() 函数推迟调用线程的运行,可通过参数secs指秒数,表示进程挂起的时间

           print "Opening the web", url
           web = urllib2.urlopen(url=url,timeout=3)
           print "Success to Open the web"
           break
       except:
           print "Open Url Failed !!! Repeat"
           time.sleep(1)
           repeat_time = repeat_time+1
           if repeat_time == 5:
                return
   #上面整段判断url能不能打开


   print "Readint the web ..."
   soup = BeautifulSoup(web.read())
   print "..."
#提取标签 a
   for tag in tags:

       #避免参数传递异常
       try:
           ret = tag['href']
       except:
           print "Maybe not the attr : href"
           continue
       o = urlparse.urlparse(ret)
       #urlparse.urlparse函数的使用
       urlparse模块主要是把url拆分为6部分,并返回元组。并且可以把拆分后的部分再组成一个url。主要有函数有urljoin、urlsplit、urlunsplit、urlparse等。 

urlparse.urlparse(urlstring[, scheme[, allow_fragments]])

    将urlstring解析成6个部分,它从urlstring中取得URL,并返回元组 (scheme, netloc, path, parameters, query, fragment),但是实际上是基于namedtuple,是tuple的子类。它支持通过名字属性或者索引访问的部分URL,每个组件是一串字符,也有可能是空的。组件不能被解析为更小的部分,%后面的也不会被解析,分割符号并不是解析结果的一部分,除非用斜线转义,注意,返回的这个元组非常有用,例如可以用来确定网络协议(HTTP、FTP等等 )、服务器地址、文件路径,等等。
       """
       #Debug I/O
       for _ret in o:
           if _ret == "":
                pass
           else:
                print _ret
       """



       #处理相对路径url
       if o[0] is "" and o[1] is "":
           print "Fix  Page: " +ret
           url_obj = urlparse.urlparse(web.geturl())
           #获取web页面的url,用urlparse函数抽离
           ret = url_obj[0] + "://" + url_obj[1] + url_obj[2] + ret
           #组成一个绝对url
           #保持url的干净
           ret = ret[:8] + ret[8:].replace('//','/')
           o = urlparse.urlparse(ret)

                     #这里不是太完善,但是可以应付一般情况

           if '../' in o[2]:
                paths = o[2].split('/')
               for i inrange(len(paths)):
                    if paths[i] == '..':
                        paths[i] = ''
                        if paths[i-1]:
                            paths[i-1] = ''
                tmp_path = ''
                for path in paths:
                    if path == '':
                        continue
                    tmp_path = tmp_path + '/' +path
                ret =ret.replace(o[2],ret_path)
           print "FixedPage: " + ret


      上面整段都是判断获到的url是绝对url还是相对url。如果是相对url,则还需进行重组成完善的绝对url。包括url中含../的情况进行处理。但是处理../的情况不理解!!???

       #协议处理
       if 'http' not in o[0]:
           print "Bad  Page:" + ret.encode('ascii')
           continue

       #url合理性检验
       if o[0] is "" and o[1] is not "":
           print "Bad  Page: " +ret
           continue

       #域名检验
       if domain not in o[1]:
       #变量domain用来检验获取的所有url是否是改域名下的
           print "Bad  Page: " +ret
           continue

       #整理,输出
       newpage = ret
       if newpage not in sites:
           print "Add New Page: " + newpage
           pages.add(newpage)
   return pages

#dfs算法遍历全站
def dfs(pages):
    #无法获取新的url说明便利完成,即可结束dfs
   if pages is set():
       return
   global url
   global domain
   global sites
   global visited
   sites = set.union(sites,pages)
   for page in pages:
       if page not in visited:
           print "Visiting",page
           visited.add(page)
           url = page
           pages = get_local_pages(url, domain)
           dfs(pages)

   print "sucess"
 #整段程序下来,一直不知道 变量domain用来检验获取的所有url是否是改域名下的

pages = get_local_pages(url, domain)
dfs(pages)
for i in sites:
print i
posted @ 2021-06-08 09:54  潘福龙  阅读(94)  评论(0编辑  收藏  举报