爬蟲代碼實現五:解析所有分頁url並最佳化解析實作類別

來源:互聯網
上載者:User

標籤:nod   部分   策略   方法   exce   ini   lock   通過   path   

 

 

,我們進入優酷首頁,可以看到電視劇列表,我們稱這個頁面為電視劇列表頁,而點擊進入某個電視劇,則稱為電視劇詳情頁。那麼如何擷取所有分頁以及對應的詳情頁呢,通過下面的分頁得到。

因此,首先,我們將StartDSJCount中的url從詳情頁改為列表頁,

 

 由於這裡我們想擷取列表頁對應的所有分頁詳情頁,因此,我們需要在page中添加一個urlList屬性,然後給它get/set方法。這裡如果自動產生set方法,那麼我們在set時還要new一個list,有點麻煩,這裡我們先暫時只自動產生get方法,然後我們自己手動去寫set方法。

而在這個set方法中,我們傳入的參數是一個url而不是一個urlList,這樣我們只要傳入這一個url,然後在set方法中將這個url添加到list中去,而如果傳一個urlList作為參數,則每次都要new一個list。

Page類重構如下:

package com.dajiangtai.djt_spider.entity;

import java.util.ArrayList;
import java.util.List;

/**
* 儲存頁面資訊實體類
* @author dajiangtai
* created by 2017-01-09
*
*/
public class Page {

//頁面內容
private String content;

//總播放量
private String allnumber;

//每日播放增量
private String daynumber;

//評論數
private String commentnumber;

//收藏數
private String collectnumber;

//贊
private String supportnumber;

//踩
private String againstnumber;

//電視劇名稱
private String tvname;

//頁面url
private String url;

//子集資料
private String episodenumber;

//電視劇id
private String tvId;

//儲存電視劇url(包括列表頁url和詳情頁url)
private List<String> urlList = new ArrayList<String>();

//自動產生get方法
public List<String> getUrlList() {
return urlList;
}

//手動寫set方法,即addUrl方法,傳入一個url,同樣實現setUrlList效果

public void addUrl(String url){
this.urlList.add(url);
}
public String getContent() {
return content;
}

public void setContent(String content) {
this.content = content;
}

public String getAllnumber() {
return allnumber;
}

public void setAllnumber(String allnumber) {
this.allnumber = allnumber;
}

public String getDaynumber() {
return daynumber;
}

public void setDaynumber(String daynumber) {
this.daynumber = daynumber;
}

public String getCommentnumber() {
return commentnumber;
}

public void setCommentnumber(String commentnumber) {
this.commentnumber = commentnumber;
}

public String getCollectnumber() {
return collectnumber;
}

public void setCollectnumber(String collectnumber) {
this.collectnumber = collectnumber;
}

public String getSupportnumber() {
return supportnumber;
}

public void setSupportnumber(String supportnumber) {
this.supportnumber = supportnumber;
}

public String getAgainstnumber() {
return againstnumber;
}

public void setAgainstnumber(String againstnumber) {
this.againstnumber = againstnumber;
}

public String getTvname() {
return tvname;
}

public void setTvname(String tvname) {
this.tvname = tvname;
}

public String getUrl() {
return url;
}

public void setUrl(String url) {
this.url = url;
}

public String getEpisodenumber() {
return episodenumber;
}

public void setEpisodenumber(String episodenumber) {
this.episodenumber = episodenumber;
}

public String getTvId() {
return tvId;
}

public void setTvId(String tvId) {
this.tvId = tvId;
}


}

 接下來。我們在YOUKUProcessService重構,對url分別進行判斷,如果是詳情頁,則...處理。也就是,將原來的詳情頁處理的代碼都放到if判斷處理括弧中去。

同時,YOUKUProcessService中原來詳情頁的處理代碼,全部封裝到一個方法中去,通過觀察,我們只需要傳入TagNode rootNode,Page page即可。其餘的代碼原封不動的複製到這個方法中。

因此,YOUKUProcessService代碼重構如下:

package com.dajiangtai.djt_spider.service.impl;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.htmlcleaner.XPatherException;

import com.dajiangtai.djt_spider.entity.Page;
import com.dajiangtai.djt_spider.service.IProcessService;
import com.dajiangtai.djt_spider.util.HtmlUtil;
import com.dajiangtai.djt_spider.util.LoadPropertyUtil;
import com.dajiangtai.djt_spider.util.RegexUtil;

