• 第一个爬虫(对百度贴吧图片的爬取)


    一直想写爬虫,也想学python,最近看了慕课网爬虫的视频, 就自己试着写了个爬虫。单线程爬取,效率很慢,谢谢百度没有封IP 哈哈哈。

    我是win7 64位, python3.4,还用到了urllib, BeautifulSoup,

    python2.7用不了, 最主要就是urllib 和print 有点不同吧。

    我的思路是: 利用firefox的网页分析工具。

    1.找出当前贴吧首页所有标题的地址(一页应该是五十个),把地址截取下来放在set里面。

    外标题的地址格式:http://tieba.baidu.com/p/4220129198 ,所以利用BeautifulSoup可以轻易得到内容。

    那么该主题的地址就是 http://tieba.baidu.com/p/4220129198 ,字符串拼接一下即可。

    2.遍历刚才所有的标题地址,进入该主题,收集图片地址,如果有分页,进入分页,挨着收集到最后一页。

    分页还是上面的那样也是用的pn,所以直接找到尾页(<a href="/p/4220129198?pn=90">尾页</a>),然后循环从pn=2遍历到尾页(pn=90)即可。

    2.1 访问刚才收集到的所有图片地址,下载。

    3.遍历完第一页所有标题地址后,进入第二页(#url='http://tieba.baidu.com/f?%s&ie=utf-8&pn=2'%(urltbname)),再继续重复1,2步骤。

    直到最后一页。(但是这个过程很长, 所以除了很少贴的贴吧, 我并没有爬完一个帖子数比较多的贴吧)

    代码贴在下面,如果对你有帮助,很开心。

    相当一部分的网页解析代码没有提取出来。。

    spider_main.py

    #coding:UTF8
    '''
    Created on 2016年1月25日
    
    
    @author: thewindkee
    '''
    from tieba import tieba_picdownloader,tieba_htmldownloader,tieba_parser,url_manager
    import urllib.request
    from bs4 import BeautifulSoup
    import re
    import os
    from urllib import parse
    import time
    class SpiderMain(object):
        def __init__(self):
            self.piclinks=set()
            self.urls = url_manager.UrlManager()
            self.downloader = tieba_htmldownloader.HtmlDownloader()
            self.parser = tieba_parser.HtmlParser()
            self.picdownloader=tieba_picdownloader.PicDownloader()
            #用于统计总图片数,和爬取过的主题数,总网页数
            self.piccount=0
            self.titlecount=0
            self.pagecount=0
        def __crawl__(self,tiebaname):
            folderpwd=os.getcwd()+'\%s'%(tiebaname)
            urltbname=parse.urlencode({'kw':'%s'%(tiebaname)})
            #创建以贴吧名为名的文件夹
            if  not os.path.exists(folderpwd): 
                os.makedirs(tiebaname)
            os.chdir(folderpwd)
            baseurl='http://tieba.baidu.com'
            #/f?kw=kw=%E5%9B%9B%E5%B7%9D%E5%A4%A7%E5%AD%A6&ie=utf-8&pn=0
            url='http://tieba.baidu.com/f?%s&ie=utf-8'%(urltbname)
            #开始得到外页所有标题地址
            #<a class="last" href="/f?kw=%E5%9B%9B%E5%B7%9D%E5%A4%A7%E5%AD%A6&ie=utf-8&pn=670700">尾页</a>
            tiebacont=self.downloader.download(url)
            soup=BeautifulSoup(tiebacont,'html.parser',from_encoding='utf-8')
            #得到贴吧最后一页的pn值
            lastpagenum=self.parser._get_outermaxpage(tiebacont)
            print(lastpagenum)
            while self.titlecount<=lastpagenum:
                url=url+'&pn=%s'%(self.titlecount)
                print('当前url=',url)
                tiebacont=self.downloader.download(url)
                soup=BeautifulSoup(tiebacont,'html.parser',from_encoding='utf-8')
                titlelinks=soup.find_all('a',href=re.compile(r"/p/d+"),class_="j_th_tit")
                #用dataurls装外标题地址
                dataurls=set()
                for link in titlelinks:
                    dataurls.add(urllib.parse.urljoin(baseurl,link['href']))
                print(dataurls)
                #开始爬取当前页所有主题
                while dataurls.__len__()>0:
                    self.titlecount+=1
                    self.pagecount+=1
                    dataurl=dataurls.pop()
                    #爬取每个标题内容
                    cont=self.downloader.download(dataurl)
                    if cont is None:
                        continue
                    self.piclinks=self.parser._get_piclinks(cont)
        #             for piclink in self.piclinks:
        #                 print('打印piclink',piclink)
                    #分页图片链接收集
                    #得到最后一页的pn值
                    maxpage=self.parser._get_innermaxpage(cont)
                    #把urlnum提取出来,每个主题对应一个/p/urlnum
                    ithr=re.finditer(r"d+", dataurl)
                    urlnum=ithr.__next__().group()
                    if maxpage !=0:
                        print('组装并进入分页')
                            #http://tieba.baidu.com/p/3844xx489x?pn=2
                        for i in range(2,maxpage+1):
                            li=['http://tieba.baidu.com/p/',urlnum,'?pn=',str(i)]
                            nextpageurl=''.join(li) #下一页的url
                            print('nextpageurl=',nextpageurl)
                            cont=self.downloader.download(nextpageurl)
                            if cont is None:
                                continue
                            newpiclinks=self.parser._get_piclinks(cont)
                            self.piclinks=self.piclinks.union(newpiclinks);
                            self.pagecount+=1
                    #开始下载该主题的全部图片,保存在urlnum目录下
                    i=0;
                    for piclink in self.piclinks:
                        i+=1
                        folder=urlnum
                        savename=''.join([urlnum,'n',str(i)])
                        savetype='.jpg'
                        print('图片地址=%s'%(piclink))
                        self.picdownloader.save(piclink,folder,savename,savetype)
                        self.piccount+=1
        #                 self.picdownloader.save(piclink,folder,str(count),savetype)
                    self.piclinks.clear()
                print('目前总共爬取过%d个主题,%d个网页,下载了%d张图片'%(self.titlecount,self.pagecount,self.piccount))
    tiebaname='人很少'
    print(parse.urlencode({'kw':'%s'%(tiebaname)}))
    spider=SpiderMain()
    start=time.time()
    spider.__crawl__(tiebaname)
    end=time.time()
    print(end-start)

    下载网页

    tieba_htmldownloader.py

    # coding:utf8
    import urllib.request
    class HtmlDownloader(object):
    
    
        def download(self,url):
            if url is None:
                return None
            request=urllib.request.Request(url)
            request.add_header("user-agent", "Mozilla/5.0")
            response=urllib.request.urlopen(url)
            if response.getcode() !=200:
                return None
            return response.read()

    解析网页

    tieba_parser.py

    #coding:utf8
    from bs4 import BeautifulSoup
    import urllib.parse
    import re
    class HtmlParser(object):
        def _get_page(self,page):
            it=re.finditer(r"pn=d+", str(page))
    #         ithr=re.finditer(r"d+", str(out))
    #         print(out)
            maxpage=0;
            count=0;
            for ma in it:
                count+=1
                maxpage=ma.group()
    #         print('maxage=',maxpage)
            if count!=0:
                maxpage=re.finditer(r"d+", maxpage).__next__().group()
    #             urlnum=ithr.__next__().group()
                return int(maxpage)
            return 0
        def _get_outermaxpage(self,cont):
            soup=BeautifulSoup(cont,'html.parser',from_encoding='utf-8')
            lastpage=soup.find_all('a',href=re.compile(r'/f?.+'),class_='last')
            print('lastpage=',lastpage)
            return self._get_page(lastpage)
        
        
        ''' 传入 :outis  ['<a href="/p/4078134144?pn=2">2</a><a href="/p/4078134144?pn=3">3</a><a href="/p/4078134144?pn=4">4</a><a href="/p/4078134144?pn=5">5</a><a href="/p/4078134144?pn=2">下一页</a><a href="/p/4078134144?pn=5">尾页</a>']
                得到 :5 '''
        def _get_innermaxpage(self,cont):
            psoup=BeautifulSoup(cont,'html.parser',from_encoding='utf-8')
            allpages=psoup.find_all('li',class_="l_pager pager_theme_4 pb_list_pager")
            return self._get_page(allpages)
        
        '''得到所有此页图片的链接'''
        def _get_piclinks(self,cont):
           #爬取每个标题内容
            psoup=BeautifulSoup(cont,'html.parser',from_encoding='utf-8')
            #找到图片
    #        '''<img class="BDE_Image" height="198" width="198" size="5002" src="http://imgsrc.baidu.com/forum/w%3D580/sign=44e7872b8d82b9013dadc33b438da97e/82d63af33a87e950be315f6017385343faf2b4ea.jpg" style="cursor: url("http://tb2.bdstatic.com/tb/static-pb/img/cur_zin.cur"), pointer;">'''
            piclinks=psoup.find_all('img',src=re.compile(r"http://imgsrc.baidu.com.+"),class_="BDE_Image")
            picurls=set()
            #开始获取所有图片地址
            for piclink in piclinks:
                picurls.add(piclink['src'])
    #         print(piclink['src'])
            return picurls
            

    图片下载

    tieba_picdownloader.py

    #coding:utf8
    '''
    Created on 2016年1月26日
    
    
    @author: thewindkee
    '''
    import os
    import urllib.request
    from tieba import tieba_htmldownloader
    class PicDownloader(object):
            
        def save(self,url,folder,savename,savetype):
            #folder为要保存的文件夹,savename+savetype组成文件名.类型
            #保存文件时候注意类型要匹配,如要保存的图片为jpg,则打开的文件的名称必须是jpg格式,否则会产生无效图片
            path=os.getcwd()+'\%s'%(folder)
            if  not os.path.exists(path): 
                os.makedirs(folder)
            pwd=''.join([path,'\',savename,savetype])
    #         pwd=''.join([os.getcwd(),'\',savename,savetype])
            cont=tieba_htmldownloader.HtmlDownloader().download(url)
            if cont is None:
                print(cont)
                return
            file = open(pwd,'wb')
            file.write(cont)
            file.close()
            print('Pic Saved!') 
            

    链接管理

    url_manager.py

    # coding:utf8
    class UrlManager(object):
        def __init__(self):
            self.new_urls=set()
            self.old_urls=set()
            
        def add_new_url(self,url):
            if url is None:
                return
            if url not in self.new_urls and url not in self.old_urls:
                self.new_urls.add(url)
            
        def add_new_urls(self,urls):
            if urls is None or len(urls)==0:
                return
            for url in urls:
                self.add_new_url(url)
        def has_new_url(self):
            return len(self.new_urls)!=0
        
        def get_new_url(self):
            new_url=self.new_urls.pop()
            self.old_urls.add(new_url)
            return new_url
        

    抓出来的效果:

    水平有限,请见谅。。写了第一篇博客,作为纪念。

    PS:我最多爬了一千多个标题。而且其中有个贴吧,爬到图中的时候,突然在下载-保存图片的过程中停了下来,

    但是控制台没有停,曾经以为是打开没有打开网页,导致后续动作停止,如果有知道的望告知。

    2016-8-22 , 原因是没有设置timeout ,写成下面的就好了,超时会报异常。

    with request.urlopen(url,timeout=10)as resp:

  • 相关阅读:
    关于List,Set,Map集合的遍历方法
    关于内部类的了解
    for循环打印图形的详细解析(三角形)
    SSH 免密码登录
    foreach 和 for 的区别
    有关Java集合的区别,和常用方法的总结
    hadoop完全分布式的安装
    对于Oracle analyze table的使用总结 . 对于Oracle analyze table的使用总结 .
    Oracle执行计划与统计信息的一些总结
    oracle10g 统计信息查看、收集
  • 原文地址:https://www.cnblogs.com/thewindkee/p/12873295.html
Copyright © 2020-2023  润新知