专栏首页微光点亮星辰Java爬虫(3)——拼接url抓取“加载更多”内容

Java爬虫(3)——拼接url抓取“加载更多”内容

上期我们说到phantomjs可模拟点击按钮行为,通过点击完所有”加载更多”按钮来抓所有内容。比如这个网页http://e.vnexpress.net/news/news

我们要抓取红线所标注的超链接,将url入库,点击“view more stories”会出现更多列表,然而有些网页是,点到所有隐藏内容都出现后,那个“view more stories”按钮还是存在,给判断模拟点击行为何时终止造成困难。或许聪明的朋友会说:“有什么困难的?模拟一次点击行为,抓取一次网页,从下至上获取列表项的url,当url与数据库中已经抓取的url重复时,停止获取。当从下至上获取的第一个url就与数据库中的url重复时,说明已经获取整张网页的所有内容,可停止模拟点击行为……“。这的确是个办法,但存在着大量判断和对网页的重复抓取,我们有个更优雅的解决方式,何乐而不为呢??(另外真要模拟的话selenium比phantomjs更稳定的…)

想必大家通过标题就已经知道接下来我要说的所谓的更优雅的方式是什么了吧……没错!拼接url。

打开控制台的network模块,

点击“view more stories”按钮,出现以下网络请求,注意这个type为xhr的网络请求,这正是向后台请求更多列表项的。观察请求头,

请求的参数有两个,于是尝试拼接url如下

http://e.vnexpress.net/news/news?cate_id=1003894&page=4

得到了13个列表项,但是点击加载更多按钮,新增的却是15个,也只是少了两个列表项,不怎么影响整体抓的效果,就采用此方式来抓了,拼到什么时候为止呢??当page参数大于实际最大页数时,有的网页是不出现“view more stories”按钮,有的仍然出现按钮,但显示的都是最大页数那一页的内容。根据不同情况来判断是否停止抓取。

下面来贴代码:(大部分基础代码已在前文解释过,就不赘述了)

package edu.nju.opsource.vnexpress.linktype;
import java.io.InputStream;
import java.util.Date;
import java.util.LinkedList;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import com.onezetta.downloader.EasyHttpDownloader;
import edu.nju.opensource.common.beans.ELinkState;
import edu.nju.opensource.common.beans.Link;
import edu.nju.opensource.common.beans.LinkType;
import edu.nju.opensource.common.beans.Site;
import edu.nju.opsource.vnexpress.utils.IdXmlUtil;
public class LinkType_NewsList extends LinkType {
private Document doc = null;
private Site site = null;
private LinkedList<Link> newItems = null;
private Link nextPage = null;
public LinkType_NewsList() {
super();
// TODO Auto-generated constructor stub
}
public LinkType_NewsList(String linkTypeName) {
super.setLinkTypeName(linkTypeName);
}
public LinkType_NewsList(Site site) {
super.setLinkTypeName("vnexpress.newslist");
this.site = site;
}
public void get(String url) {
super.get(url);
String content = new EasyHttpDownloader(url).run();
if (content != null) {
this.doc = Jsoup.parse(content, this.site.getUrl().getUrl());
System.out.println(" ... has Crawled.");
} else {
setState(ELinkState.CRAWLFAILED);
System.out.println(" ... crawled failed.");
}
}
// 把新闻列表条目的链接插入表
// 在总项目中被调用的核心函数handle()
@Override
public boolean handle(Link parentlink) throws Exception {
if (getState() == ELinkState.CRAWLFAILED)
return false;
Elements news = this.doc.select("div.list_news_folder.col_720 h4.title_news_site");
this.newItems = new LinkedList();
boolean flag = false;
for (Element newItem : news) {
Elements tmp = newItem.select("a");
if ((tmp != null) && (tmp.size() != 0)) {
Link link = new Link(tmp.first().attr("abs:href"), new LinkType_News(), ELinkState.UNCRAWL, parentlink,
this.site).setLinkDate( new java.sql.Date(new Date().getTime()));
// System.out.println(tmp.first().attr("abs:href"));
int rst = link.insert();
if (rst == -1)
flag = true;// link exist
}
}
if (flag) {
setState(ELinkState.CRAWLED);
return false;
}
Elements nextPageE = this.doc.select("a#vnexpress_folder_load_more");
           String url = getNextPageUrl();
if ((nextPageE != null) && (nextPageE.size() != 0)) {
System.out.println(url);
this.nextPage = new Link(url, new LinkType_NewsList(this.site), ELinkState.UNCRAWL, parentlink, this.site).setLinkDate( new java.sql.Date(new Date().getTime()));
this.nextPage.insert();
}
else {
IdXmlUtil.setIdByName("news", 2 + "");
}
setState(ELinkState.CRAWLED);
return true;
}
public static String getNextPageUrl() {
int id = IdXmlUtil.getIdByName("news");
// IdXmlUtil用来存储当前已抓取的pageid其实这个工具完全没必要,因为数据库的//url有此参数啊。。。当时脑抽没想到,搞麻烦了。。。
IdXmlUtil.setIdByName("news", (id + 1) + "");
//此文章的精髓
String url = "http://e.vnexpress.net/news/news?cate_id=1003894&page=" + id;
return url;
}
@Override
public String getLinkTextData() {
// TODO Auto-generated method stub
return null;
}
@Override
public InputStream getLinkBinaryData() {
// TODO Auto-generated method stub
return null;
}
@Override
public String getLinkUniqeID(String paramString) {
// TODO Auto-generated method stub
return null;
}
@Override
public Date getLinkDate() {
// TODO Auto-generated method stub
return null;
}
public Site getSite() {
return this.site;
}
@Override
public void setSite(Site site) {
// TODO Auto-generated method stub
this.site = site;
}
public LinkedList<Link> getNewItems() {
return this.newItems;
}
public void setNewItems(LinkedList<Link> newItems) {
this.newItems = newItems;
}
public Link getNextPage() {
return this.nextPage;
}
public void setNextPage(Link nextPage) {
this.nextPage = nextPage;
}
@Override
public String toString() {
return "LinkType_newsList [doc=" + doc + ", site=" + site + ", newItems=" + newItems + ", nextPage="
+ nextPage + "]";
}
}

