php多线程抓取多个网页(3.怎样支持分布式?暂时最简单的想法-苏州安嘉)

优采云 发布时间: 2022-01-26 02:16

  php多线程抓取多个网页(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

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线