public class YOUKUProcessService implements IProcessService {

public void process(Page page) {
String content = page.getContent();
HtmlCleaner htmlCleaner = new HtmlCleaner();
//利用htmlCleaner對網頁進行解析,得到根節點
TagNode rootNode = htmlCleaner.clean(content);

//如果是詳情頁
if(page.getUrl().startsWith("http://www.youku.com/show_page")){
parseDetail(rootNode,page);
}else{

}

}



//解析電視劇詳情頁
public void parseDetail(TagNode rootNode,Page page){
// /html/body/div[4]/div/div[1]/div[2]/div[2]/ul/li[11]
//對XPath做相應的調整,使其有效,如果不該寫,則使用debug模式,會發現evaluateXPath為[]
//總播放數
// String allnumber = HtmlUtil.getFieldByRegex(rootNode, parseAllNumber, allnumberRegex);
String allnumber = HtmlUtil.getFieldByRegex(rootNode, LoadPropertyUtil.getYOUKY("parseAllNumber"), LoadPropertyUtil.getYOUKY("allnumberRegex"));
//System.out.println("總播放數數量為:"+allnumber);
page.setAllnumber(allnumber);

//總播放數
String commentnumber = HtmlUtil.getFieldByRegex(rootNode, LoadPropertyUtil.getYOUKY("parseCommentNumber"), LoadPropertyUtil.getYOUKY("commentnumberRegex"));
//System.out.println("總評論數量為:"+commentnumber);
page.setCommentnumber(commentnumber);

//總播放數
String supportnumber = HtmlUtil.getFieldByRegex(rootNode, LoadPropertyUtil.getYOUKY("parseSupportNumber"), LoadPropertyUtil.getYOUKY("supportnumberRegex"));
//System.out.println("總評論數量為:"+supportnumber);
page.setSupportnumber(supportnumber);

page.setDaynumber("0");
page.setAgainstnumber("0");
page.setCollectnumber("0");

//Pattern numberPattern = Pattern.compile(regex,Pattern.DOTALL);
Pattern numberPattern = Pattern.compile(LoadPropertyUtil.getYOUKY("idRegex"),Pattern.DOTALL);
page.setTvId("tvId_"+RegexUtil.getPageInfoByRegex(page.getUrl(), numberPattern, 1));
}
}

以上是詳情頁處理,那麼如果是列表頁怎麼處理呢?

我們開啟一個列表頁,然後儲存這個列表頁url的下一頁的url。如何擷取到當前這一頁的下一頁的url呢?通過

點擊這個紅色部分實現。接下來我們按F12查看代碼是怎麼回事。

1.我們擷取到紅色部分的XPath:

這段XPath對應的代碼如下:

我們通過這個XPath直接擷取到它的下一個連結地址href即可。

2.我們將這個XPath添加到設定檔中去。nextUrlRegex=//*[@id="m13050845531"]/div[2]/div/div/div[2]/a

allnumberRegex=(?<=\u603B\u64AD\u653E\u6570\uFF1A)[\\d,]+
commentnumberRegex=(?<=\u8BC4\u8BBA\uFF1A)[\\d,]+
supportnumberRegex=(?<=\u9876\uFF1A)[\\d,]+
parseAllNumber=/body/div/div/div/div/div/ul/li[11]
parseCommentNumber=//div[@class=\"p-base\"]/ul/li[12]
parseSupportNumber=//div[@class=\"p-base\"]/ul/li[13]
idRegex=http://list.youku.com/show/id_([\\w]+).html
nextUrlRegex=//*[@id="m13050845531"]/div[2]/div/div/div[2]/a

 

3.其實這裡的思想很簡單,通過xpath定位到a標籤。那麼,else如何寫代碼呢?在這裡,我們封裝好一個方法,用於或許xpath對應代碼中的屬性值。

因此,在HtmlUtil中添加一個方法,用於擷取標籤屬性值

package com.dajiangtai.djt_spider.util;

import java.util.regex.Pattern;

import org.htmlcleaner.TagNode;
import org.htmlcleaner.XPatherException;

