接上一节。
网页数据的分析是根据需要来处理,比如说你是抓取某个小说站点的小说内容,那你就需要去分析网页上小说内容的特定标识,然后根据标识获取小说内容。
我这里就简单的直接分析URL上的关键字来进行信息抓取。
需求
抓取URL上带有news或者blog的网页信息,将其整个页面信息保存到文件中。
配置
- 在SpiderConfig类中添加配置:
1 2 3 4 5 6 7 8
|
public int minerThreadNum = 2;
public List<String> keys;
|
- 修改application.properties(.yml),增加新配置属性,如下图:
队列管理
SpiderQueue中增加存储队列,主要方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
private static volatile Queue<SpiderHtml> store = new LinkedList<SpiderHtml>();
public synchronized static void addStore(SpiderHtml html) { store.add(html); }
public synchronized static SpiderHtml storePoll() { return store.poll(); }
public static boolean storeIsEmpty() { return store.isEmpty(); }
|
抓取分析任务
主要作用是将等待队列中的URL拉去出来,依次再抓取网页信息,分析URL关键字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
| package mobi.huanyuan.spider.runable;
import mobi.huanyuan.spider.SpiderApplication; import mobi.huanyuan.spider.SpiderQueue; import mobi.huanyuan.spider.bean.SpiderHtml; import mobi.huanyuan.spider.config.SpiderConfig; import org.apache.commons.lang3.StringUtils; import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import org.slf4j.Logger; import org.slf4j.LoggerFactory;
import java.util.HashSet; import java.util.List; import java.util.Set;
public class SpiderParseHtmlRunnable implements Runnable { private static final Logger logger = LoggerFactory.getLogger(SpiderParseHtmlRunnable.class);
private SpiderConfig config;
public SpiderParseHtmlRunnable(SpiderConfig config) { this.config = config; }
@Override public void run() { while (!SpiderApplication.isStopping) { parse(); } }
private synchronized void parse() { SpiderHtml html = SpiderQueue.waitingMinePoll(); if (null == html || StringUtils.isBlank(html.getHtml())) { return; } if (html.getDepth() < config.getMaxDepth()) { logger.info("获取页面[{}]下所有URL。。。。。。 当前线程 [{}]", html.getUrl(), Thread.currentThread().getName()); Set<String> urls = getAllUrl(html.getUrl()); for (String url : urls) { if (null == url || url.equals("")) { continue; } if (url.substring(url.length() - 1).equals("/")) { url = url.substring(0, url.length() - 1); }
SpiderHtml minerUrl = new SpiderHtml(); minerUrl.setUrl(url); minerUrl.setDepth(html.getDepth() + 1); if (!checkKeys(url, config.getKeys())) { continue; } SpiderQueue.addUnVisited(minerUrl); SpiderQueue.addUrlSet(minerUrl.getUrl()); } } }
public static Set<String> getAllUrl(String url) { Set<String> urls = new HashSet<>(); try { Connection conn = Jsoup.connect(url); conn.header("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.27 Safari/525.13"); Document document = conn.timeout(5000).get(); Elements hrefs = document.select("a[href]"); for (Element href : hrefs) { urls.add(href.attr("href")); } } catch (Exception e) { logger.info("获取URL出现异常,异常URL[{}]", url); logger.info("异常信息[{}]", e.getMessage()); } return urls; }
public static boolean checkKeys(String key, List<String> keys) { boolean flag = false; for (String k : keys) { if (key.contains(k)) { flag = true; break; } } return flag; } }
|
爬虫主类修改
修改Spider的start方法,增加分析线程逻辑。
1 2 3 4 5
| for(int i = 0; i < spiderConfig.getMinerThreadNum(); i++){ SpiderParseHtmlRunnable parseHtmlRunnable = new SpiderParseHtmlRunnable(spiderConfig); threadPoolTaskExecutor.execute(parseHtmlRunnable); }
|
监控线程
这里增加一个监控线程,作用就是在爬虫队列里边的数据处理完成之后,监控线程关闭线程池、停止运行程序。
在springboot的main方法中添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
threadPoolTaskExecutor = (ThreadPoolTaskExecutor) context.getBean("threadPoolTaskExecutor"); threadPoolTaskExecutor.execute(() -> { while (!isStopping) { try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } if (SpiderQueue.unVisitedIsEmpty() && SpiderQueue.waitingMineIsEmpty() && SpiderQueue.storeIsEmpty()) { isStopping = true; threadPoolTaskExecutor.shutdown(); logger.info("程序结束。。。。。。当前线程[{}]", Thread.currentThread().getName()); long endTime = System.currentTimeMillis(); logger.info("已经访问队列URL大小[{}]当前线程[{}]", SpiderQueue.getUrlSetSize(), Thread.currentThread().getName()); logger.info("用时[{}ms]当前线程[{}]", endTime - starTime, Thread.currentThread().getName());
context.close(); System.exit(0); } } });
|