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();
}
}
57
1
public class CrawlerUtil {
2
3
/**
4
* 獲取主網頁的內容
5
*
6
* @param url 網頁url
7
* @param requestMethod 請求方式
8
* @param refer post內容
9
* @return 網頁內容
10
*/
11
public static String sendHttpRequest(String url, RequestMethod requestMethod, String refer) {
12
refer = refer == null || "".equals(refer) ? null : refer;
13
StringBuffer buffer = new StringBuffer();
14
try {
15
//建立連接
16
URL requestUrl = new URL(url);
17
HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
18
connection.setRequestMethod(requestMethod.getValue());
19
switch (requestMethod) {
20
case GET:
21
connection.connect();
22
break;
23
case POST:
24
if (refer != null) {
25
OutputStream out = connection.getOutputStream();
26
out.write((refer.getBytes("UTF-8")));
27
out.close();
28
}
29
break;
30
default:
31
break;
32
}
33
//獲取網頁內容
34
InputStream in = connection.getInputStream();
35
InputStreamReader inputStreamReader = new InputStreamReader(in, "UTF-8");
36
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
37
String str = null;
38
while ((str = bufferedReader.readLine()) != null) {
39
buffer.append(str);
40
}
41
//關閉資源
42
bufferedReader.close();
43
inputStreamReader.close();
44
in.close();
45
in = null;
46
connection.disconnect();
47
48
} catch (MalformedURLException e) {
49
e.printStackTrace();
50
} catch (IOException e) {
51
e.printStackTrace();
52
}
53
54
return buffer.toString();
55
}
56
57
}
觀察鏈接,發現其變動的實際上就兩個部分,日期和最后部分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);
}
21
1
/**
2
* 雙流新聞網地址
3
*/
4
private static final String NEWS_URL = "http://epaper.slnews.net.cn/html/%s/%s.htm";
5
6
/**
7
* 獲取特定日期的新聞網的版面地址url
8
* <p>
9
* 默認不填寫factor參數的話,則url為第一版面鏈接,填入factor值node_2
10
* </p>
11
*
12
* @param date 日期
13
* @param factor 板面,形式為node_?
14
* 文章,形式為content_?
15
* @return 新聞網地址url
16
*/
17
public static String takePageUrl(Date date, String factor) {
18
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM/dd");
19
factor = factor == null ? "node_2" : factor;
20
return String.format(NEWS_URL, format.format(date), factor);
21
}
獲取到的網頁內容中,根據網頁代碼結構,使用正則來匹配獲取我們想要的內容,先寫一個通用的匹配方法:
/**
* 獲取內容匹配的元素集合
*
* @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;
}
21
1
/**
2
* 獲取內容匹配的元素集合
3
*
4
* @param content 網頁內容
5
* @param reg 匹配正則
6
* @return 元素集合
7
*/
8
private static List<String> takeElementList(String content, String reg) {
9
log.debug("start take elements from content by Reg");
10
List<String> list = new ArrayList<String>();
11
//定義正則規則
12
Pattern pattern = Pattern.compile(reg);
13
Matcher matcher = pattern.matcher(content);
14
while (matcher.find()) {
15
String element = matcher.group(1);
16
list.add(element);
17
log.debug(element);
18
}
19
log.debug("take elements end");
20
return list;
21
}
那么主要的就很簡單了,獲取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);
}
x
1
/**
2
* 獲取特定日期新聞網的版面鏈接元素
3
*
4
* @param date 日期
5
* @return 版面鏈接的元素集合
6
*/
7
public static List<String> takeNodeUrlEleList(Date date) {
8
String url = takePageUrl(date, null);
9
String content = CrawlerUtil.sendHttpRequest(url, RequestMethod.GET, null);
10
String reg = "<a id=pageLink href=.*?(node_\\d+?)\\.htm>.*?<\\/a>";
11
return takeElementList(content, reg);
12
}
13
14
/**
15
* 獲取指定日期指定版面的所有文章鏈接元素集合
16
*
17
* @param date 日期
18
* @param node 版面元素,格式為node_?
19
* @return 文章鏈接的元素集合
20
*/
21
public static List<String> takeNewsUrlEleList(Date date, String node) {
22
String url = takePageUrl(date, node);
23
String content = CrawlerUtil.sendHttpRequest(url, RequestMethod.GET, null);
24
String reg = "<a href=.*?(content_\\d+?)\\.htm>";
25
return takeElementList(content, reg);
26
}
那么獲取新聞文章網頁內容的方法也就無非是上面兩個的嵌套循環:
/**
* 抓取指定日期新聞頁面內容集合
*
* @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;
}
1
/**
2
* 抓取指定日期新聞頁面內容集合
3
*
4
* @param date 日期
5
* @return 新聞頁面內容
6
*/
7
public static List<String> takeNewsPageList(Date date) {
8
log.info("start crawl news page content. date:" + date);
9
List<String> newsList = new ArrayList<String>();
10
List<String> nodeEleList = NewsCrawler.takeNodeUrlEleList(date);
11
for (String nodeEle : nodeEleList) {
12
List<String> newsEleList = NewsCrawler.takeNewsUrlEleList(date, nodeEle);
13
for (String newsEle : newsEleList) {
14
String url = NewsCrawler.takePageUrl(date, newsEle);
15
String content = CrawlerUtil.sendHttpRequest(url, RequestMethod.GET, null);
16
newsList.add(content);
17
}
18
}
19
log.info("crawl news page content end. page amount:" + newsList.size());
20
return newsList;
21
}
接下來,可以再根據需要對內容進行進一步的加工,因為獲取的新聞文章網頁內容也包含了太多我們完全不需要的內容和html代碼,比如我們只需要網頁內容的新聞部分,那么就再通過查看該html代碼結構,使用正則表達式繼續提取即可。
大概就是這個樣子,大功告成,一個簡單的示例。
