爬虫新姿势——使用Chrome Devtools写一个小说爬虫

目前,绝大部分的爬虫教程都是基于Python和Node.js。其实,只要有Chrome浏览器,使用Chrome F12打开的的Devtools就能随时随地轻轻松松写一个爬虫,完全不用装其它语言环境。今天就介绍一下只使用Chrome Devtools来爬取网站www.biqudu.com/31_31729/小说并保存为文本文件的爬虫。

如何在Chrome Devtools里面写爬虫代码

Devtools提供了Snippets功能让我们可以在这里写JavaScript代码,步骤参考下图:

步骤说明

  1. 打开source标签

  2. 左侧选择Snippets标签

  3. 点击New Snippets新建一个Snippets

  4. 开始写代码

  5. 点击运行代码

  6. 查看控制台输出


准备爬虫工具函数

1.加载第三方库

根据Url加载一个第三方库,可以用这个函数加载jquery,underscore等工具库,加载完成后就可以在代码中使用这些库了,本例中使用这个函数加载async异步并发控制库。

复制代码
async function loadLibrary(url) {
    return new Promise((resolve, reject) => {
        let script = document.createElement('script');
        script.onload = resolve;
        script.onerror = reject;
        script.src = url;
        document.body.appendChild(script);
    });
}

2.下载文件到本地

将string下载到文本文件

复制代码
function saveFile(string, fileName) {
    var a = document.createElement('a');
    a.download = fileName;
    var blob = new Blob([string], {
        type: 'text/plain'
    });
    a.href = window.URL.createObjectURL(blob);
    a.click();
}

3.下载HTML

使用了Fetch api,根据url下载一个html文本文件并转换成DOM元素后返回,返回的元素具有DOM api,例如 querySelector,方便对节点的提取和分析。

复制代码
async function getHtml(url) {
    let response = await fetch(url);
    let htmlText = await response.text();
    let html = document.createElement('html');
    html.innerHTML = htmlText;
    return html;
}

准备爬虫业务函数

1.获取小说的所有章节信息

分析小说主页www.biqudu.com/31_31729/,

通过document.querySelectorAll('#list dd a') 可以获取包含所有章节名称和链接的a标签元素。

复制代码
async function getDirectory(url) {
    let page = await getHtml(url);
    let directory = Array.from(page.querySelectorAll('#list dd a'));
    //去除顶部最新12个章节
    return directory.slice(12);
}

2.获取一个章节的内容

分析小说章节 www.biqudu.com/31_31729/21...,章节内容位于ID为content的DIV元素中

复制代码
async function getSection({ href, innerText: title }) {
    console.log(`开始获取 ${title}`);
    let html = await getHtml(href);
    let content = html.querySelector('#content');
    Array.from(content.querySelectorAll('script')).forEach(scriptTag => content.removeChild(scriptTag));
    var text = title + '\r\n' + content.innerText + '\r\n';
    return text;
}

完整代码

因为小说有几百几千章节,不可能一个一个章节下载,那样速度太慢了。也不能一下子全下载。所以

爬取时使用了async异步并发控制库(这个async和async function里面的async只是名字一样而已),并发数量为6,设置大了也没用因为Chrome浏览器对同一域名下的同时请求数量是6。

完整代码运行步骤

  1. Chrome浏览器打开小说主页如:www.biqudu.com/31_31729/

  2. 在小说主页页面打开Devtools 新建snippets并将下面的完整代码粘贴进去

  3. 点击运行代码开始爬取小说

    (async function () {
    // https://www.biqudu.com/31_31729/
    async function loadLibrary(url) {
    return new Promise((resolve, reject) => {
    let script = document.createElement('script');
    script.onload = resolve;
    script.onerror = reject;
    script.src = url;
    document.body.appendChild(script);
    });
    }

    复制代码
     function saveFile(string, fileName) {
         var a = document.createElement('a');
         a.download = fileName;
         var blob = new Blob([string], {
             type: 'text/plain'
         });
         a.href = window.URL.createObjectURL(blob);
         a.click();
     }
    
     async function getHtml(url) {
         let response = await fetch(url);
         let htmlText = await response.text();
         let html = document.createElement('html');
         html.innerHTML = htmlText;
         return html;
     }
    
     async function getDirectory(url) {
         let page = await getHtml(url);
         let directory = Array.from(page.querySelectorAll('#list dd a'));
         //去除顶部最新12个章节
         return directory.slice(12);
     }
    
     async function getSection({ href, innerText: title }) {
         console.log(`开始获取 ${title}`);
         let html = await getHtml(href);
         let content = html.querySelector('#content');
         Array.from(content.querySelectorAll('script')).forEach(scriptTag => content.removeChild(scriptTag));
         var text = title + '\r\n' + content.innerText + '\r\n';
         return text;
     }
    
     async function run() {
         let asyncLibUrl = 'https://cdn.bootcss.com/async/2.1.4/async.js';
         await loadLibrary(asyncLibUrl);
         let directory = await getDirectory(location.href);
         let q = window.async.queue(async function (section, taskDone) {
             try {
                 section.text = await getSection(section);
             } catch (e) {
                 console.error(e);
                 section.text = "章节下载失败:" + e;
             } finally {
                 taskDone();
             }
         }, 6);//并发送设成6
    
         q.drain = function () {
             let name = document.querySelector('#maininfo h1').innerText + '.txt';
             console.log(`小说《${name}》下载完成`);
             let content = "";
             directory.forEach(function ({ text }) {
                 content += text;
             });
             saveFile(content, name);
         }
    
         q.push(directory);
     }
    
     await run();

    }());

相关推荐
Moment4 分钟前
给大家推荐一个超好用的 Marsview 低代码平台 🤩🤩🤩
前端·javascript·github
小满zs8 分钟前
Zustand 第三章(状态简化)
前端·react.js
普宁彭于晏10 分钟前
元素水平垂直居中的方法
前端·css·笔记·css3
Tianyanxiao13 分钟前
华为×小鹏战略合作:破局智能驾驶深水区的商业逻辑深度解析
大数据·人工智能·经验分享·华为·金融·数据分析
恋猫de小郭21 分钟前
为什么跨平台框架可以适配鸿蒙,它们的技术原理是什么?
android·前端·flutter
云浪25 分钟前
元素变形记:CSS 缩放函数全指南
前端·css
一只小波波呀37 分钟前
打卡第48天
python
whoarethenext39 分钟前
C++ OpenCV 学习路线图
c++·opencv·学习
明似水40 分钟前
用 Melos 解决 Flutter Monorepo 的依赖冲突:一个真实案例
前端·javascript·flutter
钮钴禄·爱因斯晨1 小时前
Java 面向对象进阶之多态:从概念到实践的深度解析
java·开发语言·数据结构