Python程序设计实例 |爬取网络中的小说

news2024/11/17 23:49:39

 

网络文学是新世纪我国流行文化中的重要领域,年轻人对网络小说更是有着广泛的喜爱。本文以抓取网络小说正文为例编写一个简单、实用的爬虫脚本。

01、分析网页

很多人在阅读网络小说时都喜欢本地阅读,换句话说就是把小说下载到手机或者其他移动设备上阅读,这样不仅不受网络的限制,还能够使用阅读App调整出自己喜欢的显示风格。但遗憾的是,各大网站很少会提供整部小说的下载功能,只有部分网站会给VIP会员开放下载多个章节内容的功能。对于普通读者而言,虽然VIP章节需要购买阅读,但是至少还是希望能够把大量的免费章节一口气看完。用户完全可以使用爬虫程序来帮助自己把一个小说的所有免费章节下载到TXT文件中,以方便在其他设备上阅读(这里也要提示大家支持正版,远离盗版,提高知识产权意识)。

以逐浪小说网为例,从排行榜中选取一个比较流行的小说(或者是大家感兴趣的)进行分析,首先是小说的主页,其中包括了各种各样的信息(如小说简介、最新章节、读者评论等),其次是一个章节列表页面(有的网站也称为“最新章节”页面),而小说的每一章有着单独的页面。很显然,如果用户能够利用章节列表页面来采集所有章节的URL地址,那么我们只要用程序分别抓取这些章节的内容,并将内容写入本地TXT文件,即可完成小说的抓取。

在查看章节页面之后,用户十分遗憾地发现,小说的章节内容使用JS加载,并且整个页面使用了大量的CSS和JS生成的效果,这给用户的抓取增加了一点难度。使用requests或者urllib库直接请求章节页面的URL是不现实的,但用户可以用Selenium来轻松搞定这个问题,对于一个规模不大的任务而言,在性能和时间上的代价还是可以接受的。

接下来分析一下如何定位正文元素。使用开发者模式查看元素(见图1),用户发现可以使用read-content这个ID的值定位到正文。不过class的值也是read-content,在理论上似乎可以使用class名定位,但Selenium目前还不支持复合类名的直接定位,所以使用class来定位的想法只能先作罢。

■ 图1 开发者模式下的小说章节内容

提示/

虽然Selenium目前只支持对简单类名的定位,但是用户可以使用CSS选择的方式对复合类名进行定位,大家感兴趣的可以了解Selenium中的find_element_by_css_selector()方法。

02、编写爬虫

使用Selenium配合Chrome进行本次抓取,除了用pip安装Selenium之外,首先需要安装ChromeDriver,可访问以下地址将其下载到本地:

https://sites.google.com/a/chromium.org/chromedriver/downloads

进入下载页面后(见图2),根据自己系统的版本进行下载即可。

■ 图2 ChromeDriver的下载页面

之后,使用selenium.webdriver.Chrome(path_of_chromedriver)语句可创建Chrome浏览器对象,其中path_of_chromedriver就是下载的ChromeDriver的路径。

在脚本中,用户可以定义一个名为NovelSpider的爬虫类,使用小说的“全部章节”页面URL进行初始化(类似于C语言中的“构造”),同时它还拥有一个list属性,其中将会存放各个章节的URL。类方法如下。

  • get_page_urls():从全部章节页面抓取各个章节的URL。

  • get_novel_name():从全部章节页面抓取当前小说的书名。

  • text_to_txt():将各个章节中的文字内容保存到TXT文件中。

  • looping_crawl():循环抓取。

思路梳理完毕后就可以着手编写程序了,最终的爬虫代码见例1。

【例1】网络小说的爬取程序。

