再写一个用BeautifulSoup抓站的工具,体会BeautifulSoup的强大。

根据小说索引页获取小说全部章节内容并在本地整合为小说全文。不过不是智能的,不同的站点对代码需要做相应的修改。

  1. #!/usr/bin/env python
  2.  
  3. import os
  4. import sys
  5. import re
  6. import time
  7. import chardet
  8. import urllib.request as ur
  9. from urllib.parse import urljoin,urlparse
  10. from bs4 import BeautifulSoup
  11. from threading import Thread
  12.  
  13. class Download(Thread): #为每个章节分配多线程
  14. def __init__(self,filepath,info):
  15. Thread.__init__(self)
  16. self.filepath = filepath
  17. (self.link,self.chapter) = info
  18.  
  19. def run(self):
  20. print('开始下载: '+self.chapter)
  21. section(self.filepath,self.chapter,self.link)
  22. print('完成下载: '+self.chapter)
  23.  
  24. def getData(url): #主要用于判断页面编码,但是发现BeautifulSoup自带判定能力,故废弃此函数
  25. charsets = 'utf8'
  26. response = ur.urlopen(url,timeout = 10)
  27. html = response.read()
  28. charinfo = chardet.detect(html)
  29. charsets = charinfo['encoding']
  30. data = html.decode(charsets)
  31. return data
  32.  
  33. def merge(tmpFiles,targetFile): #将下载的章节合并
  34. for tmpFile in tmpFiles:
  35. with open(targetFile,'a+') as wfile:
  36. wfile.write(open(tmpFile,'r').read())
  37. os.remove(tmpFile)
  38.  
  39. def content(link): #获取章节页面的小说内容。对于不同的站点,在此函数内修改获取章节内容的代码
  40. html = ur.urlopen(link,timeout = 10)
  41. soup =BeautifulSoup(html)
  42. contents = soup.find(id = 'readtext').p.span.text.replace('  ','\n') #BeautifulSoup会自动将&nbsp;转换为空格,<br/>转换为特殊符号
  43. return contents
  44.  
  45. def section(filepath,chapter,link): #下载章节内容
  46. while True: #反复请求页面
  47. try:
  48. with open(filepath,'w') as nfile:
  49. nfile.write(chapter+'\n'+content(link)+'\n')
  50. break
  51. except:
  52. pass
  53.  
  54. def index(url): #获取章节索引
  55. indexs = []
  56. while True: #反复请求页面
  57. try:
  58. html = ur.urlopen(url,timeout = 10)
  59. #html = html.read().decode('gb2312')
  60. #html = getData(url)
  61. soup = BeautifulSoup(html,from_encoding = 'gbk')#BeautifulSoup能自动识别编码,但是会将gbk页面识别为gb2312页面,可能导致页面内部分数据获取失败,故显式指定
  62. break
  63. except:
  64. pass
  65. title = soup.find(name = 'div',attrs = {'class':'booktext'}).text
  66. indexDiv = soup.find(name = 'div',attrs = {'class':'booktext'})
  67. indexUl = [ul for ul in indexDiv.find_all('ul') if ul][1:]
  68. for ul in indexUl:
  69. indexList = [li.a for li in ul.find_all('li') if li]
  70. index = [(urljoin(url,a.get('href')),a.text) for a in indexList if a]
  71. indexs +=index
  72. return indexs
  73.  
  74. def novel(url):
  75. tmpFiles = []
  76. tasks = []
  77. try:
  78. indexs = index(url)
  79. tmpDir = os.path.join(os.getcwd(),'tmp')
  80. if not os.path.exists(tmpDir): #创建章节片段存放的临时目录
  81. os.mkdir(tmpDir)
  82. for i,info in enumerate(indexs):
  83. tmpFile = os.path.join(tmpDir,str(i))
  84. tmpFiles.append(tmpFile)
  85. task = Download(tmpFile,info) #开启新线程下载章节内容
  86. task.setDaemon(True)
  87. task.start()
  88. tasks.append(task)
  89. if len(tasks) >= 20: #将线程总数控制在20个以内,如果线程过多会导致程序崩溃
  90. while len([task for task in tasks if task.isAlive()]):
  91. print( '进度: {} / {}'.format(i+1-len([task for task in tasks if task.isAlive()]),len(indexs))) #显示下载进度
  92. time.sleep(2)
  93. tasks = []
  94. if i == len(indexs) - 1:
  95. while len([task for task in tasks if task.isAlive()]):
  96. print( '进度: {} / {}'.format(len(indexs) - len([task for task in tasks if task.isAlive()]),len(indexs)))
  97. time.sleep(2)
  98. print( '进度: {} / {}'.format(len(indexs),len(indexs)))
  99. print('开始整合......')
  100. merge(tmpFiles,os.path.join(os.getcwd(),title+'.txt'))
  101. print('下载成功!')
  102. except Exception as ex:
  103. print(ex)
  104. print('下载失败!')
  105. sys.exit()
  106. def main(argv):
  107. try:
  108. novel(argv[0])
  109. except KeyboardInterrupt as kbi: #使用<C-c>中断下载后仍然能将已下载的章节合并
  110. tmpDir = os.path.join(os.getcwd(),'tmp')
  111. if os.path.exists(tmpDir):
  112. tmpFiles = [os.path.join(tmpDir,tfile) for tfile in os.listdir(tmpDir) if os.path.isfile(os.path.join(tmpDir,tfile))]
  113. print('开始整合不完整的下载......')
  114. try:
  115. merge(tmpFiles,os.path.join(os.getcwd(),'不完整文档.txt'))
  116. if os.path.exists(os.path.join(os.getcwd(),'不完整文档.txt')):
  117. print('部分章节下载成功!')
  118. else:
  119. print('下载失败!')
  120. except:
  121. print('下载失败!')
  122. sys.exit()
  123. os.rmdir(tmpDir)
  124. else:
  125. print('下载失败!')
  126. sys.exit()
  127. if os.path.exists(os.path.join(os.getcwd(),'tmp')):
  128. os.rmdir(os.path.join(os.getcwd(),'tmp'))
  129.  
  130. if __name__ == "__main__":
  131. if len(sys.argv) > 1:
  132. main(sys.argv[1:])
  133. #http://www.lueqiu.com/

