通过JS + PHP实现简易小说采集

devtools/2024/10/9 3:58:03/

先申明下,这个只是用来作为采集的一个样本,请大家还是尊重知识产权,看正版的书籍。

一、简要说明:

主要用到:jQuery, PHP
主要思路:

1. 通过js来循环访问本地的php文件,并传输书本网址,编号等;

2. php获取小说站相应页面的内容,存入本地文件夹,并返回结果给js

3. js根据收到的结果来进行下一步处理,如果当前书本已经采集完,则采集下一本


注意点:

只能采集指定网址小说,并该网站网页结构固定且简单,否则可能会失败

同时目前只能每次都从第一章开始采集,虽然会跳过已保存过的,但是还需要进一步优化,比如:
通过最新更新的章节,获取到还有多少章需要采集,然后只采集对应的章节

比如从书本的首页获取到所有章节对应的链接,而不是单纯的从0开始循环,避免有的章节是跳过某个数字的,导致后面的中断

二、HTML js代码部分(记得要有jQuery文件) 

异步循环部分代码来自ai

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><script src="jquery-3.6.0.min.js" charset="utf-8"></script><title>实时数据采集与保存</title><style>#output, #output1 {display: inline-block;margin-top: 20px;padding: 10px;border: 1px solid #ddd;max-height: 600px;overflow-y: auto;width:400px;}</style></head><body><h1>实时数据采集与无刷输出</h1><div id="output1"><h4>采集中的书名(最底部的为正在采集的)</h4></div><div id="output">正在等待数据...</div><script>let url = new URL(window.location.href);// 使用URLSearchParams获取查询参数,这个主要是用来执行从第几本书开始let params = new URLSearchParams(url.search);//api_id是用来指定那本书用,这里是数字var api_id = params.get('id')if(api_id !=null && api_id !=""){//定义书籍网站地址,注意书籍对应的网址需要是有规律的数字,比如从1开始到99999const api = "https://想要采集的看书站网址/book/"// 调用函数开始数据采集  collectDataFromApi(api,api_id)  .then(() => {  console.log('数据采集结束');  })  .catch(error => {  console.error('数据采集失败', error);  });}else{displayData("未提供书本id"); }// 定义一个异步函数来获取数据  async function fetchData(api,api_id,idx) {  try {//通过ajax向后端php文件传输api并获取返回的数据const response = await $.ajax({ url:'get_book.php',   method:'POST', data:{'api':api,'api_id':api_id,'idx':idx},dataType:'JSON', }); console.log('res',response)//通过返回的code,输出不同的结果if(response.code==0){//这个不知道什么原因,无法获取displayData(response.msg,"书名")}if(response.code==1){if(response.bookname!=null){//当返回的数据中包含书名时,在前端输出当前采集的书名(只有第一章的时候才会返回)displayData(response.bookname,"书名")}//输出刚采集的章节名displayData(response.msg,"章节名"); } //返回给函数来执行相应操作return response} catch (error) {  //输出错误信息//这里不知道什么原因,第一次获取书籍信息的时候会报错误,第二次循环获取第一章的时候就不会console.error('错误:', error);  throw error; // 重新抛出错误以便上层调用者可以处理  }  }  // 定义一个异步函数来循环访问 API  async function collectDataFromApi(api,api_id) {  //循环传输章节页码并传给后端phpfor (var i=0;i<99999;i++) {  try {  const data = await fetchData(api,api_id,i);// 处理获取到的数据  //console.log('获取数据成功:', data); if(data.code ==2){//当php访问对应页面为空(即没有该页面时)则表示这本书采集完了,采集下一本书to_next(api,api_id) return;}// 你可以在这里添加其他逻辑,比如存储数据到数据库等  } catch (error) {  // 处理错误,例如记录日志或重试  console.error(`错误,跳转到下一轮`, error);  // 如果需要,可以在这里添加重试逻辑  }  }  }  //这个是重新开始新一本书籍的循环获取//只适用于书本网页id是有序通过+1的形式function to_next(api,api_id){collectDataFromApi(api,parseInt(api_id)+1) }//用于在前端网页显示当前采集的结果function displayData(data,type) {// 在页面中追加显示采集的数据if(type=="书名"){const output = document.getElementById('output1');const newEntry = document.createElement('div');newEntry.innerHTML = `正在采集:<strong>`+data+`</strong> `;output.appendChild(newEntry);// 保持滚动条在底部,方便查看最新数据output.scrollTop = output.scrollHeight;}else{const output = document.getElementById('output');const newEntry = document.createElement('div');newEntry.innerHTML = `<strong>`+data+`</strong> -- 已保存`;output.appendChild(newEntry);output.scrollTop = output.scrollHeight;}}</script></body>
</html>

 三、php部分

其中cut裁切获取内容部分,需要修改成你需要采集的网页内对应的标签字符串

