Daily Archives: 10/04/2020

Scrapy – Tiến trình cào dữ liệu UR2IM

Quá trình khai quật dữ liệu của bạn có thể khác nhau tùy vào từng bước nhưng nhìn chung sẽ tuân theo một tiến trình có tên UR2IM. Hình mô tả:

URL

Đầu tiên là URL, chúng ta cần phải biết chúng ta muốn cào dữ liệu ở trang web nào, chẳng hạn https://phocode.com. Rồi vô bên trong trang web đó sẽ lại có nhiều URL dẫn đến các trang con khác, chẳng hạn https://phocode.com/scrapy…v.v Và để bắt đầu quá trình cào dữ liệu trang web. Chúng ta dùng lệnh scrapy shell <url>:

$ scrapy shell https://phocode.com
...
...
[s] View(response) View response in a browser

Một số trang web sẽ không trả lời vì chúng ta gửi thiếu một header là user-agent. Để giải quyết vấn đề này thì chúng ta có thể gửi thêm thông tin về user-agent thông qua tham số -s. Ví dụ:

$ scrapy shell -s USER_AGENT="Mozilla/5.0" https://phocode.com

Chúng ta sẽ tìm hiểu thêm về user-agent sau.

Ngoài ra chúng ta có thể bật tính năng debug tương tác để dễ dàng tìm lỗi trong quá trình cào dữ liệu sau này bằng tham số --pdb như sau:

$ scrapy shell --pdb https://phocode.com

Request và Response

Sau khi đã chạy lệnh scrapy shell thì chúng ta sẽ được trao quyền chạy các lệnh Python như bình thường và có thể dùng các biến toàn cục của Scrapy.

Khi chúng ta gửi lệnh scrapy shell thì Scrapy đã thực hiện việc gửi một “request” GET đến trang web và nhận được thông tin trả lời, mã trả lời có thể là các mã HTTP quen thuộc như 200 (thành công), 301 (redirect), 404 (lỗi không tìm thấy)…. Và sau đó chúng ta có thể truy xuất tới dữ liệu trả về trong biến response của Scrapy, biến này có một trường tên là body và có kiểu là một mảng kí tự, chẳng hạn chúng ta có thể xem 50 phần tử đầu tiên trong mảng này bằng cách gọi response.body[:50]:

Và đây cũng chính là 50 kí tự đầu tiên khi bạn xem mã nguồn HTML của blog Phở Code

Item

Bây giờ chúng ta sẽ thử cào dữ liệu, và dùng trang https://phocode.com/python/scrapy/scrapy-gioi-thieu/ để thử nghiệm 🙂

$ scrapy shell https://phocode.com/python/scrapy/scrapy-gioi-thieu/

Trên trang web có rất nhiều thứ, nhưng đa phần chỉ là những thứ liên quan tới giao diện như logo, mục lục, links… Cào dữ liệu thì chúng ta quan tâm đến những thứ như… dữ liệu, chẳng hạn chúng ta có thể quan tâm tới việc lấy tiêu đề trang, ở đây là chuỗi “Scrapy – Giới thiệu”, chúng ta sẽ lấy XPath của tiêu đề. Dùng Console của Chrome, chúng ta copy được đoạn XPath như sau:

//*[@id="post-9277"]/header/h1

Bạn có thể thấy là Chrome trả về cho chúng ta XPath dùng attribute id với một id rất cụ thể là post-9277, đây là id do website tự tạo ra, nên không tốt nếu dùng nó làm XPath (xem bài trước để hiểu hơn).

Chúng ta sẽ đơn giản hóa nó một chút, thử lấy element <h1> với cú pháp //h1. Tuy nhiên làm thế cũng không tốt vì Scrapy sẽ trả về 2 element h1 là tên bài viết và tên trang web. Chúng ta có thể sửa lại là //header/h1 và đây là XPath đúng để lấy ra duy nhất tên của bài viết.

Bây giờ chúng ta chạy XPath đó với Scrapy xem được cái gì

$ response.xpath('//header/h1/text()').extract()
['Scrapy - Giới thiệu']

Chúng ta lấy được một mảng, có 1 phần tử là chuỗi mang giá trị là “Scrapy – Giới thiệu”. Bạn có thể thấy hàm text() được gọi phía sau h1, có tác dụng chỉ lấy phần nội dung của element này và extract() là để lấy giá trị của text. Nếu không có hàm text() thì chúng ta sẽ nhận được tất cả các attribute còn lại. Nếu không có hàm extract() thì chúng ta sẽ nhận được element thay vì chỉ phần text. Bạn có thể thử bỏ 1 trong 2 hoặc cả 2 để xem kết quả trả về là gì.

Nếu không có text() hay extract() thì chúng ta sẽ nhận được giá trị trả về là một đối tượng Selector từ phương thức xpath().

[<Selector xpath="//header/h1" data='...'>]

Đối tượng này chứa nội dung bên trong là đoạn code HTML của thứ mà chúng ta muốn lấy. Scrapy còn có một phương thức khác cũng có thể dùng để lấy Selector là css(). Ví dụ:

$ response.css('.entry-title')
[<Selector xpath="descendant-or-self::...">]

Và chúng ta cũng có thể dùng extract() để lấy dữ liệu cần lấy bên trong ra như khi dùng với xpath(). Và chúng ta cũng còn có thể gọi chúng nối tiếp nhau, để có thể dễ dàng lấy được dữ liệu mà chúng ta muốn. Ví dụ:

$ response.css('.entry-title').xpath('text()').extract()
['Scrapy - Giới thiệu']

Một ghi chú ở đây đó là khi chúng ta tìm element thông qua CSS thì Scrapy sẽ tự động dịch đoạn CSS đó sang code XPath luôn, chỉ là CSS thì thông thường dễ viết hơn XPath. Do đó chúng ta có thể viết css()xpath() nối tiếp nhau.

Tuy nhiên trong serie này chúng ta sẽ làm việc với XPath là chủ yếu.