php多线程抓取多个网页(3.怎样支持分布式?暂时最简单的想法-苏州安嘉)
优采云 发布时间: 2022-01-26 02:16php多线程抓取多个网页(3.怎样支持分布式?暂时最简单的想法-苏州安嘉)
一、要求
1.定期抓取网站新闻标题、内容、发布时间和来源。
2.程序需要支持分布式、多线程
二、设计
1.网站是固定的,但是以后可能会添加新的网站来抢,每个网站内容节点设计都不一样,所以需要支持动态配置才能添加网站 以方便未来的扩展,每次都需要开发干预。
2.网站html 节点的结构可能会发生变化,因此也可以支持可配置的提取节点。
3.如何支持分布式?暂时最简单的思路就是在多台机器上部署一个程序,新建一个或者一个部署程序来创建一个定时任务,在某个时间开始每台机器应该抓取哪一个网站 ,暂时不能支持同一个网站@网站同时可以支持被多台机器同时抓取,会比较麻烦,使用分布式队列。所以暂时一个 网站 只会被单台机器同时抓取。
4.多线程,如何多线程?多线程爬取我这里有两种实现:
(1)一个线程抓取一个网站,维护自己的url队列进行广度抓取,同时抓取多个网站。如图:
(2)多个线程同时抓取不同的网站。如图:
以上两种方法其实各有优缺点。这取决于我们如何选择。
方法一:每个线程创建自己的队列。图中的队列可以不用concurrentQueue。优点:不涉及控制并发。每个网站线程抓取一个网站,抓取完成后销毁线程自动回收。易于控制。缺点:无法扩展线程数。比如只有3个网站时,最多只能开3个线程去抢,不能开更多,有一定的局限性。
方法二:N个线程同时抢N个网站s,线程数与网站s个数无关。优点:线程数可以调整,与抓取网站的个数无关。3 网站我们可以开 4 5 或者 10 这个可以根据你的硬件资源来调整。缺点:需要控制并发,并且控制什么时候销毁线程(thread1空闲,队列为空不代表任务可以结束,可能thread2的结果还没有返回),当捕获到网站响应慢,会拖慢整个爬虫的进度。
三、实现
爬取方式最终选择了方式二,因为线程数是可以配置的!
使用的技术:
用了jfinal后,发现这个东西不太合适,但是由于项目进度问题,还是用了。
maven项目管理
码头服务器
mysql
日食发展
项目需要关注的难点:
(1)合理控制N个线程正常抓取网站,当所有线程工作完成且待抓取队列为空时,N个线程同时退出并销毁。
(2)不同网站设计节点不同,需要为每个网站配置需要抓取的URL以及抓取的节点内容在html节点中的位置。
(3)个性化内容处理,由于html结构设计问题,爬取的内容可能会有一些多余的html标签,或者如何处理多余的内容。
实现一:线程管理建立一个以线程为中心的管理控制器,负责线程销毁。如图所示:
(1)创建一个中央管理器,管理器存储N个线程的信号数组标记来标记线程是否空闲,并创建N个标记的线程切换。用于同时结束N个线程。
(2)线程开始抓取请求链接时设置idle为false,抓取链接后继续循环取队列(循环过程中判断开关是否为真)。当轮询结果从队列为空,把线程设为idle=true,并休眠1S(看个人喜好)。
(3)线程中心中央管理器调度定时检测任务,每1s检测一次线程的信号数组tag和queue.size(),此时queue.size()==0且数组tag全部标记为 idle ,将线程切换器设置为 false (关闭,所以线程将结束 while 退出)
如图,线程在4个线程状态后可以自行退出:
核心控制函数和线程函数代码如下:
CoreTaskController.java
<p>import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.log4j.Logger;
import com.crawler.core.conf.CrawlerConf;
/**
* 任务核心控制器
* @author Jacky
* 2015-9-30
*/
public class CoreTaskController {
public static Logger log = Logger.getLogger(CoreTaskController.class);
/**
* 信号量,用来标记当前线程是否空闲
* 0表示空闲
* 1表示繁忙
* CrawlerConf.getMaxT()表示获取最大线程数
*/
public static volatile int[] signal = new int[CrawlerConf.getMaxT()];;
public static volatile boolean totalSwitcher = true;
/**
* 线程开关
* 1表示打开
* 0表示关闭
*/
public static volatile int[] switcher = new int[CrawlerConf.getMaxT()];
public static Timer timer;
public static void init() {
// 开启所有线程开关
for(int i=0;i