# NovelSpider.py
import selenium.webdriver,time,re
from selenium.common.exceptions import WebDriverException
class NovelSpider():
def  init (self,url):
self.homepage = url
self.driver= selenium.webdriver.Chrome(path of chromedriver)
self.page list =[]
def del (self):
self.driver.quit(
def get page urls(self):
homepage = self.homepage
self.driver.get(homepage
self.driver.save screenshot('screenshot.png')
self.driver.implicitly_wait(5)elements = self.driver.find elements by tag name('a')
for one in elements:
page_url = one.get attribute('href')
pattern ='http:\/\/book .zhulang .com\/\df6) /\d + .html
if re.match(pattern,page url):
print(page url)
self.page list.append(page_url)
def looping crawl(self):
homepage = self.homepage
filename = self.get_novel name(homepage) + '.txtself.get_page_urls()
pages = self.page_list
# print(pages)
for page in pages:
self.driver.get(page,
print('Next page:')
self.driver.implicitly wait(3)title = self.driver.find_element by_tag_name('h2').textres = self.driver.find element by id('read - content')text= n'+ title +  n'
for one in res.find elements by xpath('./p'):text += one.texttext +=n
self.text to txt(text,filename)
time.sleep(1)
print(page + '\t\t\tis Done!')
def get novel name( self,homepage):
self.driver.get(homepage)
self.driver.implicitly wait(2)
res = self.driver.find element by tag name( 'strong').find element by xpath('./a')if res is not None and len(res.text) > 0:
return res.textelse:
return 'novel
def text to txt(self,text,filename):if filename[ - 4:]!=.txt':print(Error,incorrect filename')else:
with open(filename,'a') as fp:
fp.write(text)fp.write(n')
if name == main ':
hp_url = input(输入小说"全部章节"页面:)
path of chromedriver ='your path of chrome driver!
try:
sp1 = NovelSpider(hp_url)
sp1.looping_crawl()
del sp1
except WebDriverException as e:
print(e.msg)

__init__()和__del__()方法可以视为构造函数和析构函数,分别在对象被创建和被销毁时执行。在__init__()中使用一个URL字符串进行了初始化,而在__del__()方法中退出了Selenium浏览器。try-except语句执行主体部分并尝试捕获WebDriverException异常(这也是Selenium运行时最常见的异常类型)。在lopping_crawl()方法中则分别调用了上述其他几个方法。

driver.save_screenshot()方法是selenium.webdriver中保存浏览器当前窗口截图的方法。

driver.implicitly_wait()方法是Selenium中的隐式等待,它设置了一个最长等待时间,如果在规定的时间内网页加载完成,则执行下一步,否则一直等到时间截止,然后再执行下一步。

提示/

显式等待会等待一个确定的条件触发然后才进行下一步,可以结合ExpectedCondition共同使用,支持自定义各种判定条件。隐式等待在编写时只需要一行,所以编写十分方便,其作用范围是WebDriver对象实例的整个生命周期,会让一个正常响应的应用的测试变慢,导致整个测试执行的时间变长。

driver.find_elements_by_tag_name()是Selenium用来定位元素的诸多方法之一,所有定位单个元素的方法如下。

  • find_element_by_id():根据元素的id属性来定位,返回第一个id属性匹配的元素;如果没有元素匹配,会抛出NoSuchElementException异常。

  • find_element_by_name():根据元素的name属性来定位,返回第一个name属性匹配的元素;如果没有元素匹配,则抛出NoSuchElementException异常。

  • find_element_by_xpath():根据XPath表达式定位。

  • find_element_by_link_text():用链接文本定位超链接。该方法还有子串匹配版本find_element_by_partial_link_text()。

  • find_element_by_tag_name():使用HTML标签名来定位。

  • find_element_by_class_name():使用class定位。

  • find_element_by_css_selector():根据CSS选择器定位。

寻找多个元素的方法名只是将element变为复数elements,并返回一个寻找的结果(列表),其余和上述方法一致。在定位到元素之后,可以使用text()和get_attribute()方法获取其中的文本或各个属性。

page url = one.get attribute('href')

这行代码使用get_attribute()方法来获取定位到的各章节的URL地址。在以上程序中还使用了re(Python的正则模块)中的re.match()方法,根据正则表达式来匹配page_url。形如:

'http"\/\/book\.zhulang\.com\/\d{6}\/\d+\.html'

这样的正则表达式所匹配的是下面这样一种字符串:

http://book,zhulang.com/A/B.HTML

其中,A部分必须是6个数字,B部分必须是一个以上的数字。这也正好是小说各个章节页面的URL形式,只有符合这个形式的URL链接才会被加入page_list中。

re模块的常用函数如下。

  • compile():编译正则表达式,生成一个Pattern对象。之后就可以利用Pattern的一系列方法对文本进行匹配/查找(当然,匹配/查找函数也支持直接将Pattern表达式作为参数)。

  • match():用于查找字符串的头部(也可以指定起始位置),它是一次匹配,只要找到了一个匹配的结果就返回。

  • search():用于查找字符串的任何位置,只要找到了一个匹配的结果就返回。

  • findall():以列表形式返回能匹配的全部子串,如果没有匹配,则返回一个空列表。

  • finditer():搜索整个字符串,获得所有匹配的结果。与findall()的一大区别是,它返回一个顺序访问每一个匹配结果(Match对象)的迭代器。

  • split():按照能够匹配的子串将字符串分割后返回一个结果列表。

  • sub():用于替换,将母串中被匹配的部分使用特定的字符串替换掉。

提示/

正则表达式在计算机领域中应用广泛,大家有必要好好了解一下它的语法

在looping_crawl()方法中分别使用了get_novel_name()获取书名并转换为TXT文件名,get_page_urls()获取章节页面的列表,text_to_txt()保存抓取到的正文内容。在这之间还大量使用了各类元素定位方法(如上文所述)。

03、运行并查看TXT文件

这里选取一个小说——逐浪小说网的《绝世神通》,运行脚本并输入其章节列表页面的URL,可以看到控制台中程序成功运行时的输出,如图3所示。

■ 图3 小说爬虫的输出

抓取结束后,用户可以发现目录下多出一个名为“screenshot.png”的图片(见图4)和一个“绝世神通.txt”文件(见图5),小说《绝世神通》的正文内容(按章节顺序)已经成功保存。

■ 图4 逐浪小说网的屏幕截图

■ 图5 小说的部分内容

程序圆满地完成了下载小说的任务,缺点是耗时有些久,而且Chrome占用了大量的硬件资源。对于动态网页,其实不一定必须使用浏览器模拟的方式来爬取,可以尝试用浏览器开发者工具分析网页的请求,获取到接口后通过请求接口的方式请求数据,不再需要Selenium作为“中介”。另外,对于获得的屏幕截图而言,图片是窗口截图,而不是整个页面的截图(长图),为了获得整个页面的截图或者部分页面元素的截图,用户需要使用其他方法,如注入JavaScript脚本等,本文就不再展开介绍。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1032717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TQ210-Bootloader-Uboot(LTS)

Bootloader的作用 Bootloader是位于计算机系统启动过程中的程序,它的主要作用是将操作系统从磁盘等外部存储介质加载到计算机内存中,并启动操作系统执行。Bootloader通常包括硬件初始化、自检、异常处理和启动操作系统等功能。它是计算机系统中非常重要…

Mac电脑系统怎么样才能干干净净地卸载应用程序?

Mac系统怎么样才能干干净净地卸载应用程序,不留下隐私数据和用户信息呢?如果有方法的话,那么该方法对于Mac电脑小白是否友好呢? CleanMyMac就是一款用于清理Mac系统下应用程序的一款清理工具,其内置了应用程序的安全卸…

第二证券:智能网联汽车产业迎催化 容量电价政策出台可期

昨日,A股延续调整态势,沪指失守3100点,深成指跌破10000点大关,创业板跌约1%再创阶段新低;两市成交额保持在地量水平,再创年内新低。到收盘,沪指跌0.77%报3084.7点,深成指跌0.9%报998…

速卖通新品如何推广,速卖通的推广渠道有哪些?——站斧浏览器

速卖通的推广渠道非常多样化,卖家可以根据自己的需求和预算选择合适的渠道来推广产品,提高曝光度和销售量,能够有效地提高产品的知名度和信任度。 速卖通新品如何推广? 速卖通上有数以百万计的卖家,每天都有大量的新…

批量寄件教程

快递行业的发展,和企业之间其实是正向的影响。为什么这么说呢?企业因公寄件,能为快递公司贡献一定寄件量,而快递行业的发展,不管是运输速度的提升,服务质量的提高,都能为企业的发展提供帮助&…

气膜建筑在施工工期方面的优势

充气膜建筑基础处理简单,迁移的损耗非常小,拆装方便,可快速安装,快速拆卸,可以很容易地建成季节性建筑,解决露天场馆因为“雨、晒、冷、雪”等导致部分时间不能营业的难题。 气膜建筑的柔性特点及其简约性使…

Stable Diffusion WebUI插件posex安装以及无法使用完美解决办法汇总

posex是一个很好用的3Dopenpose编辑器。 我们只需要去官网找到源码就可以查看其用法。 对于安装大家应该都知道怎么去安装。 1. 如何安装 (1)一体包安装方式 类似于秋叶一体包直接在webui界面搜索posex就可以直接install。 最新版本好像已经取消了。 (2)手动安装方式…

记一次MySQL安装过程中遇到的问题

由于太久没用MySQL,今天在重装MySQL时遇到一个问题,被卡了近2个小时。。。。。。 由于我本人原先安装过MySQL,所以在重装的时候必须要先卸载原先的MySQL。 下面先给出正确的卸载流程(作者就是在卸载的时候操作失误导致安装过程被…

Python灰帽编程——定制EXP之RCE

文章目录 定制EXP之RCE1. 常见模块介绍1.1 base641.1.1 base64 编码1.1.2 base64 解码 1.2 string 2. 常规 EXP 编写2.1 phpstudy_2016-2018_rce2.1.1 漏洞利用脚本2.1.2 进阶脚本 2.2 SQL 注入 EXP2.2.1 布尔盲注2.2.2 延时注入 2.3 metinfo_5.0.4 EXP编写2.3.1 SQL注入漏洞 3…

【Linux操作系统教程】用户管理与权限管理你真的懂了吗(三)

😄作者简介: 小曾同学.com,一个致力于测试开发的博主⛽️,主要职责:测试开发、CI/CD 如果文章知识点有错误的地方,还请大家指正,让我们一起学习,一起进步。😊 座右铭:不想…

早餐与风景

来吧,我用流水账描述下这一天。 时维九月,北京的早上有点冷,因为今天有个市场活动要去支撑,按照会议时间的要求,我需要在早上7点半就赶到会场,所以昨天晚上我加班到凌晨处理完了今天要给出去的材料&#xf…

ArrayList 的自动扩容机制

触发扩容 ArrayList 是一个数组结构的存储容器,默认情况下,数组的长度是 10 当然我们也可以在构建 ArrayList 对象的时候自己指定初始长度。随着在程序里面不断的往 ArrayList 中添加数据,当添加的数据达到 10 个的时候,ArrayLis…

[Linux入门]---Linux项目自动化构建工具-make/Makefile

目录 1.背景2.make指令输入make默认为Makefile文件第一条指令执行Makefile文件对gcc指令特殊处理及原理特殊符号 3.总结 1.背景 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力一个工程中的源文件不计数,其按类型、功能、模块分别放…

【MATLAB第76期】基于MATLAB的代表性样本筛选方法合集(针对多输入单输出数据)

【MATLAB第76期】基于MATLAB的代表性样本筛选方法合集(针对多输入单输出数据) 前有筛选变量方法,如局部敏感性分析和全局敏感性分析方法介绍 。 今天提出另外一种思路,去对样本进行筛选。 使用场景: 场景1&#xff1a…

期刊目录解析 | 慎投!又2本“On Hold”SCI期刊被踢!

科睿唯安官方目前对SCI期刊的管理可以说是相当严格的,每个月都会出评估报告,如果任何一本期刊有问题,就会先被“On Hold”,这代表需要重新评估是否符合SCI标准,有可能直接被剔除。 此前,小编也为大家统计了…

rabbitMQ (1)

文章目录 1. RabbitMQ 介绍1.1 几个重要概念1.2 RabbitMq 的工作原理 2 RabbitMQ 安装3. RabbitMQ 入门操作3.1 添加依赖3.2 生产者代码3.3 消费者代码 4. Work Queues5. 管理端页面创建队列 1. RabbitMQ 介绍 引用 : RabbitMQ 是一个消息中间件:它接受…

Axure设计之引入ECharts图表

ECharts是一款基于JavaScript的可视化图表库,它提供了丰富的图表类型和交互功能,可以轻松地创建各种类型的图表,如折线图、柱状图、饼图、散点图等。 想要通过Axure实现ECharts示例中的某些图表效果,单纯靠Axure自带的功能是很难实…

C# 实现数独游戏

1.数独单元 public struct SudokuCell{public SudokuCell() : this(0, 0, 0){}public SudokuCell(int x, int y, int number){X x; Y y; Number number;}public int X { get; set; }public int Y { get; set; }public int Number { get; set; }} 2.数独创建 public class …

JVM 参数详解

GC有两种类型:Scavenge GC 和Full GC 1、Scavenge GC 一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,堆的Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到…

mysql 命令

1.以root身份登录MySQL服务器 mysql -u root -p 2.输入root用户密码 显示命令 1. 显示数据库列表 show databases; 刚开始时才两个数据库:mysql和test。mysql库很重要它里面有MYSQL的系统信息,我们改密码和新增用户,实际上就是用这个库进…