1、我写的这个爬虫是根据新浪微博的网址去爬它的文章的。
2、根据页面的标签来爬。
3、新浪的热门文章有一道防爬虫的机制,其实也简单,就是请求头的问题,写了就能绕过登陆模块直接进行爬数据了。
4、这只是一个小demo,效果通了的话再看看原理吧。
那先放一份完整的方法体吧
public Map<String, String> sinacrawlervoid(String url) throws Exception{
// TODO Auto-generated constructor stub
// 输入网址 url
String content = null; //用于存储返回的html代码
Parser parser = new Parser(); //用于转存为htmlparser可操作的格式
//使用httpclient,主要用来使用请求头发送数据,接收相应返回的数据
HttpClient client = new HttpClient();
System.out.println(url);
// 设置请求头,绕过新浪防爬虫机制
HttpMethod method = new GetMethod(url);
try {
method.addRequestHeader("Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
method.addRequestHeader("Accept-Language", "zh-CN,zh;q=0.8");
method.addRequestHeader("Cache-Control", "no-cache");
method.addRequestHeader("Connection", "keep-alive");
method.addRequestHeader("Pragma", "no-cache");
method.addRequestHeader("Content-Type", "text/html; charset=UTF-8");
method.addRequestHeader("Cookie",
"SCF=AuD5cb41vDeFJn-dHO9IrNlcTxq50vNxCCYhPUNxdhtrbUJEQJfxBkljP-8wVrATBTvovTuTFzt4y-mBLm4CYwk.; SUB=_2AkMvByHQdcNhrAJZnPsVzW3gZYlH-jzEiebBAn7uJhMyAxh77nsjqSUtNIvrdGFsXzKp8txVMqmdsjMuNA..; bai=360; login_sid_t=2aedb1fd26eea03d14da1916937ae80c; _s_tentry=-; UOR=,www.weibo.com,spr_sinamkt_buy_srwj1_weibo_t111; Apache=7890377011951.901.1482461705049; SINAGLOBAL=7890377011951.901.1482461705049; ULV=1482461705057:1:1:1:7890377011951.901.1482461705049:; TC-Page-G0=444eec11edc8886c2f0ba91990c33cda; TC-V5-G0=f88ad6a0154aa03e3d2a393c93b76575; WBStorage=2c466cc84b6dda21|undefined");
method.addRequestHeader("Host", "weibo.com");
method.addRequestHeader("Origin", "http://news.sohu.com/");
method.addRequestHeader("Referer", "http://www.weibo.com/?category=1760");
method.addRequestHeader("Upgrade-Insecure-Requests", "1");
method.addRequestHeader("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36");
// 把请求头method文件发送过去,POST方法
client.executeMethod(method);
} catch (HttpException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
// 接收新浪返回的html文件,并把它存入content文件中
content = method.getResponseBodyAsString();
//把html文件写入到parser文件中
parser.setInputHTML(content);
//作者
String authornode = getNewsAuthor(parser);
// System.out.println("作者:"+authornode);
//parser.reset()用于重置parser,不然每次使用之后就编程只有过滤后的代码
parser.reset();
// 标题
String titlenode = getTitle(parser);
// System.out.println("标题:"+titlenode);
parser.reset();
String datenode = getdatelist(parser);
// System.out.println("时间:"+datenode);
parser.reset();
String contentnode = getnewsContent(parser);
// System.out.println("内容:"+contentnode);
Map<String,String> map=new HashMap<String,String>();
System.out.println(authornode);
System.out.println(authornode);
System.out.println(titlenode);
System.out.println(datenode);
map.put("内容:", contentnode);
map.put("作者:", authornode);
map.put("标题:",titlenode);
map.put("时间:", datenode);
return map;
}
那我就解释一下这个方法体:
1、首先要获取网址url。
HttpMethod method = new GetMethod(url);
2、因为要绕过新浪的防爬虫机制,所以写个请求头,最后再以post的方式发送出去,对应的方法是
client.executeMethod();
method.addRequestHeader("Accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
method.addRequestHeader("Accept-Language", "zh-CN,zh;q=0.8");
method.addRequestHeader("Cache-Control", "no-cache");
method.addRequestHeader("Connection", "keep-alive");
method.addRequestHeader("Pragma", "no-cache");
method.addRequestHeader("Content-Type", "text/html; charset=UTF-8");
method.addRequestHeader("Cookie",
"SCF=AuD5cb41vDeFJn-dHO9IrNlcTxq50vNxCCYhPUNxdhtrbUJEQJfxBkljP-8wVrATBTvovTuTFzt4y-mBLm4CYwk.; SUB=_2AkMvByHQdcNhrAJZnPsVzW3gZYlH-jzEiebBAn7uJhMyAxh77nsjqSUtNIvrdGFsXzKp8txVMqmdsjMuNA..; bai=360; login_sid_t=2aedb1fd26eea03d14da1916937ae80c; _s_tentry=-; UOR=,www.weibo.com,spr_sinamkt_buy_srwj1_weibo_t111; Apache=7890377011951.901.1482461705049; SINAGLOBAL=7890377011951.901.1482461705049; ULV=1482461705057:1:1:1:7890377011951.901.1482461705049:; TC-Page-G0=444eec11edc8886c2f0ba91990c33cda; TC-V5-G0=f88ad6a0154aa03e3d2a393c93b76575; WBStorage=2c466cc84b6dda21|undefined");
method.addRequestHeader("Host", "weibo.com");
method.addRequestHeader("Origin", "http://news.sohu.com/");
method.addRequestHeader("Referer", "http://www.weibo.com/?category=1760");
method.addRequestHeader("Upgrade-Insecure-Requests", "1");
method.addRequestHeader("User-Agent",
"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36");
// 把请求头method文件发送过去,POST方法
client.executeMethod(method);
3、接受网址的返回结果,这时候就是页面的源码了。
Parser parser = new Parser(); //用于转存为htmlparser可操作的格式
// 接收新浪返回的html文件,并把它存入content文件中
content = method.getResponseBodyAsString();
//把html文件写入到parser文件中
parser.setInputHTML(content);
4、 解析html源码获取到自己想要的数据,一般是根据id来区分
//作者
String authornode = getNewsAuthor(parser);
//parser.reset()用于重置parser,不然每次使用之后就编程只有过滤后的代码
parser.reset();
// 标题
String titlenode = getTitle(parser);
parser.reset();
String datenode = getdatelist(parser);
parser.reset();
String contentnode = getnewsContent(parser);
Map<String,String> map=new HashMap<String,String>();
map.put("内容:", contentnode);
map.put("作者:", authornode);
map.put("标题:",titlenode);
map.put("时间:", datenode);
return map;
我这里是存再map集合里面。
当然你要是存到一个类中或者一个其它集合中也可以的。
这里说一下,上面的那些方法是我自己写的过滤,你也可以根据自己的需要去写过滤器。
//获取新闻作者
private static String getNewsAuthor( Parser parser) {
String authornode = "";
try {
//标签过滤器href="/u/5242412695"
NodeList authorlist = parser.extractAllNodesThatMatch(new AndFilter(new TagNameFilter("a"), new HasAttributeFilter("class", "S_txt1")));
TextExtractingVisitor visitor = new TextExtractingVisitor();
authorlist.visitAllNodesWith(visitor);
authornode = visitor.getExtractedText();
authornode = authornode.replaceAll("\\s", "");
} catch (ParserException ex) {
Logger.getLogger(sinacrawlervoid.class.getName()).log(Level.SEVERE, null, ex);
}
return authornode;
}
二、说一下,这个htmlparser一些接口的说明吧。
TextExtractingVisitor 这个类呢,是用来解析parser这个文本的,然后使用它的getExtractedText这个方法就能过滤掉其它的数据取得纯文本信息。
Node中包含的方法有几类:
对于树型结构进行遍历的函数,这些函数最容易理解:
Node getParent ():取得父节点
NodeList getChildren ():取得子节点的列表
Node getFirstChild ():取得第一个子节点
Node getLastChild ():取得最后一个子节点
Node getPreviousSibling ():取得前一个兄弟(不好意思,英文是兄弟姐妹,直译太麻烦而且不符合习惯,对不起女同胞了)
Node getNextSibling ():取得下一个兄弟节点
取得Node内容的函数:
String getText ():取得文本
String toPlainTextString():取得纯文本信息。
String toHtml () :取得HTML信息(原始HTML)
String toHtml (boolean verbatim):取得HTML信息(原始HTML)
String toString ():取得字符串信息(原始HTML)
Page getPage ():取得这个Node对应的Page对象
int getStartPosition ():取得这个Node在HTML页面中的起始位置
int getEndPosition ():取得这个Node在HTML页面中的结束位置
用于Filter过滤的函数:
void collectInto (NodeList list, NodeFilter filter):基于filter的条件对于这个节点进行过滤,符合条件的节点放到list中。
用于Visitor遍历的函数:
void accept (NodeVisitor visitor):对这个Node应用visitor
用于修改内容的函数,这类用得比较少:
void setPage (Page page):设置这个Node对应的Page对象
void setText (String text):设置文本
void setChildren (NodeList children):设置子节点列表
其他函数:
void doSemanticAction ():执行这个Node对应的操作(只有少数Tag有对应的操作)
Object clone ():接口Clone的抽象函数。
要是有兴趣呢,可以下我工作前期写的工作项目集合。
https://download.csdn.net/download/fg676123/10906304