最近學習了下webmagic,學webmagic是因為想折騰下爬蟲,但是自己學java的,又不想太費功夫,所以webmagic是比較好的選擇了。
寫了幾個demo,源碼流程大致看了一遍。想着把博客園的文章列表爬下來吧。
首頁顯示的就是第一頁文章的列表,
但是翻頁按鈕不是鏈接,而是動態的地址:
實際請求的地址及參數:
針對這個動態頁面的情況,有兩種解決方案:
1. webmagic模擬post請求,獲取返回頁面。
1 public class CnblogsSpider implements PageProcessor { 2 3 private Site site = Site.me().setRetryTimes(3).setSleepTime(1000).setTimeOut(10000) 4 .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"); 5 6 public static final String URL_LIST = "https://www.cnblogs.com/mvc/AggSite/PostList.aspx"; 7 8 public static int pageNum = 1; 9 10 public void process(Page page) { 11 12 if (page.getUrl().regex("^https://www\\.cnblogs\\.com$").match()) { 13 try { 14 page.addTargetRequests(page.getHtml().xpath("//*[@id=\"post_list\"]/div/div[@class='post_item_body']/h3/a/@href").all()); 15 pageNum++; 16 //模擬post請求 17 Request req = new Request(); 18 req.setMethod(HttpConstant.Method.POST); 19 req.setUrl("https://www.cnblogs.com/mvc/AggSite/PostList.aspx"); 20 req.setRequestBody(HttpRequestBody.json("{CategoryType: 'SiteHome', ParentCategoryId: 0, CategoryId: 808, PageIndex: " + pageNum 21 + ", TotalPostCount: 4000,ItemListActionName:'PostList'}", "utf-8")); 22 page.addTargetRequest(req); 23 } catch (Exception e) { 24 e.printStackTrace(); 25 } 26 } else if (page.getUrl().regex(URL_LIST).match() && pageNum <= 200) { 27 try { 28 Thread.sleep(5000); 29 List<String> urls = page.getHtml().xpath("//*[@class='post_item']//div[@class='post_item_body']/h3/a/@href").all(); 30 page.addTargetRequests(urls); 31 //模擬post請求 32 Request req = new Request(); 33 req.setMethod(HttpConstant.Method.POST); 34 req.setUrl("https://www.cnblogs.com/mvc/AggSite/PostList.aspx"); 35 req.setRequestBody(HttpRequestBody.json("{CategoryType: 'SiteHome', ParentCategoryId: 0, CategoryId: 808, PageIndex: " + ++pageNum 36 + ", TotalPostCount: 4000,ItemListActionName:'PostList'}", "utf-8")); 37 page.addTargetRequest(req); 38 System.out.println("CurrPage:" + pageNum + "#######################################"); 39 40 } catch (Exception e) { 41 e.printStackTrace(); 42 } 43 } else { 44 // 獲取頁面需要的內容,這里只取了標題,其他信息同理。 45 System.out.println("抓取的內容:" + page.getHtml().xpath("//a[@id='cb_post_title_url']/text()").get()); 46 } 47 } 48 49 public Site getSite() { 50 return site; 51 } 52 53 public static void main(String[] args) { 54 Spider.create(new CnblogsSpider()).addUrl("https://www.cnblogs.com").thread(3).run(); 55 } 56 }
2.使用webmagic-selenium
1 public class SeleniumCnblogsSpider implements PageProcessor { 2 3 private Site site = Site.me().setRetryTimes(3).setSleepTime(1000).setTimeOut(10000) 4 .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"); 5 6 public static final String URL_LIST = "https://www\\.cnblogs\\.com/#p\\d{1,3}"; 7 8 public static int pageNum = 1; 9 10 public void process(Page page) { 11 12 13 if (page.getUrl().regex("^https://www\\.cnblogs\\.com$").match()) {//爬取第一頁 14 try { 15 page.addTargetRequests(page.getHtml().xpath("//*[@id=\"post_list\"]/div/div[@class='post_item_body']/h3/a/@href").all()); 16 17 pageNum++; 18 page.addTargetRequest("https://www.cnblogs.com/#p2"); 19 } catch (Exception e) { 20 e.printStackTrace(); 21 } 22 } else if (page.getUrl().regex(URL_LIST).match() && pageNum <= 200) {//爬取2-200頁,一共有200頁 23 try { 24 List<String> urls = page.getHtml().xpath("//*[@class='post_item']//div[@class='post_item_body']/h3/a/@href").all(); 25 page.addTargetRequests(urls); 26 27 page.addTargetRequest("https://www.cnblogs.com/#p" + ++pageNum); 28 System.out.println("CurrPage:" + pageNum + "#######################################"); 29 30 } catch (Exception e) { 31 e.printStackTrace(); 32 } 33 } else { 34 // 獲取頁面需要的內容 35 System.out.println("抓取的內容:" + page.getHtml().xpath("//a[@id='cb_post_title_url']/text()").get()); 36 } 37 } 38 39 public Site getSite() { 40 return site; 41 } 42 43 public static void main(String[] args) { 44 System.setProperty("selenuim_config", "D:/config.ini");//配置文件,我用的webmagic0.7.2,低版本可能不需要該文件,但也不支持phantomjs. 45 Downloader downloader = new SeleniumDownloader();//調用seleniumdownloader,這個downlaoder可以驅動selenium,phantomjs等方式下載,由config.ini配置 46 downloader.setThread(10); 47 Spider.create(new SeleniumCnblogsSpider()).setDownloader(downloader).addUrl("https://www.cnblogs.com").thread(10).runAsync(); 48 } 49 }
另附我的config.ini和pom文件:
1 # What WebDriver to use for the tests 2 driver=phantomjs 3 #driver=firefox 4 #driver=chrome 5 #driver=http://localhost:8910 6 #driver=http://localhost:4444/wd/hub 7 8 # PhantomJS specific config (change according to your installation) 9 #phantomjs_exec_path=/Users/Bingo/bin/phantomjs-qt5 10 phantomjs_exec_path=d:/phantomjs.exe 11 #phantomjs_driver_path=/Users/Bingo/Documents/workspace/webmagic/webmagic-selenium/src/main.js 12 phantomjs_driver_loglevel=DEBUG
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 3 <modelVersion>4.0.0</modelVersion> 4 <groupId>com.summit</groupId> 5 <artifactId>WebMagicDemo</artifactId> 6 <version>0.0.1-SNAPSHOT</version> 7 8 <dependencies> 9 <dependency> 10 <groupId>us.codecraft</groupId> 11 <artifactId>webmagic-core</artifactId> 12 <version>0.7.2</version> 13 </dependency> 14 <dependency> 15 <groupId>us.codecraft</groupId> 16 <artifactId>webmagic-extension</artifactId> 17 <version>0.7.2</version> 18 </dependency> 19 <dependency> 20 <groupId>us.codecraft</groupId> 21 <artifactId>webmagic-selenium</artifactId> 22 <version>0.7.2</version> 23 </dependency> 24 <dependency> 25 <groupId>org.seleniumhq.selenium</groupId> 26 <artifactId>selenium-java</artifactId> 27 <version>2.41.0</version> 28 </dependency> 29 </dependencies> 30 </project>
如果依賴版本與此不一致,可能會出問題。
后記:
本文主要記錄了我在解決webmagic爬取動態頁面的心得。
方法1在可以獲取動態訪問地址的情況下用,比如通過調試工具,我可以找到第二頁實際的訪問地址是:https://www.cnblogs.com/mvc/AggSite/PostList.aspx,用這種方法實測效率比較高。但復雜場景下不推薦。
方法2主要是針對復雜場景,在實際地址很難找或者隱藏,網站有反扒措施的情況下通常很好用,因為它是模擬的實際的瀏覽器,比較耗費資源,效率比方法1低 。
webmagic0.7.2 支持selenium (chrome),phantomjs的模擬瀏覽器行為的方式獲取頁面。我在方法2中使用的是phantomjs下載的。selenium 的方式我也試過,但是每次調用下載就會彈出瀏覽器窗口,很是不爽,也沒找到如何解決,所以就不推薦了。
抓取結果截圖: