编写Python脚本抓取网络小说来制作自己的阅读器


Posted in Python onAugust 20, 2015

你是否苦恼于网上无法下载的“小说在线阅读”内容?或是某些文章的内容让你很有收藏的冲动,却找不到一个下载的链接?是不是有种自己写个程序把全部搞定的冲动?是不是学了 python,想要找点东西大展拳脚,告诉别人“哥可是很牛逼的!”?那就让我们开始吧! 哈哈~
    好吧,我就是最近写 Yii 写多了,想找点东西调剂一下.... = =

    本项目以研究为目的,所有版权问题我们都是站在作者的一边,以看盗版小说为目的的读者们请自行面壁!
    说了这么多,我们要做的就是把小说正文的内容从网页上爬下来,我们的研究对象是全本小说网....再次声明,不对任何版权负责....
    一开始先做最基础的内容,就是把某一章的内容抓取下来。

    环境:Ubuntu, Python 2.7

    基础知识
    这个程序涉及到的知识点有几个,在这里列出来,不详细讲,有疑问的直接百度会有一堆的。
    1.urllib2 模块的 request 对像来设置 HTTP 请求,包括抓取的 url,和伪装浏览器的代理。然后就是 urlopen 和 read 方法,都很好理解。
    2.chardet 模块,用于检测网页的编码。在网页上抓取数据很容易遇到乱码的问题,为了判断网页是 gtk 编码还是 utf-8 ,所以用 chardet 的 detect 函数进行检测。在用 Windows 的同学可以在这里 http://download.csdn.net/detail/jcjc918/8231371 下载,解压到 python 的 lib 目录下就好。
    3. decode 函数将字符串从某种编码转为 unicode 字符,而 encode 把 unicode 字符转为指定编码格式的字符串。
     4. re 模块正则表达式的应用。search 函数可以找到和正则表达式对应匹配的一项,而 replace 则是把匹配到的字符串替换。

    思路分析:
    我们选取的 url 是 http://www.quanben.com/xiaoshuo/0/910/59302.html,斗罗大陆的第一章。你可以查看网页的源代码,会发现只有一个 content 标签包含了所有章节的内容,所以可以用正则把 content 的标签匹配到,抓取下来。试着把这一部分内容打印出来,会发现很多 <br /> 和  ,<br /> 要替换成换行符,   是网页中的占位符,即空格,替换成空格就好。这样一章的内容就很美观的出来了。完整起见,同样用正则把标题爬下来。

    程序

# -*- coding: utf-8 -*- 
 
import urllib2 
import re 
import chardet 
 
 
class Book_Spider: 
 
  def __init__(self): 
    self.pages = [] 
 
  # 抓取一个章节 
  def GetPage(self): 
    myUrl = "http://www.quanben.com/xiaoshuo/0/910/59302.html"; 
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 
    headers = { 'User-Agent' : user_agent } 
    request = urllib2.Request(myUrl, headers = headers) 
    myResponse = urllib2.urlopen(request) 
    myPage = myResponse.read() 
 
    #先检测网页的字符编码,最后统一转为 utf-8 
    charset = chardet.detect(myPage) 
    charset = charset['encoding'] 
    if charset == 'utf-8' or charset == 'UTF-8': 
      myPage = myPage 
    else: 
      myPage = myPage.decode('gb2312','ignore').encode('utf-8') 
    unicodePage = myPage.decode("utf-8") 
 
    try: 
      #抓取标题 
      my_title = re.search('<h1>(.*?)</h1>',unicodePage,re.S) 
      my_title = my_title.group(1) 
    except: 
      print '标题 HTML 变化,请重新分析!' 
      return False 
     
    try: 
      #抓取章节内容 
      my_content = re.search('<div.*?id="htmlContent" class="contentbox">(.*?)<div',unicodePage,re.S) 
      my_content = my_content.group(1) 
    except: 
      print "内容 HTML 变化,请重新分析!" 
      return False 
     
    #替换正文中的网页代码 
    my_content = my_content.replace("<br />","\n") 
    my_content = my_content.replace(" "," ") 
 
    #用字典存储一章的标题和内容 
    onePage = {'title':my_title,'content':my_content} 
    return onePage 
 
 
  # 用于加载章节 
  def LoadPage(self): 
    try: 
      # 获取新的章节 
      myPage = self.GetPage() 
       
      if myPage == False: 
        print '抓取失败!' 
        return False 
       
      self.pages.append(myPage) 
    except: 
      print '无法连接服务器!' 
 
  #显示一章 
  def ShowPage(self,curPage): 
      print curPage['title'] 
      print curPage['content'] 
 
  def Start(self): 
    print u'开始阅读......\n' 
    #把这一页加载进来 
    self.LoadPage() 
    # 如果self的pages数组中存有元素 
    if self.pages: 
      nowPage = self.pages[0] 
      self.ShowPage(nowPage) 
 
 
