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

一直想写爬虫,也想学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:

 

posted @ 2016-01-28 14:57  thewindkee  阅读(207)  评论(0编辑  收藏  举报