Trong các bài tutorial trước, chúng ta đã viết chương trình sử dụng Jsoup, Boilerpipe, Selenium và Apache Tika để tìm kiếm và trích xuất các thông tin cần thiết. Trong bài tutorial này, chúng ta sẽ viết chương trình để giải quyết bài toán: “Tìm tất cả các bài mới đăng trên một trang Web bất kỳ” bằng cách sử dụng kết hợp Jsoup và RSS (Really Simple Syndication). RSS cho phép người dùng có thể nhanh chóng truy cập các tin tức và bài đăng mới nhất từ một nguồn tin cho trước và hầu hết các trang Web trên mạng Internet đều cung cấp tính năng này.
Ở đây, chúng ta sẽ sử dụng thư viện ROME để tiến hành download và xử lý dữ liệu RSS, do đó ta thêm dependency của ROME vào file pom.xml như sau:
1 2 3 4 5 6 |
<!-- https://mvnrepository.com/artifact/com.rometools/rome --> <dependency> <groupId>com.rometools</groupId> <artifactId>rome</artifactId> <version>1.12.0</version> </dependency> |
Thư viện ROME là một thư viện Java tương đối dễ sử dụng, không chỉ cho phép người dùng tạo ra các RSS feeds và đăng tải các RSS feeds mà còn cho phép ta đọc các RSS feeds từ một nguồn tin cho trước. Trong bài này chúng ta sẽ sử dụng ROME để tìm kiếm tất cả các bài mới đăng từ trang tin tức CNN với hàm getRSSFeed(String urlString) như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
private static void getRSSFeed(String urlString) { URL url; XmlReader reader = null; SyndFeed feed = null; try { //Using ROME library to get the RSS feeds url = new URL(urlString); reader = new XmlReader(url); feed = new SyndFeedInput().build(reader); System.out.println("Feed Title: "+ feed.getTitle()); System.out.println("Feed Date: "+ feed.getPublishedDate()); System.out.println("\nList of the latest news:\n"); List<SyndEntry> entries = feed.getEntries(); int i=0; //Loop through each entry for (SyndEntry entry : entries) { i++; //Display basic information of each post System.out.println(i+". "+entry.getTitle()); System.out.println(entry.getLink()); System.out.println(entry.getPublishedDate()); //Display the description and content if(entry.getDescription()!=null) System.out.println("Description: "+entry.getDescription().getValue()); List<SyndContent> contents = entry.getContents(); for (SyndContent content : contents) { System.out.println(content.getValue()); } System.out.println(); } } catch (Exception e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } |
Tiến hành chạy thử hàm getRSSFeed() ở trên với tham số đầu vào là đường dẫn RSS của CNN về chủ để Khoa học và Không gian ta được kết quả:
1 2 |
//Get the latest news of CNN about Science and Space getRSSFeed("http://rss.cnn.com/rss/edition_space.rss"); |
Ngoài đường dẫn RSS ở trên, CNN còn có nhiều đường dẫn RSS khác (các RSS url được tổng hợp tại địa chỉ: http://edition.cnn.com/services/rss/). Do đó để có thể cập nhật tất cả các bài mới đăng trên CNN, chúng ta sẽ sử dụng Jsoup để liệt kê tất cả các đường dẫn RSS này . Sau khi tìm được tất cả các đường dẫn RSS của CNN, chúng ta sẽ sử dụng hàm getRSSFeed() để tìm các bài mới đăng của từng đường dẫn RSS.
Việc liệt kê tất cả các RSS urls của CNN được thực hiện bởi hàm feedUrlFinder(String sourceUrl) như sau (các bạn tham khảo thêm cách sử dụng Jsoup tại bài tutorial Viết chương trình sử dụng Jsoup và Tìm kiếm trên Google với Jsoup):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
private static Map feedUrlFinder(String sourceUrl) { //Using LinkedHashMap to store the Map with insertion order Map<String,String> urlMap=new LinkedHashMap<String, String>(); Document document= null; String currentTitle = null; try { document = Jsoup.connect(sourceUrl).get(); //All RSS url is in the cnnRSS class Elements rssElements=document.getElementsByClass("cnnRSS"); for(Element element:rssElements) { //Extract the title and url String title = element.text(); String url = element.getElementsByTag("a").attr("href"); //Find the associate title and url if(!title.equals("")&&!title.equals(url)) { currentTitle=title; } if(title.equals(url)) { urlMap.put(currentTitle,url); } } } catch (IOException e) { e.printStackTrace(); } return urlMap; } |
Sử dụng hàm feedUrlFinder() ở trên, ta tìm được tất cả 20 đường dẫn RSS của CNN như sau:
1 2 3 4 5 6 7 8 9 |
//Get all RSS urls of CNN Map<String,String> feedSource=feedUrlFinder("http://edition.cnn.com/services/rss/"); int i=0; System.out.println("List of all CNN RSS URLs: "); for(Map.Entry entry:feedSource.entrySet()) { i++; System.out.println(i+", "+entry.getKey()+":"+entry.getValue()); } |
Sử dụng hàm getRSSFeed() để tìm các bài đăng mới của từng đường dẫn RSS ở trên ta được kết quả:
1 2 3 4 5 |
System.out.println("\nThe latest news of each CNN RSS URL: "); for(Map.Entry entry:feedSource.entrySet()) { getRSSFeed(entry.getValue().toString()); } |
Lưu ý:
– Phần lớn các trang web chỉ liệt kê một số lượng nhất định các bài mới post (thông thường từ 10-25 bài) trong RSS feed và ta không thể tìm được các bài post vượt quá số lượng này hoăc lấy được thông tin về các bài post cũ hơn. Một số chương trình như Feedly hoặc GoogleReader API có thể cho ta các kết quả trên là do các chương trình này đã lưu trữ dữ liệu từ trước (lấy và lưu trữ dữ liệu RSS feeds của từng ngày)
– RSS không tự động đẩy dữ liệu mới được cập nhập về phía người dùng, do đó ta phải thường xuyên truy cập địa chỉ Feed URL để kiểm tra xem có dữ liệu mới hay không (thiết lập chương trình kiểm tra 2-3 lần/giờ).
Như vậy, ta đã hoàn thành việc viết chương trình sử dụng Jsoup và RSS để tìm tất cả các bài mới đăng trên trang CNN. Các bạn có thể tham khảo full code của chương trình dưới đây.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
import com.rometools.rome.feed.synd.SyndContent; import com.rometools.rome.feed.synd.SyndEntry; import com.rometools.rome.feed.synd.SyndFeed; import com.rometools.rome.io.SyndFeedInput; import com.rometools.rome.io.XmlReader; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; import java.net.URL; import java.util.*; public class RSSProcessor { public static void main(String[] args) { //Get the latest news of CNN about Science and Space getRSSFeed("http://rss.cnn.com/rss/edition_space.rss"); //Get all RSS urls of CNN Map<String,String> feedSource=feedUrlFinder("http://edition.cnn.com/services/rss/"); int i=0; System.out.println("List of all CNN RSS URLs: "); for(Map.Entry entry:feedSource.entrySet()) { i++; System.out.println(i+", "+entry.getKey()+":"+entry.getValue()); } System.out.println("\nThe latest news of each CNN RSS URL: "); for(Map.Entry entry:feedSource.entrySet()) { getRSSFeed(entry.getValue().toString()); } } /** * Using Jsoup to find all RSS urls of CNN news * @param sourceUrl the url of CNN rss * @return List of links and their title */ private static Map feedUrlFinder(String sourceUrl) { //Using LinkedHashMap to store the Map with insertion order Map<String,String> urlMap=new LinkedHashMap<String, String>(); Document document= null; String currentTitle = null; try { document = Jsoup.connect(sourceUrl).get(); //All RSS url is in the cnnRSS class Elements rssElements=document.getElementsByClass("cnnRSS"); for(Element element:rssElements) { //Extract the title and url String title = element.text(); String url = element.getElementsByTag("a").attr("href"); //Find the associate title and url if(!title.equals("")&&!title.equals(url)) { currentTitle=title; } if(title.equals(url)) { urlMap.put(currentTitle,url); } } } catch (IOException e) { e.printStackTrace(); } return urlMap; } /** * Using ROME library to get all latest news * @param urlString The url of RSS * @return */ private static void getRSSFeed(String urlString) { URL url; XmlReader reader = null; SyndFeed feed = null; try { url = new URL(urlString); reader = new XmlReader(url); feed = new SyndFeedInput().build(reader); System.out.println("Feed Title: "+ feed.getTitle()); System.out.println("Feed Date: "+ feed.getPublishedDate()); System.out.println("\nList of the latest news:\n"); List<SyndEntry> entries = feed.getEntries(); int i=0; //Loop through each entry for (SyndEntry entry : entries) { i++; //Display basic information of each post System.out.println(i+". "+entry.getTitle()); System.out.println(entry.getLink()); System.out.println(entry.getPublishedDate()); //Display the description and content if(entry.getDescription()!=null) System.out.println("Description: "+entry.getDescription().getValue()); List<SyndContent> contents = entry.getContents(); for (SyndContent content : contents) { System.out.println(content.getValue()); } System.out.println(); } } catch (Exception e) { e.printStackTrace(); } finally { if (reader != null) { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } } } |