一篇博客我们成功地从网页上爬下了小说的一个章节,理所当然地,接下来我们要把整本小说都爬下来。首先,我们要把程序从原来的读完一章就结束,改成读完一章之后可以继续进行下一章的阅读。

    注意到每个小说章节的网页下面都有下一页的链接。通过查看网页源代码,稍微整理一下(  不显示了),我们可以看到这一部分的 HTML 是下面这种格式的:

[html]  view plain copy
  1. <div id="footlink">  
  2.   <script type="text/javascript" charset="utf-8" src="/scripts/style5.js"></script>  
  3.   <a href="http://www.quanben.com/xiaoshuo/0/910/59301.html">上一页</a>      
  4.   <a href="http://www.quanben.com/xiaoshuo/0/910/">返回目录</a>      
  5.   <a href="http://www.quanben.com/xiaoshuo/0/910/59303.html">下一页</a>  
  6. </div>  

     上一页  返回目录 下一页 都在一个 id 为 footlink  的 div 中,如果想要对每个链接进行匹配的话,会抓取到网页上大量的其他链接,但是 footlink 的 div 只有一个啊!我们可以把这个 div 匹配到,抓下来,然后在这个抓下来的 div  里面再匹配 <a> 的链接,这时就只有三个了。只要取最后一个链接就是下一页的 url 的,用这个 url 更新我们抓取的目标 url ,这样就能一直抓到下一页。用户阅读逻辑为每读一个章节后,等待用户输入,如果是 quit 则退出程序,否则显示下一章。


     基础知识:

     上一篇的基础知识加上 Python 的 thread 模块.


     源代码:

[python]  view plain copy
  1. # -*- coding: utf-8 -*-  
  2.   
  3. import urllib2  
  4. import re  
  5. import thread  
  6. import chardet  
  7.   
  8. class Book_Spider:  
  9.   
  10.     def __init__(self):  
  11.         self.pages = []  
  12.         self.page = 1  
  13.         self.flag = True  
  14.         self.url = "http://www.quanben.com/xiaoshuo/10/10412/2095096.html"  
  15.   
  16.     # 将抓取一个章节  
  17.     def GetPage(self):  
  18.         myUrl = self.url  
  19.         user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'  
  20.         headers = { 'User-Agent' : user_agent }  
  21.         req = urllib2.Request(myUrl, headers = headers)  
  22.         myResponse = urllib2.urlopen(req)  
  23.         myPage = myResponse.read()  
  24.   
  25.         charset = chardet.detect(myPage)  
  26.         charset = charset['encoding']  
  27.         if charset == 'utf-8' or charset == 'UTF-8':  
  28.             myPage = myPage  
  29.         else:  
  30.             myPage = myPage.decode('gb2312','ignore').encode('utf-8')  
  31.         unicodePage = myPage.decode("utf-8")  
  32.   
  33.         # 找出 id="content"的div标记  
  34.         #抓取标题  
  35.         my_title = re.search('<div.*?id="title"><h1>(.*?)</h1></div>',unicodePage,re.S)  
  36.         my_title = my_title.group(1)  
  37.         #抓取章节内容  
  38.         my_content = re.search('<div.*?id="content">(.*?)</div>',unicodePage,re.S)  
  39.         my_content = my_content.group(1)  
  40.         my_content = my_content.replace("<br />","\n")  
  41.         my_content = my_content.replace(" "," ")  
  42.   
  43.         #用字典存储一章的标题和内容  
  44.         onePage = { 'title':my_title,'content':my_content}  
  45.   
  46.         #找到页面下方的连接区域  
  47.         foot_link = re.search('<div.*?id="footlink">(.*?)</div>',unicodePage,re.S)  
  48.         foot_link = foot_link.group(1)  
  49.         #在连接的区域找下一页的连接,根据网页特点为第三个  
  50.         nextUrl = re.findall(u'<a.*?href="(.*?)">(.*?)</a>',foot_link,re.S)  
  51.         nextUrl = nextUrl[2][0]  
  52.         # 更新下一次进行抓取的链接  
  53.         self.url = nextUrl  
  54.   
  55.         return onePage  
  56.   
  57.     # 用于加载章节  
  58.     def LoadPage(self):  
  59.         while self.flag:  
  60.             if(len(self.pages) - self.page < 3):  
  61.                 try:  
  62.                     # 获取新的页面  
  63.                     myPage = self.GetPage()  
  64.                     self.pages.append(myPage)  
  65.                 except:  
  66.                     print '无法连接网页!'  
  67.   
  68.     #显示一章  
  69.     def ShowPage(self,curPage):  
  70.             print curPage['title']  
  71.             print curPage['content']  
  72.             print "\n"  
  73.             user_input = raw_input("当前是第 %d 章,回车读取下一章或者输入 quit 退出:" % self.page)  
  74.             if(user_input == 'quit'):  
  75.                 self.flag = False  
  76.             print "\n"  
  77.   
  78.     def Start(self):  
  79.         print u'开始阅读......\n'  
  80.   
  81.         # 新建一个线程  
  82.         thread.start_new_thread(self.LoadPage,())  
  83.   
  84.         # 如果self的page数组中存有元素  
  85.         while self.flag:  
  86.             if self.page <= len(self.pages):  
  87.                 nowPage = self.pages[self.page-1]  
  88.                 self.ShowPage(nowPage)  
  89.                 self.page += 1  
  90.   
  91.         print u"本次阅读结束"  
  92.   
  93.   
  94. #----------- 程序的入口处 -----------  
  95. print u""" 
  96. --------------------------------------- 
  97.    程序:阅读呼叫转移 
  98.    版本:0.2 
  99.    作者:angryrookie 
  100.    日期:2014-07-07 
  101.    语言:Python 2.7 
  102.    功能:按下回车浏览下一章节 
  103. --------------------------------------- 
  104. """  
  105.   
  106. print u'请按下回车:'  
  107. raw_input(' ')  
  108. myBook = Book_Spider()  
  109. myBook.Start()  

<dl class="digg digg&#95;enable" id="btnDigg" style="display&#58;inline&#45;block&#59; float&#58;left&#59; width&#58;72px&#59; height&#58;72px&#59; overflow&#58;hidden&#59; margin&#58;0px 2px&#59; background&#58;rgb&#40;255&#44;121&#44;0&#41;"> 顶 <dd style="margin&#58;0px&#59; color&#58;rgb&#40;255&#44;255&#44;255&#41;&#59; line&#45;height&#58;22px&#59; font&#45;family&#58;Arial"> 1 </dd> </dl> <dl class="digg digg&#95;enable" id="btnBury" style="display&#58;inline&#45;block&#59; float&#58;left&#59; width&#58;72px&#59; height&#58;72px&#59; overflow&#58;hidden&#59; margin&#58;0px 2px&#59; background&#58;rgb&#40;255&#44;121&#44;0&#41;"> 踩 </dl>