php"><?php$api=$_POST['api'];$api_id = $_POST['api_id'];$idx =$_POST['idx'];
/*** 先分析是不是第一页* 是的话,就先获取书本基础信息,并将数据存入text* * 注意:当前cut截取只适用于代码中起止标签和要采集的网页标签对应的,需要根据自己找的网站自定义**/if($idx==0){$content = get($api.$api_id.'/');   //获取书籍首页全部内容$info = cut($content,'<div class="info">','<div class="link wap_none">');   //从上面的内容中截取介绍部分$cover  = cut($info,'src="','"');  //封面$book_name = cut($info,'<h1>','</h1>'); //书名$author = cut($info,'作者:','</span>'); //作者$status = cut($info,'状态:','</span>'); //状态$last_update = cut($info,'更新:','</span>'); //最后更新时间$new_chapter = cut($info,'最新:','</span>'); //最新章节$intro = cut($info,'<dd>','<span class="noshow"'); //简介$up_time = date('Y-m-d H:i'); //保存时间,用于记录你采集保存的时间$book_dir = "books/".$api_id.'_'.$book_name;  //书本存放目录//先创建book_id对应的文件夹,用于存放书内容文件if(!file_exists($book_dir)){mkdir($book_dir,0777,true); }//下载封面到本地$localPath = $book_dir.'/cover.jpg';$imgContent = file_get_contents($cover);file_put_contents($localPath, $imgContent);//拼接书籍基本信息,用来存到本地,方便后面获取$book_desc_info = "书名:".$book_name.chr(10)."作者:".$author.chr(10)."状态:".$status.chr(10)."最后更新时间:".$last_update.chr(10)."最新章节:".$new_chapter.chr(10)."简介:".$intro.chr(10)."上传时间:".$up_time.chr(10)."封面:".$cover.chr(10);//保存到本地,存成txt文件$book_desc= fopen($book_dir."/book_desc.txt", "w") or die("Unable to open file!");fwrite($book_desc, $book_desc_info);fclose($this_chapter_txt);echo ('{"code":0,"msg":"'.$book_name.'"}');}else{
/*** 然后再进行获取章节数据 **///拼接上书本id和当前需要获取的章节id$url = $api.$api_id.'/'.$idx.".html";//逻辑同上面,获取内容,裁切获取需要的内容//同样需要根据网页调整裁切的起止标签$chapter_content = get($url);if($chapter_content !=""){$book_name = cut($chapter_content,'<a href="/book/'.$api_id.'/">','</a>');$book_content = get_content($chapter_content);$this_chapter = cut($chapter_content,'<h1 class="wap_none">','</h1>');$this_chapter = str_replace("!",'!',$this_chapter);$this_chapter = str_replace("'",'"',$this_chapter);$this_chapter = str_replace("(",'(',$this_chapter);$this_chapter = str_replace(")",')',$this_chapter);$this_chapter = str_replace("?",'',$this_chapter);$this_chapter = str_replace("@",'',$this_chapter);$book_dir = "books/".$api_id.'_'.$book_name;//先创建book_id对应的文件夹,用于存放书内容文件if(!file_exists($book_dir)){mkdir($book_dir,0777,true); }//将章节名存入章节列表file_put_contents($book_dir."/chapter_list.txt",$idx."._.".$this_chapter.chr(10),FILE_APPEND);if(!file_exists($book_dir."/".$this_chapter.".txt") || filesize($book_dir."/".$this_chapter.".txt") ==0){$this_chapter_txt = fopen($book_dir."/".$this_chapter.".txt", "w") ;//or die("Unable to open file!");if($this_chapter_txt){//如果创建并打开当前章节的txt文件fwrite($this_chapter_txt, $book_content);//关闭该章节文档fclose($this_chapter_txt);if($idx==1){echo '{"code":1,"msg":"'.$this_chapter.'","bookname":"'.$api_id.'_'.$book_name.'"}';}else{echo '{"code":1,"msg":"'.$this_chapter.'"}'; }}else{file_put_contents($book_dir."/wrong_chapter.txt",$this_chapter.chr(10),FILE_APPEND);echo '{"code":1,"msg":"未成功保存当前章节:'.$this_chapter.'"}';}  }else{if($idx==1){echo '{"code":1,"msg":"该章节已存在:'.$this_chapter.'","bookname":"'.$api_id.'_'.$book_name.'"}';}else{echo '{"code":1,"msg":"该章节已存在:'.$this_chapter.'"}'; }}}else{echo '{"code":2,"msg":"获取章节结束"}';}}//获取文件详细内容function get_content($content){//设置截取的开始和结束$start1 = '<div id="chaptercontent" class="Readarea ReadAjax_content" style="font-size: 20px;">';$start2 = '<div id="chaptercontent" class="Readarea ReadAjax_content">';if(strpos($content,$start1)){$start=$start1;}elseif(strpos($content,$start2)){$start=$start2;}else{$start = '<div id="chaptercontent">';}$end = '<p class="readinline">';//进行截取$res =  cut($content,$start,$end);//判断处理双换行,转成数组if(strpos($res,'<br /><br />')){$temp_res = explode("<br /><br />",$res);}elseif(strpos($res,'<br />')){$temp_res = explode("<br />",$res);}//获取数组长度$rows = count($temp_res);//判断出包含qu70.cc的值,删除if(strpos(end($temp_res),'qu70.cc')){$temp_res = array_pop($temp_res);}elseif(strpos($temp_res[$rows-2],'qu70.cc')){unset($temp_res[$rows-2]);}//再转成分行的字符串$res = implode(chr(10),$temp_res);return ($res);  }//根据起止位置,裁切获取指定区间内容function cut($content,$start,$end) {$r = explode($start, $content);if (isset($r[1])) {$r = explode($end, $r[1]);return $r[0];}return '';}//Curl get请求function get($url) {$ch = curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.7 (KHTML, like Gecko) Chrome/20.0.1099.0 Safari/536.7 QQBrowser/6.14.15493.201');curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);$result = curl_exec($ch);curl_close($ch);return $result;}

这样,就会从你指定书籍id开始,不断循环获取,并保存到服务器books文件夹内了

采集中的前端页面效果如图

再次申明下,这个只是一个思路,只用于学习,再次希望大家尊重知识产权


http://www.ppmy.cn/devtools/123176.html

相关文章

【计算机网络】Tcp/IP五层协议,Udp报文组成,Udp与Tcp的区别

Tcp/IP五层协议 TCP/IP模型是计算机网络的核心协议之一&#xff0c;通常被分为五层&#xff0c;每一层都有其独特的功能和作用。以下是TCP/IP模型的五层协议的简要描述&#xff1a; 物理层&#xff1a;这一层涉及实际的物理连接&#xff0c;定义了硬件传输介质的特性&#xff…

[C++]使用纯opencv部署yolov11-pose姿态估计onnx模型

【算法介绍】 使用纯OpenCV部署YOLOv11-Pose姿态估计ONNX模型是一项具有挑战性的任务&#xff0c;因为YOLOv11通常是用PyTorch等深度学习框架实现的&#xff0c;而OpenCV本身并不直接支持加载和运行PyTorch模型。然而&#xff0c;可以通过一些间接的方法来实现这一目标&#x…

Linux中如何修改root密码

在 Linux 中&#xff0c;修改 root 用户密码可以通过以下步骤进行。你需要具有超级用户权限才能执行这些操作。 方法一&#xff1a;使用 passwd 命令修改 root 密码 使用具有超级用户权限的账户登录 如果你已经以 root 身份登录&#xff0c;或者你当前账户具备超级用户权限&am…

汽车电气系统中KL30、KL15、KL50、KLR、KL31、KL87、KL75的作用

目录 1、KL30 (Battery Positive Terminal) 2、KL15 (Ignition Switch, Positive) 3、KL50 (Starter Motor Terminal) 4、KLR (Ignition-Off Draw) 5、KL31 (Ground) 6、KL87 (Relay Output) 7、KL75 (Accessory) 在汽车电气系统中&#xff0c;KL系列的术语起源于德国&a…

《计算机原理与系统结构》学习系列——计算机的算数运算(下)

系列文章目录 目录 浮点数的表示和运算浮点数的表示浮点数的规格化浮点数标准IEEE754浮点数表示范围浮点数的转换浮点数的运算浮点数加法浮点数加法的硬件实现 精度浮点乘法浮点运算硬件 MIPS中的浮点指令 浮点数的表示和运算 浮点数的表示 表达非整型的数 可以表达很小和很大…

Prompt 模版解析:诗人角色的创意引导与实践

Prompt 模版解析&#xff1a;诗人角色的创意引导与实践 Prompt 模版作为一种结构化工具&#xff0c;旨在为特定角色——本例中的“诗人”——提供明确的指导和框架。这一模版详尽地描绘了诗人的职责、擅长的诗歌形式以及创作规则&#xff0c;使其能在自动化系统中更加精确地执…

k8s 之安装busybox

作者&#xff1a;程序那点事儿 日期&#xff1a;2024/02/12 14:56 busybox是linux的一个工具镜像&#xff0c;包含我们常用的一些工具。可以利用这个工具来做一些测试的操作。 安装命令 # 运行一个容器。 kubectl run -it --image busybox:1.28.4 dns-test --restartN…

深入理解Web浏览器与服务器的连接过程

目录 1. 域名解析&#xff1a;找到地址 2. TCP连接&#xff1a;建立通信 3. HTTP请求&#xff1a;点菜 4. 服务器处理请求&#xff1a;厨房做菜 5. HTTP响应&#xff1a;上菜 6. 客户端接收响应&#xff1a;品尝美食 7. 关闭TCP连接&#xff1a;吃完离开 8. 持久连接&a…