#----------- 程序的入口处 ----------- 
print u""" 
--------------------------------------- 
  程序:阅读呼叫转移 
  版本:0.1 
  作者:angryrookie 
  日期:2014-07-05 
  语言:Python 2.7 
  功能:按下回车浏览章节 
--------------------------------------- 
""" 
 
print u'请按下回车:' 
raw_input() 
myBook = Book_Spider() 
myBook.Start()

程序运行完在我这里可是很好看的,不信请看:^_^

编写Python脚本抓取网络小说来制作自己的阅读器

理所当然地,接下来我们要把整本小说都爬下来。首先,我们要把程序从原来的读完一章就结束,改成读完一章之后可以继续进行下一章的阅读。
    注意到每个小说章节的网页下面都有下一页的链接。通过查看网页源代码,稍微整理一下(  不显示了),我们可以看到这一部分的 HTML 是下面这种格式的:

<div id="footlink"> 
 <script type="text/javascript" charset="utf-8" src="/scripts/style5.js"></script> 
 <a href="http://www.quanben.com/xiaoshuo/0/910/59301.html">上一页</a>   
 <a href="http://www.quanben.com/xiaoshuo/0/910/">返回目录</a>   
 <a href="http://www.quanben.com/xiaoshuo/0/910/59303.html">下一页</a> 
</div>

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

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

     源代码:

# -*- coding: utf-8 -*- 
 
import urllib2 
import re 
import thread 
import chardet 
 
class Book_Spider: 
 
  def __init__(self): 
    self.pages = [] 
    self.page = 1 
    self.flag = True 
    self.url = "http://www.quanben.com/xiaoshuo/10/10412/2095096.html" 
 
  # 将抓取一个章节 
  def GetPage(self): 
    myUrl = self.url 
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 
    headers = { 'User-Agent' : user_agent } 
    req = urllib2.Request(myUrl, headers = headers) 
    myResponse = urllib2.urlopen(req) 
    myPage = myResponse.read() 
 
    charset = chardet.detect(myPage) 
    charset = charset['encoding'] 
    if charset == 'utf-8' or charset == 'UTF-8': 
      myPage = myPage 
    else: 
      myPage = myPage.decode('gb2312','ignore').encode('utf-8') 
    unicodePage = myPage.decode("utf-8") 
 
    # 找出 id="content"的div标记 
    try: 
      #抓取标题 
      my_title = re.search('<h1>(.*?)</h1>',unicodePage,re.S) 
      my_title = my_title.group(1) 
    except: 
      print '标题 HTML 变化,请重新分析!' 
      return False 
     
    try: 
      #抓取章节内容 
      my_content = re.search('<div.*?id="htmlContent" class="contentbox">(.*?)<div',unicodePage,re.S) 
      my_content = my_content.group(1) 
    except: 
      print "内容 HTML 变化,请重新分析!" 
      return False 
     
    my_content = my_content.replace("<br />","\n") 
    my_content = my_content.replace(" "," ") 
 
    #用字典存储一章的标题和内容 
    onePage = {'title':my_title,'content':my_content} 
 
    try: 
      #找到页面下方的连接区域 
      foot_link = re.search('<div.*?class="chapter_Turnpage">(.*?)</div>',unicodePage,re.S) 
      foot_link = foot_link.group(1) 
      #在连接的区域找下一页的连接,根据网页特点为第三个 
      nextUrl = re.findall(u'<a.*?href="(.*?)".*?>(.*?)</a>',foot_link,re.S) 
      nextUrl = nextUrl[2][0] 
      # 更新下一次进行抓取的链接 
      self.url = nextUrl 
    except: 
      print "底部链接变化,请重新分析!" 
      return False 
 
    return onePage 
 
  # 用于加载章节 
  def LoadPage(self): 
    while self.flag: 
      if(len(self.pages) - self.page < 3): 
        try: 
          # 获取新的页面 
          myPage = self.GetPage() 
 
          if myPage == False: 
            print '抓取失败!' 
            self.flag = False 
       
          self.pages.append(myPage) 
        except: 
          print '无法连接网页!' 
          self.flag = False 
 
  #显示一章 
  def ShowPage(self,curPage): 
      print curPage['title'] 
      print curPage['content'] 
      print "\n" 
      user_input = raw_input("当前是第 %d 章,回车读取下一章或者输入 quit 退出:" % self.page) 
      if(user_input == 'quit'): 
        self.flag = False 
      print "\n" 
 
  def Start(self): 
    print u'开始阅读......\n' 
 
    # 新建一个线程 
    thread.start_new_thread(self.LoadPage,()) 
 
    # 如果self的page数组中存有元素 
    while self.flag: 
      if self.page <= len(self.pages): 
        nowPage = self.pages[self.page-1] 
        self.ShowPage(nowPage) 
        self.page += 1 
 
    print u"本次阅读结束" 
 
 
