list的add方法 ,foreach循环添加map---List.add(map)(通过一个java爬虫案例说明)

案例:一个java爬虫程序

1.案例说明

做了一个爬取某程的旅游网站的java程序,程序主要爬取安庆酒店的某些相关信息。
材料准备:jsoup-1.8.1.jar(需要此架包的联系博主,有任何问题欢迎随时咨询
直接上代码:

package util;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class CopyOfDataDownUtil {

    /**
     * 
     * @param url
     * @param enconding
     * @return
     */
    public static String getHtmlResourceByUrl(String url,String enconding){
        StringBuffer buffer=new StringBuffer();
        InputStreamReader isr=null;
        try {
            //建立网络链接
            URL urlOb=new URL(url);
            //打开网络链接
            URLConnection uc=urlOb.openConnection();
            //建立文件输入流
            isr=new InputStreamReader(uc.getInputStream(),enconding);
            //缓冲
            BufferedReader reader=new BufferedReader(isr);
            //建立临时文件
            String line=null;
            //开始读取源代码
            while(((line=reader.readLine())!=null)){
            //将临时文件保存到StringBuffer
            buffer.append(line+"\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //防止空指针
            if(isr!=null)
                try {
                    isr.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
        } 
        return buffer.toString();
    }
    //解析源代码  获取相应的数据
    public static List> getHotelInfo(String url,String encoding){

        //根据网址获取网址源代码
        String html=getHtmlResourceByUrl(url, encoding);
        //解析源代码
        Document document=Jsoup.parse(html);
        //获取hotel_list 盒子里面的容器
        Element element=document.getElementById("hotel_list");
        //拿到每一个酒店
        Elements elements=element.getElementsByClass("searchresult_info");

        List> maps=new ArrayList>();


        //拿到每一个酒店的名称,图片 描述 遍历
        for (Element el : elements) {
            HashMap map=new HashMap();
            //获取酒店的图片
            String imgSrc=el.getElementsByTag("img").attr("src");
            //获取酒店的描述
            String content=el.getElementsByClass("searchresult_htladdress").text();
            //获取酒店的名称
            String title=el.getElementsByTag("img").attr("alt");
            map.put("imgSrc", imgSrc);
            map.put("content", content);
            map.put("title", title);
            maps.add(map);

        }

        return maps;
    }

    public static void main(String[] args) {


        //得到一个list集合
        List>     html=getHotelInfo("http://hotels.ctrip.com/hotel/anqing177#ctm_ref=ctr_hp_sb_lst",  "utf-8");
        for (HashMap hashMap : html) {
            String img=hashMap.get("imgSrc");
            String content=hashMap.get("content");
            String title=hashMap.get("title");
            System.out.println("酒店名称=="+title);
            System.out.println("酒店图片=="+img);
            System.out.println("酒店描述=="+content);
            System.out.println("==================");
        }
    }
}

爬取结果展示:

酒店名称==安庆南翔邦臣大酒店
酒店图片==//dimg13.c-ctrip.com/images/fd/hotel/g4/M00/4D/55/CggYHFZtNGOAcgEYAD_r23Wd2JU142_R_130_130.jpg
酒店描述==宜秀区迎宾路2号,客运中心斜对面。 【 火车站区域】
==================
酒店名称==安庆国际大酒店
酒店图片==//dimg12.c-ctrip.com/images/fd/hotel/g3/M0A/1F/C9/CggYG1YDNNCAGAPfAAMJyLEJA5I525_R_130_130.jpg
酒店描述==大观区集贤北路2号,与菱湖北路交叉口。 【 火车站区域 经济开发区】
==================
酒店名称==安庆迎宾馆
酒店图片==//dimg10.c-ctrip.com/images/hotel/12000/11647/2badfa77a5b54bbb830796843baf61e9_R_130_130.jpg
酒店描述==迎江区同安路2号,近盛唐湾路。 【 火车站区域】
==================
酒店名称==安庆唯美·艾伦艺术酒店
酒店图片==//dimg12.c-ctrip.com/images/200p0c00000063u5bC82A_R_130_130.jpg
酒店描述==大观区集贤南路163号,近市府路。 【 市中心区域】
==================
酒店名称==7+1商务酒店(安庆沿江路店)
酒店图片==//dimg10.c-ctrip.com/images/hotel/62000/61889/99e6cc62ed894c1e8f9f768e3ae04744_R_130_130.jpg
酒店描述==迎江区沿江中路59号(三号码头),近龙山路。 【 市中心区域 经济开发区】
==================
酒店名称==如家快捷酒店(安庆迎江寺华中西路店)
酒店图片==//dimg13.c-ctrip.com/images/200c0e0000007866w926B_R_130_130.jpg
酒店描述==迎江区华中西路413号,近湖心南路。 【 市中心区域】
==================
酒店名称==安庆天域时代酒店
酒店图片==//dimg13.c-ctrip.com/images/fd/hotel/g5/M04/1D/AF/CggYsFcPCziAaPi_AAFdyLV02mE016_R_130_130.jpg
酒店描述==宜秀区中兴大道146号,近雷池路。 【 火车站区域】
==================
酒店名称==安庆温州国际大酒店
酒店图片==//dimg11.c-ctrip.com/images/fd/hotel/g3/M08/68/C0/CggYGVYIQmmAIxGbABsXaFPnGhg218_R_130_130.jpg
酒店描述==宜秀区元山路光彩四期四号综合楼4号楼,近龙眠山南路。 【 火车站区域】
==================
酒店名称==安庆天域花园酒店
酒店图片==//dimg12.c-ctrip.com/images/hotel/122000/121831/19698142b05f464cabb92f86b5116b94_R_130_130.jpg
酒店描述==迎江区龙眠山路158号,龙眠山路和振风大道交叉口。 【 东部新城】
==================
酒店名称==安庆尊悦酒店
酒店图片==//dimg12.c-ctrip.com/images/hotel/66000/65569/64e0aaae605246bba5e152a42c39dcff_R_130_130.jpg
酒店描述==迎江区人民路529号,市立医院旁。 【 市中心区域】
==================
酒店名称==安庆五千年文博园皖府国际大酒店
酒店图片==//dimg12.c-ctrip.com/images/2004050000000rvv4FC7E_R_130_130.jpg
酒店描述==高速路口前方500米(近人民东路)
==================
酒店名称==安庆英德利大酒店
酒店图片==//dimg10.c-ctrip.com/images/fd/hotel/g3/M02/D3/8F/CggYGVZD8reAUr4VAAG1cJ1n_yY131_R_130_130.jpg
酒店描述==宜秀区中兴大道122号,近余店路。 【 火车站区域】
==================
酒店名称==锦江之星(安庆人民路步行街店)
酒店图片==//dimg11.c-ctrip.com/images/fd/hotel/g5/M0B/C5/53/CggYr1cz1FSANdsfAAVBOvwCOF4893_R_130_130.jpg
酒店描述==迎江区人民路563号(肯德基楼上),江边外滩公园旁。 【 市中心区域】
==================
酒店名称==安庆东驰假日酒店
酒店图片==//dimg10.c-ctrip.com/images/hotel/750000/749621/03da3ccb2f514557ac1c7bbfd79dd6cd_R_130_130.jpg
酒店描述==宜秀区湖心北路107号,近山城花园。 【 火车站区域】
==================
酒店名称==安庆光彩假日大酒店
酒店图片==//dimg10.c-ctrip.com/images/2003050000000ioy1F5D9_R_130_130.jpg
酒店描述==宜秀区龙眠山南路与天柱山路交叉口,近客运中心站向南300米。 【 火车站区域】
==================
酒店名称==安庆双龙假日大酒店
酒店图片==//dimg10.c-ctrip.com/images/hotel/100000/99590/5DA8FA64-DD6F-4DE6-BE19-6EF03496CC4F_R_130_130.jpg
酒店描述==宜秀区开发区迎宾路1号,光彩大市场西大门对面。 【 火车站区域】
==================
酒店名称==安庆君怡精品酒店
酒店图片==//dimg10.c-ctrip.com/images/fd/hotel/g3/M02/FB/00/CggYG1YXDXSAdblFAAF1pAI1G80650_R_130_130.jpg
酒店描述==迎江区菱湖南路315号(安庆二中隔壁),安师院老校区斜对面,近宜城路。 【 市中心区域】
==================
酒店名称==安庆速8酒店(宜城路店)
酒店图片==//dimg13.c-ctrip.com/images/200q0f0000007lyk4A5AF_R_130_130.jpg
酒店描述==迎江区宜城路22号,汽车南站旁。 【 市中心区域】
==================
酒店名称==安庆元素酒店
酒店图片==//dimg12.c-ctrip.com/images/hotel/108000/107691/14840d66a3de486c907691c8ed24d8ed_R_130_130.jpg
酒店描述==迎江区人民路408号,市中心繁华路段,与建设路交叉口,步行街边。 【 市中心区域】
==================
酒店名称==喆啡酒店(安庆人民路步行街店)
酒店图片==//dimg10.c-ctrip.com/images/200i050000000ilrm35B0_R_130_130.jpg
酒店描述==迎江区人民路441号,近湖心南路。 【 市中心区域】
==================
酒店名称==安庆独秀国际大酒店
酒店图片==//dimg11.c-ctrip.com/images/20010g000000865f601E6_R_130_130.jpg
酒店描述==怀宁河滨路91号
==================
酒店名称==7天连锁酒店(安庆龙山路步行街店)
酒店图片==//dimg12.c-ctrip.com/images/fd/hotel/g1/M01/D0/E9/CghzfVT9pKmAXUnqAAJ-ksYQb0Y132_R_130_130.jpg
酒店描述==大观区龙山路129号,近孝肃路。 【 市中心区域】
==================
酒店名称==莫泰168(安庆菱湖南路莲湖公园店)
酒店图片==//dimg12.c-ctrip.com/images/fd/hotel/g1/M03/0D/27/CghzflW7kSqATdy5AAM4TIUF_eM612_R_130_130.jpg
酒店描述==迎江区菱湖南路219号,安庆市青少年宫隔壁。 【 市中心区域】
==================
酒店名称==安庆德克精品客房
酒店图片==//dimg10.c-ctrip.com/images/t1/hotel/1018000/1017970/c866dc0badc846f7b0aa7214eb49d937_R_130_130.jpg
酒店描述==大观区集贤南路2号图书城6-8,近中国农业银行。 【 市中心区域 经济开发区】
==================
酒店名称==安庆黄梅山庄
酒店图片==//dimg12.c-ctrip.com/images/hotel/85000/84714/F385CC1E57A84010855E07F7D4FAE4FC_R_130_130.Jpg
酒店描述==迎江区湖心中路9号,菱湖公园内。 【 市中心区域】
==================

总结:最终的结果是好的,爬取成功。不过中间也遇到了很多大大小小的问题。下面会一一列举分析,有不到位的地方,欢迎批评。

2.问题分析(以下为问题出现的次序,第三个是重点)

2.1空指针问题

1.问题描述

起初刚把程序写完之后,一运行报了一个空指针异常。

2.问题分析

这个异常报在下面这行代码:

//错误写法
HashMap<String, String> map=null;

大家知道问题是什么吗?报空指针异常的原因是定义的map没有初始化,应该改为下面这样:

//正确写法
HashMap<String, String> map=new HashMap<String, String>();

3.问题总结

总结:map定义的时候必须正确初始化,因为集合不光要实例化,还要指定存放的数据的类型。

2.2爬取内容为空问题

1.问题描述

解决完了空指针异常再次运行程序出现了新问题:爬取的内容为空。

2.问题分析

爬取到的内容为空值。为什么会这样呢?看看代码感觉写的没问题呀!(这是程序员的第一大错觉)然后我就开始debug。发现在getHotelInfo方法中遍历每一个酒店的时候,取酒店图片,酒店描述,酒店名称的时候,这些变量的值都有,为什么到main方法中maps的值就为空了呢?代码如下:

//解析源代码  获取相应的数据
    public static List<HashMap<String,String>> getHotelInfo(String url,String encoding){

        //根据网址获取网址源代码
        String html=getHtmlResourceByUrl(url, encoding);
        //解析源代码
        Document document=Jsoup.parse(html);
        //获取hotel_list 盒子里面的容器
        Element element=document.getElementById("hotel_list");
        //拿到每一个酒店
        Elements elements=element.getElementsByClass("searchresult_info");

        List<HashMap<String, String>> maps=new ArrayList<HashMap<String,String>>();


        //拿到每一个酒店的名称,图片 描述 遍历
        //程序运行到这时里面的变量imgSrc content title map maps都有值
        for (Element el : elements) {
            HashMap<String, String> map=new HashMap<String, String>();
            //获取酒店的图片
            String imgSrc=el.getElementsByTag("img").attr("src");
            //获取酒店的描述
            String content=el.getElementsByClass("searchresult_htladdress").text();
            //获取酒店的名称
            String title=el.getElementsByTag("img").attr("alt");
            map.put("imgSrc", imgSrc);
            map.put("content", content);
            map.put("title", title);
            maps.add(map);

        }
        //运行到这时,maps为空导致main方法的html变量的值为空
        return maps;
    }
//main方法
public static void main(String[] args) {

        //得到一个list集合
        List> html=getHotelInfo("http://hotels.ctrip.com/hotel/anqing177#ctm_ref=ctr_hp_sb_lst", "utf-8");

看了好久的代码,也debug了好久,都没有发现有问题,我就纳闷了,这种时候是程序员最痛苦的时候,不过也是最增长经验时候。最后我看了看网页的源代码发现是我把div的class属性值给写错了。。。低级失误

//拿到每一个酒店
//酒店的class属性值(这是后来修改过的)
Elements elements=element.getElementsByClass("searchresult_info");

3.问题总结

总结:这种失误是很低级,为了避免这种失误一些关键的地方不要手敲,此时复制粘贴是最好的防错办法。

2.3list中添加map时前面的老值被新值覆盖

1.问题描述

改为上一个错误之后,运行结果也不是空值了,但是新的问题又来了:所以的酒店名称,图片,描述都是同一个。

2.问题分析

这是怎么回事呢?第一感觉是新值把老值给覆盖了。后来证明我的第一感觉是对的。foreach循环添加map—maps.add(map)的时候,由于没有每次都new map,导致每次foreeach都是把新值赋给了原来的引用地址,也就是说内存中是一个map,每次循环都只是修改了这个map的属性值,但是maps的map数量是正确的,只是这些map都是同一个map,所以所有的酒店信息都是一样的,但是酒店数量是正确的。
错误代码:(正确写法在下面)

        //new list对象
        List<HashMap<String, String>> maps=new ArrayList<HashMap<String,String>>();
        //new 一个map对象
        //错误做法:这样内存中就只有一个map对象
        //         因为此时在foreach中一直修改的是同一个对象,每次循环都要创建对象
        HashMap<String, String> map=new HashMap<String, String>();

        //拿到每一个酒店的名称,图片 描述 遍历
        for (Element el : elements) {

            //获取酒店的图片
            String imgSrc=el.getElementsByTag("img").attr("src");
            //获取酒店的描述
            String content=el.getElementsByClass("searchresult_htladdress").text();
            //获取酒店的名称
            String title=el.getElementsByTag("img").attr("alt");
            map.put("imgSrc", imgSrc);
            map.put("content", content);
            map.put("title", title);
            maps.add(map);

        }

正确代码:

        //new list对象
        List<HashMap<String, String>> maps=new ArrayList<HashMap<String,String>>();


        //拿到每一个酒店的名称,图片 描述 遍历
        for (Element el : elements) {
            //new 一个map对象
            //正确做法:在每一次循环中重新new一个map对象,这样内存中的值就不会存在老值覆盖新值的问题
            //         因为此时内存中就不是一直修改同一个对象,而是每次都有一个新的对象
            HashMap<String, String> map=new HashMap<String, String>();
            //获取酒店的图片
            String imgSrc=el.getElementsByTag("img").attr("src");
            //获取酒店的描述
            String content=el.getElementsByClass("searchresult_htladdress").text();
            //获取酒店的名称
            String title=el.getElementsByTag("img").attr("alt");
            map.put("imgSrc", imgSrc);
            map.put("content", content);
            map.put("title", title);
            maps.add(map);

        }

3.问题总结

总结:list.add加入了map的内存地址,不new的话会在list加入多条数据,同一个内存地址,就会导致所以的数据的值都是一样的,解决办法:每次创建一个新的对象

你可能感兴趣的:(java)