Python爬虫系列-爬取小说(解决遇到cookie验证爬取不了的问题)

前段时间五一放假,在家没事干,在哔哩哔哩上瞎看,发现一个搞笑视频合集不错,闲来无事正好晚上催眠用,名称叫《我靠打爆学霸兑换黑科技》,里面的男主角生为一个高中生学习天赋惊人,各种逆袭外挂的人生,看的令人神往。链接如下:雯锐动画投稿视频-雯锐动画视频分享-哔哩哔哩视频 (bilibili.com)icon-default.png?t=N7T8https://space.bilibili.com/357072740/video

 可惜动画更新的很慢,所以我直接搜索看后面章节的小说。经常看小说的都知道,看免费小说网站的广告多,我一般都是花个半天写了个脚本,爬取下来看,所以也就有了这篇文章。网站界面如下:

本来应该是很简单的脚本,但是这个网站对每一章的小说有cookie验证,导致用传统的requests库爬不完整,服务器发现cookie不正确后,会把缺失的html返回给我。如下图,可以看到连requests返回的body都是不对的,更别提后面的了。

使用chrome浏览器的开发者工具(F12快捷键打开),发现打开单个章节前面并没有javascript文件调用(也就是说小说内容不是通过javascript生成的),也没有其他网址调用,直接就返回了html超链接文本,如下:

尝试了使用requests加headers请求头、session等方法都失败。因为网站有cookie验证,如果用requests库模拟有点难实现,如下图:

既然有cookie校验,那就使用selenium和ChromeDriver来爬取即可。不了解的朋友,可用把selenium想象成一个可以用python代码控制的Chrome浏览器,它是有界面的(我喜欢叫它有头版爬虫),requests相对的就是无头版爬虫(headless),通过下面的方法可以安装。

安装 Selenium 需要进行以下几个步骤:

  1. 安装 Selenium 包
  2. 安装浏览器驱动程序(例如 ChromeDriver 或 GeckoDriver)

以下是详细步骤:

1. 安装 Selenium 包

你可以使用 pip 来安装 Selenium 包。打开终端或命令提示符,然后运行以下命令:

pip install selenium

2. 安装浏览器驱动程序

Selenium 需要一个浏览器驱动程序来控制浏览器。常用的浏览器驱动程序有:

  • ChromeDriver:用于控制 Google Chrome 浏览器
  • GeckoDriver:用于控制 Mozilla Firefox 浏览器
安装 ChromeDriver
  1. 访问 https://chromedriver.chromium.org/downloads
  2. 根据你的操作系统下载相应版本的 ChromeDriver。
  3. 解压缩下载的文件,将可执行文件放在一个目录中,并将该目录添加到系统的 PATH 环境变量中。

或者,你可以使用 webdriver-manager 自动安装和管理 ChromeDriver:

pip install webdriver-manager

使用 webdriver-manager 来自动下载和使用 ChromeDriver,示例代码如下:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# 自动下载和启动 ChromeDriver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

# 打开一个网页
driver.get('https://www.baidu.com')

# 关闭浏览器
driver.quit()

我们通过上面的分析可以知道小说都在网页元素id为"nr"的里面,所以使用上面的selenium例子,我们可以编写如下代码启动selenium,打开我们需要的小说章节,并保存下小说内容:

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# 自动下载和启动 ChromeDriver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

def getText(name,url):
    starttime = time.time()
    # 打开一个网页
    driver.get(url)

    try:
        element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,'nr')))
        txts = element.text.replace("\n","").replace("    ","\n")# 这两句是格式化小说文本,输出到txt文档里保存
        txts = txts + "\n" if txts[1] == "第" else name + txts + "\n\n"
        with open("我靠打爆学霸兑换黑科技.txt",'a+',encoding="utf-8") as f:
            f.write(txts)
            f.close()
        endtime = time.time()
        print(name,"已完成!",f"耗时{(endtime - starttime)}秒.")
    except TimeoutError:
        print("Time out!")