截图:

Python3利用BeautifulSoup4抓取站点小说全文的代码的更多相关文章

  1. python3.4学习笔记(十七) 网络爬虫使用Beautifulsoup4抓取内容

    python3.4学习笔记(十七) 网络爬虫使用Beautifulsoup4抓取内容 Beautiful Soup 是用Python写的一个HTML/XML的解析器,它可以很好的处理不规范标记并生成剖 ...

  2. 对比使用Charles和Fiddler两个工具及利用Charles抓取https数据(App)

    对比使用Charles和Fiddler两个工具及利用Charles抓取https数据(App) 实验目的:对比使用Charles和Fiddler两个工具 实验对象:车易通App,易销通App 实验结果 ...

  3. 利用Crowbar抓取网页异步加载的内容 [Python俱乐部]

    利用Crowbar抓取网页异步加载的内容 [Python俱乐部] 利用Crowbar抓取网页异步加载的内容 在做 Web 信息提取.数据挖掘的过程中,一个关键步骤就是网页源代码的获取.但是出于各种原因 ...

  4. 利用Fiddler抓取websocket包

    一.利用fiddler抓取websockt包 打开Fiddler,点开菜单栏的Rules,选择Customize Rules... 这时会打开CustomRules.js文件,在class Handl ...

  5. Python3.x:抓取百事糗科段子

    Python3.x:抓取百事糗科段子 实现代码: #Python3.6 获取糗事百科的段子 import urllib.request #导入各类要用到的包 import urllib import ...

  6. Python爬虫实战八之利用Selenium抓取淘宝匿名旺旺

    更新 其实本文的初衷是为了获取淘宝的非匿名旺旺,在淘宝详情页的最下方有相关评论,含有非匿名旺旺号,快一年了淘宝都没有修复这个. 可就在今天,淘宝把所有的账号设置成了匿名显示,SO,获取非匿名旺旺号已经 ...

  7. 利用wireshark抓取远程linux上的数据包

    原文发表在我的博客主页,转载请注明出处. 前言 因为出差,前后准备总结了一周多,所以博客有所搁置.出差真是累人的活计,不过确实可以学习到很多东西,跟着老板学习做人,学习交流的技巧.入正题~ wires ...

  8. 利用wget 抓取 网站网页 包括css背景图片

    利用wget 抓取 网站网页 包括css背景图片 wget是一款非常优秀的http/ftp下载工具,它功能强大,而且几乎所有的unix系统上都有.不过用它来dump比较现代的网站会有一个问题:不支持c ...

  9. Python3利用BeautifulSoup4批量抓取站点图片的代码

    边学边写代码,记录下来.这段代码用于批量抓取主站下所有子网页中符合特定尺寸要求的的图片文件,支持中断. 原理很简单:使用BeautifulSoup4分析网页,获取网页<a/>和<im ...