/**
* 頁面解析工具類
* @author Administrator
*
*/
public class HtmlUtil {

//擷取標籤屬性值
public static String getAttributeByName(TagNode rootNode,String xpath,String att){
String result = null;
Object[] evaluateXPath = null;
try {
evaluateXPath = rootNode.evaluateXPath(xpath);
if(evaluateXPath.length>0){
TagNode node = (TagNode)evaluateXPath[0];
return node.getAttributeByName(att);
}
} catch (XPatherException e) {
e.printStackTrace();
}
return result;
}

public static String getFieldByRegex(TagNode rootNode,String xpath,String regex){

String number = "0";
Object[] evaluateXPath = null;
try {
evaluateXPath = rootNode.evaluateXPath(xpath);
if(evaluateXPath.length>0){
TagNode node = (TagNode)evaluateXPath[0];
Pattern numberPattern = Pattern.compile(regex,Pattern.DOTALL);
number = RegexUtil.getPageInfoByRegex(node.getText().toString(), numberPattern, 0);
}
} catch (XPatherException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return number;



}
}

4.如何擷取下一頁呢?直接調用HtmlUtil封裝好的方法:

String nextUrl = HtmlUtil.getAttributeByName(rootNode, LoadPropertyUtil.getYOUKY("nextUrlRegex"), "href");

5.

 

那如果是點擊到了34頁,再查看下一頁對應的代碼,則為紅色部分,沒有href屬性,那麼nextUrl也就為空白。

如果它不為空白,我們就將這個下一頁的url添加到urlList集合中。所以,代碼如下:

String nextUrl = HtmlUtil.getAttributeByName(rootNode, LoadPropertyUtil.getYOUKY("nextUrlRegex"), "href");
if(nextUrl!=null){
page.addUrl(nextUrl);
}

6.以上是擷取下一頁的url,現在我們需要擷取分頁顯示的所有詳情頁的url。我們隨便點擊兩個電視劇查看它的xpath,發現:

它倆的共性在,不同的ul下的li[1]/a,因此,我們只需要改成公用的ul即可。在youku.properties添加:eachDetailUrlRegex=//*[@id="m13050845531"]/div[1]/div/ul/li[1]/a。

它表示所有的ul下的li[1]/a,也就是當前頁顯示的所有電視劇的xpath。

7.通過eachDetailUrlRegex我們可以擷取到多個ul下的li[1]/a的xpath,然後調用rootNode.evaluateXPath(LoadPropertyUtil.getYOUKY("eachDetailUrlRegex"));得到一個數組,再對數組進行遍曆,也就是找到所有的詳情頁的a,然後讀取href屬性,再將詳情頁的url也加到urlList集合中。
if(evaluateXPath.length>0){

//擷取下一頁的url
String nextUrl = HtmlUtil.getAttributeByName(rootNode, LoadPropertyUtil.getYOUKY("nextUrlRegex"), "href");
if(nextUrl!=null){
page.addUrl(nextUrl);  //將下一頁的url添加到urlList集合中
}

//擷取詳情頁的url
try {
Object[] evaluateXPath =
rootNode.evaluateXPath(LoadPropertyUtil.getYOUKY("eachDetailUrlRegex"));
if(evaluateXPath.length>0){
for(Object object:evaluateXPath){
TagNode tagnode = (TagNode) object;
String detailUrl = tagnode.getAttributeByName("href");
page.addUrl(detailUrl);//將詳情頁的url添加到urlList集合中
}
}

8.最後,YOUKUProcessService 代碼如下:

package com.dajiangtai.djt_spider.service.impl;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.htmlcleaner.XPatherException;

import com.dajiangtai.djt_spider.entity.Page;
import com.dajiangtai.djt_spider.service.IProcessService;
import com.dajiangtai.djt_spider.util.HtmlUtil;
import com.dajiangtai.djt_spider.util.LoadPropertyUtil;
import com.dajiangtai.djt_spider.util.RegexUtil;

public class YOUKUProcessService implements IProcessService {

public void process(Page page) {
String content = page.getContent();
HtmlCleaner htmlCleaner = new HtmlCleaner();
//利用htmlCleaner對網頁進行解析,得到根節點
TagNode rootNode = htmlCleaner.clean(content);

//如果是詳情頁
if(page.getUrl().startsWith("http://www.youku.com/show_page")){
parseDetail(rootNode,page);
}else{
//擷取下一頁的url
String nextUrl = HtmlUtil.getAttributeByName(rootNode, LoadPropertyUtil.getYOUKY("nextUrlRegex"), "href");
if(nextUrl!=null){
System.out.println(nextUrl); //列印下一頁的url
page.addUrl(nextUrl);
}

//擷取詳情頁的url
try {
Object[] evaluateXPath =
rootNode.evaluateXPath(LoadPropertyUtil.getYOUKY("eachDetailUrlRegex"));
if(evaluateXPath.length>0){
for(Object object:evaluateXPath){
TagNode tagnode = (TagNode) object;
String detailUrl = tagnode.getAttributeByName("href");
System.out.println(detailUrl);//列印詳情頁的url
page.addUrl(detailUrl);
}
}
} catch (XPatherException e) {

e.printStackTrace();
}
}

}

//解析電視劇詳情頁
public void parseDetail(TagNode rootNode,Page page){
// /html/body/div[4]/div/div[1]/div[2]/div[2]/ul/li[11]
//對XPath做相應的調整,使其有效,如果不該寫,則使用debug模式,會發現evaluateXPath為[]
//總播放數
// String allnumber = HtmlUtil.getFieldByRegex(rootNode, parseAllNumber, allnumberRegex);
String allnumber = HtmlUtil.getFieldByRegex(rootNode, LoadPropertyUtil.getYOUKY("parseAllNumber"), LoadPropertyUtil.getYOUKY("allnumberRegex"));
//System.out.println("總播放數數量為:"+allnumber);
page.setAllnumber(allnumber);

//總播放數
String commentnumber = HtmlUtil.getFieldByRegex(rootNode, LoadPropertyUtil.getYOUKY("parseCommentNumber"), LoadPropertyUtil.getYOUKY("commentnumberRegex"));
//System.out.println("總評論數量為:"+commentnumber);
page.setCommentnumber(commentnumber);

//總播放數
String supportnumber = HtmlUtil.getFieldByRegex(rootNode, LoadPropertyUtil.getYOUKY("parseSupportNumber"), LoadPropertyUtil.getYOUKY("supportnumberRegex"));
//System.out.println("總評論數量為:"+supportnumber);
page.setSupportnumber(supportnumber);

page.setDaynumber("0");
page.setAgainstnumber("0");
page.setCollectnumber("0");

//Pattern numberPattern = Pattern.compile(regex,Pattern.DOTALL);
Pattern numberPattern = Pattern.compile(LoadPropertyUtil.getYOUKY("idRegex"),Pattern.DOTALL);
page.setTvId("tvId_"+RegexUtil.getPageInfoByRegex(page.getUrl(), numberPattern, 1));
}
}

9.這裡運行StartDSJCount的main方法,輸出的值都為null,採用debug模式調試,只要一運行,就報異常

 

 原因:我們測試的url不是之前的詳情頁的url,而是電視列表的url,如果是瀏覽器輸入該url沒有問題,但是如果是通過程式去訪問,則有些網站往往採取反爬蟲策略,當它測試到是程式在讀取url訪問,就會自動阻止。這個問題怎麼解決呢?我們通過瀏覽器類比訪問,而不是單純的通過程式來訪問。

按F12,選擇network,再重新整理網頁,再選擇network欄最上方顯示的,發現Headers下有個User代碼

重構PageDownLoadUtil代碼:

1.定義private final static String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36";;

2.request.setHeader("User-Agent",USER_AGENT);

PageDownLoadUtil代碼如下:

package com.dajiangtai.djt_spider.util;

import java.io.IOException;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import com.dajiangtai.djt_spider.entity.Page;
import com.dajiangtai.djt_spider.service.impl.HttpClientDownLoadService;


/**
* 頁面下載工具:將整個頁面的html源碼全部下載下來
* @author lch
* created by 2017/1/9
*
*/
public class PageDownLoadUtil {

private final static String USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36";

public static String getPageContent(String url){
HttpClientBuilder builder = HttpClients.custom();
CloseableHttpClient client = builder.build();

HttpGet request = new HttpGet(url);
String content = null;
try {
request.setHeader("User-Agent",USER_AGENT);
CloseableHttpResponse response = client.execute(request);
HttpEntity entity = response.getEntity();
content = EntityUtils.toString(entity,"utf-8");
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return content;

}

public static void main(String[] args) {
String url = "http://list.youku.com/show/id_z9cd2277647d311e5b692.html?spm=a2h0j.8191423.sMain.5~5~A!2.iCUyO9";
//String content = PageDownLoadUtil.getPageContent(url);
//System.out.println(content);
HttpClientDownLoadService down = new HttpClientDownLoadService();
Page page = down.download(url);
System.out.println(page.getContent());
}
}

 

再次運行StartDSJCount的main方法測試,如下:

下一頁的url以及詳情頁的url是輸出了,但是,爬取的這些值都為null。為什麼呢?

其實這裡為null是正常的。因為,我們測試的url是列表url,而不是詳情頁url,只有詳情頁才有總播放量、評論數、贊等。

 

爬蟲代碼實現五:解析所有分頁url並最佳化解析實作類別

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.