[01-01] 示例:用Java爬取新闻


1、分析url

《空港双流》数字报刊,访问地址为:http://epaper.slnews.net.cn,现在为了抓取每篇新闻的网页内容。

在浏览器访问该链接后,发现链接出现了变化,看样子是后端服务器进行了重定向:

观察该链接,发现定向链接规则显然是包含日期规则,2018-01/10,表示2018年01月10日的报刊,也就是定位为当天的日期,试着修改为前一天,即2018-01/09,页面果然发生了跳转,没问题。跳转到第二天,即还没有到来的11号,页面显示未找到。

从页面结构可以看到,报刊分为按版面分区,每个版面下包含不同的文章:

用浏览器调试查看一下网页源码,可以看到版面部分的链接结构为node_?.htm,而文章部分的链接结构是content_?.htm形式(?指代数字):

 
 
那么显然思路就有了:
  • 先得到所有版面的url
  • 访问版面网页并抓取其中的所有文章的url
  • 最后访问文章url就可以得到新闻网页内容了

2、代码部分

根据爬虫的基本原理,先写一个返回指定url的网页内容的方法:
public class CrawlerUtil {  

    /**
     * 获取主网页的内容
     *
     * @param url 网页url
     * @param requestMethod 请求方式
     * @param refer post内容
     * @return 网页内容
     */
    public static String sendHttpRequest(String url, RequestMethod requestMethod, String refer) {
        refer = refer == null || "".equals(refer) ? null : refer;
        StringBuffer buffer = new StringBuffer();
        try {
            //建立连接
            URL requestUrl = new URL(url);
            HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
            connection.setRequestMethod(requestMethod.getValue());
            switch (requestMethod) {
                case GET:
                    connection.connect();
                    break;
                case POST:
                    if (refer != null) {
                        OutputStream out = connection.getOutputStream();
                        out.write((refer.getBytes("UTF-8")));
                        out.close();
                    }
                    break;
                default:
                    break;
            }
            //获取网页内容
            InputStream in = connection.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(in, "UTF-8");
            BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
            String str = null;
            while ((str = bufferedReader.readLine()) != null) {
                buffer.append(str);
            }
            //关闭资源
            bufferedReader.close();
            inputStreamReader.close();
            in.close();
            in = null;
            connection.disconnect();

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return buffer.toString();
    }

}

观察链接,发现其变动的实际上就两个部分,日期和最后部分node或content,那么写一个获取链接的方法:
/**
 * 双流新闻网地址
 */
private static final String NEWS_URL = "http://epaper.slnews.net.cn/html/%s/%s.htm";

/**
 * 获取特定日期的新闻网的版面地址url
 * <p>
 * 默认不填写factor参数的话,则url为第一版面链接,填入factor值node_2
 * </p>
 *
 * @param date   日期
 * @param factor 板面,形式为node_?
 *               文章,形式为content_?
 * @return 新闻网地址url
 */
public static String takePageUrl(Date date, String factor) {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM/dd");
    factor = factor == null ? "node_2" : factor;
    return String.format(NEWS_URL, format.format(date), factor);
}

获取到的网页内容中,根据网页代码结构,使用正则来匹配获取我们想要的内容,先写一个通用的匹配方法:
/**
 * 获取内容匹配的元素集合
 *
 * @param content 网页内容
 * @param reg     匹配正则
 * @return 元素集合
 */
private static List<String> takeElementList(String content, String reg) {
    log.debug("start take elements from content by Reg");
    List<String> list = new ArrayList<String>();
    //定义正则规则
    Pattern pattern = Pattern.compile(reg);
    Matcher matcher = pattern.matcher(content);
    while (matcher.find()) {
        String element = matcher.group(1);
        list.add(element);
        log.debug(element);
    }
    log.debug("take elements end");
    return list;
}

那么主要的就很简单了,获取node版面的集合,获取content文章的集合,这里主要需要注意的是正则的书写:
/**
 * 获取特定日期新闻网的版面链接元素
 *
 * @param date 日期
 * @return 版面链接的元素集合
 */
public static List<String> takeNodeUrlEleList(Date date) {
    String url = takePageUrl(date, null);
    String content = CrawlerUtil.sendHttpRequest(url, RequestMethod.GET, null);
    String reg = "<a id=pageLink href=.*?(node_\\d+?)\\.htm>.*?<\\/a>";
    return takeElementList(content, reg);
}

/**
 * 获取指定日期指定版面的所有文章链接元素集合
 *
 * @param date 日期
 * @param node 版面元素,格式为node_?
 * @return 文章链接的元素集合
 */
public static List<String> takeNewsUrlEleList(Date date, String node) {
    String url = takePageUrl(date, node);
    String content = CrawlerUtil.sendHttpRequest(url, RequestMethod.GET, null);
    String reg = "<a href=.*?(content_\\d+?)\\.htm>";
    return takeElementList(content, reg);
}

那么获取新闻文章网页内容的方法也就无非是上面两个的嵌套循环:
/**
 * 抓取指定日期新闻页面内容集合
 *
 * @param date 日期
 * @return 新闻页面内容
 */
public static List<String> takeNewsPageList(Date date) {
    log.info("start crawl news page content. date:" + date);
    List<String> newsList = new ArrayList<String>();
    List<String> nodeEleList = NewsCrawler.takeNodeUrlEleList(date);
    for (String nodeEle : nodeEleList) {
        List<String> newsEleList = NewsCrawler.takeNewsUrlEleList(date, nodeEle);
        for (String newsEle : newsEleList) {
            String url = NewsCrawler.takePageUrl(date, newsEle);
            String content = CrawlerUtil.sendHttpRequest(url, RequestMethod.GET, null);
            newsList.add(content);
        }
    }
    log.info("crawl news page content end. page amount:" + newsList.size());
    return newsList;
}

接下来,可以再根据需要对内容进行进一步的加工,因为获取的新闻文章网页内容也包含了太多我们完全不需要的内容和html代码,比如我们只需要网页内容的新闻部分,那么就再通过查看该html代码结构,使用正则表达式继续提取即可。

大概就是这个样子,大功告成,一个简单的示例。

posted @ 2018-01-10 16:52  Dulk  阅读(2604)  评论(0编辑  收藏  举报