#----------- 程序的入口处 ----------- 
print u""" 
--------------------------------------- 
  程序:阅读呼叫转移 
  版本:0.2 
  作者:angryrookie 
  日期:2014-07-07 
  语言:Python 2.7 
  功能:按下回车浏览下一章节 
--------------------------------------- 
""" 
 
print u'请按下回车:' 
raw_input(' ') 
myBook = Book_Spider() 
myBook.Start()

现在这么多小说阅读器,我们只需要把我们要的小说抓取到本地的 txt 文件里就好了,然后自己选个阅读器看,怎么整都看你了。

    其实上个程序我们已经完成了大部分逻辑,我们接下来的改动只需要把抓取到每一章的时候不用显示出来,而是存入 txt 文件之中。另外一个是程序是不断地根据下一页的 Url 进行抓取的,那么什么时候结束呢?注意当到达小说的最后一章时下一页的链接是和返回目录的链接是一样的。所以我们抓取一个网页的时候就把这两个链接拿出来,只要出现两个链接一样的时候,就停止抓取。最后就是我们这个程序不需要多线程了,我们只要一个不断在抓取小说页面的线程就行了。
    不过,小说章节多一点时候,等待完成的时间会有点久。目前就不考虑这么多了,基本功能完成就 OK....

    基础知识:前面的基础知识 - 多线程知识 + 文件操作知识。

     源代码:

# -*- coding:utf-8 -*- 
 
import urllib2 
import urllib 
import re 
import thread 
import chardet 
 
class Book_Spider: 
 
  def __init__(self): 
    self.pages = [] 
    self.page = 1 
    self.flag = True 
    self.url = "http://www.quanben.com/xiaoshuo/0/910/59302.html" 
 
  # 将抓取一个章节 
  def GetPage(self): 
    myUrl = self.url 
    user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)' 
    headers = { 'User-Agent' : user_agent } 
    req = urllib2.Request(myUrl, headers = headers) 
    myResponse = urllib2.urlopen(req) 
    myPage = myResponse.read() 
 
    charset = chardet.detect(myPage) 
    charset = charset['encoding'] 
    if charset == 'utf-8' or charset == 'UTF-8': 
      myPage = myPage 
    else: 
      myPage = myPage.decode('gb2312','ignore').encode('utf-8') 
    unicodePage = myPage.decode("utf-8") 
 
    # 找出 id="content"的div标记 
    try: 
      #抓取标题 
      my_title = re.search('<h1>(.*?)</h1>',unicodePage,re.S) 
      my_title = my_title.group(1) 
    except: 
      print '标题 HTML 变化,请重新分析!' 
      return False 
     
    try: 
      #抓取章节内容 
      my_content = re.search('<div.*?id="htmlContent" class="contentbox">(.*?)<div',unicodePage,re.S) 
      my_content = my_content.group(1) 
    except: 
      print "内容 HTML 变化,请重新分析!" 
      return False 
     
    my_content = my_content.replace("<br />","\n") 
    my_content = my_content.replace(" "," ") 
 
    #用字典存储一章的标题和内容 
    onePage = {'title':my_title,'content':my_content} 
 
    try: 
      #找到页面下方的连接区域 
      foot_link = re.search('<div.*?class="chapter_Turnpage">(.*?)</div>',unicodePage,re.S) 
      foot_link = foot_link.group(1) 
      #在连接的区域找下一页的连接,根据网页特点为第三个 
      nextUrl = re.findall(u'<a.*?href="(.*?)".*?>(.*?)</a>',foot_link,re.S) 
      #目录链接 
      dir_url = nextUrl[1][0] 
      nextUrl = nextUrl[2][0] 
      # 更新下一次进行抓取的链接 
      self.url = nextUrl 
 
      if(dir_url == nextUrl): 
        self.flag = False 
         
      return onePage 
    except: 
      print "底部链接变化,请重新分析!" 
      return False 
 
  # 用于加载章节 
  def downloadPage(self): 
 
    f_txt = open(u"斗罗大陆.txt",'w+') 
    while self.flag: 
      try: 
        # 获取新的页面 
        myPage = self.GetPage() 
         
        if myPage == False: 
            print '抓取失败!' 
            self.flag = False 
 
        title = myPage['title'].encode('utf-8') 
        content = myPage['content'].encode('utf-8') 
 
        f_txt.write(title + '\n\n') 
        f_txt.write(content) 
        f_txt.write('\n\n\n') 
 
        print "已下载 ",myPage['title'] 
 
      except: 
        print '无法连接服务器!' 
        self.flag = False 
         
    f_txt.close() 
 
  def Start(self): 
    print u'开始下载......\n' 
 
    self.downloadPage() 
 
 
    print u"下载完成" 
 
 
