zoukankan      html  css  js  c++  java
  • 23hh小说网——爬虫1.0python

    修改点:

    1. decode('gbk')修改为decode('gbk','replace'),在遇到不能识别的字符时直接用?替换

    2. 将原来的单线程变为多线程,现在处理速度快了好多

      1 #! /bin/python
      2 # -*- coding:utf-8 -*-
      3 
      4 # --------------------------------------------
      5 #     程序:【23hh小说网】爬虫
      6 #     版本:0.2.2
      7 #     作者:Silence
      8 #     日期:2014-04-08
      9 #     功能:1. 提供一个目录页,把这个小说的全部章节都抓取下来,保存为一个文件
     10 #         2. 提供一个正在看的目录页,把这个章节及以后的所有章节都抓取下来,
     11 #          3. 增加错误时的重试机制
     12 # ---------------------------------------------
     13 import threading
     14 import urllib2
     15 import re
     16 import os
     17 from Queue import Queue
     18 
     19 class Spider_Thread(threading.Thread):
     20     """单进程的爬网页很耗时的,所以考虑做成多进程的
     21         参数通过queue来传递"""
     22 
     23     def __init__(self, t_name,queue):
     24         threading.Thread.__init__(self ,name = t_name)
     25         self.data = queue
     26         self.errorInfo = {}
     27 
     28     def run(self):
     29         while self.data.qsize() > 0:
     30             pageInfo = self.data.get()
     31             print '线程%s正在爬第%d个页面'%(self.getName(),pageInfo.keys()[0])
     32             try:
     33                 self.novel = Novel_Tool(pageInfo.values()[0]['pageurl'],'N')
     34                 decodePageResp = self.novel.getDecodePage(pageInfo.values()[0]['pageurl'])
     35                 pageContent = self.novel.getPageContent(decodePageResp)
     36                 self.novel.writeToFile(pageContent,pageInfo.values()[0]['pagename'])
     37             except Exception,e:
     38                 print '爬第%d个页面时出错了' %pageInfo.keys()[0]
     39                 self.errorInfo[pageInfo.keys()[0]] = pageInfo.values()
     40                 pass
     41         if self.errorInfo.__len__() > 0:
     42             print '出错的页面信息有:
    ',self.errorInfo
     43         self.novel = None
     44 
     45         
     46 # 主要是用来提供一些公用的方法,不作为主函数调用
     47 class Novel_Tool():
     48 
     49     def __init__(self,weburl,saveAsOne):
     50         self.url = weburl
     51         self.headers = {
     52             'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'
     53         }
     54         self.saveAsOne = saveAsOne
     55         self.pagesInfo = {}
     56         self.errorPage = []
     57 
     58     #获取当前页面的编码格式,现在某些小说网喜欢用gbk来编码
     59     # 但是,也有一些不厚道的网站,他们的实际编码格式不是在页面中charset中定义的格式,暂时先忽略了
     60     def getPageType(self,content):
     61         pattern = re.compile('charset=.*?"')
     62         pagetype = pattern.search(content).group()
     63         pagetype = pagetype[8:len(pagetype) - 1]
     64         return pagetype
     65 
     66     def start(self):
     67         if novelUrl.find('html') > 0:
     68             self.spiderPagesFromCurrent()
     69         else:
     70             pageInfos = self.getAllUrlsAndNames()
     71             self.spiderAllPagesFromOne(pageInfos)
     72         self.doStat()
     73 
     74     def doStat(self):
     75         print '本次共尝试爬章节 %d,其中爬成功章节数 %d' %(self.pageInfos.__len__(),self.pageInfos.__len__() - self.errorPage.__len__())
     76         print '失败的章节信息为:',errorPage
     77 
     78     def retryErroePage(self,errorPages):
     79         print '准备重试错误页面中....'
     80         self.spiderAllPagesFromOne(errorPages)
     81 
     82     def spiderPagesFromCurrent(self):
     83         pageurl = self.url
     84         index = 1
     85         while pageurl.find('index.html') == -1:
     86             try:
     87                 decodePageResp = self.getDecodePage(pageurl)
     88                 pageContent = self.getPageContent(decodePageResp)
     89 
     90                 self.writeToFile(pageContent,self.getPageTitle(decodePageResp))
     91                 pageurl = self.getNextPage(decodePageResp)
     92             except Exception,e:
     93                 print '爬第%d个页面时出错了' %index
     94                 self.errorPage = {index:pageInfo['pageurl']}
     95                 pass
     96             finally:
     97                 index = index + 1
     98 
     99     # 依次遍历所有的章节,并爬下来
    100     def spiderAllPagesFromOne(self,pageInfo):
    101         for index,pageInfo in pageInfo.items():
    102             print '正在爬第%d个页面……'%index
    103             try:
    104                 decodePageResp = self.getDecodePage(pageInfo['pageurl'])
    105                 pageContent = self.getPageContent(decodePageResp)
    106                 self.writeToFile(pageContent,pageInfo['pagename'])
    107             except Exception,e:
    108                 print '爬第%d个页面时出错了' %index
    109                 self.errorPage = {index:pageInfo['pageurl']}
    110                 pass
    111 
    112     # 获取正文的标题            
    113     def getPageTitle(self,content):
    114         charToTitleRex = re.compile('h1>(.|s)*?</h1')
    115         pageTitle = charToTitleRex.search(content).group()
    116         pageTitle = pageTitle[3:len(pageTitle)-4]
    117         return pageTitle
    118 
    119     def writeToFile(self,content,filename):
    120         if os.path.exists(os.getcwd() + '/Novels'):
    121             if not os.path.isdir(os.getcwd() + '/Novels'):
    122                 os.rename('Novels','Novels.bak')
    123                 os.mkdir(os.getcwd() + '/Novels')
    124         else:
    125             os.mkdir(os.getcwd() + '/Novels')
    126 
    127         if self.saveAsOne == 'N':
    128             ofile = open(os.getcwd() + '/Novels/' + filename,'w')
    129         else:
    130             ofile = open(os.getcwd() + '/Novels/novel.txt','a')
    131 
    132         try:
    133             ofile.write(content)
    134         except Exception, e:
    135             print '存储网页',filename,'出错!'
    136             pass
    137         finally:
    138             ofile.close()
    139     
    140     def getDecodePage(self,pageurl):
    141         req = urllib2.Request(
    142             url = pageurl,
    143             headers = self.headers
    144             )
    145         # print pageInfo['pageurl']
    146         pageResponse = urllib2.urlopen(req).read()
    147         decodePageResp = pageResponse.decode(self.getPageType(pageResponse),'replace').encode('utf-8')
    148         return decodePageResp
    149 
    150         # 章节内容
    151 
    152     def getPageContent(self,decodePageResp):
    153         contentPattern = re.compile('(<dd id="contents">)((.|s)*?)(</dd>)')
    154         content = contentPattern.search(decodePageResp).group(2)
    155         content = self.replaceWebTag(content)
    156         return content    
    157 
    158     # 获取下一页的地址
    159     def getNextPage(self,content):
    160         # 先获取到下一页的位置
    161         footlinkRex = re.compile('(footlink">)(.*?)</dd>')
    162         foot = footlinkRex.search(content).group(2)
    163         pattern = re.compile(r'(返回目录.*?(<a.*?">下一页))')
    164         m = pattern.search(foot).groups()
    165         nextUrl = m[len(m)-1][9:m[len(m)-1].find('">')]
    166 
    167         return self.url[0:self.url.rfind('/')+1] + nextUrl
    168 
    169     def getAllUrlsAndNames(self):
    170         # 先请求目录页,获取所有的目录章节和链接
    171         req = urllib2.Request(
    172             url = self.url,
    173             headers = self.headers
    174         )
    175         myResponse = urllib2.urlopen(req).read()
    176         decodeResp = myResponse.decode(self.getPageType(myResponse)).encode('utf-8')
    177 
    178         print '正在分析目录页面,请稍后…………'
    179         pageRex = re.compile('<a href=".*?</td>') #定义获取所有章节页面链接的正则
    180         pageUrlRex = re.compile('".*?"')    #获取章节url的正则
    181         pageNameRex = re.compile('>.*?<')    #获取章节名字的正则
    182 
    183         pages = pageRex.findall(decodeResp)
    184         index = 1
    185         for page in pages:
    186             pageurl = pageUrlRex.search(page).group()
    187             pageurl = pageurl[1:len(pageurl) - 1]
    188             pageurl = self.url + pageurl
    189 
    190             pagename = pageNameRex.search(page).group()
    191             pagename = pagename[1:len(pagename) - 1]
    192 
    193             # print pagename + '     ' + pageurl
    194             self.pagesInfo[index] = {
    195                 'pagename' : pagename,
    196                 'pageurl' : pageurl
    197             }
    198             index = index + 1
    199         print '目录页分析完成!该小说共有%d个章节'%index
    200         return self.pagesInfo
    201 
    202     def getNovelName(self,content):
    203         titleRex = re.compile('<h1>.*?</h1>')
    204         title = titleRex.search(content).group()
    205         return title[4:len(title) - 5]
    206 
    207     def replaceWebTag(self,content):
    208         charToNoneRex = re.compile(r'&nbsp;')
    209         charToNewLineRex = re.compile("<br />|<br>|<br/>")
    210 
    211         content = charToNoneRex.sub("",content)
    212         content = charToNewLineRex.sub("
    ",content)
    213         return content
    214 
    215 if __name__ == '__main__':
    216     print u"""
    217 # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * #
    218 #     程序:【23hh小说网】爬虫                                                        #
    219 #     版本:1.0                                                                    #
    220 #     作者:Silence                                                                #
    221 #     日期:2014-04-08                                                                #
    222 #     操作:启动后输入要爬得小说目录页地址,就可以自动爬了                                #
    223 #     功能:1. 提供一个目录页,把目录页中所有的目录章节都抓出来(默认是:23hh的争霸天下);    #
    224 #         2. 提供一个正在看的目录页,把这个章节及以后的所有章节都抓取下来,                #
    225 #         分章节保存在启动脚本目录下的Novels目录下;                                    #
    226 #         如果该目录下已经有一个Novels,则把这个Novels改名为Novels.bak                    #
    227 # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * # * #"""
    228 
    229     novelUrl = raw_input('请输入要爬的小说地址(默认是:23hh的争霸天下章节目录)
    ')
    230     if novelUrl == '':
    231         novelUrl = 'http://www.23hh.com/book/43/43957/'
    232     elif novelUrl.find('html') > 0:
    233         novelUrl = novelUrl
    234 
    235     saveAsOne = raw_input('是否保存为一个文件?是为Y,否为N
    ')
    236     if saveAsOne not in ['Y','N']:
    237         saveAsOne = 'N'
    238 
    239     Novel = Novel_Tool(novelUrl,saveAsOne)
    240 
    241     if not novelUrl.find('html') > 0:
    242         queue = Queue()
    243         pageInfos = Novel.getAllUrlsAndNames()
    244         for key,value in pageInfos.items():
    245             queue.put({key:value})
    246 
    247         thread1 = Spider_Thread('thread1',queue)
    248         thread2 = Spider_Thread('thread2',queue)
    249         thread3 = Spider_Thread('thread3',queue)
    250         thread4 = Spider_Thread('thread4',queue)
    251         thread1.start()
    252         thread2.start()
    253         thread3.start()
    254         thread4.start()
    255     else:
    256         Novel.start()
  • 相关阅读:
    浏览器图片渲染优化教程
    C#把 DataTable转换为Model实体
    .NET静态变量与静态方法并发的问题
    三种方法查看MySQL数据库的版本
    Mysql 存储引擎 InnoDB与Myisam的主要区别
    dotnet core 出现Can not find runtime target for framework '.NETCoreApp,Version=v1.6' 的解决办法
    细说 C# 中的 IEnumerable和IEnumerator接口
    ASP.NET CORE dotnet run 命令使用debug方式运行
    安装了插件情况下 强制开启火狐浏览器的多进程功能
    JSON 字符串中的中括号和大括号区别详解
  • 原文地址:https://www.cnblogs.com/SilenceCity/p/3653248.html
Copyright © 2011-2022 走看看