一、基本流程
1. 获取链接列表
2. 判断链接是否重复,并解析网页
3. 将数据写入数据库
4. 多线程并发执行
二、具体步骤
1. 获取链接列表
这一步比较简单,只需了解待爬网页特性即可,并把正确的链接等数据放入redis列表即可
//获取页面中文章网址等相关信息,并存入队列中
int start = html.indexOf(":[{") + ":[".length();
html = html.substring(start);
int end = html.indexOf("}]")+1;
//
if(end>0){
html = html.substring(0, end);
//System.out.println(html);
//用于检测字符串是否以指定的前缀开始
String str_html=html;
//System.out.println(html.length());
int str_end=0;
boolean flag=true;
while(flag&&str_html.length()>0&&str_html.startsWith("{")){
String s_html=null;
str_end=str_html.indexOf("}")+2;
if(str_end<str_html.length()){
s_html=str_html.substring(0, str_end-1);
str_html=str_html.substring(str_end);
}
else{
flag=false;
s_html=str_html;
}
if(html.startsWith("{"))
{
JSONObject info=JSONObject.fromObject(s_html);
//System.out.println(info.toString());
info.put("local3",cid_name);
//System.out.println(info.toString().getBytes());
ju.lpush(redisKey_wemedia, info.toString().getBytes());
}
}
}
2. 判断链接是否重复,并解析网页
这里用到了布隆筛选器,不过不是我自己写的代码。通过某一键的多个哈希值来判断该键是否存在,若有一个哈希结果显示该键不存在,则这个键一定不存在,但反之不一定。通过这个筛选器判断出一条链接信息是否需要加入数据库队列中
3. 将数据写入数据库
从数据库队列中拿出链接数据,解析链接下的网页,并写入数据库中,或者其他地方。
//解析url,获取正文
public String getContent(String url){
String content=null;
ArrayList<header> list=new ArrayList<header>();
header h=new header("User-Agent","Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Mobile Safari/537.36");
list.add(new header("Host","html2.qktoutiao.com"));
//list.add(new header("Connection","keep-alive"));
list.add(h);
String html=pageDown.Pageinfo(url, list,"utf-8");
// System.out.println(url);
// System.out.println(html);
Document doc=Jsoup.parse(html,url);
Elements es=doc.getElementsByTag("p");
content= es.text();
return content;
}
注意的是最好保证从网页中获取的数据都不是空的,特别是正文和标题
4. 多线程并发执行
设置多个线程并发执行,要注意隔一段时间监测是否有线程挂掉了,对于挂了的重新new一个
//任务调度
ArrayList<Thread> allThreadsListSd = new ArrayList<Thread>();
ArrayList<CrawlSysScheduler> allSimpleCrawlerListSd = new ArrayList<CrawlSysScheduler>();
for (int i = 0; i < slaveNumSd; i++) {
CrawlSysScheduler tmpSimpleCrawler = new CrawlSysScheduler();
allSimpleCrawlerListSd.add(tmpSimpleCrawler);
allThreadsListSd.add(new Thread(tmpSimpleCrawler));
}
for (int i = 0; i < slaveNumSd; i++) {
allThreadsListSd.get(i).start();
}
//写入数据库
ArrayList<Thread> allThreadsListDB = new ArrayList<Thread>();
ArrayList<DBtask> allSimpleDBList = new ArrayList<DBtask>();
for (int i = 0; i < slaveNumDB; i++) {
DBtask tmpSimpleCrawler = new DBtask();
allSimpleDBList.add(tmpSimpleCrawler);
allThreadsListDB.add(new Thread(tmpSimpleCrawler));
}
for (int i = 0; i < slaveNumDB; i++) {
allThreadsListDB.get(i).start();
}
while (true) {
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0; i < slaveNumSd; i++) {
if (!allThreadsListSd.get(i).isAlive()) {
System.out.println("allThreadsListSd"+i);
allThreadsListSd.remove(i);
allSimpleCrawlerListSd.remove(i);
CrawlSysScheduler tmpSimpleCrawler = new CrawlSysScheduler();
allSimpleCrawlerListSd.add(i, tmpSimpleCrawler);
Thread tmpThread = new Thread(tmpSimpleCrawler);
tmpThread.start();
allThreadsListSd.add(i, tmpThread);
}
}
for (int i = 0; i < slaveNumDB; i++) {
if (!allThreadsListDB.get(i).isAlive()) {
System.out.println("allThreadsListDB"+i);
allThreadsListDB.remove(i);
allSimpleDBList.remove(i);
DBtask tmpSimpleCrawler = new DBtask();
allSimpleDBList.add(i, tmpSimpleCrawler);
Thread tmpThread = new Thread(tmpSimpleCrawler);
tmpThread.start();
allThreadsListDB.add(i, tmpThread);
}
}
}
我这只写了两种任务线程,其中1为单独一个,2.3合并在一起为一个,后期优化时可以适当分工,加快处理速度