#----------- 程序的入口处 ----------- 
print u""" 
--------------------------------------- 
  程序:阅读呼叫转移 
  版本:0.3 
  作者:angryrookie 
  日期:2014-07-08 
  语言:Python 2.7 
  功能:按下回车开始下载 
--------------------------------------- 
""" 
 
print u'请按下回车:' 
raw_input(' ') 
myBook = Book_Spider() 
myBook.Start()

编写Python脚本抓取网络小说来制作自己的阅读器

Python 相关文章推荐
Python抓取Discuz!用户名脚本代码
Dec 30 Python
使用Python操作MySQL的一些基本方法
Aug 16 Python
Python中最大最小赋值小技巧(分享)
Dec 23 Python
python语音识别实践之百度语音API
Aug 30 Python
python使用多进程的实例详解
Sep 19 Python
python绘制直方图和密度图的实例
Jul 08 Python
Python+AutoIt实现界面工具开发过程详解
Aug 07 Python
python 中的[:-1]和[::-1]的具体使用
Feb 13 Python
python-docx文件定位读取过程(尝试替换)
Feb 13 Python
Python是怎样处理json模块的
Jul 16 Python
python多线程方法详解
Jan 18 Python
Python matplotlib多个子图绘制整合
Apr 13 Python
使用Python求解最大公约数的实现方法
Aug 20 #Python
使用Python3编写抓取网页和只抓网页图片的脚本
Aug 20 #Python
详解Python3中yield生成器的用法
Aug 20 #Python
Python中集合的内建函数和内建方法学习教程
Aug 19 #Python
深入解析Python中的集合类型操作符
Aug 19 #Python
Python中的集合类型知识讲解
Aug 19 #Python
深入理解Python中字典的键的使用
Aug 19 #Python
You might like
ThinkPHP中处理表单中的注意事项
2014/11/22 PHP
浅谈json_encode用法
2015/03/05 PHP
使用Zttp简化Guzzle 调用
2017/07/02 PHP
Yii2框架实现登陆添加验证码功能示例
2018/07/12 PHP
PHP观察者模式定义与用法实例分析
2019/03/22 PHP
php实现获取近几日、月时间示例
2019/07/06 PHP
php解析非标准json、非规范json的方式实例
2020/12/10 PHP
PHP序列化和反序列化深度剖析实例讲解
2020/12/29 PHP
jQuery + Flex 通过拖拽方式动态改变图片的代码
2011/08/03 Javascript
js定时器的使用(实例讲解)
2014/01/06 Javascript
深入理解JavaScript系列(27):设计模式之建造者模式详解
2015/03/03 Javascript
JS实现超简单的汉字转拼音功能示例
2016/12/22 Javascript
JS简单生成随机数(随机密码)的方法
2017/05/11 Javascript
Angular项目如何升级至Angular6步骤全纪录
2018/09/03 Javascript
js中call()和apply()改变指针问题的讲解
2019/01/17 Javascript
vue组件定义,全局、局部组件,配合模板及动态组件功能示例
2019/03/19 Javascript
JS如何寻找数组中心索引过程解析
2020/06/01 Javascript
支付宝小程序实现省市区三级联动
2020/06/21 Javascript
解决vue组件销毁之后计时器继续执行的问题
2020/07/21 Javascript
解决vue安装less报错Failed to compile with 1 errors的问题
2020/10/22 Javascript
[49:05]OG vs Newbee 2019DOTA2国际邀请赛淘汰赛 胜者组 BO3 第二场 8.21.mp4
2020/07/19 DOTA
Python中的sort()方法使用基础教程
2017/01/08 Python
python使用生成器实现可迭代对象
2018/03/20 Python
python pandas库中DataFrame对行和列的操作实例讲解
2018/06/09 Python
python如何实现不可变字典inmutabledict
2020/01/08 Python
利用python实现逐步回归
2020/02/24 Python
Python统计学一数据的概括性度量详解
2020/03/03 Python
对python中return与yield的区别详解
2020/03/12 Python
使用卷积神经网络(CNN)做人脸识别的示例代码
2020/03/27 Python
如何用Python徒手写线性回归
2021/01/25 Python
Fresh馥蕾诗英国官网:法国LVMH集团旗下高端天然护肤品牌
2018/11/01 全球购物
应届生保险求职信
2013/11/11 职场文书
保护环境倡议书
2014/04/14 职场文书
省级青年文明号申报材料
2014/05/23 职场文书
2015年医院护理部工作总结
2015/04/23 职场文书
22句经典语录:送给优柔寡断和胡思乱想的朋友们
2019/12/13 职场文书