爬虫新姿势——使用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();
    

    }());

相关推荐
学不会•1 小时前
css数据不固定情况下,循环加不同背景颜色
前端·javascript·html
Theodore_10222 小时前
4 设计模式原则之接口隔离原则
java·开发语言·设计模式·java-ee·接口隔离原则·javaee
网易独家音乐人Mike Zhou3 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
安静读书3 小时前
Python解析视频FPS(帧率)、分辨率信息
python·opencv·音视频
zy张起灵3 小时前
48v72v-100v转12v 10A大功率转换电源方案CSM3100SK
经验分享·嵌入式硬件·硬件工程
活宝小娜4 小时前
vue不刷新浏览器更新页面的方法
前端·javascript·vue.js
程序视点4 小时前
【Vue3新工具】Pinia.js:提升开发效率,更轻量、更高效的状态管理方案!
前端·javascript·vue.js·typescript·vue·ecmascript
coldriversnow4 小时前
在Vue中,vue document.onkeydown 无效
前端·javascript·vue.js
我开心就好o4 小时前
uniapp点左上角返回键, 重复来回跳转的问题 解决方案
前端·javascript·uni-app
----云烟----4 小时前
QT中QString类的各种使用
开发语言·qt