python 爬虫下载小说

在开始之前,我们需要对小说网站(八一中文网)进行相应的分析。进入斗破苍穹第一章,右键点击检查,查看文章内容的具体位置。

 从上图能看出文章内容直接存在于网页当中,是一个静态网页,直接提取对应标签内的文本即可。下面我利用单线程爬虫和多线程爬虫分别对斗破苍穹进行爬取。

一. 单线程爬虫

单线程爬虫适合爬取此网站任何小说,但是爬取的速度相对较慢。引用requests库和bs4库,对文章内容进行抓取,并定义文章的链接和请求头。请求头将爬虫模拟成浏览器请求,防止网站的反爬机制。

# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup

baseurl='https://www.81zw.com/'
url=baseurl+'/book/7574/18096.html'
headers={'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36'}

 首先获取对应链接的网页内容并对网页内容进行处理。其中 r.text 为对应网页的源代码,使用bs4库对源代码进行规范化处理。

r = requests.get(url=url, headers=headers)
text = r.text
soup = BeautifulSoup(text,'html.parser')

在获取网页的源代码之后,我们要获取有文章内容的标签的内容。从此图我们可以看到,文章内容存放在一个<div>标签中,故获取这个标签的内容即可。

 使用bs4库的find方法查找对应id的<div>标签的内容

text = soup.find('div',id='content').text    #文章内容

 此时的text的内容输出结果下图所示,换行和每一段的缩进需要进行额外处理。

将text(文章内容)放入列表中,对列表进行遍历,若遇到4个'\xa0',即上图红框标注的4个空位符,将第2个空位符改为换行符'\n',最后将列表转换成字符串,即可正确排版。

text1=list(text)        #排版处理
for i in range(len(text1)):
	if text1[i]=='\xa0' and text1[i+1]=='\xa0' and text1[i+2]=='\xa0' and text1[i+3]=='\xa0':
		text1[i+1]='\n'
		i=i+2
text ="".join(text1)

 正确排版的结果:

接下来我们需要获取文章的题目,在文章标题处右键检查,得到下图结果,获取方法和上面获取内容相同。

title为获取的文章题目

title = soup.find('div',class_='bookname').h1.text

下面我们获取下一章的url,在源代码中找到此处,会发现除了最后的那个数字变化,其他的地方没有变化,故只需更新url的后半段就可以,然后通过新的url进行下一章的爬取。

 通过查找所有的a标签并比对a标签的文本得到新url。

for i in soup.find_all('a'):
	if i.text=="下一章":
	    url=baseurl+i['href']

最后进行文件的保存,我将所有的文章保存到一个txt文件中,便于在手机上打开。

text_path = 'D:\\小说\\斗破苍穹.txt'
with open(text_path,'a+',encoding='utf-8') as f:
	f.writelines('\n')
	f.writelines('\n')
	f.writelines(title)
	f.writelines('\n')
	f.writelines(text)

最终得到的结果:

最后整理一下上述代码

# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
baseurl='https://www.81zw.com/'
url=baseurl+'/book/7574/18096.html'
headers={'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Mobile Safari/537.36'}
while True:
	try:
		r = requests.get(url=url, headers=headers)
		text = r.text
		soup = BeautifulSoup(text,'html.parser')
		title = soup.find('div',class_='bookname').h1.text
		title1=list(title)
		title2=[]
		for i in range(len(title1)):        #处理文章标题中可能出现的特殊符号
			if title1[i]!='"':
				title2.append(title1[i])
			else:
				pass
		title="".join(title2)
		text = soup.find('div',id='content').text
		text1=list(text)
		for i in soup.find_all('a'):
			if i.text=="下一章":
				url=baseurl+i['href']
		for i in range(len(text1)):
			if text1[i]=='\xa0' and text1[i+1]=='\xa0' and text1[i+2]=='\xa0' and text1[i+3]=='\xa0':
				text1[i+1]='\n'
				i=i+2
		text ="".join(text1)
		text_path = 'D:\\小说\\斗破苍穹.txt'
		with open(text_path,'a+',encoding='utf-8') as f:
			f.writelines('\n')
			f.writelines('\n')
			f.writelines(title)
			f.writelines('\n')
			f.writelines(text)
		print(title)
	except:
		print(1)

 二. 多线程爬虫

单线程爬虫虽然较为稳定,但是抓取文章的时间过长,cpu效率过低,故我在查阅相关资料之后又写了一个多线程爬虫,可以在半分钟内抓取整本小说。

多线程爬虫首先需要将整本书的各个章节的url集合到urls列表中。斗破苍穹这本书的各章url正好是连续的,方便使用多线程爬虫爬取。(有很多小说的各章节url是不连续的,可以爬取小说的url到一个数组,其他操作相同,小伙伴们可以自己尝试一下)。

import requests
from bs4 import BeautifulSoup
import threading
import time
urls=[
	f"https://www.81zw.com/book/7574/{page}.html"	
	for page in range(18096,19728)
]
urls1=[
	f"https://www.81zw.com/book/7574/{page}.html"	
	for page in range(19857006,19857011)
]
urls2=[
	f"https://www.81zw.com/book/7574/{page}.html"	
	for page in range(20222594,20222612)
]
urls.extend(urls1)
urls.extend(urls2)

在此代码中我为每个章节都定义了一个线程,故爬取的结果是无序的。我在保存文章内容的时候没有将所有的文章保存在一个txt文件中,而是每个章节保存成一个txt文件,并将文件名改为对应的章节号,便于在后面合并所有章节

多线程爬虫的函数主体和单线程爬虫差不多,只是在文件名的处理和文件保存处有细微的差别。

def spider(url):
	r = requests.get(url=url)
	text = r.text
	soup = BeautifulSoup(text,'html.parser')
	title = soup.find('div',class_='bookname').h1.text
	title1=list(title)
	title2=[]
	a=0
	b=0
	for i in range(len(title1)):
		if title1[i]=='第':
			a=i
		if title1[i]=='章':
			b=i
			break
	for i in range(a+1,b):
		title2.append(title1[i])
	title="".join(title2)
	a3=chinese2digits(title)
	a4=str(a3)
	a5='第'+a4+'章'
	title2=[]
	title2.append(a5)
	for i in range(b+1,len(title1)):
		title2.append(title1[i])
	title="".join(title2)
	text = soup.find('div',id='content').text
	text1=list(text)
	for i in range(len(text1)):
		if text1[i]=='\xa0' and text1[i+1]=='\xa0' and text1[i+2]=='\xa0' and text1[i+3]=='\xa0':
			text1[i+1]='\n'
			i=i+2
	text ="".join(text1)
	text_path = 'D:\\斗破苍穹2\\'+a4+'.txt'
	with open(text_path,'a+',encoding='utf-8') as f:
		f.writelines('\n')
		f.writelines('\n')
		f.writelines(title)
		f.writelines('\n')
		f.writelines(text)
#	print(title)

定义多线程执行函数(每个章节定义一个线程):

def mul_spider():
	threads=[]
	for url in urls:
		threads.append(
			threading.Thread(target=spider, args=(url,))
		)
	for thread in threads:
		thread.start()

定义文字数字转阿拉伯数字函数(文件名保存为阿拉伯数字便于排序)

def chinese2digits(uchars_chinese):
    total = 0
    r = 1              #表示单位:个十百千...
    for i in range(len(uchars_chinese) - 1, -1, -1):
        val = common_used_numerals_tmp.get(uchars_chinese[i])
        if val >= 10 and i == 0:  #应对 十三 十四 十*之类
            if val > r:
            	r = val
            	total = total + val
            else:
            	r = r * val
        elif val >= 10:
            if val > r:
            	r = val
            else:
            	r = r * val
        else:
            total = total + r * val
    return total

最后整理一下上述代码:

# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
import threading

urls=[
	f"https://www.81zw.com/book/7574/{page}.html"	
	for page in range(18096,19728)
]
urls1=[
	f"https://www.81zw.com/book/7574/{page}.html"	
	for page in range(19857006,19857011)
]
urls2=[
	f"https://www.81zw.com/book/7574/{page}.html"	
	for page in range(20222594,20222612)
]
urls.extend(urls1)
urls.extend(urls2)
common_used_numerals_tmp ={'零':0, '一':1, '二':2, '两':2, '三':3, '四':4, '五':5, '六':6, '七':7, '八':8, '九':9, '十':10, '百':100, '千':1000}
common_used_numerals= dict(zip(common_used_numerals_tmp.values(), common_used_numerals_tmp.keys())) #反转
def chinese2digits(uchars_chinese):
    total = 0
    r = 1              #表示单位:个十百千...
    for i in range(len(uchars_chinese) - 1, -1, -1):
        val = common_used_numerals_tmp.get(uchars_chinese[i])
        if val >= 10 and i == 0:  #应对 十三 十四 十*之类
            if val > r:
            	r = val
            	total = total + val
            else:
            	r = r * val
        elif val >= 10:
            if val > r:
            	r = val
            else:
            	r = r * val
        else:
            total = total + r * val
    return total
def spider(url):
	r = requests.get(url=url)
	text = r.text
	soup = BeautifulSoup(text,'html.parser')
	title = soup.find('div',class_='bookname').h1.text
	title1=list(title)
	title2=[]
	a=0
	b=0
	for i in range(len(title1)):
		if title1[i]=='第':
			a=i
		if title1[i]=='章':
			b=i
			break
	for i in range(a+1,b):
		title2.append(title1[i])
	title="".join(title2)
	a3=chinese2digits(title)
	a4=str(a3)
	a5='第'+a4+'章'
	title2=[]
	title2.append(a5)
	for i in range(b+1,len(title1)):
		title2.append(title1[i])
	title="".join(title2)
	text = soup.find('div',id='content').text
	text1=list(text)
	for i in range(len(text1)):
		if text1[i]=='\xa0' and text1[i+1]=='\xa0' and text1[i+2]=='\xa0' and text1[i+3]=='\xa0':
			text1[i+1]='\n'
			i=i+2
	text ="".join(text1)
	text_path = 'D:\\斗破苍穹\\'+a4+'.txt'
	with open(text_path,'a+',encoding='utf-8') as f:
		f.writelines('\n')
		f.writelines('\n')
		f.writelines(title)
		f.writelines('\n')
		f.writelines(text)
#	print(title)
def mul_spider():
	threads=[]
	for url in urls:
		threads.append(
			threading.Thread(target=spider, args=(url,))
		)
	for thread in threads:
		thread.start()

if __name__ == "__main__":
	mul_spider()

运行结果:31秒爬完整部小说并保存到对应文件夹

下面我们使用一段代码将这个文件夹中的文件按顺序合并

text_path=[
	f"D:\\斗破苍穹\\{page}.txt"
	for page in range(1,1624)
]
text_path2="D:\\斗破苍穹.txt"
for i in range(0,1624):
	try:
		f=open(text_path[i],"r",encoding='utf-8')
		str=f.read()
		f.close()
		with open(text_path2,'a+',encoding='utf-8') as f:
			f.writelines(str)
	except:
		print(i)

 代码运行完成后在对应的位置就可以看到合并后的结果了

然后将文件发到手机上,愉快的看小说吧,再也不用怕断网看不了小说了!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值