if __name__ == "__main__":
    getText("第1章 这新生有点东西(第1/2页)","https://www.keleshuba.net/book/386750/219595519.html")

这里最关键的一句就是如下:

element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,'nr')))

 这行代码使用了 Selenium 的显式等待(Explicit Wait)功能,以确保在执行后续操作之前,页面上特定的元素已经加载并存在。下面是逐步解释:
1. WebDriverWait(driver, 10)
WebDriverWait 是一个等待类,它会反复检查某个条件直到它成立(例如,某个元素存在),或者达到最大等待时间。
driver 是你的 WebDriver 实例,用于控制浏览器。
10 是最大等待时间(以秒为单位)。在这段时间内,WebDriver 会每隔一段时间(10秒)检查一次条件是否成立。
2. .until(EC.presence_of_element_located((By.ID, 'nr')))
.until 方法接受一个等待条件,在条件满足之前会一直等待。
EC 代表 expected_conditions 模块,包含了一组预定义的条件供显式等待使用。
EC.presence_of_element_located 是一个条件,表示要等待某个元素出现在 DOM 中,但不一定可见。
(By.ID, 'nr') 是定位元素的方式:
By.ID 指定使用元素的 ID 属性来查找。
'nr' 是目标元素的 ID 值

所以上面简单说,就是每10s一次检查id为"nr"的元素是否出现。

然后我们分析下小说的章节目录页,这个没有cookie校验,普通的requests就能实现,我们把里面的章节名和网址拿出来,就能传入上面的getText获得每一章的小说内容。

代码如下:

import requests
from bs4 import BeautifulSoup as bs

def getMainUrl(Mainurl):
    res = requests.get(Mainurl)
    soup = bs(res.content,"html.parser")
    datas = soup.select("ul#allChapters2.chapter li a")
    for data in datas:
        name = data.text
        url = data["href"]
        print(name,Mainurl + url)
        getText(name,Mainurl + url)

if __name__ == "__main__":
    getMainUrl("https://www.keleshuba.net/book/386750/")

代入getText前,先用print输出看看章节名称和网址对不对,如下图:

最后我们把所有的东西都拼接到一起,完整代码如下:

import requests
from bs4 import BeautifulSoup as bs
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time

# 自动下载和启动 ChromeDriver
service = Service(ChromeDriverManager().install())
driver = webdriver.Chrome(service=service)

def getMainUrl(Mainurl):
    res = requests.get(Mainurl)
    soup = bs(res.content,"html.parser")
    datas = soup.select("ul#allChapters2.chapter li a")
    for data in datas:
        name = data.text
        url = data["href"]
        getText(name,Mainurl + url)
        
def getText(name,url):
    starttime = time.time()
    # 打开一个网页
    driver.get(url)

    try:
        element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID,'nr')))
        txts = element.text.replace("\n","").replace("    ","\n")# 这两句是格式化小说文本,输出到txt文档里保存
        txts = txts + "\n" if txts[1] == "第" else name + txts + "\n\n"
        with open(r"E:\我靠打爆学霸兑换黑科技.txt",'a+',encoding="utf-8") as f:
            f.write(txts)
            f.close()
        endtime = time.time()
        print(name,"已完成!",f"耗时{(endtime - starttime)}秒.")
    except TimeoutError:
        print("Time out!")


if __name__ == "__main__":
    starttime = time.time()
    getMainUrl("https://www.keleshuba.net/book/386750/")
    endtime = time.time()
    print(f"已全部完成,耗时 {(endtime - starttime) / 60} 分钟!")

 上面的代码很简单,希望对喜欢爬虫的朋友有借鉴作用,谢谢观看,再见! 

PS:

with open(r"E:\我靠打爆学霸兑换黑科技.txt",'a+',encoding="utf-8") as f:

 这句有小说保存路径,请改成自己的。

2024.07.03更新:
这篇的爬虫已失效,请移步至新帖查看方法:
Python爬虫系列-爬取小说20240703更新(Request方法)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虫鸣@蝶舞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值