public class WangyiCrawlerTask implements CrawlerTask { /** 新闻显示条数的限制,默认100 */ private int limit = 100; /** 文档服务器地址 */ private String baseUrl = "http://119.254.110.32:8080/HBaseDfs/dfs"; /** 初始网易新闻信息ajax请求网址,得到的是JSON字符串 */ private final String rawUrl = "http://j.news.163.com/hy/newshot.s?newchannel=news&channel=10&offset=0&limit=%d&city=%s"; static HttpQuery browser = HttpQuery.getInstance(); /** 日志管理对象 */ private static final Logger LOGGER = LoggerFactory.getLogger(WangyiCrawlerTask.class); /** * 构造方法 * * @param limit */ public WangyiCrawlerTask(int limit) { this.limit = limit; } /** * 根据城市名,获取得到新闻主页面的url * * @return */ private String createUrl(String city) { return String.format(rawUrl, limit, city); } /** * 解析页面中的一项JSON数据,并包装到WebPage当中,并返回WebPage * * @param obj * @param session */ private ParseResult parsePage(JSONObject obj) { WebPage page = new WebPage(); String content = ""; String pageUrl = ""; try { // 每个页面的内容依然是ajax,这里得到的是JSON数据的URL String contentUrl = WangyiPageParser.getContentUrl(obj); pageUrl = WangyiPageParser.getPageUrl(obj); String title = obj.getString("title"); String webSite = "网易新闻"; JSONObject contentJSON = WangyiPageParser.getContentJSON(contentUrl); content = WangyiPageParser.getContent(contentJSON); Timestamp publishDate = WangyiPageParser.getPublishDate(contentJSON); // 不用自带的summary,截取content前200个字符获取summary String summary = WangyiPageParser.getSummary(content); // 如果content或者title为空,则不保存页面 if (content.equals("") || title.equals("") || title == null) { return null; } page.setTitle(title); page.setUrl(pageUrl); page.setWebSite(webSite); page.setDownloadDate(new Timestamp(System.currentTimeMillis())); page.setTitle(title); page.setSummary(summary); page.setPublishDate(publishDate); page.setType(1); page.setIndexedStatus(3); } catch (Exception e) { LOGGER.error("网易新闻页面信息提取失败!"); return null; } if (content.equals("") || content == null) return null; ParseResult res = new ParseResult(); res.setPage(page); res.setContent(content); return res; } /** * 根据ajax请求链接爬取内容 解析流程是:从rawUrl拼接上城市和limit组成一个原始页面的ajax请求url,获取内容解析成JSONArray, * array中的一项包含有title,和url_163属性("/docs/10/2015052804/AQM4VA369001VA37.html") * 和rawPageUrl:"http://j.news.163.com"拼接后得到访问的页面 我们在url缓存,存入mysql以及HBase的url都是这个pageUrl * docID("AQM4VA369001VA37"),利用docID填充入rawContentUrl * :"http://j.news.163.com/hy/doc.s?info=2&type=10&hash=&docid=%s" 组成一条新闻的ajax请求,请求返回的是一个json字符串 * 解析之后,得到contentJSON,根据这个数据得到里面的content,publish_date字段 * * @param url 从createUrl中获取到的拼接好的字符串,这是一条某城市新闻的ajax请求页面 * @author BAO */ private void craw(String url, List<ParseResult> list) { String html = null; try { html = WangyiPageParser.getHtmlFromUrl(url); } catch (BaseHttpException e1) { LOGGER.error(e1.getMessage() + "获取AJAX页面失败", e1); return; } // 解析ajax请求主页的json,如果失败就直接返回 JSONArray array = null; try { array = JSONArray.parseArray(html); } catch (Exception e) { LOGGER.error(e.getMessage() + "页面JSON解析失败", e); return; } for (int i = 0; i < array.size(); i++) { JSONObject obj = null; try { obj = array.getJSONObject(i); } catch (Exception e) { LOGGER.error(e.getMessage() + "JSON内容获取失败", e); } // 如果obj为null,就跳过 if (obj == null) continue; ParseResult result = parsePage(obj); if (result != null) { list.add(result); } if (!SystemProps.isTest()) { TimerUtils.delayForSeconds(2); } } } private void trySaveParseResults(List<ParseResult> results) { CacheClient session = CommonUtils.getCacheClient(); int count = 0; try { for (int i = 0; i < results.size(); i++) { ParseResult result = results.get(i); try { if (!CommonUtils.checkUrl(session, result.getPage().getUrl())) { WebPage page = result.getPage(); String content = result.getContent(); // 保存文章到HBase文档服务器 try { if (SystemProps.storeable()) { CommonUtils.storage(true, page.getId(), content, true); } } catch (Exception e) { LOGGER.error("保存至文档服务器失败! : " + e.getMessage()); continue; } // 保存页面到sql数据库 // WebPageRepository.save(page); try { WebPageManager.getInstance().savePage(page); } catch (SQLException e) { LOGGER.error(e.getMessage() + "保存至sql数据库失败", e); continue; } catch (Exception e) { LOGGER.error(e.getMessage() + "保存至sql数据库失败", e); continue; } // 保存contentUrl,即AJAX的JSON数据的URL CommonUtils.storeUrl(session, page.getUrl()); count++; } } catch (Exception e) { LOGGER.error(e.getMessage(), e); } } } finally { LOGGER.info("存放爬取结果" + count + "条"); try { CommonUtils.recycleCacheClient(session); } catch (Exception e) { LOGGER.error(e.getMessage() + "释放cacheclient失败"); } } } public void run() { try { List<String> cityNames = City.getCityNames(); LOGGER.info("开始爬取网易新闻,城市列表:" + cityNames); for (String cityname : cityNames) { List<ParseResult> results = new ArrayList<ParseResult>(); try { LOGGER.info("启动网易新闻AJAX爬取 城市:" + cityname); craw(createUrl(cityname), results); LOGGER.info("网易新闻AJAX爬取 城市:" + cityname + "完毕"); } finally { try { LOGGER.info("开始存放爬取结果 城市:" + cityname); trySaveParseResults(results); LOGGER.info("存放网易新闻结束 城市:" + cityname); } catch (Exception e) { LOGGER.error(e.getMessage() + "存放网易新闻:" + cityname + " 时出现错误!", e); } catch (Throwable e) { LOGGER.error(e.getMessage() + "存放网易新闻:" + cityname + " 时出现严重错误!", e); } } TimerUtils.delayForSeconds(30); } } catch (Exception e) { LOGGER.error(e.getMessage() + "网易新闻AJAX爬虫出现未知异常!", e); } } }
private String request(String url) throws BaseHttpException { String html = HttpQuery.getInstance().get(url).asString(); return html; }