Xây dựng crawler siêu đơn giản với Java (Phần 2)

Tổng quan

Start coding!

1. Xác định các nội dung cần thu thập

Để lấy được nội dung trang web, chúng ta có thể sử dụng một tính năng tạo HTTP request đơn giản mà Java cung cấp là class URL.

Để tạo một request đến trang web cần lấy nội dung, chúng ta sử dụng hàm dưới đây:

private static String getContentFrom(String link) throws IOException {
        // Gởi HTTP request và nhận về kết quả là chuỗi các thẻ HTML
        URL url = new URL(link);
        Scanner scanner = new Scanner(new InputStreamReader(url.openStream()));
        scanner.useDelimiter("\\\\Z");
        String content = scanner.next();
        scanner.close();
        // xoá các ký tự ngắt dòng (xuống dòng)
        content = content.replaceAll("\\\\R", "");
        return content;
    }
</pre

Khi request thành công, các trang web thường trả về các thẻ HTML. Nếu nơi nhận là trình duyệt thì các thẻ HTML này sẽ được dựng hình (render) thành giao diện của trang web. Còn công cụ crawler của chúng ta chỉ xem đấy là các chuỗi ký tự. Việc chúng ta cần làm là tìm những vị trí chứa thông tin cần thiết bên trong chuỗi ấy. Biểu thức chính quy (regex) có thể được áp dụng trong trường hợp này.

Như vậy, chúng ta cần làm hai bước sau để lấy được thông tin trong trang web:

  • Bước 1: Xác định vị trí thông tin cần lấy trong chuỗi HTML để tìm được quy tắc đánh dấu
  • Bước 2: Dựa vào quy tắc đánh dấu trên, chúng ta xác định biểu thức chính quy phù hợp để lọc được chuỗi thông tin cần thiết

Để đơn giản hoá Bước 1 — Tìm vị trí các thông tin cần thiết trong chuỗi HTML trả về, chúng ta có thể sử dụng chức năng Inspect (trong bộ Developer Tools) của trình duyệt Google Chrome hoặc Firefox để tra đến vị trí mã nguồn thông qua giao diện trực quan.

Dưới đây là phần minh hoạ các bước trên cho việc liệt kê danh sách những danh mục tin ở trang chủ của trang web batdongsan.com.vn (Như đã trình bày Bước 1 trong mục Thiết kế tổng quan).

1.1. Hướng dẫn bước 1

  • Truy cập trang web batdongsan.com.vn trên trình duyệt
  • Mở chức năng Inspect với phím tắt Option + CMD + I (hệ điều hành MacOS) hoặc Ctrl + Shift + P (hệ điều hành Windows)
  • Di chuyển chuột đến mục Nhà đất bán > Bán căn hộ chung cư trên thanh định hướng (menu) > Bấm chuột phải > Chọn Inspect.

Rê chuột đến mục Nhà đất bán > Bán căn hộ chung cư trên thanh định hướng (menu), click chuột phải, chọn Inspect.

  • Qua nội dung hiển thị trên tab Element, chúng ta có thể thấy mã HTML của những thẻ đánh dấu mục Bán căn hộ chung cư như bên dưới:

Qua nội dung hiển thị trên tab Element, chúng ta có thể thấy mã HTML của những thẻ đánh dấu mục Bán căn hộ chung cư như trên.

Như vậy, chúng ta xác định được mã đánh dấu các đường link của các danh mục tin trên trang web này là thẻ <a> nằm trong thẻ <li> có class là lv1.

1.2. Hướng dẫn bước 2

Qua bước 1, chúng ta xác định được rằng, để lấy được đường link của danh mục con Bán căn hộ chung cư thì phải lấy thuộc tính href của thẻ <a> nằm trong <li class="lv1">.

Vậy regex có thể dùng ở đây là:

 "<li class='lv1'><a href='(.*?)' class='haslink '>"

Giá trị của thông tin mà chúng ta muốn tìm là đường link nằm giữa <li class='lv1'><a href=' và ' class='haslink '> được đại diện bằng các kí tự (.*?).