随机推荐

  1. 关于python中文件导入的若干问题

    __init__文件 同一级目录下直接import导入就可以了,如果是在不同的目录下面被导入文件的文件夹下面必须有__init__.py文件,即使这个文件是空的也可以.当然这个文件也可以初始一些数据 ...

  2. ajax传递数组到后台

    //实体类 public class Person { private int ID{get;set;} private string Name{get;set;} private int Age{g ...

  3. Bay Trail平板安装Ubuntu ThinkPad 8(20BNA00RCD)

    首先说下没搞定的事项: 1,grub不能启动w10 2,ubuntu不能检测到无线网卡硬件 3,ubuntu更新软件和语言支持时,unpacking文件时经常都会发生卡死(怀疑是emmc驱动的问题) ...

  4. linux 时间同步

    [转自 qing_gee的专栏 :http://blog.csdn.net/qing_gee/article/details/42234997 ] 前言:在我们的项目中,需要同步Linux服务器的时间 ...

  5. [转]:C#的ToString如何格式化字符串

    C 货币 2.5.ToString("C") ¥2.50 D 十进制数 25.ToString("D5") 00025 E 科学型 25000.ToString ...

  6. CentOS 7 下的LAMP实现以及基于https的虚拟主机

    系统环境:CentOS 7Apache 2.4php 5.4MariaDB 5.5 项目需求:创建3个虚拟主机,分别架设phpMyadmin,wordpress,Discuz其中phpMyadmin提 ...

  7. AOP和IOC的作用

    IOC:控制反转,是一种设计模式.一层含义是控制权的转移:由传统的在程序中控制依赖转移到由容器来控制:第二层是依赖注入:将相互依赖的对象分离,在spring配置文件中描述他们的依赖关系.他们的依赖关系 ...

  8. Mybatis 新增修改一条SQL

    如果在INSERT语句末尾指定了ON DUPLICATE KEY UPDATE,并且插入行后会导致在一个UNIQUE索引或PRIMARY KEY中出现重复值,则执行旧行UPDATE:如果不会导致唯一值 ...

  9. rabbitMQ+php

    RabbitMQ与PHP(一) 项目中使用RabbitMQ作为队列处理用户消息通知,消息由前端PHP代码产生,处理消息使用Python,这就导致代码一致性问题,调整消息定义时需要PHP和Python都 ...

  10. Tengine

    Tengine是由淘宝网发起的Web服务器项目.它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性. 外文名 Tengine 发起单位 淘宝网 基    础 Nginx 目    ...