本以为爬虫系列写到这儿就完结了的,其实还有个问题,就是我们在浏览器上试url来查看效果,然而有的网站以这种方式尝试看不到任何内容,这是因为浏览器输入网址是以get请求,有些网站的后台内容不接受get请求。这样的话,我们该怎样查看拼接url的效果呢???

期待下期吧 >0<

本文分享自微信公众号 - 微光点亮星辰(SandTower),作者:小娜

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2016-11-14

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 图图谈设计模式_原型设计模式_java

    那为什么呢,原因很简单,效率高。最近做项目就用到该模式,实体A关联实体B,B关联实体C。而通过实现序列化和反序列化的深度复制,只需复制A,那么关联的对象都可以同...

    聚沙成塔
  • 图图谈设计模式_建造者设计模式_java

    还是那句话,设计模式是一种思想,编程靠的就是思想。高级编程就是面向接口和抽象类编程。望读者有个概念,框架源码最为体现,公司封装的框架我虽然没接触过,但是我相信也...

    聚沙成塔
  • MSYS2模拟Linux环境

    首先介绍一下MSYS2是什么,简单来说就是在windows平台上安装一个软件,用于模拟Linux的环境,我们在使用github的终端的时候可能也见过类似...

    聚沙成塔
  • React Native与Android 原生通信

    我们用React Native 做混合开发的时候免不了要原生和React Native 进行通信交互,这篇文章就是分享原生模块与JS传递数据的几种方式。

    HelloJack
  • 动手实现AOP

    “ 在前面的两篇文章中,我们了解了Spring AOP的应用以及两种动态代理的实现,但是如何实现AOP我想小伙伴应该会很疑惑,所以今天我们自己来动手撸代码简单的...

    每天学Java
  • 二叉树删除节点-简化版

    shengjk1
  • android 应用模式之mvp

    说到MVP就不得不提到MVC,做过J2EE的猿友们肯定知道MVC是个什么东西。MVC即 Model、View、Controller, 那MVP就Model、Vi...

    xiangzhihong
  • Spring注解式编程

    相比较XML配置来说,当项目过大的时候难以维护。Spring还有一种支持0配置的注解式编程。

    OPice
  • 设计模式奠基石——UML关系转化为代码

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huyuyang6688/article/...

    DannyHoo
  • 来!狂撸一款PHP现代化框架 (路由的设计)

    上一篇的标题改了一下,以一、二、三为章节对读者来说是种困扰,现在的标题是依照项目进度来编写的。上篇文章地址为 https://segmentfault.com/...

    CrazyCodes

扫码关注云+社区

领取腾讯云代金券