定向爬虫:动态加载网页的爬取

本文介绍了如何针对使用AJAX动态加载的豆瓣电影排行榜页面进行网络爬虫。通过分析JS文件,发现了请求URL的规律,特别是start和limit参数,用于获取特定范围的电影信息。同时,找到了获取每种类别电影总数的方法,从而能够全面抓取所有电影数据。爬虫策略包括构造URL、获取JSON数据和解析电影信息。

×× 前几天看了一下豆瓣电影的内容,想从豆瓣电影的分类排行榜 · · · · · 中爬取多一点的电影信息。点击一个类型进去之后发现它里面的电影信息时需要你不断往下拉动滚动条之后才会动态地加载更多的电影信息并显示出来。

观察了一下发现了它采用的是AJAX异步请求(通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新).这样就导致了查看到源代码内容(因为它是静态的)与网页内容不同。

F12之后发现了可疑的js文件,如下:
这里写图片描述

通过浏览器打开这个js文件发现它的里面就是从服务器端返回的电影信息的一个json对象数组。这个js文件的url是这样的:
https://movie.douban.com/j/chart/top_list?type=24&interval_id=100%3A90&action=&start=20&limit=20

每个这样的url除了start和limit后面的数字不一致之外其他的都相同,那么这两个数据表示的是什么呢???我想答案应该是从第20个电影信息开始往后读取20个电影的信息!

那么问题来了?!

  • 那么这个url怎么获取呢???

    1> 知道了这个url我们就不需要从网页源代码中去寻找电影的信息,每个类型的电影下面构造这样的url直接connect接收返回的json数据,分析json数据然后从json对象数组中提取电影信息。

    2>每个类型下面的电影总数怎么知道?知道了这种类型有多少个电影之后直接将start=0&limit=电影总数,就可以一次返回所有的电影信息的json数据。

      3> 通过观察发现了一个这样的js文件:
      这里写图片描述

    点击这个count的url之后发现这个里面就是每个类型的电影总数,
    这里写图片描述

    这里面的total就是电影总数。
      
      4> 这样我们就可以开始敲代码了。


!首先进入到有各种类型的页面(也就是这样的)

这里写图片描述

public class Test {
    private static HashMap<String,String> urlandnames = new HashMap<String, String>();

    public static void main(String[] args) {
        String url = "http://movie.douban.com/chart";
        GetMoviesName getMoviesName = new GetMoviesName(url);
        try {
            urlandnames = getMoviesName.getAllKinds();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Iterator it = urlandnames.keySet().iterator();    //使用iterator遍历hashmap
        while (it.hasNext()){
            RunnableOfMoviename runnable = new RunnableOfMoviename((String) it.next());    //它的返回值是object类型
            Thread thread = new Thread(runnable);
            thread.start();                         //创建多个线程并启动
        }
    }
}
public class GetMoviesName {
    private String html;
    private String url;
    private String kindofurl;
    HashMap<String, String> hrefandname = new HashMap<String, String>(); //保存电影分类的url和类型

    public GetMoviesName(String url) {
        this.url = url;
    }

    public HashMap<String, String> getAllKinds() throws IOException {
        Document kinds = Jsoup.connect(url).timeout(10000).get();
        Element element = kinds.getElementById("content");
        Elements elements = element.getElementsByClass("types");
        for (Element t:elements){
            Elements elem = t.getElementsByTag("a");
            for (Element a:elem){
            //构造每种类型电影的链接
                String kindurl = "http://movie.douban.com"+a.attr("href");
                hrefandname.put(kindurl,t.text());
            }
        }
        return hrefandname;
    }
}
public class RunnableOfMoviename implements Runnable {

    private String url = null;                //每创建一个线程就获取一种类型里面的所有电影名
    private String nameurl = null;

    public RunnableOfMoviename(String url) {
        this.url = url;
    }

    public void run() {
        String[] tempurl = url.split("&");               //获取到每个电影类型的js文件的url,得到该类型的电影个数
        String s1 = "http://movie.douban.com/j/chart/top_list_count?";
        String finalurl = s1+tempurl[1]+"&"+tempurl[2];


        String document = null;
        try {
            document = Jsoup.connect(finalurl).timeout(10000).ignoreContentType(true).execute().body();
        } catch (IOException e) {
            e.printStackTrace();
        }

        JsonParser parser = new JsonParser();      //json解析器
        JsonObject jsonObject = (JsonObject)parser.parse(document);    //获取json对象
        int movienum = jsonObject.get("total").getAsInt();

        nameurl = "http://movie.douban.com/j/chart/top_list?"+tempurl[1]+"&"+tempurl[2]+"&action=&start=0&limit="+movienum;

        String doc = null;
        try {
            doc = Jsoup.connect(nameurl).timeout(10000).ignoreContentType(true).execute().body();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //将json的一个对象数组解析成JsonElement对象
        JsonElement element = null;
        try {
            element = parser.parse(doc);  //通过JsonParser对象可以把json格式的字符串解析成一个JsonElement对象
        }catch (NullPointerException e){
            e.printStackTrace();
        }
        JsonArray jsonArray = null;
        if (element.isJsonArray()){               //JsonElement对象如果是一个数组的话转化成jsonArray
            jsonArray = element.getAsJsonArray();
        }

        Iterator it = jsonArray.iterator();           //遍历json的对象数组,将每一个对象里的title输出
        while(it.hasNext()){
            JsonObject e = (JsonObject)it.next();
            Movies movie = new Movies();

            String doubletitle = e.get("title").getAsString();
            String name = doubletitle.split("\"")[0];              //字符串分割
            double score = e.get("score").getAsDouble();
            String release_date = e.get("release_date").getAsString();
            JsonArray Elements = e.get("types").getAsJsonArray();
            ArrayList<String> array = new ArrayList<String>();
            for (Object o:Elements){
                array.add(o.toString().split("\"")[1]);
            }

            movie.setName(name);
            movie.setTypes(array.toString());
            movie.setRelease_date(release_date);
            movie.setScore(score);
        }
    }
}

这样就可以运行了

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值