【记录爬虫实战过程】入门学习详细过程·用爬虫实现小说爬取2

前言

要做一个项目,所以先学习熟练应用爬虫。
在此记录学习过程,供他人参考,也督促自己坚持学习。

大致路线:

  1. 模仿他人
  2. 自己练习
  3. 总结

这是用爬虫实现小说爬取的第一部分,主要是根据别人的例子进行模仿和练习。

这篇为第二和三部分:自己练习+总结,在原来的基础上进行扩展:添加了请求头、设置爬取时间间隔、添加异常处理机制

背景:
已掌握一些基础的相关知识,运行环境为vs code
需要安装一些爬虫所用的库文件:可以使用pip获取,例如获取request库文件:pip install request
(如果不知道如何在vscode里面导入python的库可以参考这篇文章 在vscode环境里导入python库(三种方法) | 详细过程

我的扩展

1.添加了请求头(headers):一个/随机

可以通过添加请求头来防反爬,如果不写请求头headers有可能会被封IP。

在初始化里面加上 self.headers

#防反爬
        self.headers={
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.204 Safari/537.36',
        'content-type':'charst=uft8' #设置接收数据的编码格式
        }

这里我只写了一个headers来抓到信息,一般来讲,一个请求头就行。
如果不能,我们可以升级一下,写n个请求头,然后通过随机模块来随机选择一个headers。
当然,这个做法比较笨。在查阅资料后,我发现python有一个专门的库fake_useragent来生成随机headers(相关资料
关于如何在vscode里面导入python的库可以参考这篇文章 在vscode环境里导入python库(三种方法) | 详细过程
在这里插入图片描述

#生成随机headers
from fake_useragent import UserAgent

导入库后:

self.headers={
            'User-Agent':UserAgent().random,
            'content-type':'charst=uft8' #设置接收数据的编码格式
            }

2.设置爬取时间间隔

很多网站的反爬虫机制都设置了访问间隔时间,一个IP如果短时间内超过了指定的次数就会进入“冷却CD”。
所以除了轮换IP和user_agent,还可以通过设置访问的时间间隔,比如每抓取一个页面休眠一个随机时间。(不过这个只用来爬小说好像用处不大,这里进行扩展主要是方便以后用于其他爬虫项目)

需要导入time库

#设置爬取时间间隔
import time
import random #可以设置随机睡眠时间

在主函数的下载部分添加time.sleep(0.5)函数,设置sleep时间间隔 ,参数可以为固定值或用random获得随机数

  #设置sleep时间间隔   
  time.sleep(0.5)
  #或者可以设置随机睡眠时间
  #time.sleep(random.randint(5,10))

3.添加异常处理机制

#处理exception情况
from requests.exceptions import *

修改 get_download_url(self) 函数

        try:
            req=requests.get(url=self.target,headers=self.headers) #有改动,加上了headers
            if req.status_code==200:
                req.encoding = 'utf-8' 
                html=req.text
            else:
                print('No response')
                return None

        except ReadTimeout:
            print("ReadTimeout!")   
            return None
 
        except RequestException:
            print("请求页面出错!")
            return None

4.多线程爬虫:提高效率

需要导入threading模块,这是python中专门提供的,用来做多线程编程的模块。threading模块中最常用的类是Thread。
不过查了很多资料发现,下载单本小说用多线程其实并不方便,多线程适合同时下载多部小说,所以这里就不多讲了。

以下是自己练习的全部代码:(在“笔趣阁”网站爬的《解药》)

import requests
import sys
from bs4 import BeautifulSoup
from requests.models import Response
#处理exception情况
from requests.exceptions import *

#生成随机headers
from fake_useragent import UserAgent
#设置爬取时间间隔
import time
import random
#多线程
import threading

time.sleep(random.random()*3)

class downloader(threading.Thread):
    def __init__(self):
        #防反爬
        # self.headers={
        # 'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.204 Safari/537.36',
        # 'content-type':'charst=uft8' #设置接收数据的编码格式
        # }
        self.headers={
            'User-Agent':UserAgent().random,  #获得随机headers
            'content-type':'charst=uft8' #设置接收数据的编码格式
            }

        self.server='https://www.tianxiabachang.cn'
        self.target='https://www.tianxiabachang.cn/5_5147/'
        self.names=[]  #存放章节名
        self.urls=[]  #存放章节链接
        self.nums=0  #存放章节数


    #函数说明:获取下载链接
    def get_download_url(self):
        try:
            req=requests.get(url=self.target,headers=self.headers) #有改动,加上了headers
            if req.status_code==200:
                req.encoding = 'utf-8' 
                html=req.text
            else:
                print('No response')
                return None

        except ReadTimeout:
            print("ReadTimeout!")   
            return None
 
        except RequestException:
            print("请求页面出错!")
            return None

        div_bf=BeautifulSoup(html)
        div=div_bf.find_all('div',id='list')
        a_bf=BeautifulSoup(str(div[0]))  #div中有多个元素,比如包括网站。此处选择第0项元素
        a=a_bf.find_all('a')
        self.nums=len(a[9:13])   #剔除不必要的章节,并统计章节数
        for each in a[9:13]:
            self.names.append(each.string)
            self.urls.append(self.server+each.get('href'))


    #函数说明:获取章节内容
    def get_contents(self,target):  #有改动
        req=requests.get(url=target)
        req.encoding = 'utf-8' 
        html=req.text
        bf=BeautifulSoup(html)
        texts=bf.find_all('div',id='content')
        if texts[0]:
            texts=texts[0].text.replace('\xa0'*8,'\n\n')
        return texts

    #函数说明:将爬取的文章内容写入文件
    def writer(self,name,path,text):
        write_flag=True
        with open(path,'a',encoding='utf-8') as f:
            f.write(name+'\n') #小说名字
            f.writelines(text)  #小说内容
            f.write('\n\n')


    #主函数
if __name__=="__main__":
    dl=downloader()
    dl.get_download_url()
    print('《解药》开始下载:')
    for i in range(dl.nums):
        dl.writer(dl.names[i],'解药(测试).txt',dl.get_contents(dl.urls[i]))
        print(" 已下载第"+str (i+1)+ "章:%.3f%%" % float(i/dl.nums)+'\r') #改进
        sys.stdout.write("  已下载:%.3f%%" % float(i/dl.nums)+'\r')
        sys.stdout.flush()   
        #设置sleep时间间隔   
        time.sleep(0.5)
        #time.sleep(random.randint(5,10)) # 产生 5 到 10 的一个整数型随机数填入sleep函数

    print("《解药》下载完毕")

5. 补充资料

这个视频 可以帮助梳理过程和理解一些代码的运用

总结爬取小说的大致步骤

3.1.确定爬取的url地址
3.2.发送请求
3.3.数据解析,得到目标数据
3.3.1 解析目录
3.3.2解析内容
3.3.3 处理内容
3.3.4 保存数据
3.4 完善主函数,将数据存入txt文件

1.确定爬取的url地址(分析网页性质<静态/动态>)

target = 'https://www.bqkan8.com/1_1094/'

2.发送请求

    req = requests.get(url=target)
    #解决中文乱码问题!
    Response.encoding=Response.apparent_encoding #自动识别响应体的编码
    #req.encoding = 'GB2312'  
    #req.encoding = 'utf-8'  
    html =req.text

3.数据解析,得到目标数据

(可能涉及正则表达式)

3.1 解析目录

例如,当网页检查结果是这样时:

方法一:
在这里插入图片描述
解析代码为:

#需要先导入re库
import re

result_list=re.findall('<dd><a href="(.*?)">.*</a></dd>',html)
#若有必要可以使用以下代码检查输出结果
    # print("输出结果\n")
    # print(result_list)

()表示精确匹配;
’ . '代表匹配除了换行符以外的任意字符;
’ * '代表匹配前一个字符的0次或者多次;
‘ ? ’'代表非贪婪模式,即匹配一次返回一次

方法二:
也就是之前提取小说用的方法,使用BeautifulSoup

div_bf=BeautifulSoup(html)
div=div_bf.find_all('div',id='list')

3.2解析内容

解析内容
方法一:

    html =req.text
    #print(html)
    result_list=re.findall('<div id="content">(.*?)</div>',html,re.S)
    #若有必要可以使用以下代码检查输出结果
    # print("输出结果\n")
    # print(result_list)

最后的参数 re.S让‘ . ’ 能够匹配到换行符,不然输出结果不正确

方法二:

    bf=BeautifulSoup(html)  
    texts=bf.find_all('div',id='content')     

3.3 处理内容

将一些字符替换成换行
方法一:

    #因为div中有多个元素,比如包括网站。
    #所以此处选择第0项元素
    text=result[0].replace('&nbsp;','').replace('<br />','\n')
    print(text)

方法二:

   print(texts[0].text.replace('\xa0'*8,'\n\n'))

3.3.4 保存数据

#函数说明:将爬取的文章内容写入文件
    def writer(self,name,path,text):
        write_flag=True
        with open(path,'a',encoding='utf-8') as f:
            f.write(name+'\n') #小说名字
            f.writelines(text)  #小说内容
            f.write('\n\n')

3.4 完善主函数,将数据存入TXT文件

    #主函数
if __name__=="__main__":
    dl=downloader()
    dl.get_download_url()
    print('《解药》开始下载:')
    for i in range(dl.nums):
        dl.writer(dl.names[i],'解药(测试).txt',dl.get_contents(dl.urls[i]))
        print(" 已下载第"+str (i+1)+ "章:%.3f%%" % float(i/dl.nums)+'\r') #改进
        sys.stdout.write("  已下载:%.3f%%" % float(i/dl.nums)+'\r')
        sys.stdout.flush()   
        #设置sleep时间间隔   
        time.sleep(0.5)
        #time.sleep(random.randint(5,10)) # 产生 5 到 10 的一个整数型随机数填入sleep函数

    print("《解药》下载完毕")

4.遇到的问题

4.1.出现这个报错:list index out of range在这里插入图片描述
翻译: 列表超出最大范围。
意思:假如一个列表 list 的长度为 2,你通过下标去取第三位( list[2] ),就会报错,超出范围。
这里通过下标[0]取第一个,报错了没有取到,说明这句正则表达式没有匹配到指定内容。
若解析元素不当,导致列表里面的元素为空事,就出现这种错误。
解决方法:再次检查自己的正则表达式或者解析元素是否正确,并且可以加一个判断语句:

if result[0]:
        text=result[0].replace('&nbsp;','').replace('<br />','')

或者

if texts[0]:
        print(texts[0].text.replace('\xa0'*8,'\n\n'))

还可以加异常捕获(这里就不详细阐述了)

try:
  {} #代码块
except Exception as e:
   print(e)

4.2. 小说不换行!
小说页面元素时这样的,但是用原来的代码输出的时候就没有换行
请添加图片描述

虽然原来的代码对&nbsp进行了替换。但是爬取的小说还是没有换行

texts=texts[0].text.replace('\xa0'*8,'\n\n')

请添加图片描述

解决方法:
自己设置将两个空格替换成换行

   texts=texts.replace('\u3000\u3000','\n\n') #将两个空格替换成换行

结果:
请添加图片描述

爬取小说的学习过程就到此为止了,撒花完结~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值