转载请注明出处http://blog.csdn.net/qq_26525215
本文源自【大学之旅_谙忆的博客】
简单的通过关键字爬出百度知道的一些搜索数据。
例如问题提问时间答案文本答案时间点赞数拍砖数回答人回答人级别搜索的关键字等。
答案可以有多个每个问题有多个答案应都保存。保存数据在MySql中。
在这里需要用到一个牛人的爬虫框架
WebMagic
网址http://webmagic.io/docs/zh/
我用的是IEDA工具建立的是Maven项目。
要搞爬虫一些基础的学习是不可少的比如HTMLJSP等前台知识知道怎么解析xml文档(利用XPath)用开发工具(推荐Intellij IDEA)MySQLMaven也可以学习等等。
下面就开始讲怎么抓取百度知道的数据了。
我们用百度知道搜索的时候可以看地址栏
https://zhidao.baidu.com/search?ct=17&pn=0&tn=ikaslist&rn=10&word=%E7%A4%BE%E4%BF%9D%E8%BD%AC%E7%A7%BB&fr=wwwt
这里关键词的变量名是word。那么其他变量与值可以不用管它。
缩减一下地址
https://zhidao.baidu.com/search?word=社保迁移
还有重要的一点是百度知道的html的编码是GBK的注意编码不要搞错了。
这个时候我们就可以利用这个地址来进行一些事情了。
需要学会用浏览器查看网页源码哦。
通过这种方式我们可以找出标题的规律进而通过XPath解析得到标题名
这些数据(标题问题描述答案时间等)都是可以通过上面方式得到的
这里有个小问题就是点赞数和拍砖数这样是找不到正确的方式的因为那里是动态生成。
在这里做一个演示
通过火狐浏览器的查看元素源码是这样的。
通过在地址栏前面加: view-source: 可以看网页源码。
一般点击鼠标右键可以直接看网页源码。
这个时候我们并不可以在源码中看到刚刚的代码。
也就是说我们抓取网页的源码的时候获取到的html也是没有那个的怎么办这个时候我们会发现
<span
alog-action="qb-zan-btnbestbox"
class="iknow-qb_home_icons evaluate evaluate-32
"
id="evaluate-2225390772"
data-evaluate="7"
>
</span>
data-evaluate=”7”
正好与点赞数7一样
继续对比一下其他页面会发现这个属性的值就是点赞数
那么好办了通过XPath然后加工一下一样可以获取此值。
拍砖数一样的分析然后获取就行
至于其他的数据差不多都是这样分析的。
开始本来想做成Web应用结果WebMagic爬虫框架只能在main线程才能运行所以就只做了一个简单的用main函数启动的应用了。
用到了MyBatis和Spring。主要是为了操作数据库简单一点。
你可以不用框架的主要代码只有这个类的代码
package cn.hncu;
import cn.hncu.model.Answer;
import cn.hncu.model.Keyword;
import cn.hncu.model.KeywordProblemKey;
import cn.hncu.model.Problem;
import cn.hncu.service.KeywordService;
import cn.hncu.service.KeywordServiceImpl;
import cn.hncu.tool.*;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Controller;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Html;
import javax.xml.xpath.XPath;
import java.io.IOException;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* User: 陈浩翔.
* Date: 2017/2/27.
* Time: 下午 6:29.
* Explain:
*/
public class Entrance implements PageProcessor {
private Logger logger = LoggerFactory.getLogger(Entrance.class);
private static ApplicationContext context;
private static KeywordService keywordService;
//搜索的关键字
private static String keyword = "社保转移";
//百度知道的搜索前缀
private static String url = "https://zhidao.baidu.com/search?word=";
//设置抓取页数
private static Integer pages = 20;
private Site site = Site.me().setCharset("gbk").setRetrySleepTime(4).setSleepTime(100);
private static String keywordId ;
private List<Map<String,String>> lists;
@Override
public void process(Page page) {
Html html = page.getHtml();
//问题
String problem = html.xpath("//span[@class='ask-title']/text()").toString();
logger.info("问题"+problem);
//提问者
String problemAnthor = html.xpath("//a[@alog-action='qb-ask-uname']/text()").toString();
//logger.info("提问者:"+problemAnthor);
//问题描述
String problemDescribe = html.xpath("//span[@class='con']/text()").toString();
//logger.info("问题描述:"+problemDescribe);
//提问时间
if(problem==null) {
lists = new ArrayList<Map<String, String>>();
List<String> strs = html.xpath("//dd/span[@class='mr-8']/text()").all();
List<String> quizTimes = new ArrayList<String>();
for (int i=0;i<strs.size();i++) {
//logger.info(strs.get(i)+" "+strs.get(i).matches("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]"));
if(strs.get(i).matches("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]")){
quizTimes.add(strs.get(i));
}
}
List<String> portionProblems = html.xpath("//a[@class='ti']/text()").all();
//logger.info("quizTimes.size():" + quizTimes.size());
//logger.info("portionProblems.size():" + portionProblems.size());
if(quizTimes.size()==portionProblems.size()) {
for (int i=0;i<quizTimes.size();i++) {
Map<String,String> map = new HashMap<String,String>();
map.put("quizTimes",quizTimes.get(i));
map.put("portionProblems",portionProblems.get(i));
lists.add(map);
//logger.info("部分问题:" + lists.get(i).get("portionProblems"));
//logger.info("时间:" + lists.get(i).get("quizTimes"));
}
}
}
//最佳回答
String bestAnswer =html.xpath("//pre[@class='best-text mb-10']").toString();//图片文字无法处理
if(bestAnswer==null){
bestAnswer =html.xpath("//div[@class='best-text mb-10']").toString();
}
//logger.info("最佳回答:"+bestAnswer);
//回答者
String answerAuthor = html.xpath("//a[@class='mavin-name f-14 mr-10']/text()").toString();
//如果回答者为NULL-有可能是其他的没有认证的人回答的
if(answerAuthor==null){
answerAuthor = html.xpath("//div[@class='grid f-aid ff-arial']/p/a[@class='user-name']/text()").toString();
}
//如果回答者还是为NULL-有可能是网上的人贡献的
if(answerAuthor==null){
answerAuthor = html.xpath("//div[@class='grid f-aid ff-arial']/p[@class='mb-5']/span/text()").toString();
}
//logger.info("回答者:"+answerAuthor);
//回答者等级
String anthorGrade = html.xpath("//a[@class='mavin-reply-icon f-orange']/text()").toString();
//如果回答者等级为NULL-有可能是其他的没有认证的人回答的
if(anthorGrade==null){
anthorGrade = html.xpath("//div[@class='grid f-aid ff-arial']/p[@class='mb-5']/a[@class='i-gradeIndex']").toString();
if(anthorGrade!=null){
anthorGrade = "LV"+anthorGrade.substring(anthorGrade.lastIndexOf("Index-")+6,anthorGrade.lastIndexOf(" mr-10"));
}
}
if(answerAuthor!=null&&anthorGrade==null){
anthorGrade ="0";
}
//logger.info("回答者等级:"+anthorGrade);
//推荐时间
String answerTime = html.xpath("//span[@class='grid-r f-aid pos-time answer-time f-pening']/text()").toString();
//logger.info("推荐时间:"+answerTime);
//最佳回答点赞数
String pointPraise =html.xpath("//span[@alog-action='qb-zan-btnbestbox']").toString();
if(pointPraise!=null) {
pointPraise = pointPraise.substring(pointPraise.lastIndexOf("evaluate"))
.replaceAll("evaluate=\"", "")
.replaceAll("\"> </span>", "");
}
//logger.info("最佳回答点赞数:"+pointPraise);
//最佳回答拍砖数
String contemptNumber =html.xpath("//span[@alog-action='qb-evaluate-outer']").toString();
if(contemptNumber!=null) {
contemptNumber = contemptNumber.substring(contemptNumber.lastIndexOf("evaluate"))
.replaceAll("evaluate=\"", "")
.replaceAll("\"> </span>", "");
}
//logger.info("最佳回答拍砖数:"+contemptNumber);
//将发布时间和问题放到一块去这里需要匹配
String quizTimes = null;//这个判断有漏洞的
if(problem!=null) {
for (int i = 0; i < lists.size(); i++) {
String portionProblem = lists.get(i).get("portionProblems");
String time = lists.get(i).get("quizTimes");
boolean isThisProblem = true;
for (int j = 0; j < portionProblem.length(); j++) {
if(problem.indexOf(portionProblem.charAt(j))==-1){
isThisProblem = false;
}
}
if(isThisProblem){
quizTimes = time;
continue;
}
}
}
//发表时间
//logger.info("发表时间"+quizTimes);
//其他回答答案
List<String> otherAnswers =html.xpath("//div[@class='answer-text']/span").all();
//logger.info("其他回答答案"+otherAnswers);
//其他回答的回答时间
List<String> otherAnswerTime =html.xpath("//div[@class='grid pt-5']/span[@class='pos-time']/text()").all();
//logger.info("其他回答的回答时间"+otherAnswerTime);
//其他回答的回答者
List<String> otherAnswerAnthors =html.xpath("//a[@alog-action='qb-username' and @class='user-name']/text()").all();
//logger.info("其他回答的回答者"+otherAnswerAnthors);
//其他回答的回答者等级
List<String> otherAnswerAnthorGradesLists =html.xpath("//div[@class='grid pt-5']/span[@class='i-gradeIndex']").all();
List<String> otherAnswerAnthorGrades = new ArrayList<String>();
if(otherAnswerAnthorGradesLists!=null){
for(int i=0;i<otherAnswerAnthorGradesLists.size();i++){
otherAnswerAnthorGrades.add("LV"+otherAnswerAnthorGradesLists.get(i).substring(otherAnswerAnthorGradesLists.get(i).lastIndexOf("Index-")+6,otherAnswerAnthorGradesLists.get(i).lastIndexOf("\"></span>")));
}
}
//logger.info("其他回答的回答者等级"+otherAnswerAnthorGrades);
//其他回答的点赞数
List<String> otherAnswerPointPraiseLists =html.xpath("//span[@alog-action='qb-zan-btn']").all();
List<String> otherAnswerPointPraises = new ArrayList<String>();
if(otherAnswerPointPraiseLists!=null) {
for(int i=0;i<otherAnswerPointPraiseLists.size();i++) {
otherAnswerPointPraises.add(otherAnswerPointPraiseLists.get(i).substring(otherAnswerPointPraiseLists.get(i).lastIndexOf("evaluate"))
.replaceAll("evaluate=\"", "")
.replaceAll("\"> </span>", ""));
}
}
//logger.info("其他回答的点赞数"+otherAnswerPointPraises);
//其他回答的拍砖数
List<String> otherAnswerContemptNumberLists =html.xpath("//div[@class='pos-relative']/div[@class='qb-zan-eva']/span[@alog-action='qb-evaluate-outer']").all();
List<String> otherAnswerContemptNumbers = new ArrayList<String>();
if(otherAnswerContemptNumberLists!=null) {
for(int i=0;i<otherAnswerContemptNumberLists.size();i++) {
otherAnswerContemptNumbers.add( otherAnswerContemptNumberLists.get(i).substring(otherAnswerContemptNumberLists.get(i).lastIndexOf("evaluate"))
.replaceAll("evaluate=\"", "")
.replaceAll("\"> </span>", ""));
}
}
//logger.info("其他回答的拍砖数"+otherAnswerContemptNumbers);
//如果为了效率可以开线程去存数据的
if(answerAuthor!=null || otherAnswers.size()!=0){
//问题表数据
Problem p = new Problem();
String problemId = cn.hncu.tool.UUID.getId();
p.setId(problemId);
p.setProblem(problem);
p.setQuizTime(quizTimes);
p.setProblemAnthor(problemAnthor);
p.setProblemDescribe(problemDescribe);
//关键字—问题关系表数据
KeywordProblemKey keywordProblemKey = new KeywordProblemKey();
keywordProblemKey.setKeywordId(keywordId);
keywordProblemKey.setProblemId(problemId);
//最佳答案数据
List<Answer> answers = new ArrayList<Answer>();
Answer answer = new Answer();
answer.setId(cn.hncu.tool.UUID.getId());
answer.setProblemId(problemId);
answer.setAnswerAnthor(answerAuthor);
answer.setAnswer(bestAnswer);
answer.setAnthorGrade(anthorGrade);
answer.setPointPraise(pointPraise);
answer.setAnswerTime(answerTime);
answer.setContemptNumber(contemptNumber);
answers.add(answer);
//其他答案数据
Integer otherAnswersSize = otherAnswers.size();
for(int i=0;i<otherAnswersSize;i++){
answer = new Answer();
answer.setId(cn.hncu.tool.UUID.getId());
answer.setProblemId(problemId);
if(otherAnswerAnthors.size()>i)
answer.setAnswerAnthor(otherAnswerAnthors.get(i));
if(otherAnswers.size()>i)
answer.setAnswer(otherAnswers.get(i));
if(otherAnswerAnthorGrades.size()>i)
answer.setAnthorGrade(otherAnswerAnthorGrades.get(i));
if(otherAnswerPointPraises.size()>i)
answer.setPointPraise(otherAnswerPointPraises.get(i));
if(otherAnswerTime.size()>i)
answer.setAnswerTime(otherAnswerTime.get(i));
if(otherAnswerContemptNumbers.size()>i)
answer.setContemptNumber(otherAnswerContemptNumbers.get(i));
answers.add(answer);
}
keywordService.insertAll(p,keywordProblemKey,answers);
}
// if (page.getResultItems().get("name") == null) {
// //skip this page
// page.setSkip(true);
// }
// 从搜索页面发现后续的url地址来抓取
// List<String> urls = html.links().regex("(http://zhidao.baidu.com/question/.+"+"\\?"+".+[^\"])").all();
// for(String url:urls){
// logger.info(url);
// }
if(problem==null) {
page.addTargetRequests(html.links().regex("(http://zhidao.baidu.com/question/.+"+"\\?"+".+[^\"])").all());
for(int i=1;i<pages;i++){
List<String> lists = new ArrayList<String>();
lists.add(url+keyword+"&site=-1&sites=0&date=0&pn="+(i*10));
page.addTargetRequests(lists);
}
pages=0;//只加一次进队列
}
}
@Override
public Site getSite() {
return site;
}
public static void main(String[] args) throws IOException {
//使用"spring.xml"和"spring-mybatis.xml"这两个配置文件创建Spring上下文
context = new ClassPathXmlApplicationContext("spring.xml", "spring-mybatis.xml");
keywordService = (KeywordService) context.getBean("keywordService");
//存储关键字表
Date searchTime = new Date();//搜索的时间
keywordId = cn.hncu.tool.UUID.getId();
Keyword keyword = new Keyword();
keyword.setId(keywordId);
keyword.setKeyword(Entrance.keyword);
keyword.setSearchTime(searchTime);
keywordService.insertKeyword(keyword);
Spider.create(new Entrance()).addUrl(url+Entrance.keyword)
//.addPipeline(new JsonFilePipeline("D:\\test\\"))
//开启 *个线程抓取
.thread(50)
//同步启动爬虫
.run();
}
}
数据库很简单看你自己需要哪些数据就建立哪些字段和表就行。
建立MySQL数据表的文件在项目的src/main/resources/sql/1.sql
本篇博客涉及到的源码链接
【->点击访问源码-©CHX】
本文章由[谙忆]编写 所有权利保留。
欢迎转载分享是进步的源泉。
转载请注明出处http://blog.csdn.net/qq_26525215
本文源自【大学之旅_谙忆的博客】