Bạn có thể kiểm tra kết quả tìm kiếm dựa trên regex trên bằng một chương trình demo nhỏ với những dòng mã sau:

public class DemoUsingURL {
    private static String getContentFrom(String link) throws IOException {
        ...
    }
    public static void main(String[] args) throws IOException {
        String content = getContentFrom("<https://batdongsan.com.vn>");
        // Regex
        Pattern p = Pattern.compile("<li class='lv1'><a href='(.*?)' class='haslink '>");
        Matcher m = p.matcher(content);
        while (m.find()) {
            System.out.println(m.group(1));
        }
    }
}

Kết quả của đoạn code trên như sau:

/ban-can-ho-chung-cu
/ban-nha-rieng
/ban-nha-biet-thu-lien-ke
/ban-nha-mat-pho
/ban-dat-nen-du-an
...
...
/phong-thuy-van-phong
/tin-tuc-phong-thuy-theo-tuoi
/nha-moi-gioi
/doanh-nghiep

Đây là những đường link con, có thể kết hợp với chuỗi https://batdongsan.com.vn để tạo ra đường link dẫn tới nội dung các danh mục tin được rao.

Ở kết quả trên, chúng ta thấy có một số đường link không phù hợp. Vì đó là những đường link mà chúng ta không muốn thu thập nội dung. Ví dụ: /phong-thuy-van-phong/tin-tuc-phong-thuy-theo-tuoi/nha-moi-gioi,… Chúng ta sẽ tìm cách loại bỏ những kết quả không mong đợi này.

Xem xét lại mã HTML của trang web, chúng ta sẽ phát hiện ra các thẻ <li class='lv1'> chứa các thông tin cần thiết này nằm trong 2 nội dung sau:

Nhà đất bán

<li class='lv0'><a href="/nha-dat-ban" class="haslink ">Nhà đất bán</a>
...nội dung các danh mục con của mục Nhà đất bán ở đây
</li>

Nhà đất cho thuê

<li class='lv0'><a href="/nha-dat-cho-thue" class="haslink ">Nhà đất cho thuê</a>
...nội dung các danh mục con của mục Nhà đất cho thuê ở đây
</li>

Đoạn code trên cần sửa lại như dưới đây để loại ra những link không cần thiết:

public class DemoUsingURL {
    private static String getContentFrom(String link) throws IOException {
        ...
    }
    private static List<String> getLinksFromMenu(String content, String menuPattern) {
        // Regex
        List<String> links = new ArrayList<>();
        Pattern p = Pattern.compile(menuPattern);
        Matcher m = p.matcher(content);
        while (m.find()) {
            Pattern p2 = Pattern.compile("<li class='lv1'><a href='(.*?)' class='haslink '>");
            Matcher m2 = p2.matcher(m.group(1));
            while (m2.find()) links.add(m2.group(1));
        }
        return links;
    }
    public static void main(String[] args) throws IOException {
        String content = getContentFrom("<https://batdongsan.com.vn>");
        String sellMenuPattern = "<li class='lv0'><a href='/nha-dat-ban' class='haslink '>Nhà đất bán</a><ul>(.*?)</ul>";
        List<String> sellLinks = getLinksFromMenu(content, sellMenuPattern);
        String rentalMenuPattern = "<li class='lv0'><a href='/nha-dat-cho-thue' class='haslink '>Nhà đất cho thuê</a><ul>(.*?)</ul>";
        List<String> rentalLinks = getLinksFromMenu(content, rentalMenuPattern);
        System.out.println(sellLinks);
        System.out.println(rentalLinks);
    }
}

Ở đoạn code trên, mình tách phần lấy nội dung từ đường dẫn trang web thành hàm getContentFrom, và một hàm tách link từ nội dung có tên là getLinksFromMenu. Hàm main sử dụng hai hàm được khai báo ở trên để lấy các đường link nằm trong mục Nhà đất bán và Nhà đất cho thuê.

Tham khảo khóa học lập trình web 6 tháng, đảm bảo 100% công việc đầu ra!

Nguồn: https://topdev.vn/blog/xay-dung-crawler-sieu-don-gian-voi-java/


Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

Leave a Reply

Your email address will not be published. Required fields are marked *