学习Python爬虫记录(2)

书接上文:学习Python爬虫记录贴_青邃的博客-CSDN博客

在上一篇文章,我们成功的编写了一个简易的爬虫,但是我想爬取整个小说,所以我们接着往下看

本文分两个部分:

1、思路分析

2、单线程爬取整本小说

3、多线程爬取小说

一、思路分析

import requests as req
from bs4 import BeautifulSoup as bs
html='https://www.bqkan8.com/1_1496/450365.html'#写入网址
txt=req.get(url=html)#对该网址进行爬取 
txt=bs(txt.text,'html.parser') 
txt=txt.find_all('div',id='content')
txt=(str(txt).replace('<br/><br/>',''))
print(txt.replace('        ','\n\n'))

这是上一篇文文章我们所编写的代码,接下来我想爬取整本小说。首先,爬取内容需要获取它的URL,也就是说我们首先要获取全部的URL,并把这些URL存储到一个列表里面,需要用的时候把URL调出来使用

二、单线程爬取小说

好的,思路有了,接下来就是编写代码了:

首先,我们编写一个获取小说目录的爬虫,并且对获取的内容进行筛选除去不需要的东西,编写代码如下:

import requests as req
from bs4 import BeautifulSoup as ds
url1='https://www.bqkan8.com/1_1496'
text1=req.get(url=url1)
bf=ds(text1.content,'html.parser')
text2=bf.find_all('div',class_="listmain")
print(text2)

然后我们得到了:

继续分析得到的内容,首先,我们爬取整本小说,这前12章是最新章节,排进去肯定是回混乱顺序,所以这前12章不要,其次要把章名和URL单独提取出来。

为了方便,我这里是使用一个列表来存储所有的URL和章名,同时是使用stringget.()方法得到其中的URL

话不多说,我们看代码理解:

import requests as req
from bs4 import BeautifulSoup as ds
jsq=0
url1='https://www.bqkan8.com/1_1496'
encoding='utf-8'
text1=req.get(url=url1)
bf=ds(text1.content,'html.parser')
text2=bf.find_all('div',class_="listmain")
text2=text2[0]
a=text2.find_all('a')
list1=[]
url1='https://www.bqkan8.com'
for i in a:
    if jsq>11:
        wz=url1+i.get('href')
        list1.append(wz)
    else:
        jsq=jsq+1
print(list1)

之所以添加一个for循环进去,是因为我们要去除最新的12个章节,这样我们获得的网址就是从小说第一章开始的输出的结果是这样的:

这下,网址搞定了,我们就可以开始编写爬取小说正文内容的程序了,只需要将我们上文编写的程序稍作修改就行:

import requests as req
from bs4 import BeautifulSoup as ds
jsq=0
url1='https://www.bqkan8.com/1_1496'
encoding='utf-8'
text1=req.get(url=url1)
bf=ds(text1.content,'html.parser')
text2=bf.find_all('div',class_="listmain")
text2=text2[0]
a=text2.find_all('a')
r=open('E:/暑期爬虫学习/小说.txt','a',encoding='utf-8')
list1=[];zjjs=0
url1='https://www.bqkan8.com'
for i in a:
    if jsq>11:
        wz=url1+i.get('href')
        list1.append(wz)
        zjjs+=1
    else:
        jsq=jsq+1
jsq=1
for i in list1:
    nr=req.get(url=i)
    text3=ds(nr.content,'html.parser')
    text3=text3.find_all('div',id='content')
    text3=text3[0].text.replace("        ",'\n\n')
    r.write(text3)
    jd=jsq/zjjs*100;jd=round(jd,2)
    print(f'下载进度{jd}%')
    jsq+=1
r.close()
print('下载完成!\n程序退出')

这里选择的是for循环对每一个网址进行循环获取,通过open实现文件的本地保存,然后你运行了一下,发现...

妈耶,一本13MB的小说要下载半个多小时,这效率也忒低了吧!

所以让我们进入本文,也是我学习爬虫最重要的一个节点:Thread+queue,线程与队列的结合使用

三、多线程爬取小说

关于Python的“假多线程”我在这里就不多说了,网上有很多关于这方面的总结。首先我门了解一下我们这次需要使用到的的工具 threading 线程函数:

import threading #引用线程函数
import time
def work(worker):
    print(f"工人{worker}正在工作") #定义一个可以运行的程序
    time.sleep(5)
thread1=threading.Thread(target=work,args=(1,)) #target后面跟着的是这个线程执行的程序,args则是参数,这个参数是元组的形式表现出来的所以后面要加一个逗号
thread2=threading.Thread(target=work,args=(2,))
thread1.start()
thread2.start()

注意,在该程序中我设置了五秒的系统休眠,但是运行程序的时候我们发现两个线程几乎是没有停顿的运行。

接下来就是队列函数 queue()我在这里使用的是它的三种运行方式中的优先级队列queue.PriorityQueue(),简单地说就是给每一个添加进队列的任务加一个标识符,标识符越小的任务越先出来。

我们来看代码,代码很长,但是并不复杂,需要慢慢看注释慢慢理解:

import requests as req
from bs4 import BeautifulSoup as bf
import threading
import queue
address=input('请输入存储地址:(如 E:/xxx/xxx.txt):')
url1=input('请输入小说的目录网址(在此小说网张内寻找:https://www.bqkan8.com/),注意:必须是完整的地址!!!\n请输入:')
def nove(url1):
    nove_directory_url=req.get(url=url1)
    nove_directory_url=bf(nove_directory_url.content,'html.parser')
    nove_directory_url=nove_directory_url.find_all('div',class_='listmain')
    nove_directory_url=nove_directory_url[0]
    nove_directory_url=list(nove_directory_url.find_all('a'))
    return nove_directory_url       #小说目录网址与小说章名的获取

def nove_directory_url_get(nove_directory_url1):
    nove_directory_url_list=[];jsq=0
    for i in nove_directory_url1:
        if jsq>11:
            nove_directory='https://www.bqkan8.com'+i.get('href')
            nove_directory_url_list.append(nove_directory)
        else:
            jsq=jsq+1
    return nove_directory_url_list     #小说每章URL的获取

def nove_directory_get(nove_directory):
    nove_directory_list=[];jsq=0
    for i in nove_directory :
        if jsq>11:
            nove_directory1=i.string
            nove_directory_list.append(nove_directory1)
        else:
            jsq=jsq+1
    return nove_directory_list  #小说章名的获取

def nove_text(url2):
    nove_text=req.get(url=url2)
    nove_text=bf(nove_text.content,'html.parser')
    nove_text=nove_text.find_all('div',id='content')
    nove_text=nove_text[0].text.replace('        ','\n\n')
    return nove_text       #小说正文的获取

nove_url_and_directory=nove(url1)#获取小说章名与网址列表
nove_directory_url_list=nove_directory_url_get(nove_url_and_directory)#获取小说每章的URL列表
nove_directory_list=nove_directory_get(nove_url_and_directory)#获取小说每章的章名列表


def nove_text_(nove_text_url,nove_title):
    text1=(nove_title+'\n\n')
    text2=nove_text_url
    global text3
    text3={}
    nove_text1='\n\n'+text1+nove_text(text2[1])
    text3[text2[0]]=nove_text1 #使用字典的形式暂存,并将队列中的标识符赋给字典key值,将正文内容给字典的value值

long=len(nove_directory_list)#得到章节数
nove_directory_url_queue=queue.PriorityQueue()#建立一个网址队列
nove_directory_list_queue=queue.PriorityQueue()#建立一个章节名称队列
z=0
for i in nove_directory_list:
    nove_directory_list_queue.put([z,i])
    z+=1 #为章名队列添加任务即添加章名进入队列

x=0
for i in nove_directory_url_list:
    nove_directory_url_queue.put([x,i])
    x+=1#为网址队列添加任务并附上标识符
print('下载队列加载完成')

long2=10
while nove_directory_url_queue is not True: #循环条件是网址队列为空
    thread1=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(),nove_directory_list_queue.get()[1]))
    thread2=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread3=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread4=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread5=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread6=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread7=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread8=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread9=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread10=threading.Thread(target=nove_text_,args=(nove_directory_url_queue.get(), nove_directory_list_queue.get()[1]))
    thread1.start()
    thread2.start()
    thread3.start()
    thread4.start()
    thread5.start()
    thread6.start()
    thread7.start()
    thread8.start()
    thread9.start()
    thread10.start()
    thread1.join()
    thread2.join()
    thread3.join()
    thread4.join()
    thread5.join()
    thread6.join()
    thread7.join()
    thread8.join()
    thread9.join()
    thread10.join()#阻塞线程,可以理解为停止线程
    t=open(f'{address}','a',encoding='utf-8')
    for i in sorted(text3):#对字典的key值进行排序,得到排序好的内容
        txt=text3[i]
        t.write(txt)#写入文件
    f=long2/long
    f=round(f,4)
    print('下载进度:',f*100,'%')
    long2=long2+10
    t.close()
print('下载完成!')

这里主要是使用到了字典的排序方法,这样就能得到排序后的小说内容,使写入的内容不是杂乱无序的。

这个程序并不完善,比如最后的线程拿去任务那个环节......

但是精力有限,毕竟高考快到了:)

最后祝愿能阅读到最后的每位,写程序没保存的时候不会停电2333

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值